mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 00:04:01 +00:00
fix(launch): type a plain Orca agent line as is in tcsh and nu
In tcsh and nu, every Orca-built agent line ran through `/bin/sh '<script>'`, so the agent was a child of sh in sh's process group. When the prompt was pasted after the agent was ready, the paste guard read sh in front and, on a loaded Mac, reported the prompt not delivered (4/4 live runs in tcsh). Now only a line that needs it goes through `/bin/sh`: one over 512 bytes, with a control byte, or with a character those shells would not read literally (`!`, backslash, `"`, `$`, backtick). A plain line such as `claude '--dangerously-skip-permissions'` is typed as on main, so the agent is the shell's own job.
This commit is contained in:
@@ -146,6 +146,38 @@ describe('stageStartupCommand', () => {
|
||||
}
|
||||
)
|
||||
|
||||
// Why: behind `/bin/sh` the agent is not the shell's own job, and a pasted prompt waits on that.
|
||||
it.each([['/bin/tcsh'], ['/usr/bin/nu'], [undefined]])(
|
||||
'types a short plain Orca-built line as is for a shell that cannot source it (%s)',
|
||||
(shellPath) => {
|
||||
const command = `claude '--dangerously-skip-permissions' '--model' 'claude-opus-5-5[1m]'`
|
||||
expect(
|
||||
stageStartupCommand({
|
||||
command,
|
||||
shellPath,
|
||||
orcaBuiltLine: true,
|
||||
platform: 'darwin',
|
||||
directory
|
||||
})
|
||||
).toEqual({ command, delivery: 'typed' })
|
||||
expect(readdirSync(directory)).toEqual([])
|
||||
}
|
||||
)
|
||||
|
||||
it.each([[`claude 'it'\\''s'`], ['claude "x"'], ["claude '$HOME'"], ["claude 'a`b'"]])(
|
||||
'stages a short Orca-built line tcsh would not read literally: %s',
|
||||
(command) => {
|
||||
const staged = stageStartupCommand({
|
||||
command,
|
||||
shellPath: '/bin/tcsh',
|
||||
orcaBuiltLine: true,
|
||||
platform: 'darwin',
|
||||
directory
|
||||
})
|
||||
expect(staged.command).toBe(`/bin/sh '${staged.scriptPath}'`)
|
||||
}
|
||||
)
|
||||
|
||||
it('types a short Orca-built line in a shell that reads its quoting literally', () => {
|
||||
const command = `claude 'wow!! great'`
|
||||
expect(
|
||||
|
||||
@@ -32,9 +32,9 @@ export type StartupCommandStaging = {
|
||||
failure?: string
|
||||
}
|
||||
|
||||
// Why only these type a short line as is: Orca's portable quoting is verified literal in these. Any
|
||||
// other shell (tcsh, nu, xonsh, pwsh...) runs an agent launch line Orca built through `/bin/sh`
|
||||
// instead; a command the user wrote for that shell is typed as it always was.
|
||||
// Why only these type any short line as is: Orca's portable quoting is verified literal in these. Any
|
||||
// other shell (tcsh, nu, xonsh, pwsh...) runs an agent launch line Orca built through `/bin/sh` when
|
||||
// it holds anything but plain characters; a command the user wrote for that shell is typed as it was.
|
||||
const STAGING_SHELLS = new Set(['bash', 'zsh', 'sh', 'dash', 'fish', 'ksh', 'mksh'])
|
||||
|
||||
// Why not ksh: it runs a sourced file's commands in the shell's own process group, so Ctrl-Z could
|
||||
@@ -63,17 +63,15 @@ export function shouldStageStartupCommand(args: {
|
||||
return false
|
||||
}
|
||||
const shellName = stagingShellName(args.shellPath)
|
||||
if (shellName === null) {
|
||||
// Why every length: Orca's POSIX quoting is literal only in the shells above (tcsh doubles a
|
||||
// quoted backslash and expands `!!`), so another shell only ever sees the script's inert path.
|
||||
return args.orcaBuiltLine === true
|
||||
}
|
||||
const body = stripSubmitTerminator(args.command)
|
||||
return (
|
||||
(hasControlByte(body) ||
|
||||
encoder.encode(body).byteLength > TYPED_STARTUP_LINE_PROMPT_BUDGET_BYTES) &&
|
||||
(SOURCING_SHELLS.has(shellName) || args.orcaBuiltLine === true)
|
||||
)
|
||||
const needsStaging =
|
||||
hasControlByte(body) || encoder.encode(body).byteLength > TYPED_STARTUP_LINE_PROMPT_BUDGET_BYTES
|
||||
if (shellName === null) {
|
||||
// Why these characters: tcsh doubles a quoted backslash and expands `!!`, and nu cannot read
|
||||
// `'\''`. A plain line is typed so the agent is the shell's own job, where the paste guard finds it.
|
||||
return args.orcaBuiltLine === true && (needsStaging || /[!\\"`$]/.test(body))
|
||||
}
|
||||
return needsStaging && (SOURCING_SHELLS.has(shellName) || args.orcaBuiltLine === true)
|
||||
}
|
||||
|
||||
function stagedScriptLine(shellName: string | null, quotedPath: string): string {
|
||||
|
||||
Reference in New Issue
Block a user