mirror of
https://github.com/stablyai/orca.git
synced 2026-10-08 16:02:37 +00:00
* fix(pty): deliver multiline agent-launch prompts via bracketed paste Multiline agent-launch prompts (claude/codex/opencode argv injection) were mangled when Orca typed the startup command into the interactive shell. The command is single-quoted, but its literal embedded newlines survive quoting; bash readline / zsh zle read every raw LF as accept-line (Enter), so the first newline submits an unterminated single-quoted command, drops the shell into PS2 (>) continuation, and the rest executes piecemeal — backticks/$ evaluate, quotes go unbalanced, and the agent never receives the intact prompt. Short single-line prompts worked because they have no embedded newline. Fix: when a startup command contains a newline, wrap the payload in bracketed-paste markers (ESC[200~ … ESC[201~) before the trailing submit CR/LF so the line editor inserts the multiline text literally and only the trailing byte submits it. The single-line fast path is unchanged. Gated on the target line editor having bracketed-paste mode active (Orca-wrapped bash/zsh) so shells without it never echo the markers as garbage; Orca's bash rc wrappers now force `enable-bracketed-paste on` (zsh has it on by default). Applied consistently across every startup-command delivery path: - src/main/providers/local-pty-shell-ready.ts (in-process / degraded local) - src/main/daemon/terminal-host.ts (daemon host — primary local) - src/relay/pty-handler.ts (SSH relay, remote host) - src/renderer/src/lib/ssh-background-startup-delivery.ts (hidden SSH tab) All share src/shared/startup-command-submission.ts. Windows cmd.exe and other shells keep the current CR submit path (no regression); PSReadLine/POSIX bash/zsh get the fix. * Fix multiline detection for CRLF-terminated startup commands Strip the entire CRLF terminator (or lone CR/LF) from startup commands before checking if the body contains newline characters. Previously, slicing off only the last character of a CRLF-terminated command left a trailing CR in the body. This caused a single-line command to be incorrectly categorized as multiline and wrapped in bracketed paste.
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { buildStartupCommandSubmission } from './startup-command-submission'
|
|
|
|
describe('buildStartupCommandSubmission', () => {
|
|
it('appends the submit byte to a single-line command unchanged', () => {
|
|
expect(
|
|
buildStartupCommandSubmission('claude', { submit: '\n', bracketedPasteSafe: true })
|
|
).toBe('claude\n')
|
|
expect(
|
|
buildStartupCommandSubmission('claude', { submit: '\r', bracketedPasteSafe: true })
|
|
).toBe('claude\r')
|
|
})
|
|
|
|
it('preserves a caller-supplied trailing submit byte on single-line commands', () => {
|
|
expect(
|
|
buildStartupCommandSubmission('claude\n', { submit: '\r', bracketedPasteSafe: true })
|
|
).toBe('claude\n')
|
|
})
|
|
|
|
it('treats a CRLF-terminated single-line command as single-line', () => {
|
|
expect(
|
|
buildStartupCommandSubmission('claude\r\n', { submit: '\r', bracketedPasteSafe: true })
|
|
).toBe('claude\r\n')
|
|
})
|
|
|
|
it('wraps a multiline command in bracketed paste with a trailing submit byte', () => {
|
|
const command = "claude 'first\nsecond'"
|
|
expect(buildStartupCommandSubmission(command, { submit: '\n', bracketedPasteSafe: true })).toBe(
|
|
`\x1b[200~${command}\x1b[201~\n`
|
|
)
|
|
})
|
|
|
|
it('strips a trailing submit byte before bracket-wrapping the multiline body', () => {
|
|
const body = "claude 'first\nsecond'"
|
|
expect(
|
|
buildStartupCommandSubmission(`${body}\n`, { submit: '\r', bracketedPasteSafe: true })
|
|
).toBe(`\x1b[200~${body}\x1b[201~\r`)
|
|
})
|
|
|
|
it('keeps the raw path for multiline commands when bracketed paste is unsafe', () => {
|
|
const command = 'echo one\necho two'
|
|
expect(
|
|
buildStartupCommandSubmission(command, { submit: '\n', bracketedPasteSafe: false })
|
|
).toBe(`${command}\n`)
|
|
})
|
|
})
|