Skip to content

fix(pty): give fish login shells the Prime Agent status wrapper - #13875

Closed
brennanb2025 wants to merge 5 commits into
mainfrom
brennanb2025/sta-3927-fish-prime-agent-wrapper
Closed

brennanb2025 wants to merge 5 commits into
mainfrom
brennanb2025/sta-3927-fish-prime-agent-wrapper

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A user whose login shell is fish — on WSL, macOS, or Linux — launched Prime Agent normally but lost Orca's Prime working/status reporting. Fish is not POSIX, so getPosixPrimeAgentShellWrapper() ([[ ]], local, case/esac, shift 2) can never be sourced by it. Fish started cleanly and handed prime-agent the user's bare argv; the --extension injection simply never happened. Fixes STA-3927, filed against merged #13430.

What changed

  • src/main/pty/prime-agent-fish-shell-wrapper.ts — new. Fish twin of the POSIX wrapper: function … end, $argv / $argv[1], set -l, set -lx for the function-scoped ORCA_AGENT_HOOK_ENDPOINT export, switch/case with a quoted "--extension=*" (an unmatched glob is a hard error in fish), set -e argv[1..2] in place of shift 2, and string trim -r -c / for the HOME trailing-slash trim.
  • Delivered as a file — shell-ready/fish/orca.fish, alongside the existing bash/rcfile and zsh/ wrappers — not inlined into the launch argv. Inlining would have to survive escapeWslShCommandForWindows(), which backslash-escapes every $.
  • src/main/shell-templates.ts — getFishShellReadyWrapperFileContent() plus getFishInitCommand(), which folds the wrapper source and the existing shell-ready marker into one -C string. Multi--C support varies by fish version, so one combined init command is the safe shape.
  • Wired in at all three sites: src/main/providers/local-pty-shell-ready.ts, src/main/daemon/shell-ready.ts, and a new fish) arm in buildWslInteractiveLoginShellCommand() (src/shared/wsl-login-shell-command.ts). The fish arm is guarded by [ -f … ], so a failed wrapper write still falls through to the shared bare exec "$_orca_wsl_shell" -l rather than erroring on source.
  • Fish is now wrapped on the attribution (markerless) path too, matching bash and zsh. It previously returned args: null there, which left a plain fish pane — exactly where a user types prime-agent by hand — unwrapped. The marker itself stays gated on ORCA_SHELL_READY_MARKER inside the init text, so a markerless pane still emits nothing.

Every semantic of the bash wrapper is preserved and tested: typed launches, explicit launches, bare attach vs attach <agent>, the management-subcommand skip list, both --daemon-socket forms before stop/rename, explicit --extension / --extension= passthrough, and the degrade-to-original-argv path when the managed extension file is missing.

The existing fish shell-ready marker behavior is unchanged — it still rides --on-event fish_prompt and still fires exactly once.

Deliberately out of scope (one thing per PR); follow-ups filed:

  • STA-3936 — the identical fish gap in the OMP wrapper (getPosixOmpShellWrapper)
  • STA-3937 — fish gaps for CODEX_HOME, OPENCODE_CONFIG_DIR, MIMOCODE_HOME, and the attribution / agent-teams shim PATHs
  • STA-3938 — buildWslLoginShellCommand() routing fish to /bin/sh -lc for non-interactive detection probes
  • STA-3939 — ORCA_PRIME_AGENT_STATUS_EXTENSION is missing from the needsNoMarkerWrapper env list (affects bash and zsh too), found while working this fix

Screenshots

No visual change.

Testing

  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • pnpm build — not run. No build-surface change: this PR touches only main-process TypeScript and the shell text it generates, all of which pnpm typecheck covers. No packaging, native module, or asset change.
  • Added or updated high-quality tests that would catch regressions
