mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(desktop-terminal): trim trailing idle prompt on no-separator shells
cleanReviveSnapshot only dropped the trailing prompt when a blank separator sat above it (starship add_newline), so shells that print the prompt with no preceding blank line — default PowerShell (PS C:\..>), bash user@host:~$ — kept the idle prompt in the saved buffer and showed a duplicate under the fresh boot prompt on every relaunch of an *active* session. An interactive shell always reprints its prompt after a command, so the tail of an idle buffer is the prompt, never history. Drop the short block after a blank separator when present, otherwise drop the trailing single-line prompt. Command output is preserved; the fresh shell reprints the live prompt on boot.
This commit is contained in:
parent
164d9126cc
commit
817969f5a2
2 changed files with 45 additions and 28 deletions
|
|
@ -33,25 +33,40 @@ describe('isIdlePromptOnly', () => {
|
|||
})
|
||||
|
||||
describe('cleanReviveSnapshot', () => {
|
||||
it('drops a short trailing prompt block after a blank separator', () => {
|
||||
it('drops a spaced trailing prompt block after a blank separator (starship)', () => {
|
||||
const snapshot = ['echo hi', 'hi', '', PS_PROMPT].join('\r\n')
|
||||
|
||||
expect(cleanReviveSnapshot(snapshot)).toBe('echo hi\r\nhi')
|
||||
})
|
||||
|
||||
it('keeps real output when the tail after the blank line is long', () => {
|
||||
const tail = ['line1', 'line2', 'line3', 'line4', 'line5']
|
||||
const snapshot = ['start', '', ...tail].join('\r\n')
|
||||
it('drops a multi-line prompt block after a blank separator (powerline)', () => {
|
||||
const snapshot = ['work', '', '┌─ user@host ~/project', '└─$'].join('\r\n')
|
||||
|
||||
expect(cleanReviveSnapshot(snapshot)).toBe(['start', '', ...tail].join('\r\n'))
|
||||
expect(cleanReviveSnapshot(snapshot)).toBe('work')
|
||||
})
|
||||
|
||||
it('leaves a prompt with no preceding blank line untouched (heuristic limitation)', () => {
|
||||
// Default PowerShell prints no blank line before its prompt, so the trailing
|
||||
// prompt survives here — the idle-accumulation path (isIdlePromptOnly) is what
|
||||
// actually covers that shell, not this trimmer.
|
||||
it('drops a single-line trailing prompt with no preceding blank line (PowerShell)', () => {
|
||||
// Default PowerShell prints no blank line before its prompt; the fresh shell
|
||||
// reprints it on boot, so the redundant idle prompt must be trimmed here.
|
||||
const snapshot = ['echo hi', 'hi', PS_PROMPT].join('\r\n')
|
||||
|
||||
expect(cleanReviveSnapshot(snapshot)).toBe(snapshot)
|
||||
expect(cleanReviveSnapshot(snapshot)).toBe('echo hi\r\nhi')
|
||||
})
|
||||
|
||||
it('keeps command output and drops only the trailing prompt on a long history', () => {
|
||||
const history = ['cmd1', 'out1', 'cmd2', 'out2']
|
||||
const snapshot = [...history, PS_PROMPT].join('\r\n')
|
||||
|
||||
expect(cleanReviveSnapshot(snapshot)).toBe(history.join('\r\n'))
|
||||
})
|
||||
|
||||
it('reduces a lone prompt to an empty buffer', () => {
|
||||
expect(cleanReviveSnapshot(PS_PROMPT)).toBe('')
|
||||
expect(cleanReviveSnapshot([PS_PROMPT, '', ''].join('\r\n'))).toBe('')
|
||||
})
|
||||
|
||||
it('returns empty for a blank-only buffer without throwing', () => {
|
||||
expect(cleanReviveSnapshot('')).toBe('')
|
||||
expect(cleanReviveSnapshot('\r\n \r\n')).toBe('')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -174,32 +174,34 @@ const visibleText = (line: string) => stripEscapeSequences(line).replace(/[\s%]/
|
|||
|
||||
// Trim the shell's trailing idle prompt from a serialized snapshot before it's
|
||||
// persisted. Without it, the saved buffer ends in the old prompt, so the next
|
||||
// launch replays it directly above the fresh shell's prompt ("double bar"). The
|
||||
// prompt is the short block after the last blank line (starship's add_newline
|
||||
// gap); only a short tail is dropped, so real command output is never trimmed and
|
||||
// configs without that blank line simply keep the historical prompt (no loss).
|
||||
// launch replays it directly above the fresh shell's prompt ("double bar").
|
||||
//
|
||||
// An interactive shell always reprints its prompt after a command finishes, so
|
||||
// the tail of an idle buffer is the prompt, never real history. Two prompt
|
||||
// shapes exist:
|
||||
// - Spaced/multi-line (starship add_newline, powerline): a blank line sits
|
||||
// just above the prompt, so the short block after the last blank is dropped.
|
||||
// - Single-line (default PowerShell `PS C:\..>`, bash `user@host:~$`): no blank
|
||||
// separator, so the final line itself is the prompt and is dropped.
|
||||
// The fresh shell reprints the current prompt on boot either way, so only the
|
||||
// redundant idle prompt is removed — command output is preserved.
|
||||
export function cleanReviveSnapshot(serialized: string): string {
|
||||
const visible = (line: string) => stripEscapeSequences(line).replace(/[\s%]/g, '')
|
||||
const lines = serialized.split(/\r?\n/)
|
||||
|
||||
while (lines.length && visible(lines[lines.length - 1]) === '') {
|
||||
while (lines.length && !visibleText(lines[lines.length - 1])) {
|
||||
lines.pop()
|
||||
}
|
||||
|
||||
let lastBlank = -1
|
||||
|
||||
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
||||
if (visible(lines[i]) === '') {
|
||||
lastBlank = i
|
||||
|
||||
break
|
||||
}
|
||||
if (lines.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// A prompt is a short block; a long tail after the blank is real output, leave it.
|
||||
if (lastBlank >= 0 && lines.length - 1 - lastBlank <= 3) {
|
||||
lines.length = lastBlank
|
||||
}
|
||||
const lastBlank = lines.findLastIndex(line => !visibleText(line))
|
||||
const spacedPrompt = lastBlank >= 0 && lines.length - 1 - lastBlank <= 3
|
||||
|
||||
// Spaced prompt (starship/powerline): drop the block after the blank
|
||||
// separator. Otherwise the last line is the single-line prompt itself.
|
||||
lines.length = spacedPrompt ? lastBlank : lines.length - 1
|
||||
|
||||
return lines.join('\r\n')
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue