mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
git mv the 12 packages from session-persistence/, session-projection/, session-title/, and telemetry/ into one session/ group per the regrouping RFC; merge the four group READMEs into one bilingual triplet; rewrite the group segment in tsconfig references (intra-group references shorten to ../<pkg>), tsconfig.base.json paths/globs, knip.json keys, vitest include, gate scripts, and authored doc/note citations; regenerate module graph, doc graphs, catalogs, and the lockfile importer keys. No npm names change. Full unit suite: 8779 passed; the 18 reported failures reproduce as env flakes (ambient-proxy IPv6 tunneling, watched-dir inotify timeouts under parallel load) — each passes in isolation with NO_PROXY set, matching their known pre-existing behavior on master.
75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
/** Title text normalization and UTF-8-safe truncation. */
|
|
|
|
/** Operating-system-command escape sequences, including unterminated tails. */
|
|
const OSC_SEQUENCE = /(?:\u001B\]|\u009D)(?:(?!\u0007|\u001B\\)[\s\S])*(?:\u0007|\u001B\\|$)/gu
|
|
/** Control-sequence-introducer escapes such as SGR color codes. */
|
|
const CSI_SEQUENCE = /(?:\u001B\[|\u009B)[0-?]*[ -/]*[@-~]/gu
|
|
/** Remaining two-byte ESC control sequences. */
|
|
const ESC_SEQUENCE = /\u001B[@-_]/gu
|
|
/** Non-whitespace C0/C1 control characters. */
|
|
const CONTROL_CHARACTER = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/gu
|
|
/** Directional and invisible controls that can make a displayed title deceptive. */
|
|
const DIRECTIONAL_CONTROL = /[\u200B\u200E\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF]/gu
|
|
|
|
/** Reject an invalid public text limit. */
|
|
function assertPositiveInteger(name: string, value: number): void {
|
|
if (!Number.isInteger(value) || value <= 0) {
|
|
throw new Error(`${name} must be a positive integer`)
|
|
}
|
|
}
|
|
|
|
/** Remove controls and produce one trimmed, whitespace-normalized line. */
|
|
function cleanTitleText(input: string): string {
|
|
return input
|
|
.replace(OSC_SEQUENCE, '')
|
|
.replace(CSI_SEQUENCE, '')
|
|
.replace(ESC_SEQUENCE, '')
|
|
.replace(CONTROL_CHARACTER, '')
|
|
.replace(DIRECTIONAL_CONTROL, '')
|
|
.replace(/\s+/gu, ' ')
|
|
.trim()
|
|
}
|
|
|
|
/**
|
|
* Truncate a string to a UTF-8 byte budget without splitting a Unicode code point.
|
|
* @param input - normalized title text.
|
|
* @param maxBytes - positive UTF-8 byte budget.
|
|
* @returns the longest leading code-point prefix within the budget.
|
|
*/
|
|
export function truncateTitleUtf8(input: string, maxBytes: number): string {
|
|
assertPositiveInteger('maxBytes', maxBytes)
|
|
if (Buffer.byteLength(input, 'utf8') <= maxBytes) return input
|
|
let used = 0
|
|
let output = ''
|
|
for (const character of input) {
|
|
const bytes = Buffer.byteLength(character, 'utf8')
|
|
if (used + bytes > maxBytes) break
|
|
output += character
|
|
used += bytes
|
|
}
|
|
return output
|
|
}
|
|
|
|
/**
|
|
* Normalize one accepted session title and enforce its UTF-8 byte budget.
|
|
* @param input - untrusted title text.
|
|
* @param maxBytes - positive maximum encoded size.
|
|
* @returns a terminal-safe one-line title, possibly empty after sanitization.
|
|
*/
|
|
export function normalizeSessionTitle(input: string, maxBytes: number): string {
|
|
return truncateTitleUtf8(cleanTitleText(input), maxBytes).trimEnd()
|
|
}
|
|
|
|
/**
|
|
* Derive the deterministic first-message fallback.
|
|
* @param input - text from the first eligible human message.
|
|
* @param maxWords - positive whitespace-delimited word cap.
|
|
* @param maxBytes - positive UTF-8 byte cap.
|
|
* @returns the normalized leading words within both limits.
|
|
*/
|
|
export function fallbackSessionTitle(input: string, maxWords: number, maxBytes: number): string {
|
|
assertPositiveInteger('maxWords', maxWords)
|
|
const words = cleanTitleText(input).split(' ').filter(Boolean).slice(0, maxWords)
|
|
return truncateTitleUtf8(words.join(' '), maxBytes).trimEnd()
|
|
}
|