File What it covers
src/main/pty/prime-agent-fish-shell-wrapper.test.ts (new, 33 cases) Mirrors prime-agent-shell-wrapper.test.ts against real fish — the whole argv matrix, plus fish-specific hazards: set -e argv[1..2] when attach has no trailing args, multi-word operands surviving a bare $argv, and the HOME trailing-slash trim
src/main/pty/prime-agent-shell-wrapper-parity.test.ts (new) Runs the product launch configs for bash and fish side by side through one fake prime-agent and asserts identical argv capture
src/main/daemon/shell-ready-fish.node-pty.test.ts (new) Real fish under node-pty. Holds the two PTY-level fish cases (the pre-existing marker test moved here unchanged, plus a new one proving prime-agent is wrapped in a real login shell). Extracted because shell-ready.test.ts would otherwise cross the max-lines ceiling — no suppression was added
src/main/providers/wsl-fish-login-shell.test.ts (new) Executes the generated guest login script (sh -c "<script>") driving a real fish, with a shadowed getent pinning the login shell. Covers the wrapped case, the missing-wrapper fallback, and a /bin/sh control proving the branch is actually being steered
src/main/providers/windows-shell-args.test.ts, src/main/daemon/shell-ready.test.ts, src/main/providers/local-pty-shell-ready.test.ts, src/shared/wsl-login-shell-command.test.ts Wrapper file materialization, the single combined -C, the markerless attribution config, rewrite-on-missing, and the WSL fish arm including its escapeWslShCommandForWindows form
.github/workflows/pr.yml All four new suites added to the one CI job that installs fish, and to the shard --exclude list. Without this they gate off a missing binary and skip silently — see AI Review below

Every oracle asserts through filesystem markers (a fake prime-agent that writes its argv to a capture file), never by scraping the terminal buffer.

RED→GREEN was verified by reverting the fix in place: the parity oracle's fish capture collapses to BEGIN\nARG=ask\nEND\n — exactly the signature an independent repro recorded at HEAD — and the guest-script execution test fails with the same shape. Both go green with the fix.

Commands run:

pnpm exec vitest run --config config/vitest.config.ts \
  src/main/pty/ src/main/daemon/ src/main/providers/ \
  src/shared/wsl-login-shell-command.test.ts
pnpm typecheck
pnpm lint
pnpm test

The WSL guest-script suite was additionally run against fish 3.6 in a Linux container, not just the local fish 4.8 — the first CI run failed there on a macOS-only assumption in the harness (see the PR comment) and this is how it was reproduced and confirmed fixed.

Note for anyone reproducing locally: src/main/ipc/pty.test.ts fails 10 tests when the suite is run from inside an Orca terminal — the ambient ZDOTDIR / ORCA_* env leaks into its spawn-env assertions. It fails the same 10 at origin/main, and passes clean under env -i. Unrelated to this PR.

AI Review Report

Ran /code-review at high effort over the full diff, plus a self-audit. The review built both wrappers to a scratch dir and ran a bash-vs-fish argv differential across ~20 cases (no args, --help/-h/--version/-v, glob/empty/newline/$-bearing operands, attach with and without operands, both --daemon-socket forms, explicit --extension/--extension=, HOME with trailing slashes and spaces, the WSL endpoint export). The fish wrapper came back byte-for-byte argv-identical to the bash wrapper in every case, including the set -lx export reaching the child through the nested launch function. No correctness defect was found in the generated fish code.

It raised three issues, all fixed in this PR:

  1. The regression tests would never have run in CI (high). Only the shell contracts job installs fish; it enumerates its test files explicitly. All the new fish suites landed in the sharded test job, which has no fish, so hasFish was false and every one skipped silently — and moving the live-fish PTY test out of shell-ready.test.ts had also removed the repo's only executed fish coverage. Fixed by adding all four files to the shell-contracts run list and the shard --exclude list, with a comment on the job saying any new live-fish test must be added to both.
  2. The fix only covered fish launches carrying a startup command (medium). getAttributionShellLaunchConfig('fish') still returned args: null, so an ordinary fish pane reproduced the original symptom for a hand-typed prime-agent, while bash/zsh got their wrapper unconditionally. Fixed by dropping the emitReadyMarker gate on the fish branch in both launch paths and gating only the marker.
  3. The guest-script tests were vacuous on Linux (medium). They relied on getent being absent so the script would fall back to $SHELL — true on macOS, false on every CI runner, where getent returns the account's real shell and the script dispatches to bash) while the assertions still pass off the bash wrapper. Fixed by shadowing getent on PATH to pin the login shell, and adding a /bin/sh control that must come back unwrapped — so a shadow that stopped steering the branch fails the suite.

