Commit Graph
6 Commits
Author SHA1 Message Date
Brennan Benson 6858e072cf fix(terminal): agent pane auto-launch lost under fish + Starship (STA-3417) (#12840)
* fix(terminal): extend the shell-ready startup barrier to fish (STA-3417)

Fish never emitted the OSC 777 shell-ready marker, so agent launch
commands were written into the PTY while fish/Starship were still
initializing: the daemon path wrote them synchronously at session
create and the local path blind-wrote ~30ms after the first output
byte. The command was echoed by the kernel but never executed.

- shell-templates: shared fish --init-command that emits the marker
  once on the first fish_prompt event (the earliest point fish's own
  reader owns the PTY, mirroring zsh's zle-line-init marker)
- daemon shell-ready: fish joins the startup barrier so the launch
  command queues until the marker (timeout fallback unchanged)
- local-pty-shell-ready: fish launch config gains the marker wrapper
- codex-startup-delivery/tui-agent-startup: omp/pi/opencode plans now
  request shell-ready delivery (codex parity) so the SSH renderer path
  also waits for the prompt; plain payload-free codex stays on the
  markerless fast path

* fix(terminal): answer DA1 past the shell-ready barrier

The barrier queues all inbound input until the ready marker, including the
renderer's DA1 reply. A shell that withholds its first prompt until DA1 is
answered — fish waits 10s — therefore never emits the marker that would
release the reply it is waiting for. Measured: 10.37s to launch an agent,
versus 0.35s once the reply lands.

Answer DA1 from the daemon while the barrier holds, writing straight to the
subprocess so the reply bypasses the queue, and consume the query so the
renderer's xterm cannot also reply. Released on ready, timeout, or dispose,
handing DA1 back to the renderer for steady state.

Consolidates the identical DA1 handler the ConPTY override already used.

* fix(terminal): prevent duplicate startup DA1 replies
2026-08-09 14:29:14 -07:00
Rod BoevandOrcaWin 59460f7576 fix(terminal): derive zsh wrapper ZDOTDIR from %x so non-ASCII WSL logins load .zshrc (#8003) (#8209)
The shell-ready zsh wrapper restored ZDOTDIR from the env-imported
`${ZDOTDIR}`. On Windows+WSL the wrappers are generated with a Windows
path baked in but sourced via /mnt/c, and for non-ASCII Windows
usernames (e.g. a Korean login) zsh corrupts environment values whose
UTF-8 bytes fall in its 0x84-0x9D token range while processing startup
files. The corrupted `${ZDOTDIR}` failed the self-check, so the wrapper
fell back to the unusable baked Windows literal and the user's ~/.zshrc
never loaded — a bare `HOSTNAME%` prompt with no theme/aliases/PATH.

Derive the wrapper dir from `${${(%):-%x}:h}` instead — %x is zsh's
internal script name for the file being sourced and is not subject to
the env-import corruption. `${ZDOTDIR}` is kept only as a fallback when
%x expansion yields nothing; the existing final restore still validates
with -f before trusting the value. On native macOS/Linux the derived
value equals the old one, so behavior there is unchanged.

Covers local PTYs, the daemon, and the Windows→WSL launch path, which
all share getZshEnvTemplate. Adds a live-zsh regression test that sources
the wrappers from a non-ASCII (token-range) runtime path.

Supersedes the ORCA_ORIG_ZDOTDIR path-normalization approach with a wrapper self-location fix that also covers the non-ASCII (token-range) corruption trigger.

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-14 01:55:04 -07:00
Jinjing 620b475acd Bug wsl terminal zsh shell ready wrapper exports (#8025)
* Fix WSL zsh ZDOTDIR restore using runtime-sourced path instead of baked

Windows-generated shell-ready wrappers are sourced via /mnt/c on WSL, where
the generation-time path baked into .zshenv doesn't exist. Capture the
runtime ZDOTDIR before it's unset and prefer it (when it still points to a
valid wrapper dir) over the baked literal when restoring ZDOTDIR, so user
.zshrc/.zshenv files load correctly under WSL.

* Fix zsh ZDOTDIR self-restore to guard against unset variables

Add ":-" default expansion when referencing _orca_wrapper_zdotdir_self so
the wrapper doesn't abort under `set -u` when the variable is unset, and
extend the shell-ready test to assert login-shell behavior (IS_LOGIN)
and improve failure diagnostics on zsh spawn.
2026-07-09 22:41:58 -07:00
a0818b9a79 Fix agent tabs opening as a bare shell with oh-my-zsh vi-mode (#5315)
* fix(terminal): emit zsh shell-ready marker even when a user zle-line-init hook fails

Orca's zsh wrapper registered its OSC-777 shell-ready marker via
`add-zle-hook-widget line-init`. zsh's azhw dispatcher aborts the whole
hook chain when an earlier hook returns non-zero, and oh-my-zsh's vi-mode
installs a raw `zle-line-init` that exits 1 when VI_MODE_SET_CURSOR is
unset. The marker never fired, so every queued startup command sat on the
daemon's 15s pre-ready timeout — agent quick-launches looked like they
just opened a bare terminal.

Own `zle-line-init` directly: emit the marker first, then chain to any
previously-installed widget (called as a plain function so $WIDGET stays
`zle-line-init` for downstream azhw dispatchers). The registration is
idempotent — a re-source keeps the originally captured widget instead of
clobbering the chain. Applied to both the daemon and local-PTY wrappers
via a shared template helper.

* test(shell-ready): skip global zsh rcs so CI compinit can't block the marker tests

The real-zsh node-pty tests spawned 'zsh -l'/'zsh -i', which on CI runners
sources /etc/zsh/* whose global compinit hits insecure (group-writable) fpath
dirs and blocks on an interactive 'insecure directories [y/n]?' prompt before
zle-line-init fires. The marker never appeared and the tests timed out. Spawn
with -o noglobalrcs so only our ZDOTDIR wrapper/fixtures load; the marker
contract is unaffected.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-06-13 14:16:05 -07:00
Jinwoo HongandOrca 3600cab06d Fix zsh ZDOTDIR context in shell-ready wrappers (#4667)
Co-authored-by: Orca <help@stably.ai>
2026-06-04 17:17:47 -07:00
ddda84f529 fix(shell-ready): honor user's ZDOTDIR (XDG-layout fix, redo of #1737) (#1947)
* fix(shell-ready): honor ZDOTDIR without breaking zsh scoping

Fixes #1866

This reimplements PR #1737 (reverted in #1864) with a safer approach
that preserves normal zsh startup semantics.

**Core fix**: Discover ZDOTDIR by sourcing user ~/.zshenv in a subshell
instead of inside a wrapper function. This preserves top-level zsh
scoping for common patterns like `typeset -U path` that broke in the
original implementation.

**Shell safety improvements**:
- Use `printf '%s\n'` instead of `echo` for capturing ZDOTDIR (handles
  special characters in paths safely)
- Subshell isolates early returns and side effects from wrapper

**Code quality**:
- Extract duplicated zsh wrapper template to `src/main/shell-templates.ts`
- Both local-pty and daemon paths now share identical wrapper logic

**Test coverage**:
- Add live zsh subprocess tests that spawn real zsh to verify:
  - XDG ZDOTDIR discovery works
  - `typeset -U path` in .zshrc preserves top-level scoping
  - Early returns in .zshenv don't crash the wrapper
  - Vanilla (non-XDG) configs fall back to HOME correctly
- Template structure tests validate subshell discovery logic

Before (broken):
```zsh
__orca_source_user_zshenv() {
  source "$HOME/.zshenv"  # typeset becomes function-scoped
}
```

After (fixed):
```zsh
_orca_discovered_zdotdir=$(
  unset ZDOTDIR
  [[ -f "$HOME/.zshenv" ]] && source "$HOME/.zshenv" 2>/dev/null
  printf '%s\n' "${ZDOTDIR}"
)
export ORCA_ORIG_ZDOTDIR="${_orca_discovered_zdotdir:-${_orca_spawn_orig_zdotdir:-$HOME}}"
```

The subshell sources .zshenv at top-level (preserving normal scoping),
captures only the ZDOTDIR value, then exits. User rcfiles (.zshrc, etc.)
are still sourced at the wrapper's top level, so all scoping works normally.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Add shell-script-literal test framework and improvements

Adds a new declarative test framework for shell-ready tests that uses
literal shell scripts (copy-pastable into terminals) with inline snapshots.

Framework features:
- Shell scripts as string literals with # Run: marker to split setup/test
- Direct script execution (no brittle parsing) via temp files
- Path normalization for reproducible snapshots (<HOME>, <WRAPPER_DIR>)
- Auto-detects shell from command, supports bash/zsh/sh
- Inline snapshot testing with vitest toMatchInlineSnapshot()

Code quality improvements:
- Extract escapeRegex to shared string-utils.ts (deduplicates 2 copies)
- Refactor shell-templates.ts for readability (condense comments, add structure)
- Pre-compile regex patterns to avoid hot-path allocation
- Fix path normalization to sort by length (prevent nested path corruption)
- Fix actualUserHome handling to skip empty values

All tests passing (64/64 shell-ready tests, 55/55 affected tests).

Files added:
- src/main/providers/__tests__/shell-ready-framework/shell-script-test.ts
- src/main/providers/__tests__/shell-ready-framework/README.md
- src/main/providers/__tests__/shell-ready-framework-example.test.ts
- src/shared/string-utils.ts

Files modified:
- src/main/shell-templates.ts (readability cleanup)
- src/main/codex/config-toml-trust.ts (use shared escapeRegex)
- src/main/daemon/shell-ready.test.ts (updated for new framework)
- src/main/providers/local-pty-shell-ready.test.ts (updated for new framework)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(shell-ready): preserve zshenv semantics

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-05-26 00:24:45 -07:00