mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
a3edabcd7babc3a488a35be91a30da3dcd1eeaec
8914
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a3edabcd7b |
fix(package): keep cached dev Electron bundles out of app.asar (#15359)
`files` is an all-negation list, so electron-builder's default `**/*` packs anything without an explicit `!` entry. out/electron-dev holds `pnpm dev`'s per-branch Electron.app copies (~270MB each), so packaging on a machine that has run dev bundled them all. CI never creates the directory, so releases were never affected. |
||
|
|
4b0e01f613 |
fix(github): scope GHES host-auth cache to the executing connection (#14948)
* fix(github): scope GHES host-auth cache to the executing connection The gh auth answer cached for a connection-backed repo is probed without the repository cwd, so it describes that connection's runtime — not the local host. Keying only on repoPath+wslDistro let a local repo and an SSH-hosted repo at the same path share one entry, and whichever resolved first decided "is this GHES host authenticated" for both. Include the connection identity in the runtime cache key. Local and WSL keys are unchanged. * fix(github): fence GHES auth cache across SSH reconnects * fix(github): fence origin cache across SSH reconnects * fix(github): fence repository identity cache on reconnect |
||
|
|
bef76953d7 | fix(orchestration): record why a terminal's process is gone (STA-4603, STA-4536) (#15244) | ||
|
|
a3da91b10a | fix(dev): stop caching unsigned bundles and reclaim stale dev copies (#15247) | ||
|
|
fe95698b95 |
fix(relay): let one owner hold pty.ackData instead of relying on construction order (#15079)
PtyHandler registered a no-op pty.ackData handler and SshPtyConsumerSessionAdapter registered the real sourceCredit.acknowledge for the same method. onNotification is a single slot, so only the adapter's survived — and only because relay.ts constructs it second. Reversing those two lines would have made every credit-mode delivery wedge permanently once the 256KB window emptied, with no error and no log. Delete the dead no-op, and make onNotification throw on a duplicate registration the way registerPtyDataPublicationAdmission already does for the admission slot. pty.ackData was the only double-registered method in the tree, so nothing else changes behavior. The existing test asserted only that pty.ackData appeared in the handler map, which stays true when the real handler is shadowed. It now asserts PtyHandler does not own the method. |
||
|
|
a27527ae06 | fix(orchestration): deliver worker-exit escalations to lightweight Run coordinators (STA-4604) (#15235) | ||
|
|
2a760e310b |
fix(computer): report unasserted accessibility actions (#15028)
* fix(computer): report unasserted accessibility actions * fix(computer): fail closed on missing action metadata * Fix merged tab search test fixture |
||
|
|
2f0f9a8a39 |
Revert "fix(agent-hooks): bind agent status to the pane its session was spawned into (STA-2069) (#14615)" (#15295)
Reverts #14615. Its premise does not reproduce, it does not reach the failure that does, and the correction it installs can misattribute status on a path that worked before. 1. PREMISE FALSE. #14615 asserts Claude Code >= 2.1.206 hosts TUI sessions under a shared daemon. On 2.1.233 `claude daemon status` reports "not running" with 69 live interactive sessions, and every client is a direct child of its own pane's shell. Measured across the fleet: 68 distinct pane keys, zero collisions. Foreground attribution was never broken. 2. DOES NOT FIX THE REAL BUG. The failure in #9236 is real but scoped to BACKGROUNDED sessions, whose workers inherit the dispatching pane's whole ORCA_* set. #14615 mints a binding only for launches Orca constructs, so a typed `claude --bg` produces none. Fixed properly in #15304. 3. INTRODUCES A MISATTRIBUTION. Bindings are removed only on PTY death, and a user who exits Claude keeps the pane's PTY. Resuming that session in another pane does not rebind (`--resume` is a session selector, so the pin declines), and resolveBoundPaneOverride then rewrites paneKey and tabId onto the ORIGINAL pane despite a correct posted key. Demonstrated with a failing test against main; causation isolated to resolveBoundPaneOverride. Kept #14706's observations.rebind() in the conflicting hunk — it postdates #14615 and is not part of this revert. |
||
|
|
53bd956ef6 |
ci(e2e): keep the relay version markers in the e2e build artifact (#15303)
build-relay.mjs writes each relay's marker as out/relay/<platform>/.version, and upload-artifact excludes dotfiles unless include-hidden-files is set. The markers were therefore stripped from e2e-build-out, so every consumer that actually starts a relay failed with: Orca's local relay build is missing its version marker at out/relay/linux-x64/.version This stayed latent because the sharded e2e lane never sets ORCA_E2E_SSH_DOCKER, so its SSH specs skip instead of touching the relay. The changed-specs lane does set it whenever a changed spec needs Docker SSH, which is why the failure only appears on PRs that touch SSH-adjacent code. |
||
|
|
66a5e5d245 |
fix(shell): repair worktree HISTFILE in plain zsh panes via one positive feature channel (#15258)
* fix(shell): repair worktree HISTFILE in plain zsh panes via one positive feature channel
A plain zsh pane — no startup command, no agent overlay — was never wrapped, so
Orca's HISTFILE repair never ran in it. macOS `/etc/zshrc` assigns
`HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` with no check-before-set and runs
before any file Orca controls, so per-worktree history was a silent no-op for
every ordinary pane on the primary platform.
Wrapping those panes needs a way to say which wrapper features a shell should
turn on. That channel is one exported variable, ORCA_SHELL_FEATURES, carrying a
comma-separated positive allowlist from a closed set (history, markers, ready,
identity, overlay). The wrapper .zshenv reads it into a plain, non-exported
array and unsets it in its first executable lines, before the user's own
.zshenv — so the selection survives .zshenv -> .zprofile -> .zshrc -> .zlogin in
this process but physically cannot reach a child. There is no negative or
suppression variable anywhere; an absent or inherited value can only ever mean
fewer features. ORCA_HISTFILE is consumed and destroyed the same way, which
removes the root cause of #11146 instead of patching it.
All order-sensitive wrapper work now lives in one `__orca_shell_epilogue`
defined in .zshenv and invoked exactly once, from .zshrc for a non-login shell
and .zlogin for a login shell, with each feature an independent guard.
Selection is a pure function of spawn env and launch intent, so a pane wrapped
only for history gets no OSC 133 and is observably identical to the unwrapped
pane it used to be.
Generation is now fail-closed: wrapper files are written to a temp name and
renamed, every required path is verified non-empty, and ZDOTDIR is only set when
that holds. Previously a failed write still pointed ZDOTDIR at an empty dir and
the user silently lost their entire zsh config.
Orca also recognised only its own `*/shell-ready/zsh` dir shape when deciding
what the user's ZDOTDIR was, so being launched from any other terminal that had
hijacked ZDOTDIR captured that as the user's config dir. Ownership is now
established positively — a stamped marker file, or Orca's own dir shape for
wrappers written by older builds — and an inherited ZDOTDIR holding no zsh
startup file is ignored. No vendor is detected by name.
* fix(shell): make the zsh epilogue option-proof and stop history widening relay wrapping
Review follow-ups on the feature-channel PR.
- `emulate -L zsh` as the epilogue's first statement. It runs after the user's
own config, so `setopt no_unset` made the precmd_functions append a fatal
error that returned from the whole function (no ready widget, ZDOTDIR left at
Orca's wrapper dir), and `setopt ksh_arrays` made the 1-based feature
subscript drop whichever feature is listed first.
- The `/etc/zshrc` HISTFILE repair is no longer behind the `history` guard: it
undoes damage Orca's own ZDOTDIR caused, so it must also run for a shell that
re-enters the wrapper after the allowlist was consumed.
- The relay keeps its own wrapping gate. Its .zshenv resolves the user's config
dir from a ZDOTDIR Orca has already overwritten, so wrapping a remote pane
just for `history` cost a relocated-ZDOTDIR user their whole shell config.
- A failed primary spawn no longer leaks the primary shell's launch env
(wrapper ZDOTDIR + feature channel) into an unwrapped fallback pane.
* fix(shell): drop the relay wrapping gate and stop HISTFILE inheriting across Orca instances
The relay-specific gate added last round rested on a false premise:
main's hasOverlayRestoreEnv already included ORCA_REMOTE_CLI_BIN_DIR, and
ssh-pty-spawn-env sets that on every SSH pane whose session has a CLI
bridge — so ordinary remote zsh panes were already wrapped. The gate only
bit where remoteCliBridgeEnv is null (a host too old to report its
platform), where it silently dropped that pane's worktree history. All
three transports now share the features.length rule.
HISTFILE stays exported, so a newly wrapped pane handed the worktree
history path to every child, including a nested Orca whose panes then all
hit injectHistoryEnv's check-before-set and appended into the launching
worktree's file. Same class as the fish_history fix in #15195: recognise a
path Orca minted and drop it before the check, on the desktop, daemon and
relay injection paths and both history-disabled branches.
Also: run the epilogue from the wrapper .zshrc when zsh is in sh/ksh
emulation, since sourcehome() then reads $HOME/.zlogin and the wrapper's
.zlogin never runs; track fallback launch-env keys per attempt rather than
once from the primary; and guard the cross-file epilogue call so a wrapper
dir shared by two builds degrades quietly.
* fix(shell): make every wrapper file self-sufficient and retire the deleted marker vars from tests
- .zprofile/.zshrc/.zlogin each define __orca_resolve_user_config_dir. They
called it on line 2 while only .zshenv defined it, so a wrapper dir written by
two concurrently installed builds printed three "command not found" and
skipped the user's entire zsh config. New live-shell test covers it.
- Retarget every remaining ORCA_SHELL_READY_MARKER/ORCA_SHELL_STARTUP_IDENTITY
reference onto ORCA_SHELL_FEATURES, or delete it where the key is now dead.
- isOrcaMintedHistFile requires a leading '/', so a relative path of the same
shape stays the user's.
- Drop an unused no-control-regex disable, and register the two real-zsh suites
in the dedicated shell-contracts lane.
* fix(shell): stop the zsh wrapper colliding on REPLY and degrade under sh emulation
Widening wrapping from overlay/startup panes to every zsh pane turned three
latent wrapper defects into user-visible ones.
- The config-dir resolver used `REPLY`, zsh's shared scratch global, as its
out-parameter. `typeset -r REPLY` in a user config made the wrapper's first
executable assignment fatal, `typeset -i REPLY` silently resolved every path
to 0; both left HISTFILE inside Orca's wrapper dir. It now writes an
Orca-private `_orca_resolved_config_dir`, declared `typeset -g` so the
rename introduces no `warn_create_global` noise. A new rule test fails on any
generated wrapper file that writes a global outside Orca's namespace.
- The daemon dropped an inherited HISTFILE but never an inherited
ORCA_HISTFILE, which now both wraps a pane the client scoped nothing for and
re-exports another worktree's history path. The relay had the same gap on its
isolation-off and revive paths. Both now mirror the desktop.
- A user .zshenv or .zprofile ending in `emulate sh` makes zsh ignore ZDOTDIR,
so no later wrapper file is read and the epilogue never runs. Nothing can
repair HISTFILE from there, so the wrapper now detects the emulation and
hands the pane back unwrapped instead of leaving history somewhere invisible.
Also `typeset -g __orca_in_command` so the OSC 133 preexec hook prints no
warning under `setopt warn_create_global`.
|
||
|
|
6efd4061dc |
fix(git): run WSL git reads without a shell (#15257)
* fix(git): run WSL git reads without a shell WSL-routed git ran through the distro user's interactive login shell for one reason: to inherit their PATH. That shell also runs the distro's rc/motd and writes it to the stdout callers parse, which is why #10917 reports a shell banner breaking GitHub source detection. A shell-free route already existed (`--exec /usr/bin/env PATH=... git`, added for status reads in #13207) but it was opt-in, and only gitStatusReadOptionsForWorktree opted in. Every other read -- remote get-url, config --get, log, show, rev-parse -- took the login shell on every call, so the reported parse never benefited. Classify reads at the resolver instead of at each caller. A read needs nothing the login shell provides, so it takes the direct route without the caller asking. Writes and network operations stay on the login shell: they can depend on credential helpers and ssh-agent that the user's profile sets up. Subcommands that both read and write (config, remote, branch, submodule) require an explicit read flag before they qualify, so `config --get` goes direct while `config user.email x` does not. `git show` blob reads stop forcing the login shell and go direct too. The fence stays on that path: the login shell is still the fallback when the environment probe is cold or rejected, and a banner there would become file content. Behavior note: the first WSL read per distro now also warms the environment probe in the background, so a cold read spawns one extra wsl.exe. Subsequent reads start no shell at all. * fix(git): keep queried WSL remote reads on login shell * style: format WSL runner test * fix(git): match WSL read markers positionally The conditional read markers were matched anywhere after the subcommand, so a positional argument that happened to share a marker's name routed a write shell-free: `worktree remove list` read as a listing, and `submodule foreach status` as a status query -- the latter can run arbitrary commands, including network ones. Split the two kinds of marker apart. `config`/`branch` are flag-marked and still match anywhere; `remote`/`worktree`/`submodule` are action-marked and must match the first non-flag argument. The queried `remote show` rule and the `symbolic-ref` arity rule are unchanged. Neither case is reachable today -- Orca issues no `submodule foreach`, and the worktree paths are its own CLI, not git argv -- but the loose match was the shape of the defect, not those two instances. * test(git): pin read routing behind global options Writes hidden behind -c/-C/--git-dir must not reach the shell-free route, and an unparsed global form must fall back to the login shell rather than guess at the subcommand. Both directions fail safe. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> |
||
|
|
b7f2e17712 |
fix(git): fence buffered WSL login-shell reads (#15060)
* fix(git): fence buffered WSL login-shell reads Two git paths force the login shell unconditionally and buffer its whole stdout, so the distro's rc banner lands in front of the payload: - gitExecFileAsyncBuffer backs `git show :<path>` blob reads and hands the bytes straight to the diff/blob viewer, so the banner is prepended to displayed file content. - buildNetworkSshPolicyEnv probes `core.sshCommand` and treats any non-empty answer as a user-configured wrapper. A banner reads as configured, so the code skips the `ssh -o BatchMode=yes` fallback and silently disarms the guard that keeps non-interactive SSH from hanging on a prompt. Fencing is opt-in per call site rather than applied to the login-shell branch as a whole: streaming consumers (`git grep`, `ls-files -z`) parse records as they arrive, so an opening marker would be glued onto their first record. Only these two, both buffered by construction, opt in. Blob content can be binary, so the payload is sliced out of the raw bytes; decoding to find the fence would corrupt it. The markers are exposed on the captured command because the shared module is bundled for the renderer and cannot reference Buffer. Note this path is not a rare fallback: `preferWslDirectGit` is only set by gitStatusReadOptionsForWorktree, so every other WSL-routed git call takes the login shell on every invocation. * test(git): use findLast for the ssh-policy call lookup Satisfies the code-quality rule that flags filter-then-index. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
4b2c901b66 |
test(terminal): pin that the CJK block is the preedit overlay, not the cursor (#15242)
* test(terminal): pin that the CJK block is the preedit overlay, not the cursor A report described the cursor sitting on a wide character's first cell and hiding its right half, with cursor style and opacity settings ignored. Neither defect reproduces. Replaying the reporter's own captured byte stream leaves the cursor at column 11, exactly where the application asked, with correct wide and continuation cells. A block cursor also cannot hide half a glyph: it inverts the cell and the syllable renders inside the cursor span. The black block is the IME preedit overlay. macOS 2-set Korean keeps the trailing syllable composing until a terminator, so it sits in an opaque absolutely-positioned box over the grid rather than in the buffer. That box took stock upstream colours, black on white. It explains what no cursor theory can: the block appears at the composing cursor cell, no cursor option reaches it, it is identical with GPU acceleration off since it is a DOM node above both renderers, Latin never triggers it because Latin opens no composition, and Enter clears it because Enter commits the composition. Already fixed by the overlay theming in #15014, which landed a day after the reported release, so the fix ships in the next one. Tests only, no production change. Two pin the negative results so the cursor explanation cannot be re-derived, and one pins the actual mechanism at end of row, beside the existing mid-line arm. Separately confirmed and not fixed here: the WebGL renderer drops the cursor colour's alpha, so terminal cursor opacity genuinely does nothing for a block cursor, which is the default style on the default renderer. That is in the webgl addon rather than in xterm or in our code. Refs #12729 * test(terminal): make the cursor precedence assertion real and measure the overlay Review of the first pass found one assertion that could not fail. It set options.cursorStyle and then read decPrivateModes.cursorStyle, which are separate fields with separate storage, so it pinned that writing one does not clobber the other. Deleting the precedence expression from both renderers left it green. It now asserts the rendered cursor class: the option style renders, a DECSCUSR overrides it, and the reset hands control back. That fails if the precedence is removed. The overlay's rendered width is the one measurement in the report that argues against our explanation, and no test here could reach it, because the unit environment performs no layout. Adds an end-of-row browser arm beside the existing mid-line one, asserting a single composing Hangul syllable spans about two cells. That settles whether the block the reporter measured at one cell can be this overlay. Also scopes two DOM queries to the test container rather than the document, and attaches the render listener before writing so a missed render fails instead of hanging to timeout. Records in the file header what it does not establish: composing the opacity into the theme is not the same as it reaching the screen, since the webgl renderer drops the cursor colour's alpha for a block cursor. Refs #12729 |
||
|
|
3a9f40ed70 |
fix(wsl): read machine output from a fenced login shell (#15290)
* fix(wsl): read machine output from a fenced login shell Orca runs WSL reads through the distro's *interactive* login shell so PATH matches the user's own terminal (nvm, mise and asdf only install into rc files interactive shells read). An interactive shell also runs the distro's rc/motd, and stock Ubuntu 24.04 writes its "run a command as administrator" hint to stdout -- no user customization required. Every caller parsing that stream was reading the banner as data: statPath -> "To run a command as administrator...\n\ndirectory" readPath -> banner prepended to the contents of every file read preflight -> banner prepended to `gh --version` / auth output `.trim()` cannot recover any of these, so a WSL worktree's file explorer sees no valid entry types and file reads return junk. Three call sites had independently grown their own marker to survive this (`__ORCA_AGENT_PATH__`, `ORCA_WSL_GIT_READ_ENV_V1`, and a `>/dev/null` fd dance), which is the tell that it belongs in one place. Fence the payload once, in the shared builder, and hand callers a reader that returns just their bytes. The fence carries a per-call nonce so `cat`-ing a file that happens to quote a marker is not truncated. Exit status is preserved, so the ENOENT mapping still works. wsl-git-read-environment drops its bespoke marker and parsing. * test(wsl): fence the login-shell path-lookup boundary test It asserted a raw interactive login-shell read matched an absolute path, so the distro rc banner made it fail on any stock Ubuntu. It is part of the shell-contracts CI gate, where it skips on Linux and hid the break. * docs(wsl): record the guest command-execution contract Both failure modes are silent - the command runs, exits 0, and returns the wrong bytes - so the rules need to live somewhere a reader will find them before writing the next wsl.exe call site. * fix(codex): fence the WSL Codex identity probe buildWslCodexBinaryStamp reads the login shell's stdout positionally -- path before the first newline, version after -- through an interactive login shell. On a stock Ubuntu the rc banner lands ahead of the payload, so the first newline falls inside the banner and the stamp becomes path="To run a command as administrator..." with the rest as version. Both halves are non-empty, so nothing throws: the stamp is silently wrong, and an unstable stamp reads as "the Codex binary changed" and reissues the trust grant. The identity script ends in `exec`, so it never writes a closing fence; the reader returns everything after the opening one, which is exactly this case. buildWslCodexIdentityArgs becomes buildWslCodexIdentityProbe and returns the reader with the argv so the two cannot drift apart. The other three WSL Codex commands are deliberately left unfenced: availability is exit-code only, and app-server/login hand stdout to a long-running program. * fix(wsl): harden the capture fence after review - readStdout now takes the LAST opening fence, matching the lastIndexOf the wsl-git-read-environment marker used deliberately: a login shell can echo the command text before running it, repeating the fence. - local-worktree-filesystem throws instead of falling back to raw stdout when the fence is missing. The fallback silently reinstated the bug being fixed -- statPath would return the banner as a file type and readPath would return banner+contents, with no signal. Preflight keeps its fallback; its matchers scan the whole blob and tolerate a prefix. - The exit-status test asserted only that the script CONTAINS `exit $?`, which is true for any input and never executed those lines. It now runs a real distro and asserts status 2 reaches the caller, which is what statPath's ENOENT mapping depends on. - Corrected the doc: a sed backreference has no `$`, so `--` never rewrote it. Replaced with the positional and shell-local cases that were measured to differ. * fix(wsl): stop running a login shell for filesystem reads statPath/readPath/rm run coreutils at standard paths and shell builtins. They need nothing from the user's PATH, so there was never a reason to start a login shell -- and starting one is what put the distro's rc/motd on the stdout these callers parse. Fencing that output treated the symptom. Using a plain `sh -c` removes the cause: no profile, no rc, no banner, by construction. The fence and its missing-fence error go away with it. The fence stays where it is actually needed: the three places that must run the user's shell to resolve their PATH (the preflight CLI probe, the WSL git environment probe, and the Codex identity probe). Net -12 lines. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
442b46f020 |
ci(e2e): install a CJK font on the e2e runners (#15259)
The runners have no font covering Hangul, Han or Kana, so any spec that asserts how CJK text renders is measuring tofu rather than the glyph. That is not hypothetical. An end-of-row preedit spec added for #12729 measured the composition overlay at 1.02 cells against an 8.43px grid and failed its "wider than one cell" assertion. The overlay is shrink-to-fit with no width of its own, so it tracks the glyph's advance rather than the two cells the grid reserves for a wide character. With no Korean font that advance is one cell, and the assertion cannot distinguish a real result from a missing font - which is exactly the question that spec exists to answer. fonts-noto-cjk covers all three scripts and is added to both jobs that execute specs, the sharded suite and the changed-spec job, plus the ssh docker lane so the three stay consistent. This does not make any spec pass on its own. It makes the CJK ones mean something. |
||
|
|
6e8da1df8d |
fix(wsl): pass guest argv verbatim through --exec (#15039)
* fix(wsl): pass guest argv verbatim through --exec
`wsl.exe <...> -- <argv>` expands `$name` in every argument against the
guest environment before the guest ever runs. It does this even when no
shell is involved, so `-- /usr/bin/printf %s '$HOME'` prints /home/you.
Every WSL invocation went through that preprocessor, so scripts arrived
already rewritten: `awk '{print $2}'` lost its field reference, and a
POSIX script asking for the literal `$HOME` got the expanded path.
`escapeWslShCommandForWindows` tried to compensate by escaping `$`, but
it skipped any `$` preceded by a backslash, so a script containing `\$`
was still corrupted -- and half the call sites never applied it at all.
Route every invocation through `--exec`, which passes argv through
untouched, and delete the escaper. The direct-git path already used
`--exec`, so this is not a new compatibility dependency.
A guard test fails if the `--` form reappears anywhere in the tree.
Net -50 lines of production code.
* test(wsl): drop remaining escaped-dollar assertions
* fix(wsl): cover the --exec migration's blind spots
An audit of every wsl.exe invocation found sites the first pass missed,
including two it actively broke:
- config/scripts/wsl-git-shell-benchmark.mjs imported
escapeWslShCommandForWindows, which no longer exists, so the script
threw on startup. Its wslShellArgs helper also still used `--`; the
file already had an --exec helper, so route both call sites there.
- classifySubprocessCommand unwrapped `wsl.exe <...> -- <binary>` by
breaking on `--` alone. With every Orca spawn now on --exec it never
found the guest binary and bucketed all WSL subprocesses as plain
"wsl", losing the git/gh/glab breakdown. Break on either separator,
since foreign wsl.exe processes still use `--`.
CliSkillRuntimeSetup builds its setup command as a template literal
rather than an argv array, so no array-shaped search could see it. Its
decoder accepts both separators so commands persisted before this
change still decode.
The guard now scans config/ and tests/ as well as src/, and checks the
command-string spelling alongside the argv one — the two shapes that
have each shipped a regression. It skips comment lines so prose about
the old form stays allowed, and asserts it scanned a plausible file
count so a bad root cannot make it vacuous.
* fix(wsl): restore the guard's multi-line sensitivity
The guard matched line by line, so `'--',\s*'bash'` could not span a
newline -- and every argv array in this repo is formatted one element
per line, which is exactly the shape it exists to catch. Measured
against the pre-migration tree it caught 17 files before and 9 fewer
after. It now strips comment lines and matches the rejoined text, with
a case that pins the multi-line shape so this cannot silently return.
The program list is wider than shells now, which surfaced a false
positive: tmux takes a `--` separator followed by a program too
(`split-window ... -- cat`). Matching is scoped to files that mention
WSL rather than narrowing the list back.
Also:
- Replaced the `sed` regression case, which was vacuous. A backreference
contains no `$`, so it returned `bac` under both separators and would
have passed without the fix. The block claimed every case proved the
bug. Swapped in a positional argument and a shell local, both measured
to differ -- the positional is the shape `wslUncDirectoryExists` uses,
where `--` blanked `$1` so every existing directory probed as missing.
- windows-shell-args.test.ts derived its expected argv from
buildWslExecArgs, the helper under test, so six assertions would still
pass if it regressed to `--`. Spelled the expectation out.
- Dropped two comments citing the removed `--` behavior as rationale.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
|
||
|
|
96d88dd5fe | fix(tab-bar): restore the tab-results fixture after occupantAgent became required (#15277) | ||
|
|
fc8b92e507 |
docs(computer): explain screenshot file requirements (#15054)
* docs(computer): clarify screenshot output requirements * fix(cli): do not advertise an unshipped --probe flag The capabilities help line referenced --probe, which does not exist yet; it ships in a later change. Advertising it here would be false until then. * fix(cli): align computer-use screenshot guidance * docs(computer): document inline screenshot fallback * docs(computer): keep screenshot summary accurate * docs(computer): keep screenshot guidance general |
||
|
|
6414a3a2a8 |
fix(remote): keep the host's last assistant message on client-owned agent rows (#12906) (#14716)
* fix(remote): keep the host's last assistant message on client-owned agent rows A remote pane has two writers for one agent-status key: this renderer's OSC byte pipeline and the mirrored host session.tabs snapshot. When the client owns the key, buildMirroredAgentStatusPatch keeps the client's entry wholesale and copies only paneKey/worktreeId/tabId/providerSession off the host frame. lastAssistantMessage is hook-only content the byte pipeline can never see, and setAgentStatus writes payload.lastAssistantMessage straight through, so every OSC write also blanks it. The host publishes the text and the client receives it, then discards it on every republication — the remote agent row's message line is permanently empty while identity and status render fine (#12906). Adopt it across the fence the same way providerSession already is, falling back to the mirrored value so a host that stops publishing the field cannot blank a line it already delivered. * refactor: mirror providerSession's coalesce shape and tighten the comment |
||
|
|
7ba336099c |
fix(sidebar): name each split-pane agent row from its own pane title (STA-2811) (#14707)
Agent rows are per pane, but their conversation name came from `tab.title`, which carries only the FOCUSED pane's title. In a split tab every row showed one pane's name, and all of them changed when the user clicked a sibling. Rows on a multi-pane tab now resolve their own leaf's runtime pane title via the existing `resolveRuntimePaneTitleForLeaf`, and fall back to no live title rather than a sibling's. Single-pane tabs pass `undefined` and are unchanged. Extracts the subagent grouping out of build-dashboard-snapshot.ts, which was exactly at the 300-line cap. |
||
|
|
26bdfc0fe4 |
feat(agent-status): stamp observation provenance at every status ingress (STA-4293) (#14706)
Add an optional `observation` facet to agent status rows recording the origin (hook | osc | title | process | launch | orchestration), the authority that sequenced it, a per-pane incarnation, a monotonic revision, and the authority's own clock. Stamp it at every ingress; no consumer reads it. Boundary is stamped from the hook listener's existing per-provider `isNewTurnEvent`, not a second list of event-name literals. Identity-only (`providerSessionOnly`) rows are tagged `kind: 'identity-only'` so future consumers do not each rediscover that they are not turn transitions. The staleness-decay contract is documented at the type: staleness must be computed against the same authority clock that stamped `observedAt`, or replicas must decay on local receipt time. Not fixed here. Behavior-neutral: optional field on existing JSON, never persisted, never published to paired clients, and never inherited across writes. |
||
|
|
8ea5dd80c3 |
fix(antigravity): install a PreToolUse status hook without deciding tool permissions (#14701)
* fix(antigravity): install a PreToolUse status hook without deciding tool permissions
Antigravity is the only supported agent with no pre-tool signal, so its panes
show a bare "Working" spinner for the whole tool call instead of the live
"Working - <tool>(<input>)" readout every other agent gets.
The consumer side already handles it — extractAntigravityToolFields and
normalizeAntigravityEvent parse PreToolUse (including the `waiting` state for
ask_question/ask_permission) and are covered by tests. Only the installer was
missing the event.
PreToolUse was installed originally and removed in
|
||
|
|
8e13485c9b |
fix(stats): count agent sessions from hook transitions, not OSC titles (STA-2445) (#14657)
* test(stats): dual-record the OSC-title detector against canonical hook transitions
Adds an AgentSessionTransitionRecorder that derives agent-session start/stop
boundaries from agent-hook status transitions, and a side-by-side comparison
that feeds both pipelines into a real StatsCollector.
Nothing is rewired yet — this commit only measures the delta:
title detector canonical
hook-only agent 0 1
braille-spinner non-agent TUI 1 0
one agent, one reconnect 2 1
totals 3 2
Refs #10201, STA-2445.
* fix(stats): count agent sessions from hook transitions and delete the title detector
Switches StatsCollector off AgentDetector and onto the canonical agent-hook
status stream, then removes the detector and its raw-PTY invocation.
- main/index.ts subscribes the recorder to subscribeEnrichedStatus and
subscribePaneStatusClear, next to where StatsCollector is constructed.
- orca-runtime.ts no longer feeds raw PTY bytes to a stats detector.
- StatsCollector keys sessions on a stable pane key, not a per-spawn ptyId.
Fixes #10201, refs STA-2445.
|
||
|
|
eb0ec39242 |
fix(runtime): stop one unreachable relay from freezing every workspace as active (STA-517) (#14649)
* fix(runtime): stop one unreachable relay from freezing every workspace as active The worktree.ps liveness refresh is the only thing that retires an exited PTY, and mobile renders "active" straight off the summary it produces. Its aggregate inventory ran every provider through Promise.all with no per-provider deadline, so a single SSH relay that rejected — or simply did not answer inside the 3s budget, since a relay list runs to the mux's own 30s default — cost the runtime the whole inventory. Nothing was ever proven dead, so every retained pane kept reporting hasHostSidebarActivity/liveTerminalCount, and the SSH workspaces stayed "active" on mobile for as long as the connection stayed unreachable. Settle each SSH provider independently and forward the caller's deadline, so local and healthy relays are still reconciled. A provider that does not answer is unknown, not empty: the runtime's existing hasPty rescue keeps its panes. A local failure still fails the aggregate, matching pty:listSessions. The restored-orchestration-authority sweep now runs after that rescue, so a pane the controller still vouches for keeps its handle instead of losing it to a listing that merely omitted it. STA-517 * test(runtime): assert the provider scope, not the exact arity, of inventory calls These assertions exist to prove which provider scope the inventory asked for. Forwarding the caller's deadline added a second argument, which broke them on arity alone. Match the scope argument and require a numeric deadline beside it, so the intent is preserved and the budget is covered too. * test(runtime): type the inventory mock's scope parameter A bare `async () =>` mock types mock.calls as an empty tuple, so reading the scope argument off it fails typecheck. Declare the parameter the runtime actually passes. |
||
|
|
545b3fba08 |
refactor(shell): build every zsh startup wrapper from one shared builder (#15245)
* test(shell): pin every generated shell wrapper file with byte snapshots Captures .zshenv/.zprofile/.zshrc/.zlogin, the bash rcfile, and the fish init command for all three transports (local PTY, daemon/SSH, relay) so the upcoming wrapper unification can be proven byte-for-byte identical. * refactor(shell): build every zsh startup wrapper from one shared builder Local PTY, daemon/SSH, and relay each had their own copy of the zsh ZDOTDIR wrapper templates, and the copies had drifted. buildZshStartupWrapperFiles now produces .zshenv/.zprofile/.zshrc/.zlogin for all three, with every real difference expressed as a field on ZshStartupWrapperSpec. No behavior change: the generated text is byte-for-byte identical for every configuration, pinned by the snapshots committed in the previous commit (captured from the pre-refactor generators). |
||
|
|
7a695c70f1 |
test(e2e): harden triaged CI failures (#14656)
* test(e2e): harden triaged failures
* test(e2e): ship relay bundle to reusable shards
* test(e2e): tolerate expected IPC closures in daemon shutdown
A normal client exit can close the IPC channel before the finish ack
lands. Distinguish this from real failures by checking error codes,
only throwing if forced cleanup occurred or the error is not an IPC
closure.
* rm doc
* test(e2e): return termination status from legacy close handler
- terminateLegacyCloseClient now returns a discriminated union indicating
whether the process had already exited ('already-exited') or termination
was actually attempted ('termination-attempted')
- Allows finishLegacyCloseClient to only set forcedCleanup when termination
was genuinely needed, not when the process exited cleanly on its own
* test(e2e): fix dispatch contract and voice mic locator
Point the release E2E contract at the renamed build step, and assert the
relabeled microphone through the Voice pane combobox even when Radix
leaves the listbox open.
* test(e2e): add contract test for relay artifact dispatch
Validate that the relay artifact built in CI is properly uploaded,
downloaded, and passed via ORCA_RELAY_PATH to E2E test runs.
* Distinguish between terminated and already-exited processes
Detect when processes have already exited instead of always reporting
termination success. Return booleans from cleanup functions to indicate
whether they actually signalled a process, catch tree-capture failures
when the root process exits before recording completes, and use these
signals to return accurate exit status from termination handlers.
* test(e2e): stabilize file creation and voice microphone tests
Use stable locators (aria-autocomplete, named triggers) and add retry
logic to handle file scans and device events that can interfere with
listbox state. Increase timeouts to allow async operations to complete.
* Add retry logic for transient GitHub API errors in PR body updates
GitHub API occasionally returns transient 5xx errors. Retry up to 3 times
with exponential backoff (1s, 2s, 4s) to improve reliability during
temporary service disruptions. Export updatePullRequest and add sleepImpl
parameter for test injection.
* Add tab search result retention during typing
Keep search results on screen while the deferred query catches up with
the live query. Re-validates results against the current input without
dropping rows prematurely, ensuring the user can select from what they see.
* Add proper types to tab search mock
Replace `unknown` with concrete types (`OpenTabSearchResult`,
`OpenTabSearchEntries`, `SearchableWorkspaceTab`) and use type guards
for discriminated unions to improve test type safety.
|
||
|
|
640e8a4322 |
test(updater): await the linux install re-proof instead of budgeting turns (#15246)
`settleQuitAndInstall` gave the pre-install digest re-proof a fixed budget of 40 real event-loop turns. That budget is wall-clock, not work: the re-proof does two realpaths, an lstat and a streamed sha512, and on a loaded CI runner those outlast ~40ms of setTimeout(0) turns. When they did, the test asserted early and its unfinished install continued inside the *next* test — against the same mock singletons, since `vi.resetModules()` only affects later imports and leaves the old module instance running. Hence the reported pair: one case missing `post_commit_cleanup_failed`, the next seeing killAllPty called twice. Wrap `revalidateLinuxPackageForInstall` for every test in this describe (the wrapper delegates to the real implementation, so artifact state stays real) and await the promise it hands back, then drain again in `afterEach` so no re-proof can outlive the test that started it. `holdRevalidation` folds into the same probe as an opt-in mode. The turn loop stays as slack for microtask-only tails, but is no longer load-bearing: with the loop set to zero turns the suite still passes, where before the fix 11 of 19 cases failed with the reported assertions. Fixes #15243 |
||
|
|
9b1f0373eb |
fix(relay): scope shell history for Windows -> WSL panes (STA-4682) (#15236)
`injectRelayHistoryEnv` matched only bash*/zsh*, so a relay pane launched through `wsl.exe` got no HISTFILE at all and every WSL worktree shared one global history. The history file stays on the relay host under the existing flat root, so `deleteRelayHistory` remains the deletion counterpart unchanged; only the exported path is translated to drvfs for the guest, and WSLENV carries it across the boundary. Guest fish stays out of scope on purpose: its history file lives inside the distro, where the relay has no deletion path. |
||
|
|
bb09dc1749 | fix(mobile): escalate a persistently rejected Relay pairing to re-pair (STA-4681) (#15237) | ||
|
|
c40b0ab96b | fix(dev): stop macOS Keychain password prompts on pnpm dev (#15183) | ||
|
|
2b057eb21e |
fix(terminal): end a url at CJK punctuation instead of swallowing it (#15240)
Terminal URL detection treated every non-ASCII character as part of the url, so Japanese or Chinese text written straight after one was absorbed into the link. A line like PR: https://github.com/org/repo/pull/12345(作成済み・マージ待ち) underlined the whole run and opened the annotation percent-encoded onto the end of the url, which 404s. The body terminator listed only ASCII codes, so nothing at or above 0x80 could end a url. Terminating on all non-ASCII is the obvious repair and is wrong: a url path may legitimately carry unencoded CJK, and the wrapped-url tests cover exactly that - a real multi-line url whose continuation row is a Chinese path segment. That repair was written first and broke four of them. The distinction is punctuation, not ASCII. Non-ASCII punctuation, symbols and separators end a url; letters do not. Full-width brackets, an ideographic space, a full-width comma and the katakana middle dot are prose; 文档 in a path is not. Adds the extraction test file the module never had, covering the reporter's three cases, the CJK-path case that must keep working, and ASCII behaviour as a regression guard. Closes #10571 |
||
|
|
41ab3b825a |
ci: run the IME e2e suite when terminal input code changes (#15239)
PR e2e only runs specs whose own spec file changed, plus two explicit source-to-spec mappings. Neither covers the terminal pane or the xterm patch, so every terminal IME fix in the 1.4.18x window shipped without triggering a single e2e spec - including one whose diagnosis was later refuted by hardware, and one that turned out to fix a different bug than it claimed. The suite it skipped is not thin. It drives real compositions over CDP, reads real pty bytes, asserts real overlay geometry, and exercises the macOS-only input path on Linux runners through a user-agent policy override. It simply was not pointed at the code it covers. The SSH block immediately above records the same lesson from the same cause: "the Docker-SSH specs only ever ran when someone edited a spec, so four pane-restore regressions shipped from SSH source edits that touched no test." This applies it to terminal input. config/patches is included because the terminal's composition and key handling live in the xterm patch, so a change there is exactly the kind this suite exists to catch. Verified by replaying the filter against the merges that skipped it: 15218, 15223 and 15198 each now select six specs. |
||
|
|
9a41119a99 |
feat(crash-reporting): read-only Windows install-dir DACL probe breadcrumb (#15107)
* feat(crash-reporting): read-only Windows install-dir DACL probe Records whether the install tree carries an orphan S-1-15-2-* package ACE with no S-1-15-2-1/-2 to satisfy it — the state that reproduces the 0x80000003 GPU/renderer init crash 10/10 (electron/electron#51761). Diagnostic only: never writes an ACL, never changes behavior. * fix(crash-reporting): evaluate the ACL signature per target and flag locale risk Three readiness-review findings: - the signature was merged across targets, so a grant on the directory masked its absence on the module file - the exact per-file state the probe exists to detect - the well-known package name check is English-only and icacls localizes it, so a non-English box could false-positive silently; report whether the check could be trusted - the serve-mode test omitted platform, so the gate was never exercised Also switch to the durable recorder (this runs after initObservability, so the span lands in the diagnostics bundle) and correct two comments that misstated where the probe runs. |
||
|
|
d143922561 |
fix(terminal): deliver an IME commit the deferred textarea diff missed (#15198)
Picking a single Chinese character from the candidate window with a number key loses it. The character flashes and disappears. Picking the same candidate with the mouse works, and picking multi-character words with number keys works. Two paths can deliver an IME commit, and this falls between them. A keydown the input method consumed routes into a setTimeout(0) diff of the helper textarea, and that diff is what normally delivers the commit; xterm's _keyDownSeen guard exists to defer to it. When the commit arrives after that timer has already run, neither path delivers. Mouse selection works because no key is down, and a real composition session works because it takes a different path entirely. That narrows it to an input method whose commit round-trips asynchronously and which shows no in-application preedit. Track that a consumed keydown still owes its commit, and deliver only when the diff did not. The upstream guard and its single read site are untouched, which is what keeps the duplicate-commit behaviour it was added for sealed. Not doing the obvious repairs deliberately: clearing the flag, skipping it for keyCode 229, or setting it after the composition short-circuit each unblock the input path without retiring the diff, and all three were measured emitting the character twice. The patch and the lockfile hash here are generated. Review config/patches/xterm-src/@xterm__xterm@6.1.0-beta.287.src.patch, which is the hand-written source of the change; the shipped patch and both minified bundles are the regenerator's output from the pinned upstream build, so nothing in this change was hand-transcribed into a bundle. Refs xtermjs/xterm.js#6036 Closes #12099 |
||
|
|
cdd3aabdd8 |
Display occupant agent icons in tab search results (#15134)
* Display occupant agent icons in tab search results When searching for or viewing open tabs, terminal tabs now show the icon of their occupant agent (e.g., grok) instead of a generic terminal icon. This makes it clearer which tabs have agents actively running. The occupant resolution reuses the same logic as the tab-strip agent identity system, including support for launchAgent, hook status, sleeping sessions, and OSC title parsing. * Simplify tab occupant agent resolution to use unified label only Remove the recordTitle parameter and rely solely on the unified title, which already carries live OSC titles. The terminal record's own title can stay stale (e.g., "Terminal N") while the unified label reflects the current state, eliminating duplication and simplifying the contract. * Add occupantAgent field to workspace tab helper |
||
|
|
314b02ba2d |
Redesign artifacts page as full-width table with drawer (#15233)
* refactor(artifacts): redesign as full-width table with detail drawer - Artifacts list displays as a compact data table with columns (Name, Type, Size, Updated, Expires) - Selected artifact opens in a right-side drawer instead of inline preview - Search and refresh consolidated in top toolbar - Better space utilization for browsing the artifact list * refactor(artifacts,automations): extract shared list-table layout - Extract common list-table styles (container, header, row) to @/lib for consistency across artifacts and automations tables - Move row interaction utilities to @/lib/list-row-interaction for reuse - Fix drawer width to calc(100vw-80px) to avoid macOS traffic-light controls - Extract WINDOW_CONTROLS_WIDTH/HEIGHT constants so portaled surfaces avoid the Windows/Linux overlay without hardcoding pixels - Clamp artifact search query to 2KB to prevent multi-MB pastes from pinning renderer memory - Remove unused artifact list visual mock * Extract shared artifact row actions and use CSS var for traffic lights - Unify dropdown and context menu actions via artifactRowActions() to prevent them from diverging during future maintenance. - Replace hardcoded 80px with platform-aware CSS variable (--mac-traffic-lights-width) so only macOS reserves space for traffic lights; Windows and Linux controls sit on the right edge instead. |
||
|
|
f48bdf8f59 |
fix(new-workspace): keep workspace creation reachable with zero projects (#15234)
The sidebar +, the landing Create button, the board lane +, the palette create row, and the tour CTA all disabled themselves when repos was empty — a dead end, since the composer's project field can add the first project inline and auto-select it. Drop the repo-count gate from every entry point. Submitting without a project still shows the inline "Choose or add a project" error. |
||
|
|
19ba83d496 | docs(mobile): add Android APK install guidance (#14978) | ||
|
|
598ba5d276 |
fix(terminal): stop the macOS IME forwarder from running on iPadOS (#15218)
* fix(terminal): stop the macOS IME forwarder from running on iPadOS Korean typed into a terminal from an iPad web client arrives as loose jamo instead of composed syllables. The native-text forwarder is a macOS workaround: it claims a printable keydown and delivers the input method's substituted text from the input event alone. It stands aside for IME composition by checking isComposing and compositionstart. Touch iOS/iPadOS is unreliable about firing those for hardware-keyboard CJK input, so each jamo keydown is claimed as its own one-shot substitution rather than deferred to xterm's composition handling. It runs there at all because every iOS user agent contains "Mac" - "Macintosh" in iPad desktop mode, the default since iPadOS 13, and "like Mac OS X" in mobile mode. maxTouchPoints is the only signal a real Mac never sets. The Linux branch immediately below already makes the mirror-image exclusion for Android and CrOS; the Mac branch never got the same treatment. Gate the forwarder install only. isMac keeps its other five consumers - the Ctrl+C interrupt, clipboard bypass, JIS yen input and the standalone 229 keydown policy - which intentionally still treat an iPad with a hardware keyboard like macOS, matching iPadOS shortcut conventions. Without the forwarder, xterm's own composition path plus the deferred textarea diff is the sole delivery mechanism, which is already the arrangement on Linux. Not verified on hardware: the claim that composition events are absent on iPadOS is the mechanism the code supports and the only one matching the reported symptom, but no on-device capture confirms it. The platform detection stands on its own regardless. terminal-ime-input-context-refresh.ts has the same user-agent collision for its NSTextInputContext refresh. Narrower trigger surface, left for a follow-up. Refs #13345 * fix(terminal): require more than one touch point before skipping the forwarder The gate used maxTouchPoints > 0, which is looser than the idiom already in this repo. isIOSWebView in mobile/src/terminal/terminal-webview-html.ts requires more than one, because a Mac with a touch-capable peripheral can report exactly one, and such a Mac must keep the forwarder: it is a real Mac running the input method this workaround exists for. Under the old threshold that Mac silently lost native text substitution - a macOS regression introduced by a fix aimed at iPadOS. A captured iPad reports five, so the stricter bound costs nothing on the device this targets. The check stays user-agent based rather than adopting that helper's platform check, because an iPhone reports platform "iPhone" rather than "MacIntel" and would slip through. |
||
|
|
49752477a6 |
build(xterm): restore the patch regeneration harness and gate it in CI (#15223)
* build(xterm): restore the patch regeneration harness and gate it in CI docs/reference/ime-architecture.md says "Never hand-edit the bundles in the patch" and links to docs/reference/xterm-patch-regeneration.md. That doc does not exist, and neither does the harness it describes. Both landed in |
||
|
|
63dbf12d14 |
Split github client (#15214)
* refactor(github-client): reorganize client into lifecycle folders * refactor(github-client): extract PR refresh data and outcome assembly Separate the derived data calculation and outcome assembly logic from branch-lookup-resolution into dedicated modules for better separation of concerns. Modernize type import syntax and format exports consistently. * refactor(github-client): improve error handling and resilience Defensive GraphQL parsing prevents partial responses from breaking REST fallbacks. Cache failures now use shorter TTLs for faster recovery. PR operations have dedicated error classification. GraphQL mutations track rate limit usage to prevent quota exhaustion. Data validation improved to reject spurious values. * Extract check rerun error classification with operation context Create classifyRerunChecksError() to provide operation-specific error messages when check reruns fail. This replaces generic GitHub error copy with context appropriate to what the user attempted (rerun checks). Follows the pattern of classifyListPrsError and improves error handling by delegating extraction to extractExecError. * Make check-rerun not-found error message resource-neutral Error handling for failed check reruns now covers both workflow-run reruns and standalone check-run rerequests. Tests verify the neutral message works for both scenarios. |
||
|
|
604169f4af |
Filter automations list by agents (#15224)
* Add agent filter to automation list Allows filtering automations by one or more agents with search support. Status and last-run filters are reorganized into submenus. External automation entries are excluded from agent filtering scope. * Fix translation keys for agent filter in automation list Move agent search text from AgentCombobox keys to component-specific AutomationListFilterMenu keys. Adds translations across all locales. |
||
|
|
3d29a2604e |
fix(terminal-history): drop an inherited Orca fish_history so nested Orca panes stop merging worktree histories (STA-4682) (#15195)
* fix(terminal-history): drop an inherited Orca fish_history (STA-4682) fish EXPORTS `fish_history`, so an Orca launched from a fish pane keeps the launching worktree's session name in process.env. Every fish pane of the nested app then hit the check-before-set early return and wrote into that one worktree's history file, in every worktree. Drop Orca-minted names (desktop and relay prefixes) wherever the session is injected, and in the history-disabled and daemon spawn paths; a genuine user value still wins. * fix(relay): drop an inherited Orca fish_history on every spawn path (STA-4682) injectRelayFishHistoryEnv runs only for a fish pane with history isolation on and a worktreeId, so a relay that inherited an Orca-minted fish_history kept it on every other path — scoping those panes to another worktree's history file. The desktop drops it on both branches; scrub it in buildSpawnEnv so relay spawn and revive match. Also record why injectWslFishHistoryEnv keeps its own drop (redundant with both current callers, kept as the function's precondition). |
||
|
|
e39def3825 |
fix(repo-identity): bound git remote-identity probes and retire them with their repo (#15196)
* fix(repo-identity): bound git remote-identity probes and retire them with their repo
The local `git remote -v` probe ran with no timeout and no signal, and the
runner only arms its kill timer when a timeout is passed, so a hung NFS/SMB
cwd or a wedged `wsl.exe -d <distro>` left the promise unsettled and the child
alive. Because the sweep is sequential and dedupes per location, that one
wedged location stalled enrichment for every other repo.
- probeGitRemoteIdentity/detectGitRemoteIdentity take `{ signal, timeoutMs }`;
local reads get the 5s background local-git-read budget, SSH gets a budget
under the relay's 30s request timeout. Timeouts/aborts still map to
`unavailable`, never `no-remote`, so they cannot clear a resolved identity.
- In-flight probes are tracked with an AbortController and retired (aborted +
dropped) when their location is no longer backed by a repo, so a re-added
repo is not poisoned by the dead entry and a retired probe cannot re-seed a
retry deadline.
- Added the missing sweep-level guard so repos:list / projects:list /
projectHostSetups:list coalesce into one pass instead of stacking one
sequential sweep per list IPC.
STA-4452
* refactor(repo-identity): bound the enrichment listener set to stable caller references
Every call site allocated a fresh onChanged closure, so the Set that notifies
coalesced sweeps deduped nothing: during a chain that never quiesces it grew one
entry per list IPC and multiplied the repos:changed broadcast. Hoist the closures
to stable references in ipc/repos.ts and OrcaRuntimeService, and state the
contract on the set.
Also: guard the synchronous retirement call so the fire-and-forget entry point
keeps its no-throw contract, drop the placeholder promise in favour of building
the in-flight entry in one shot, and make the coalescing test use a shared list
reference plus a distinct runtime reference so it detects both stacked passes and
a dropped caller.
|
||
|
|
e2b567363b | ci: stop refreshing every apt repo three times to install fish (#15217) | ||
|
|
ffb695b958 |
fix(daemon): stop a failed spawn cancel from tearing down the shared connection (STA-4663) (#15194)
* fix(daemon): stop a failed spawn cancel from tearing down the shared connection (STA-4663)
`onCreateCancellationFailure` fired on ANY rejection of the `cancelCreateOrAttach`
RPC, including its own 5s timeout and application-level `ok:false` replies. That
called `handleDisconnect`, which rejects every in-flight request and destroys both
sockets — killing every sibling session on the daemon.
Only an undeliverable cancel now escalates, signalled by the new
`DaemonConnectionLostError`. A refused or timed-out cancel falls back to the
existing bounded `unmatchedCancelGraceMs` wait and then rejects just its own request.
Also wraps the control-socket write so a synchronous throw drops the pending entry
and its timer instead of leaking them.
STA-4663's premise — that legacy daemons reject `cancelCreateOrAttach` as an unknown
request type — is incorrect; the handler has existed since protocol v11 (
|
||
|
|
3697d68f21 |
fix(cmd-j): decline a GitLab iid match when the repo remote names a different project (STA-4450) (#15193)
* fix(cmd-j): decline a GitLab iid match when the repo remote names a different project (STA-4450) `repoMatchesGitLabSlug` laundered a definite project-path mismatch into `'unknown'` whenever the resolved identity came from a remote named `upstream`, and `worktreeMatchesGitLabUrl` treats `'unknown'` as permission to accept a bare iid. Since `deriveGitRemoteIdentity` ranks `upstream` above `origin`, any repo whose top-ranked remote is `upstream` lost GitLab project gating entirely, so an exact URL for an unrelated project could surface that workspace. Return the `matchGitRemoteKeyParts` verdict directly. Resolved identities are re-probed on a 6h TTL, so a remote naming a different project is current evidence. `'unknown'` now means only "no identity" or "unexpanded SSH host alias", both of which stay permissive as before. * docs(cmd-j): correct the identity-freshness comments and drop a duplicate test The GitLab why-comment implied resolved identities refresh unconditionally. They only refresh when a repo/project list sweep finds one past its ~6h TTL (`selectEnrichmentCandidates` runs from `repos:list`/`projects:list`/ `projectHostSetups:list`, four refreshes per sweep, after a 5m startup delay); there is no background timer. State the accepted cost instead of implying the gate is loss-free. The GitHub-side comment still claimed the identity is "chosen when the repo was added and never re-probed" — the exact claim this PR disproves. Rewrite it to the reason that still holds (one stored remote hides a fork's `origin`). Behavior on the GitHub path is unchanged; it stays with the twin ticket. Delete `does not surface an upstream-identified repo for an unrelated project iid`: the inverted test above it already asserts both halves (mismatched project declines, the named project still matches) against the same upstream-derived identity. |
||
|
|
13b10e0b54 |
ci: cut PR wall clock by caching what CI recomputes every run (#15211)
None of these change what CI checks — they remove work the runners repeated on every PR. - install-node-dependencies installed with --no-frozen-lockfile, so every job re-resolved the graph against the registry to recompute what the lockfile already pins. Measured at ~62 MB of packument metadata per job; the pnpm store cache does not cover the metadata cache, so this was paid ~39 times per run. The `git diff` guard that made the re-resolution redundant stays. - --ignore-scripts leaves node-pty with no build/Release, so ensure-native-runtime node-gyp-compiled it in every job asking for a runtime. Cache the build under an ABI-bound key (runtime, resolved Node version, node-pty patch) with no restore-keys, since a partial match is exactly the mismatched build that would be recompiled anyway. - The four fetch-depth: 0 checkouts pulled full history including every historical blob (blobs are ~89% of this repo's pack). They only need the commit graph for a merge-base diff, so fetch them blobless. Measured 30-43s each today versus 8s for the shallow checkouts. One of them, e2e-paths, gates the entire E2E chain. - E2E jobs ordered setup-node before pnpm, which meant setup-node could not find the store and no E2E job cached dependencies at all. Reorder and cache; this sits on the critical path in both the build job and each shard. - git_compatibility rebuilt Git 2.25.5 from a pinned tarball on every PR. Cache the build; the sha256 assertion still guards the miss path. - typecheck ran three independent tsc passes back to back and discarded the .tsbuildinfo each project already emits. Run them concurrently and cache the incremental state. - package (windows) built the electron-vite targets serially via build:release. Use a :parallel variant that overlaps them, matching what the Linux package job already packages and smoke-tests from. Contract tests cover each new cache's ordering and key so none of them can silently start serving a stale or ABI-mismatched artifact. |
||
|
|
a54c27f00d |
Restructure automation editor dialog into three-column layout (#14803)
* Restructure automation editor dialog into three-column layout - Separate prompt editing from settings configuration - Add Monaco editor for prompt with find widget support - Extract settings into right sidebar for better organization - Move automation name into prompt section for context - Simplify header and footer to focus on key actions - Settings controls now smoothly collapse when switching between Orca and Hermes targets * Fix React Doctor leak on automation prompt Escape listener. Move addEventListener into a helper that returns cleanup so the changed-code quality gate can see the subscription is released. * Fix stale ref closures in automation prompt editor - Move `onDismissRef.current` update into useLayoutEffect with `[onDismiss]` dependency to prevent stale closures in event listeners - Move `contentRef.current` update into the layout effect that syncs it, ensuring editor has current value when effects reference it |
||
|
|
f8e728bb8b |
fix(watcher): watch the resolved worktree root so symlinked and differently-cased paths work (#15077)
* fix(watcher): keep macOS FSEvents paths under the subscribed worktree root
macOS FSEvents reports OS-canonical paths: symlinks resolved and every
directory in its on-disk spelling. Linux (inotify) and Windows both rebuild
event paths from the directory that was subscribed, so only macOS observes
the mismatch.
Orca's watcher contract is "event paths live under worktreePath". Consumers
derive a worktree-relative path with relativePathInsideRoot(), which returns
null when the event falls outside the root -- and a null relative path drops
the event silently. So on a Mac whose worktree or folder path traverses a
symlink (~/code -> /Volumes/..., anything under /tmp or /var), or is spelled
with different casing than disk on a case-insensitive volume, every watcher
event was discarded: the editor never reloaded an agent's edit, the File
Explorer never refreshed, and Source Control never re-ran status. Nothing
errored, which is why this looked like "the file watcher stopped working"
on some machines and not others.
Rewrite event paths back onto the subscribed root inside
subscribeThroughWatcherSupervisor -- the single boundary every desktop,
runtime-environment, and SSH-relay watch passes through -- so one change
covers all three transports.
The resolution runs alongside the subscribe rather than before it: an await
ahead of the subscribe call lets a caller's abort land in a window where no
watcher-process subscription exists to cancel, which hangs the existing
cancellation contracts. The subscribe promise settles only after the rewrite
is installed, and only after the subscription itself is recorded, so
teardown never waits on a realpath.
Matching folds per path segment (NFC + case) instead of by prefix length,
because both folds change length and a folded-prefix length would slice the
raw event path mid-character. Byte-exact fast paths run first, so unaliased
roots -- every Linux and Windows watch, and most macOS ones -- cost one
string comparison per event and allocate nothing.
* fix(watcher): watch the resolved root so symlinked worktrees work on Linux too
Verified on a real Linux host: @parcel/watcher passes IN_DONT_FOLLOW |
IN_ONLYDIR to inotify_add_watch, so a symlinked worktree root fails outright
with ENOTDIR ('Not a directory'). The watch never installs and Orca caches the
root in unwatchableRoots, so it is never retried for that session. That is a
worse symptom than the macOS path-spelling mismatch and hits Linux users of
symlinked checkouts on every machine.
Hand the backend the resolved directory instead of the caller's spelling, and
keep mapping delivered paths back. Resolving the root also lets
@parcel/watcher's own ignore paths match again on macOS, where they were
computed from the unresolved root and silently excluded nothing.
The resolve is synchronous on purpose. Every caller reserves and forks its
watcher child in the same tick as the subscribe call -- capacity accounting and
cancellation ordering both depend on it, and 30+ existing tests encode it -- so
an await here would open a window where a subscribe is issued but no
cancellable child exists.
* test(watcher): use a directory junction on Windows so the alias repro runs there
Creating a directory symlink on Windows needs elevation or Developer Mode, so
the alias tests failed with EPERM on a real Windows host. A junction needs
neither, is what users actually have (a junctioned C:\dev), and realpath
resolves it identically -- so one fixture now covers all three platforms and the
end-to-end repro no longer skips outside Linux and macOS.
* test(watcher): pin the fabricated-path failure modes of the root rewrite
A rewrite that returns a WRONG path is worse than no fix -- a consumer would
act on the wrong file -- so pin the cases that could produce one: sibling
directories that share a prefix with the root (POSIX and UNC), the root itself
versus a shorter path, drive-letter casing, a root-only canonical path, and a
script where toLowerCase changes length. Found by running the rewrite over an
adversarial table; all already passed, so these lock in behaviour rather than
fix it.
* docs(watcher): drop an unverified claim about ignore paths
I claimed resolving the root also repairs @parcel/watcher's ignore-path
matching for aliased roots. Probing it on macOS shows the node_modules write is
excluded either way: FSEvents resolves symlinks in its own exclusion paths, so
the daemon filters at the source regardless of which spelling we subscribe with.
On Linux the exclusions are userspace globs relative to the watched directory
and there was no watch at all before this change, so there is nothing to
compare. Removing the claim rather than leaving a plausible-but-wrong rationale
in the module header.
* refactor(watcher): simplify root path rewriter
* test(palette): build searchable fixture documents
|