Other risks checked in the self-audit:

  • Fish scoping. set -l/set -lx scope to the enclosing block in fish, not the function. A naive port would let the ORCA_AGENT_HOOK_ENDPOINT export die at the closing end before prime-agent ran; the wrapper keeps the launch inside the same block (one duplicated call, commented).
  • Fish glob-on-parse. An unmatched wildcard in a command argument aborts the script in fish, so every wildcard pattern is quoted.
  • Path quoting, both directions. The local path single-quotes via quoteFishSingle (escapes \ and '), verified against a real path containing both ' and $. The WSL path cannot use a literal at all — the root is a WSLENV-translated Windows path, fish re-expands $ inside a double-quoted literal, and a single-quoted one cannot carry an apostrophe from a Windows profile name. It passes the path through an exported variable (a variable's value is never re-expanded) and scrubs the temp var with set -e in the same init command.
  • Cross-platform. macOS/Linux/Windows reviewed. Windows: the wrapper write is skipped on win32 exactly as before, the fish arm only executes inside a WSL guest, and no path is concatenated with a hardcoded separator (the daemon path uses join; the local provider keeps its existing POSIX-only template style since that root is consumed only by POSIX shells). No keyboard shortcut, accelerator, or UI label touched.
  • Fish version floor. Every shipped construct (string match/string trim -c, set -e slice deletion, functions -e, set -lx, switch) predates fish 3.0. --no-config is fish 3.3+ but appears only in test harnesses.

Security Audit

  • Command execution. The wrapper injects --extension "$ORCA_PRIME_AGENT_STATUS_EXTENSION" into a command prime-agent call. That value originates in Orca's own titlebar-extension-service, not user input, and is double-quoted so fish cannot word-split or glob it. command is used throughout so a user-defined prime-agent cannot recurse into the wrapper.
  • Path handling. Two distinct quoting problems, both closed and both covered by tests (see above). Neither can be broken by an apostrophe or a $ in the user's profile path.
  • File writes. One new file per wrapper root, shell-ready/fish/orca.fish, written mode 0644 into the existing user-owned userData dir by the same ensureShellReadyWrappers code that already writes the bash and zsh wrappers, under the same try/catch that degrades to an unwrapped shell on failure. No new trust boundary.
  • Env hygiene. ORCA_FISH_SHELL_READY_WRAPPER exists only long enough for fish to read it and is erased inside the same init command, so it does not linger in the user's interactive shell.
  • No new IPC surface, no network calls, no secrets, no dependency change, no auth path touched.

Follow-up needed: none for this change. The four filed tickets are pre-existing gaps, not regressions introduced here.

Notes

WSL guest execution was not validated. This was developed on macOS; real WSL cannot be exercised there. The WSL half is covered two ways short of that: assertions on the generated command string (including its escapeWslShCommandForWindows form), and an actual execution of the generated sh script driving a real fish with a pinned login shell. What that does not exercise is wsl.exe itself, WSLENV /p path translation, or /mnt/c file access from the guest. Someone on a Windows host with a fish-login WSL distro should confirm end to end.

Behavior change worth a second look: fish panes that previously launched with no args now launch as fish -l -C '<wrapper source + marker registration>'. That brings fish in line with how bash and zsh have always been launched, and the marker stays inert unless ORCA_SHELL_READY_MARKER=1, but it is a wider blast radius than the WSL-only report implies.

The RED baseline came from an independently built repro; its real-shell oracle approach — fake binary plus filesystem argv capture, no terminal scraping — is what the parity suite here is adapted from.

Fish is not POSIX, so the bash/zsh Prime wrapper is a parse error under it.
A fish login shell started cleanly and handed prime-agent the user's bare
argv, so Orca's status reporting never armed (STA-3927).

Add a fish-syntax wrapper written to shell-ready/fish/orca.fish and source it
from the local PTY, daemon, and WSL guest launch paths.
Attribution-only fish still launched bare, so a plain fish pane lost Prime
status for a hand-typed prime-agent while bash and zsh got their wrapper.

Also list the live-fish suites in the one CI job that installs fish; without
that they gate off a missing binary and skip silently.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added generated Fish wrappers for Prime Agent attribution and optional shell-ready marker registration. Daemon and local PTY launches now create and source the wrapper in shell-ready and attribution-only modes. WSL Fish login commands conditionally source the wrapper with path escaping and fallback handling. Added Fish wrapper, PTY, WSL, parity, launch configuration, and CI coverage.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding the Prime Agent status wrapper for fish login shells.
Description check ✅ Passed The description covers all required sections, testing status, review findings, security risks, platform notes, and the unrun build with a clear justification.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 56bf70f3-e31c-475c-9a72-588cc356231c

📥 Commits

Reviewing files that changed from the base of the PR and between 46756b3 and ab8151e.

📒 Files selected for processing (15)
  • .github/workflows/pr.yml
  • src/main/daemon/shell-ready-fish.node-pty.test.ts
  • src/main/daemon/shell-ready.test.ts
  • src/main/daemon/shell-ready.ts
  • src/main/providers/local-pty-shell-ready.test.ts
  • src/main/providers/local-pty-shell-ready.ts
  • src/main/providers/windows-shell-args.test.ts
  • src/main/providers/wsl-fish-login-shell.test.ts
  • src/main/pty/prime-agent-fish-shell-wrapper.test.ts
  • src/main/pty/prime-agent-fish-shell-wrapper.ts
  • src/main/pty/prime-agent-shell-wrapper-parity.test.ts
  • src/main/pty/prime-agent-shell-wrapper.ts
  • src/main/shell-templates.ts
  • src/shared/wsl-login-shell-command.test.ts
  • src/shared/wsl-login-shell-command.ts

Comment thread .github/workflows/pr.yml
Comment on lines +186 to +189
# Why fish: every live-fish suite gates on the binary being present, so
# without this they skip silently and fish is covered only by config-shape
# assertions. Any new test that spawns a real fish MUST be listed in both
# the run list below and the shard --exclude list, or it never executes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the failure mode described in the maintenance comment.

Line 189 says that omitting either list causes the test to never execute. If a test is present in the dedicated run list but missing from the shard exclusion list, it still runs in shell_contracts and may also be rediscovered by a shard. Clarify that the two lists must stay synchronized to prevent either skipped coverage or duplicate discovery.

Comment on lines +66 to +71
// Why a file, not an inlined `-C` body: the WSL launch path sources this same
// wrapper root through /mnt/c, and inlining the body into the wsl.exe argv would
// make every `$` in it collide with escapeWslShCommandForWindows.
function getFishShellReadyWrapperPath(root = getShellReadyWrapperRoot()): string {
return `${root}/fish/orca.fish`
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline src/main/providers/local-pty-shell-ready.ts --items all
sed -n '1,90p' src/main/providers/local-pty-shell-ready.ts

Repository: stablyai/orca

Length of output: 5697


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- path imports and path construction ---'
rg -n "from 'node:path'|getFishShellReadyWrapperPath|getShellReadyWrapperRoot|getRequiredShellReadyWrapperPaths|fish/orca\.fish|shell-ready" src test tests 2>/dev/null || true

printf '%s\n' '--- relevant implementation ---'
sed -n '1,115p' src/main/providers/local-pty-shell-ready.ts
sed -n '270,360p' src/main/providers/local-pty-shell-ready.ts

printf '%s\n' '--- repository test/config context ---'
git ls-files | rg '(^|/)(package\.json|.*local-pty-shell-ready.*|.*shell-ready.*|tsconfig.*)$' | head -80

Repository: stablyai/orca

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- source path helpers and usages ---'
sed -n '1,85p' src/main/providers/local-pty-shell-ready.ts
rg -n -C 5 "getFishShellReadyWrapperPath|ensureShellReadyWrappersAt|getRequiredShellReadyWrapperPaths" src/main/providers src/shared

printf '%s\n' '--- local shell-ready tests ---'
sed -n '1,45p' src/main/providers/local-pty-shell-ready.test.ts
sed -n '270,415p' src/main/providers/local-pty-shell-ready.test.ts

printf '%s\n' '--- Windows and WSL call sites ---'
rg -n -C 8 "ensureShellReadyWrappersAt|fish/orca\.fish|ORCA_FISH_SHELL_READY_WRAPPER|_orca_shell_ready_root" src/main/providers src/shared

Repository: stablyai/orca

Length of output: 36454


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const path = require('node:path')
const roots = [
  '/Users/alice/Library/Application Support/Orca/shell-ready',
  'C:\\Users\\alice\\AppData\\Roaming\\Orca\\shell-ready',
  'C:/Users/alice/AppData/Roaming/Orca/shell-ready'
]
for (const root of roots) {
  console.log(JSON.stringify({
    root,
    template: `${root}/fish/orca.fish`,
    join: path.join(root, 'fish', 'orca.fish'),
    win32Join: path.win32.join(root, 'fish', 'orca.fish')
  }))
}
JS

Repository: stablyai/orca

Length of output: 1085


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- wrapper materialization ---'
sed -n '284,355p' src/main/providers/local-pty-shell-ready.ts
sed -n '365,438p' src/main/providers/local-pty-shell-ready.ts

printf '%s\n' '--- pathWin32 usage in this file ---'
rg -n -C 3 "pathWin32" src/main/providers/local-pty-shell-ready.ts

printf '%s\n' '--- Windows path behavior of the proposed change ---'
node - <<'JS'
const path = require('node:path')
const root = 'C:\\Users\\alice\\AppData\\Roaming\\Orca\\shell-ready'
const fish = path.win32.join(root, 'fish', 'orca.fish')
console.log(JSON.stringify({
  fish,
  currentDirectoryExtraction: fish.slice(0, fish.lastIndexOf('/')),
  dirname: path.win32.dirname(fish),
  sibling: path.win32.join(root, 'bash', 'rcfile')
}))
JS

Repository: stablyai/orca

Length of output: 6180


Use join and dirname for wrapper paths.

Update wrapper path construction and directory extraction together. A join-only change makes the existing lastIndexOf('/') extraction fail on Windows.

Source: Coding guidelines

fish fstats fd 0 and Node's `input` pipe reports EISDIR there, so fish 3.x
died with 'Unable to read input file: Is a directory' before running anything.
macOS fish 4.8 tolerates the pipe, which is why this passed locally and failed
on the Linux runner. Verified green against fish 3.6 in a Linux container.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

CI caught a macOS-only assumption in the WSL guest-script test, now fixed on 5b09502766.

The harness delivered prime-agent ask to the guest login shell through spawnSync's input option. fish fstats fd 0, and Node's input pipe reports EISDIR there — fish 3.x dies with error: Unable to read input file: Is a directory (exit 127) before running anything. macOS fish 4.8 tolerates the same pipe, which is exactly why it passed locally and failed on the Linux runner. Reproduced against fish 3.6 and 3.7 in containers; it fails for every fish argv, including bare fish, so it was never about the -C shape.

Fixed by opening a real file and passing its fd as stdio[0], which fstats cleanly on both. The whole test file now runs green under fish 3.6 in a Linux container, not just on macOS.

Two things worth recording from the debugging:

  • The rest of the fish wiring was validated by that same CI run: prime-agent-fish-shell-wrapper.test.ts (33 cases), shell-ready-fish.node-pty.test.ts, and prime-agent-shell-wrapper-parity.test.ts all passed on Ubuntu. That confirms the workflow change actually put these suites on a runner with fish, which was the point of that fix.
  • fish evaluates --init-command/-C after config.fish, not before (verified on 3.6, 3.7 and 4.8). Good for us: a user's config.fish cannot shadow the wrapper. I had this backwards in STA-3937's description and have corrected it there — the env restoration that ticket covers can be plain inline set -gx, no fish_prompt event needed.

@brennanb2025

brennanb2025 commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor Author

local macOS Electron evidence

Native Windows/WSL Electron QA was unavailable because the paired endpoint timed out. This is the local macOS Electron fallback pass (re-validated with compliant UI keyboard entry).

Exact commit: 5b09502766a84d42fd478d38d37f3912ab938715
Fish version: fish, version 4.8.1 (/opt/homebrew/bin/fish)
Identity: window.api.app.getIdentity() → worktree sta-3927-fish-wrapper / branch brennanb2025/sta-3927-fish-prime-agent-wrapper / repo root matches this worktree
Automated coverage: existing WSL/Windows automated tests already green on this PR (node-pty fish, WSL login shell, parity suite)

Input method (compliant)

Commands were entered through the visible xterm (textarea.xterm-helper-textarea / “Terminal input”) using agent-browser CDP only: click to focus → keyboard inserttext for each line → press Enter. No window.api.pty.write, store actions, or API calls were used for any behavior/shell command. API was used only for identity and post-action buffer/capture verification.

Actions / results (PASS)

Launched this PR build with unique ORCA_DEV_USER_DATA_PATH, CDP port, SHELL=fish, a harmless prime-agent stub that records argv + ORCA_AGENT_HOOK_ENDPOINT, and a real extension file activating the Prime status wrapper.

Through the rendered Orca UI: opened New Terminal (real local Fish login shell). Wrapper materialized once at userData/shell-ready/fish/orca.fish. functions -q prime-agent → yes.

Case Command (UI keyboard) Result
Interactive inject prime-agent ask "hello from fish qa" --extension <managed-ext> injected; hook endpoint set
Explicit --extension prime-agent --extension <ext> ask "explicit" Single --extension only (not doubled)
Help/management bypass prime-agent help / prime-agent --version No --extension injection

Stub captures confirmed argv matrix + non-empty ORCA_AGENT_HOOK_ENDPOINT under the isolated profile. Startup stayed responsive. Logs/wrapper show one-time shell-ready materialization only (no recurring rewrite work).

Screenshots (fresh UI-keyboard pass)

mac-fish-ui-keyboard-terminal-proof.png

mac-fish-ui-keyboard-ui-context.png

Cleanup

Stopped only the QA Electron processes for this pass; deleted temporary profile, stub bin, extension, and captures. No product code changes; no merge.

Verdict: PASS — no in-scope defects found under compliant UI keyboard reproduction.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing because Orca must not redefine or rewrite typed Prime Agent commands. This Fish parity change extended an existing WSL command-interception workaround; the replacement will remove that interception across shells and preserve user argv unchanged.

@nwparker

Copy link
Copy Markdown
Contributor

Nice PR — the fish twin of the POSIX wrapper is the right shape, and the parity test running both launch configs through one fake prime-agent is a good idea I'm going to steal.

Two heads-ups from working #9993 today (#13904), since we collide.

1. This job installs fish 3.7, and that will bite the next 2031-dependent test

sudo apt-get install -y fish on ubuntu-latest gets Noble's fish 3.7. Your suites don't care — the prime-agent wrapper, shell-ready marker, and WSL login script are all version-agnostic, and running them against 3.7 is fine.

But DECSET 2031 arming lives in fish 4's Rust tty_handoff, so a test that depends on it doesn't skip on 3.7 — it fails. #13904 adds ppa:fish-shell/release-4 plus an explicit version gate, and an ORCA_REQUIRE_FISH=1 env on the job so a missing or too-old fish fails loudly instead of silently skipping while shell contracts reports green. Worth knowing about given your comment on that step correctly warns that new live-fish tests must be listed in both the run list and the shard --exclude.

We both edit those same hunks, so whoever lands second takes a small mechanical conflict. Happy to be second and resolve it — your PR was up first.

2. I implemented STA-3937 and then dropped it, rather than collide with you

While auditing fish support I hit the same gap you filed as STA-3937 and had it working: getFishAgentEnvRestoreCommands() in shell-templates.ts restoring ORCA_CODEX_HOME → CODEX_HOME, ORCA_OPENCODE_CONFIG_DIR → OPENCODE_CONFIG_DIR, ORCA_MIMOCODE_HOME → MIMOCODE_HOME via set -gx, wired into both shell-ready.ts and local-pty-shell-ready.ts, plus the markerless-path wrapping — with tests including a live-fish one asserting a config.fish agent home gets overridden by the Orca-managed one.

Then I read your PR and saw you'd already restructured that exact fish arm and filed the ticket, so I reverted all of it. Those two files are byte-identical to HEAD in #13904.

Your delivery mechanism is better than mine anyway — a sourced wrapper file rather than my newline-joined -C string, and your point about multi--C support varying by fish version is a real constraint I'd have tripped over. If you want the restore logic and its tests to fold into STA-3937, say the word and I'll hand you the diff; otherwise it's yours to write fresh.

What I did keep (no overlap with your files)

  • unset → set -e in tui-agent-startup-shell.ts. fish -c 'unset FOO' → fish: Unknown command: unset, so AI-vault resume left CODEX_HOME at Orca's routed value and codex resume <id> reported the session missing. Silent wrong result, not a visible error — this one had teeth.
  • shell-startup-env.ts now parses config.fish / conf.d/*.fish for set -gx, so a fish user's own agent homes are detectable (adjacent to STA-3937 but it's reading the user's config, not restoring Orca's).
  • isBracketedPasteSafeShell centralised — local excluded fish while daemon and relay included it, so the same fish user got multiline startup commands submitted line-by-line locally.

One interaction worth flagging: your change wrapping fish on the attribution path shifts what waitsForShellReady means for fish, and isBracketedPasteSafeShell({ shellName: 'fish', waitsForShellReady }) keys off exactly that. No textual conflict, but worth a look when we rebase.

Also FYI, from the same investigation: #13892 — OSC/DSR replies get reordered behind CPR/DA1 by the POSIX echo-safe write path and strand into the next child's stdin. Hits fish hardest since it uses DA1 as its read sentinel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants