Orca had to guess which shell would parse a queued command line, then emit
syntax for it. Guessing is unreliable for a remote or WSL host, and every
dialect-dependent function is a place to get it wrong.
Replace the guess. Everything emitted for a Unix shell is now built to be
correct in sh, bash, zsh, dash, ksh and fish alike, so no detection is needed:
- quoteStartupArg emits backslashes as "\\" and apostrophes as "'" between
single-quoted runs. Both families read that identically, unlike the sh '\''
idiom, which fish silently halves and which makes a trailing backslash a
hard syntax error.
- clearEnvCommand emits a self-contained fish/sh branch. It deliberately does
NOT call a helper defined by Orca's shell wrappers: Orca wraps only zsh, bash
and fish, so an `sh`/`dash`/`ksh` login shell launches unwrapped — and the
same text is copied to the clipboard and pasted into shells Orca never
spawned. In both, a helper would be `command not found`, which is the exact
failure this exists to avoid. Two guarded statements rather than `A && B ||
C`, because fish's `set -e` returns non-zero for an already-unset variable
and would fall through to the sh branch; a trailing `true` pins the status,
since this is the last statement of a launch line and the prompt renders it.
- One tokenizer for Unix. The input is a settings string the shell never
parses, so parsing it per-shell only made the same setting mean different
things in different workspaces.
AgentStartupShell loses its 'fish' and 'unix' members, and the three
login-shell resolvers, the fish tokenizer and the agentEnv.SHELL probe go with
them.
Per-worktree shell history now actually works:
- zsh on macOS was a no-op. /etc/zshrc assigns HISTFILE unconditionally before
any wrapper Orca controls, so the injected value was already gone — and with
ZDOTDIR still pointing at Orca's wrapper dir, history landed inside it. The
intended path rides ORCA_HISTFILE and is restored after user config.
Fixes#11044.
- fish keeps history in its own data dir keyed by session name, since it
ignores HISTFILE and has no custom-directory knob. Files are deleted rather
than truncated, a symlinked ~/.local/share no longer disables cleanup, and a
GC sweep reclaims orphans whose meta.json is gone. The sweep refuses an empty
live-worktree set (indistinguishable from a store that failed to hydrate) and
skips files younger than GC_MIN_AGE_MS, mirroring the tree GC's guard against
the live-set snapshot race.
Verified against real shells rather than asserted as strings:
startup-shell-portability.live-shell.test.ts runs 194 assertions across
sh/bash/zsh/dash/ksh/fish, and zsh-scoped-histfile.live-shell.test.ts drives a
real login zsh through /etc/zshrc. Both are vacuity-checked. The same quoting
corpus was replayed byte-exact on Linux, where /bin/sh is dash.