mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-26 17:38:36 +00:00
The hoisted shared eslint config catches pre-existing no-useless-escape and no-control-regex errors in apps/desktop that were only fixed in web/ in the previous commit. - no-useless-escape: remove unnecessary \) escapes inside character classes - no-control-regex: add eslint-disable-next-line comments for intentional \x1b terminal escape byte matching (same pattern as web/src/lib/pty-mobile-input.ts)
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
/**
|
|
* Strip terminal bracketed-paste leaks and repeated artifact tails from composer
|
|
* text before it is shown in the UI or sent to the gateway.
|
|
*
|
|
* Mirrors hermes_cli/input_sanitize.py (CLI/TUI gateway defensive path).
|
|
*/
|
|
|
|
const BRACKETED_PASTE_BOUNDARY_START = /(^|[\s\n>:\])])\[200~/g
|
|
const BRACKETED_PASTE_BOUNDARY_END = /\[201~(?=$|[\s\n<[():;.,!?])/g
|
|
const BRACKETED_PASTE_DEGRADED_START = /(^|[\s\n>:\])])00~/g
|
|
const BRACKETED_PASTE_DEGRADED_END = /01~(?=$|[\s\n<[():;.,!?])/g
|
|
|
|
const DESKTOP_PASTE_ARTIFACT = '~[[e'
|
|
|
|
/** Strip leaked bracketed-paste wrapper markers from user-visible text. */
|
|
export function stripLeakedBracketedPasteWrappers(text: string): string {
|
|
if (!text) {
|
|
return text
|
|
}
|
|
|
|
let cleaned = text
|
|
// eslint-disable-next-line no-control-regex -- terminal data may contain control chars
|
|
.replace(/\x1b\[200~/g, '')
|
|
// eslint-disable-next-line no-control-regex -- terminal data may contain control chars
|
|
.replace(/\x1b\[201~/g, '')
|
|
.replace(/\^\[\[200~/g, '')
|
|
.replace(/\^\[\[201~/g, '')
|
|
|
|
cleaned = cleaned.replace(BRACKETED_PASTE_BOUNDARY_START, '$1')
|
|
cleaned = cleaned.replace(BRACKETED_PASTE_BOUNDARY_END, '')
|
|
cleaned = cleaned.replace(BRACKETED_PASTE_DEGRADED_START, '$1')
|
|
cleaned = cleaned.replace(BRACKETED_PASTE_DEGRADED_END, '')
|
|
|
|
return cleaned
|
|
}
|
|
|
|
/** Drop a trailing run of the desktop ~[[e corruption signature (#62557). */
|
|
export function collapseRepeatedInputArtifacts(text: string, minRepeats = 4): string {
|
|
if (!text) {
|
|
return text
|
|
}
|
|
|
|
const marker = DESKTOP_PASTE_ARTIFACT
|
|
let index = text.length
|
|
let repeatCount = 0
|
|
|
|
while (index >= marker.length && text.slice(index - marker.length, index) === marker) {
|
|
repeatCount += 1
|
|
index -= marker.length
|
|
}
|
|
|
|
if (repeatCount < minRepeats) {
|
|
return text
|
|
}
|
|
|
|
let start = index
|
|
|
|
if (start >= 2 && text.slice(start - 2, start) === '[e') {
|
|
start -= 2
|
|
} else if (start >= 1 && text[start - 1] === '[') {
|
|
start -= 1
|
|
}
|
|
|
|
return text.slice(0, start)
|
|
}
|
|
|
|
/** Normalize composer text before submit or draft persistence. */
|
|
export function sanitizeComposerInput(text: string): string {
|
|
if (!text) {
|
|
return text
|
|
}
|
|
|
|
return collapseRepeatedInputArtifacts(stripLeakedBracketedPasteWrappers(text))
|
|
}
|