* Remove repeated daemon smoke builds and idle compiler slots
* Test compiler scheduling through its process interface
* Keep preview component below the existing line limit with a type-only split
* chore: upgrade terminal dependencies to the matching payload betas
* test: harden xterm payload contract, align mobile pins, fix serialize-at-ref across version bumps
- Cover foreign-payload replacement and the shared blank-cell reset in the
installed image payload contract (both mutants previously survived).
- Bump mobile's xterm, webgl and unicode11 pins to the root's betas.
- Resolve the ref's own serialize version and patch so the differential
fuzz baseline builds across a version bump.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ci: ratchet synchronous child-process launches in main and shared code
The Electron main process freezes every window while a synchronous child
process runs, and on Windows each launch is slow because antivirus scans
it. Nothing stopped new synchronous launches from landing in main-process
code, and the spawn observer behind ORCA_MAIN_THREAD_DIAGNOSTICS only saw
async spawns, so the worst blocks were invisible to it.
Ratchet: check:main-sync-spawn-ratchet parses production src/main and
src/shared files (test-only paths excluded) and fails when a file not in
config/main-sync-spawn-baseline.txt reaches execFileSync, spawnSync or
execSync from child_process, runProcessSync from @orca/process-host, or
one of the synchronous secure-file writers (they run icacls on Windows).
Named, aliased, namespace, default, require() and import() forms count.
A baseline entry whose file no longer launches synchronously fails as
stale, so the list can only shrink. It runs in lint and in the PR
static-analysis job next to the process-host import check.
Observer: runProcessSync now reports to the spawn observer, timing the
whole call (that is how long the thread was stuck) and reporting from a
finally so failing, throwing and timed-out children are counted too.
Runtime behavior is unchanged except that the diagnostics observer now
sees synchronous launches; with no observer registered the sync path
pays one extra branch.
* ci: count sync launches per file and gate the repo's sync wrappers
The sync-spawn ratchet only tracked which files launched synchronously, so a
file already on the list (wsl.ts, win32-utils.ts, git-exec-file.ts) could add
more launches unnoticed, and new callers of the repo's own synchronous
wrappers passed CI.
The baseline now stores a per-file count of uses, in the same format as the
focus-read ratchet: a rise or a new file fails, a lower or zero count fails
until `pnpm check:main-sync-spawn-ratchet --prune` lowers it, and --prune
never raises an entry. Every value reference to a gated function counts
(call, hand-off, alias and its uses, re-export); imports alone count nothing.
Gated wrappers: gitExecFileSync (and the git/runner re-export),
restrictWindowsPathSync, hardenSecurePath when called with sync: true,
grantDirAcl, getWslHome, listWslDistros, wslUncDirectoryExists,
isWslAvailable (and the wsl.ts re-export), isPwshAvailable. Their 18
current caller files join the baseline.
Failure messages now give advice per family: the async process-host API for
child-process launches, the async twin for wrappers, and for the secure
writers a note that no async secure writer exists yet, so the sync one stays
and must never become a plain write (that drops the Windows ACL hardening).
Also refresh two stale blockMs descriptions and drop a no-op line from the
spawn-observer "stays silent" test.
* ci: count hardenSecurePath unless visibly async, and tighten ratchet tests
hardenSecurePath was counted only when `{ sync: true }` was written inline,
so moving the options into a variable or a spread dropped the count and the
check then asked for a prune. It now counts every use except a call with no
options or a spread-free object literal whose `sync` is absent or false.
Also: resolve backslash paths in the importing file as well as the
specifier; still visit heritage and instantiation expressions
(`extends mixin(fn)`, `fn<T>`); state the reference-only counting limit;
and point the secure-writer advice at raising this file's baseline count.
Tests cover the hardenSecurePath shapes, the alias chains that need the
fixpoint loop, and a prune that must not raise a grown row.
* test(ci): pin the main sync-spawn ratchet background step
* fix: count secure hardening calls with spread arguments
* feat(editor): run Nim and Typst on the shared TextMate layer with per-line limits
The editor's Nim and Typst highlighting moves from vscode-textmate onto the
Shiki core + Oniguruma layer chat uses, with the same per-line length cap and
time limit. A skipped line leaves the rest of the file plain instead of
freezing the window. Chat code block labels now name catalogue languages
(vue -> Vue, ts -> TypeScript).
* test(native-chat): expect the catalogue label for a ```ts fence
* refactor(layout): keep compatibility code in named folders behind one entry
Moves the old-client translators into src/shared/workspace-layout/old-client/
and the remote layout snapshot recorder and persistence into
src/main/remote-layout-snapshot/, each behind an index.ts. A new
check-compat-boundary-imports script, run by pnpm lint and the PR workflow,
fails when code outside a compatibility folder imports past its index or
when a file other than the folder's named importer imports it. Moves only;
no behaviour change.
* test: drop whole-repo scan from boundary check unit tests; CI step runs it
* fix(lint): list only existing compatibility folders; resolve renderer aliases in renderer files
* refactor(remote): one paired E2EE handshake core for desktop, CLI, web and mobile clients
Every paired client now builds its hello, checks the host's ready, seals its
auth frame and opens host frames through src/shared/paired-e2ee-client-handshake.
Platforms inject their own NaCl/base64 primitives. The v2 client session and its
pure-JS key schedule (@noble/hashes 1.8.0, the version mobile pins) move to
src/shared, and the per-host v2 latch policy is shared with an injected store.
No wire change: frames match the captured legacy and v2 fixtures byte for byte,
and a cross-version test drives the core against the released host and the
released client against this host. The auth step is now async so a device-key
signer can slot in later.
* fix(remote): keep @noble/hashes out of the CLI's unbundled runtime
The packaged CLI loads src/shared output from disk, so a bare @noble/hashes
import failed the packaged-runtime check. The v2 key schedule is now a
primitive: Node clients use the node:crypto schedule (moved to src/shared so
the CLI can reach it), while the browser and phone keep the pure-JS one.
* fix(mobile): re-check the v2 latch before sending a legacy auth frame
The auth step is async, so another socket can latch the host to v2 while a
legacy auth frame is being sealed. Refuse it at send time, as before.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* chore(ratchet): layout-writer ratchets for main and renderer
Two per-file ratchets that may only go down: main-process callers of the
Store's layout setters plus direct edits of saved session partitions, and
renderer writes of the window's layout and per-view selection fields
(including computed-key and spread writes, counted where the key or object
is built). The frozen-mirror writers are exempt. Wired next to the
focus-read ratchet in lint and the PR workflow.
* chore(ratchet): count only membership writes of editor and browser entries
A .map over the existing openFiles or browser tab list keeps its entries
and order, so it is a content update (draft, git status, URL) and is no
longer counted by the renderer layout-writer ratchet.
* chore(ratchet): ignore variable keys and pass-through shorthands
A variable key such as exitCodes[ptyId] names no field, so only string
keys count. A shorthand key counts only in an object handed to set,
setState or a Store writer; elsewhere it passes a value on.
* chore(ratchet): prune renderer baseline after main moved
Fold the empty owner-routing baseline into the focus-read ratchet. The three
deleted focus-routing helpers stay banned as a forbidden-name list.
Co-authored-by: m4air <m4air@Mac.localdomain>
* perf(ci): skip unnecessary alias scans and compare preflight compilers
* perf(ci): overlap measured Bun typechecking with static checks
* perf(ci): use measured Bun lint runtime and strengthen compiler parity checks
* wip: delete the SSH relay server and its build, assets and tests
* wip: drop main/relay plugin conformance; WSL reader lookup test
* fix: drop the emptied relay bench; prune baselines
* test: keep the native halves of tests that also covered the relay
* chore(ci): drop shard timings for the deleted relay server tests
* fix: keep the directory listing budget that darwin terminal names now import
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* wip: connect fails for hosts the managed server can't serve; remove relay session, deploy and relay UI
* wip: delete relay-only client modules left unreachable, prune their tests and gates
* wip: format
* wip: exhaustive unserved message
* wip: drop relay attestation test for the deleted ownership proof
* fix: prune stale baselines; type the test transport stub
* fix(ssh): census proves leftover relay leases; name the remedy for older terminals; translate every connect-failure surface
* test(ssh): leftover leases defer to the host census
* test(e2e): remove relay-path SSH e2e specs and their CI lanes
* test(e2e): drop the auto-convert case that built its relay era through a live relay
* test(e2e): mark relay-era browser and catalog journeys fixme until they seed a profile
* test(e2e): seed relay-era browser and catalog journeys from a saved profile
The three journeys built their old-version state through a connect without an orcad template, which now refuses the host. They now start from a seeded relay-era profile, so the first connect converts the host as an upgrading user's would.
* chore(ci): drop shard timings for the deleted relay tests
* test(e2e): carry a relay-era login cookie across the browser conversion; own every launch
* test(ci): drop the deleted SSH layout oracle from its route
* test(e2e): open the retained browser after conversion, retrying a pre-connect card
* test(e2e): bind the relay-era browser partition before seeding its cookie
A relay-era build always recorded the partition binding before a page stored
data in it. Seeding the cookie into an unbound partition made the binding store
refuse it (browser_route_partition_binding_store_invalid), so the retained tab
showed the generic routing card and Retry could never clear it.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
* refactor(runtime): route adapter, split fences, ownership table, combined ratchet
Adds callHostRoute, which calls a host-owned method on the host a HostRoute
names and reports a reply whose host identity moved mid-call as unverifiable
instead of applying it. Host fences are split: an identity fence (pairing,
runtime connection generation, the route's own SSH target) for mutations, and
the catalog data fence built on top of it for snapshot reads, so unrelated
catalog or SSH-state churn no longer moves a mutation's fence.
HOST_OPERATION_OWNERSHIP classifies every runtime RPC namespace. A new
ratchet counts getActiveRuntimeTarget( + legacyRouteFromSettings( +
settingsForRuntimeOwner( together per renderer file (227). One exemplar, the
folder workspace review-note write, moves onto the adapter.
* fix(runtime): fence the folder note write on its server; refuse aliased ratchet helpers
The folder catalog row lives on the paired server, so its write is fenced on
that server's identity, not on the SSH target the folder names. The ratchet
now counts spaced calls and value uses, and refuses an aliased import or
re-export that would hide calls from the count.
* test(ci): pin the owner-routing ratchet step to the static phase
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging.
Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan.
Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews.
Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.
* feat(native-chat): highlight code blocks and shell commands
Uses Shiki rather than the highlight.js already in the preview: it can
resume mid-block while a reply streams.
* fix(native-chat): leave the tool row command unhighlighted
* refactor(native-chat): highlight code through a shared TextMate interface
Chat code now goes through one renderer highlighting interface built on Shiki
core and the Oniguruma WASM the editor already loads for Nim and Typst, with
VS Code Light+/Dark+. Lines that are too long or too slow leave the rest of
the block plain, failed grammar loads retry from a mounted block, every block
shares one time slice, finished-block caching counts retained text, each theme
keeps its own formatting, Vue template expressions are colored, and tool-row
commands take the grammar of the shell they ran in.
* chore(deps): drop pnpm self-install entries from the lockfile
Keeps the lockfile change to Shiki alone; the earlier commit picked up the
@pnpm/exe entries pnpm 12 writes when it runs in a fresh checkout.
* test(native-chat): give cold grammar loads time in highlighting tests
The first colored block in a test file loads its grammar and the regex
engine, which took over a second on a busy CI runner.
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* feat(native-chat): pace and fade streamed reply text
Snapshots land a clause at a time, 50-700 ms apart. Drawing behind them
at an eased speed is what makes a reply read as flowing.
* feat(native-chat): glide the transcript when following and jumping to latest
The offset is still pinned at once; only what the pin moved is eased, so
nothing races the virtualizer or a reply that keeps growing.
* chore: list remend with the other bundled renderer packages
dependencies is for packages the main process loads at runtime.
* Bound streamed reply reveal and preserve chat interactions
* Smooth overlapping chunks without postponing reveal deadlines
* Fix first-reply following and pin settled layout changes
* Pin concurrent media layout changes without a follow glide
* Keep native chat layout stable when replies complete
* Model delayed browser scroll events in navigation tests
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* Schedule unit tests across runtimes by measured duration
* Keep new SQLite fixture suites on Node after updating main
* Inject scheduling timings instead of mocking the module
* Keep agent-session database lifecycle contracts on Node
* test(e2e): add terminal layout parity check for topology refactor PRs
Runs fixed terminal-layout journeys in the real app on two builds, captures
the renderer topology and the saved workspace session, normalizes volatile
values and fails on any difference not declared for a named bug.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): record quit exit status and report paths main does not reproduce
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): invoke pnpm correctly under corepack and silence the typeless-module warning
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): accept pnpm's forwarded -- in the layout parity runner
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): close parity panes through the user's chord and treat absent maps as empty
Driving PaneManager.closePane directly left main to learn of the close from the
PTY exit, which raced quit; the keyboard path commits the close in main first.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): build parity checkouts before running, forward -g, and add a drag-out scenario
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* perf(ci): share release JavaScript across packaging hosts
* fix(ci): verify the projected web entry in release archives
* fix(ci): use the Windows system archive tool for release bundles
* test(ci): retain stylesheet evidence in release build comparisons
* test(ci): verify release parity across native color rounding
* test(ci): normalize manifest asset references without changing import order
* test(ci): compare portable outputs across Windows text and color formatting
* test(ci): preserve module identity across dependent asset hashes
* fix(ci): keep SVG build inputs identical across release hosts
* fix(ci): stabilize compiler inputs and projected web bindings
* fix(ci): retain vendor minification in projected web output
* test(ci): normalize platform-specific pnpm manifest source paths
* fix(packaging): exclude shared build staging from application files
* test: align thinking-state fixtures with the current source shape
* test(mobile): reuse message fixtures within the line limit
* Run Vitest on Bun while preserving Node runtime contracts
* Preserve runtime timing provenance and keep the Bun pin in config
* Scope builtin compatibility mocks to test-only lint exceptions
* Give capture retention fixtures distinct filesystem timestamps
* Await the copy button success state in the React fixture
* Bound Node test worker shutdown and tighten migration fixtures
* Add standalone ACP protocol client and session runtime
* Protect ACP transport teardown from late stream errors
* Retire incoming ACP request ids before publishing responses
* Narrow ACP configuration requests and transport message types
* Remove redundant ACP request handler return unions
* Keep ACP waits caller-owned and preserve protocol extensions
* Preserve open ACP decisions through prompt completion
* Generate open ACP enums and check the generated schema offline
A newer or vendor enum value (tool kind, tool status, option kind, stop
reason) no longer fails the whole message: generated enums accept the known
literals plus any other string, typed so callers can still narrow on the
known ones. The generated header now records the pinned input digests, the
generator digest and a body hash, so `verify:acp-protocol` catches a stale or
hand-edited file without network access; it runs in lint and the PR workflow.
* Land the ACP runtime contract the agent adapters use
- Deliver notifications other than session/update through
onExtensionNotification, in arrival order with session updates.
- Accept _meta on prompt, setMode, setModel, setConfigOption and cancel.
- cancel() always sends session/cancel once the session runs, since the
agent can be in a turn it began itself; only a successful send is shared,
so a failed write is retried.
- Cancel aborts each open agent request's signal and lets its handler send
its own answer; -32800 only when the handler rejects.
- Permission requests validate only the session, tool call id and options;
unreadable fields are dropped with a diagnostic, and any answer Orca
cannot send is `cancelled` instead of a JSON-RPC error. Agent-started
turns may ask; whether to show it is the caller's decision.
- AcpAgentError marks the agent's own errors; AcpInvalidResponseError keeps
the raw answer and validation issues for answers Orca could not read.
- Lines over the size limit are classified by prefix (shared with the Codex
reader): the owed request fails, an oversized agent request is answered
with an error, and an unattributable response closes the connection.
* Answer every agent request after an ACP cancel
A cancel that lands before a permission handler starts now still runs the
permission path, so the agent gets the `cancelled` outcome rather than a
request-cancelled error. A handler that ignores the abort no longer leaves
the agent waiting: once the abort has run through, any request still
unanswered gets request-cancelled. Handlers that answer on abort keep their
own reply.
Also renames a lint-rejected helper parameter, replaces a Reflect.apply in a
test, and stops the permission diagnostic from firing with an empty list.
* Let each ACP request handler own its answer after a cancel
Removes the next-event-loop-turn fallback that answered request-cancelled
for any handler still silent after a cancel. It raced answers that were
still being saved (an approval mid-journal-write reached the agent as an
error) and made the outcome depend on event-loop timing. The handler that
owns an agent request now always sends its answer, or throws for
request-cancelled; a request it never answers ends when the connection
closes. A permission whose handler had not started still answers
`cancelled`.
* Register the ACP schema verify step in the PR preflight phase test
* feat(acp): a steer's cancel asks once and never ends the agent
The runtime had one cancel: send session/cancel, wait at most 10 s for Orca's prompt to settle,
then close the connection, which ends the agent. A steer used it too, so a slow agent lost its
process just because the person added a message. requestSteerCancel() now sends session/cancel
once per prompt, cancels the agent's open requests and answers later permissions cancelled, and
never bounds or closes: the prompt's own reply ends it and the steer's prompt follows. cancel()
stays the Stop: bounded, then close. A Stop after a steer still bounds and closes. Both cancel
paths move into acp-prompt-cancel.ts over one cancel channel.
* fix(acp): a repeated steer shares the cancel in flight; say what the caller owns
Per review: a second steer before the first write lands returns that write instead of resolving
early. The steer's JSDoc says the wait for the prompt's reply is unbounded and that a prompt that
fails instead must not take the steer until the caller rebuilds the session; the Stop's says a
prompt that settles in time leaves the agent for the Stop's owner to end. The steer test now gives
the runtime a handler that would allow: the open permission's signal aborts and the late one never
reaches it.
* test(ratchet): require src/main/acp now that this PR lands it
* fix(feedback): shrink oversized screenshots instead of refusing them
Fixes#22776
* fix(feedback): release each shrink canvas and skip the PNG ladder for a JPEG
Two defects in the attach-time shrink ladder:
- Every step allocated a fresh full-size canvas and left it for the garbage
collector, so a 32-megapixel source could keep six of them alive at once.
Past the renderer's canvas memory budget Chromium hands back a blank canvas,
which would upload as a blank screenshot with nothing to notice it. Each
step's canvas is now released once toBlob has answered.
- A JPEG source ran two full-size PNG encodes first. A PNG of decoded JPEG
noise is many times larger than the source it has to undercut, so those
steps could only ever burn time and a large buffer before the first JPEG
attempt. A lossy source now starts at the JPEG steps.
* fix(feedback): keep the image read queue usable after a settle callback throws
The queue tail is what the next batch awaits. A throw in either settle callback
left it rejected, so every later attach short-circuited into "Could not read the
attached images" without reading anything — and the rejection went unhandled.
* feat(feedback): say when an attachment is still being prepared
Shrinking a full-screen capture takes long enough that the gap between picking
the file and its thumbnail appearing reads as a dropped attachment: the only
other signal was the Send button quietly staying disabled. The attachment hint
now reads "Preparing attachments…" while a batch is in flight.
* fix(feedback): report an undecodable oversized image as invalid, not too large
* fix(feedback): detect an animated PNG by walking its chunks, not a 64 KB text scan
* docs(feedback): stop claiming toBlob encodes off the renderer thread
* refactor(feedback): seed the image read queue ref directly
* test(feedback): cover the real decode, downscale and release path of the shrink
* perf(feedback): skip image batches still queued when the dialog unmounts
* fix(feedback): show the preparing hint only once a read outlasts a short delay
* test(feedback): pin the animated-PNG walk on corrupt lengths and a refused image's budget
* fix(feedback): blame the shared budget, not the file size, when a shrinkable image has no room
* fix(feedback): cap each shrunk screenshot at half the attachment budget
A shrink kept the largest step that fit everything left, so the first
full-screen screenshot took a 3.8 MB PNG, the second fell to a barely
readable JPEG and the third was refused. Re-encodes now target at most
2 MB; an image that already fits is still attached untouched. The
"larger than 4.0 MB" refusal now applies whenever the capped target an
empty budget would give was the one that refused it.
* test(feedback): pin the half-budget shrink cap independently of its constant
* fix(feedback): reserve a shrinking screenshot's capped size, not its file size, for the paste gate
A 6 MB screenshot still shrinking reserved 6 MB, more than the whole budget,
so a second paste during the shrink fell through to the textarea even though
it attached. Each pending file now reserves what the reader can commit under
the same fit and shrink-target rules. Also corrects the minimum shrink target
comment to the measured smallest step.
* docs(feedback): correct the canvas-release and shrink-queue test comments; mock toast.info in the dialog test
* Revert "refactor(feedback): seed the image read queue ref directly"
This reverts commit e2272b825eb2d26286aee3a99ca28f3ea984508e.
* fix(feedback): keep preparing hint compatible with current main
* fix(feedback): preserve mixed paste text and seed read queue on attach
* fix(feedback): preserve screenshot detail and pending paste text
Optimize oversized attachments only as full-resolution lossless PNG; refuse them with existing warnings when that cannot fit. Bound each pending batch independently so earlier optimization slack, rejected files, or removed drafts cannot under-reserve bytes and swallow co-pasted text. Regression tests demonstrate the old mixed-batch text loss.
* test(journal): retain provider handle import across main integration
* test(startup): account for encoded Windows prompt lines
* test(compatibility): retain the released journal parser dependency
---------
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* Add host-owned OpenCode and Devin account profiles
* Manage OpenCode and Devin profiles in account Settings
* Expose registered account roots to host transcript readers
* Clarify managed profile provider flags
* Retain isolated Electron home in browser sidecars
* Restore inherited account environment and preserve cleanup retries
* Check relay environment values before merging
* Consolidate managed account type imports
* fix(accounts): use existing localized provider names
Align the new Japanese account copy with the existing catalog repair policy.
* Keep managed account baselines private to the execution host
* Align account enrollment help with accepted providers and flags
* Show the active System account in managed profile lists
* Document the validated Linux managed account scope
* Fix managed account removal, credential audits, and runtime bundling
* Quarantine removed accounts and audit captured credential snapshots
* Continue Antigravity IDE and 2.0 history in new CLI conversations (#24692)
* feat(antigravity): bridge IDE history into new CLI conversations
* fix(antigravity): preserve fresh-launch model and environment for IDE references
* fix(antigravity): forward IDE history opt-in through desktop IPC
* fix(antigravity): rebuild remote IDE reference startup on its host
* fix(antigravity): register IDE continuation action labels
* fix(antigravity): confine IDE references and bound metadata reads
* fix(antigravity): localize IDE continuation badges
* Preserve scanner service cache assertions and refresh Antigravity opening metadata
* Preserve Antigravity opening joins and target folder runtime authority
* fix(opencode): retry timed-out SSH plugin updates (#24666)
Preserve bounded retry behavior and the current-main status-envelope fields.
Original-PR: #24124
Reviewed-source: 103144f9c5
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.
Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021
Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237
Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
* Skip store notifications when refreshed work items are unchanged (#24530)
An identical forced refresh previously returned an empty Zustand patch, creating a new root state and notifying every subscriber. It now returns the current state after renewing the existing freshness timestamp. Real row or metadata changes still publish once.
* Read project activity once while sorting the sidebar (#24549)
Reuse the existing pure recent rank once per actual entry tuple within each synchronous sort; discard the lookup after sorting.
* Skip impossible link and tag matches in docs search results (#24646)
* Skip impossible HTML matches when formatting docs search results
After the existing link-removal phase, skip the unchanged HTML regex only when the current string has no closing delimiter.
* Skip impossible link and tag matches in docs search results
Skip the five unchanged link regexes when their protected-code input lacks ]; skip the unchanged HTML regex when its post-link input lacks >.
* Decode complete transcript lines without copying their bytes (#24546)
Decode each single owned Buffer synchronously; preserve concatenation for multipart records and remove a redundant tail array copy.
* Clear unused speech worker timers after errors and exit (#24924)
Call the existing idle timer cleanup when private current-worker state is cleared; keep current-worker guards, transcript/error order, deliberate warmth and stop deadline unchanged.
* Reuse documentation search excerpts during result navigation (#24574)
Memoize the existing pure excerpt renderer by complete raw text for the current result array, retaining original rendering as a miss fallback.
* Append subagent handoffs without recopying their message list (#24672)
Append each message reference to its private call-local scope list; retain the final merge and existing ordering.
* Cancel plugin retry timers when their fetch is replaced (#24700)
Reuse the existing timer clear block immediately after the fetch generation advances; preserve live retries and all state publications.
* Avoid rebuilding visible file paths for single-row selection (#24743)
Skip the visible path array only for keyboard replacement selection; the existing replacement branch never reads that order.
* Reuse prepared reads while searching saved agent conversations (#24535)
Use schema-complete session columns to enable the existing bounded statement cache without caching result rows.
* Reuse discovered mobile chunks during static traversal (#24774)
Iterate the existing live visited Set in FIFO discovery order instead of maintaining a second shifted queue; both actual callers own untouched esbuild JSON metadata.
* Skip unused image-size calculations in mobile web browser requests (#24941)
Use the existing mobile density budget only for mobile view; pass the existing constant to the existing assembler for web/default mode, where that argument is discarded. No cache, policy, request or native path changes.
* Skip diff analysis after a result has been discarded (#24711)
Move the unchanged pure render-limit calculation after both existing generation and section-token rejection fences.
* Skip late browser grab toasts after their surface closes (#24727)
Reuse the existing mounted-owner ref at the toast presentation entry while preserving all admitted extraction/screenshot and clipboard completion.
* Classify native chat waiting messages once (#24771)
Use the existing native filter traversal to populate a fresh waiting array, keeping the original predicate, policy, projection and output ordering while classifying each actual private slot once.
* Skip discarded Kanban pruning for an empty selection (#24782)
Guard only the existing open pruning branch when both its private selected Set and nullable anchor are empty; retain all original pre-guard projections and nonempty/anchor-only/public-helper behavior.
* Index discovered test files once during shard selection validation (#24532)
Verified selection plans previously validated each selected filename with a scan of all discovered files. One per-call Set now handles membership checks. Exact source/discovery checks, nonempty selection, fallback to all tests, shard balancing and manifests remain intact.
* Clear the watchdog benchmark heartbeat when CPU sampling fails (#24802)
Move the existing initial CPU observation and histogram enable inside the existing try/finally so their failures clear the sampler's owned heartbeat, preserving successful operation order and original errors.
* Reuse the parsed notification when leasing a push delivery (#24645)
* Reuse the parsed notification when leasing a push delivery
Pass the notification already parsed for the dismissal check into the existing private delivery builder.
* Reuse the parsed notification when leasing a push delivery
Pass the notification already parsed for the dismissal check into the existing private delivery builder.
* fix(accounts): canonicalize account removal to rm
Use account rm as the canonical removal command and keep account remove as an alias. Preserve the existing accounts.removeData RPC and the full original 63-path account component.
Original-Account-Source: cf71ae4cb6
Frozen-Account-Base: 08ee7ba9ef
Frozen-Integrated-Main: 53f9ea7839
Private validation source only; no ref or publication.
* Update README downloads badge
* Let retired-cache GC observations settle across the existing six-turn budget (#24967)
* Collect test-selection evidence when full unit tests fail (#24955)
* Collect advisory unit-selection evidence from failed full runs
* Trigger checks after retargeting the evidence fix to main
* Check each project repository once while filtering mobile cards (#24540)
Reuse exact raw source/slug matching decisions within one project filter call, preserving the matcher, membership, negative matches, output order and identity.
* Skip renderer callbacks after their request has ended (#24631)
Reuse the existing pending-request identity check before starting its deferred renderer callback.
* Decode single-piece saved-session tails without copying them (#24632)
Decode the existing owned Buffer directly when the EOF tail has one piece; preserve the existing concatenation for multiple pieces.
* Avoid irrelevant scroll-action searches in Mac snapshots (#24731)
Check the current pure action name before searching for vertical scroll actions in the same immutable array.
* Stop copying every retained browser page before attachment (#24553)
Find the first page/generation match directly in the existing insertion-ordered Map instead of copying all page references into an array.
* Reuse event byte sizes when relay watchers notify several clients (#24558)
Store the existing grouped numeric byte-size array on the already call-local watcher batch sizing object, for reuse by later chunking clients.
* Stop building unused import candidates in the test planner (#24611)
Replace the existing ordered extension map/find with a loop that forms each candidate only when its existing lookup is reached.
* docs(terminal): explain local macOS/Linux shell startup files
Document the actual login/interactive shell startup-file order and existing shell setup behavior.
Co-authored-by: brynnclaw <261708852+brynnclaw@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(editor): recognize Ruby task and configuration files
Add Ruby task/configuration filenames to the existing generated language associations.
Co-authored-by: ggbdpq <ggbdpq@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* Speed up large Markdown Find and render oversized tables (#24948)
* Speed up large Markdown Find and render oversized tables
* Poll table preview geometry outside hidden renderers
* Preserve Markdown navigation through refresh and tab restoration
* Confirm Markdown restoration when refreshed content is ready
* Trace table refresh positions and update Unicode search reference
* Recognize queued measurement scrolls before restoring Markdown anchors
* Rebuild Markdown Find ranges after renderer components change
* fix(source-control): generate clean OpenCode messages locally and over SSH (#24613)
* fix(source-control): generate clean OpenCode answers on local and SSH hosts
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: 64bb15e3f2
fix(source-control): generate clean OpenCode answers on local and SSH hosts
Use configured models and JSON answer/error events, preserve run-first arguments, and handle the precise v2 variant rejection. Hydrate SSH execution-host PATH through the existing bounded login environment resolver before direct spawning.
Credits: andy-murr (PR #5197 SSH environment intent) and coelho-doti (PR #13065 argument-order intent).
Original-commit: 1b60ec5d11
fix(source-control): retry inline OpenCode model and variant options
Original-commit: 98fdecc7a3
Preserve OpenCode named errors without a data message
Restacked-from: 98fdecc7a3
Restacked-onto: f7b1f9d8be
* fix(ci): prevent concurrent pnpm refresh during mobile typechecks (#24776)
* fix(ci): run mobile typechecks without concurrent dependency refresh
* test(ci): check effective Linux E2E package list
* test(ci): preserve the mobile production compiler barrier
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* test(terminal): restore the live fish fixture prerequisites (#24947)
A restored pane waits for the initial status replay before subscribing to
PTY output. This fixture never settled that replay, so fish printed its
mode-2031 arm before the renderer connected. Its PTY API also omitted the
reset-input listener required by the serializer, aborting attachment.
Settle and dispose the existing startup-snapshot registration and provide
the same reset-listener mock used by the other PTY tests. The real fish
child-stdin assertions and timeouts remain unchanged. No production change.
* fix(shortcuts): defer TUI editing chords in terminal-first mode (#24640)
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: f707cde14a
fix(shortcuts): defer TUI editing chords in terminal-first mode
Original-commit: 0c6348e49e
docs(shortcuts): describe deferred preview terminal chords
Original-commit: be62c1b6c6
Align worktree history shortcut metadata with terminal conflict policy
Restacked-from: be62c1b6c6
Restacked-onto: f7b1f9d8be
* Register supervised Qoder China and Qwen Code (#24616)
* Add Qoder session history and search with real CLI coverage
* Allow the real Qoder marker file to end with a newline
* Keep Qoder tool output out of history previews and search
* Keep Qoder search pages readable by older clients
* Verify persisted Qoder history after a real generated and resumed task
* Negotiate Qoder filters before searching an older execution host
* Combine search client imports for the CI plugin gate
* Keep the relay search oracle aligned with legacy agent filtering
* Register supervised Qoder China and Qwen lifecycle integration
* Cover Qoder China mobile assets and mixed-host resume gates
* Verify Qoder provider tags against the older released wire parser
* Verify China and Qwen keep independent Windows hook scripts
* Verify Qoder registrations against the installed older Windows release
* test(qoder): align search capability contracts and pin old-host fencing
* fix(qoder): rank exact picker identities and command aliases first
* test(qoder): preserve the regional CLI shared icon expectation
Keep the full bundled-asset and no-remote-image checks, with an explicit
shared-logo basename for Qoder China. The map also works with older
catalog type unions.
* fix(qoder): align China catalog entry with fallback order
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* Use the measured pnpm lookup policy automatically in hosted root CI (#24951)
* Select lookup automatically for the measured hosted root-install profile
* Record hosted automatic-mode cold cache publication proof
* fix: bound remote generation setup and honor OpenCode option terminators
Count execution-host profile resolution inside the existing request deadline, cancel its waiter promptly, and pass only the remaining time to the child. Shared bounded profile probes keep their existing cache lifetime; the SSH transport margin is unchanged.
Read OpenCode output format from active final-argv options before -- so literal prompt arguments cannot select the JSON finalizer.
Fresh exact-source controls reproduce nine failures before; 139 related checks pass after, including primary/fallback delays, deadline boundaries, cancellation, parser metadata, and SSH lanes. Node typecheck and strict changed-file lint pass.
* Continue Antigravity IDE and 2.0 history in new CLI conversations (#24692)
* feat(antigravity): bridge IDE history into new CLI conversations
* fix(antigravity): preserve fresh-launch model and environment for IDE references
* fix(antigravity): forward IDE history opt-in through desktop IPC
* fix(antigravity): rebuild remote IDE reference startup on its host
* fix(antigravity): register IDE continuation action labels
* fix(antigravity): confine IDE references and bound metadata reads
* fix(antigravity): localize IDE continuation badges
* Preserve scanner service cache assertions and refresh Antigravity opening metadata
* Preserve Antigravity opening joins and target folder runtime authority
* fix(opencode): retry timed-out SSH plugin updates (#24666)
Preserve bounded retry behavior and the current-main status-envelope fields.
Original-PR: #24124
Reviewed-source: 103144f9c5
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.
Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021
Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237
Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
* fix(opencode): enforce deadline through executable startup
Keep synchronous Windows PATH resolution and child startup within the existing request budget.
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(accounts): recover interrupted profile removal on startup
Scan private removal backups before quarantined cleanup, reuse the existing state schema to validate the exact UUID, and preserve registered or unmarked profiles. Mark account rm as destructive using the existing typo recovery policy. Retain the full original account component and public author ancestry.
Account-PR: 24636
Original-Account-Source: cf71ae4cb6
Reviewed-Public-Parent: 6abaf736e3
Frozen-Integrated-Main: 53f9ea7839
Private source only; no ref or publication.
* fix(persistence): preserve projectGroupOrder on restart for flat folder-scan groups
Retain saved project ranks when the project remains in its folder-scan group.
Co-authored-by: lurunzi <lurunzi@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(runtime): reclaim temp files orphaned by interrupted mobile store writes
Reuse the existing stale temporary-file cleanup policy when each mobile store opens.
Co-authored-by: LDH1103 <ldh517525@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(terminal): show actionable copy for missing folder workspace paths
Show the existing folder-path failure with concrete recovery instructions.
Co-authored-by: Wayn_Liu <wayntingliu@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* docs: reference local orca.yaml and .worktreeinclude
Document the existing workspace configuration, include/copy rules and sharing behavior.
Co-authored-by: Neil <neil@stably.ai>
* fix(orchestration): allow model selection for OMP workers
Pass an explicitly requested model through the existing OMP worker launch catalog.
Co-authored-by: Neil <neil@stably.ai>
* fix(cli): preserve primitive success results in JSON output
Check for an object before inspecting screenshot fields so primitive success results remain printable.
Related: https://github.com/stablyai/orca/pull/14735
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
* Show loaded file changes before deleting a workspace
Expose up to ten already loaded changed paths without altering deletion counts, hydration or authorization.
Related: https://github.com/stablyai/orca/pull/22778
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Michiel de Gooijer <mdgooijer@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(keybindings): record and match Option+digit shortcuts on macOS
Use the physical digit key for explicitly assigned Mac Option+digit shortcuts while retaining modifier checks.
Co-authored-by: marcuslannister <marcus@lannister.cc>
Co-authored-by: Neil <neil@stably.ai>
* fix(editor): don't throw closing a stale active file
Recover stale active-file selection within the owning workspace before choosing a surviving editor.
Related: https://github.com/stablyai/orca/pull/24715
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: Neil <neil@stably.ai>
* Fix Project Settings targeting for multiple local checkouts
Carry the selected checkout identity into the existing project Settings action.
Co-authored-by: fsmeier <1506919+fsmeier@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
* feat(documents): open CSV and TSV files from the OS
Extend existing OS document associations and delivery to CSV/TSV, preserving restoration and authorization.
Co-authored-by: Neil <neil@stably.ai>
* feat(cli): link GitHub and GitLab items on worktree create and set
Expose and validate the existing workspace link fields, preserving omitted values and provider identity checks.
Related: https://github.com/stablyai/orca/pull/22609
Co-authored-by: marco song <marco.song@mvlchain.io>
Co-authored-by: SongMarco <20613630+SongMarco@users.noreply.github.com>
Co-authored-by: Luca Critelli <lucacri@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* feat(sidebar): include folder workspaces in keyboard navigation
Use the rendered sidebar row order and host identity when cycling through folder and Git workspaces.
Related: https://github.com/stablyai/orca/pull/10555
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: JeongUk Park <jeongph.dev@gmail.com>
* fix(build): turn off MSBuild file tracking for Windows native rebuilds
Default Windows native rebuilds to TrackFileAccess=false while preserving explicit caller preferences.
Co-authored-by: B1nh M1nh <43268322+b1nhm1nh@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
* Fix Ctrl+M in Linux and Windows terminals
Keep the Minimize menu item but disable its accelerator registration on Linux and Windows.
Co-authored-by: Zhichang Yu <yuzhichang@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com>
Related contribution: https://github.com/stablyai/orca/pull/24143
* fix(startup): read a nushell login PATH from $env.PATH
Use Nushell login command syntax and preserve the actual login PATH value.
Related: https://github.com/stablyai/orca/pull/22677
Co-authored-by: Kh05ifr4nD <meandSSH0219@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(cursor): run local hooks through sh for non-POSIX login shells
Keep POSIX hook syntax inside one quoted sh command so the login shell can invoke it safely.
Related: https://github.com/stablyai/orca/pull/22663
Co-authored-by: Kh05ifr4nD <meandSSH0219@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
* Fix word wrap for both panes in side-by-side diffs
Forward wrapping to both diff panes through the existing editor option path and clean up listeners.
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(monaco): highlight Svelte block closers inside markup
Return Svelte block closers to the existing markup tokenizer state.
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(repos): keep active clone dialog open on outside clicks
Ignore accidental outside dismissal only while cloning; Escape, Close and Back still cancel.
Related: https://github.com/stablyai/orca/pull/24581, https://github.com/stablyai/orca/pull/23430
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>
* fix(editor): keep chat visible after closing Markdown tabs
Count remaining unified chat tabs before clearing workspace selection during editor close.
Related: https://github.com/stablyai/orca/pull/24273, https://github.com/stablyai/orca/pull/23760
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Honor typed starting numbers in chat ordered lists
Forward the existing parsed ordered-list start attribute to both chat Markdown renderers.
Related: https://github.com/stablyai/orca/pull/19765
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Frederic Barthelemy <git@fbartho.com>
* Normalize base source once while checking changed-code diagnostics (#24557)
Reuse the exact existing moved-code matching algorithm with run-local lazy normalization of immutable base source blocks across diagnostics.
* Support real OpenCode sessions in native Chat (#24647)
* Use bounded OpenCode context for vault session continuation
OpenCode database and synthetic row paths are not text transcripts. Use the
vault preview or captured pane context, preserving actual transcript paths
containing a hash and supporting both OpenCode lanes and Windows paths.
Adapted the intent of #11859 and extended it to actual installed v2 vault rows.
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
* Read real OpenCode sessions in terminal-backed native Chat
Reuse the bounded AI Vault SQLite worker for v1 and v2 session pages and live updates. Keep terminal input as the real execution path and pace OpenCode Stop through its two-Escape interrupt.
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
* fix(opencode): publish approval cards for permission requests
* Send OpenCode native approval through its Enter selector
* Resolve mobile Chat readability for folder workspaces
* Bound OpenCode part batches and preserve v2 image attachments
* Prefer live migrated OpenCode sessions over legacy copies
* Consolidate mobile Chat eligibility test imports
* Consolidate OpenCode SQLite protocol type imports
* Update native chat settings contract for both OpenCode agents
* fix(native-chat): reconcile bounded OpenCode transcript reads
* fix(native-chat): dispatch OpenCode questions safely
* fix(native-chat): keep native discovery and transcript windows current
* feat(accounts): link standalone GLM Coding Plans (#24618)
* feat(accounts): link standalone GLM Coding Plans
Adapt the reviewed GLM accounts contribution to current main, retain Antigravity behavior, guard late credential results, expose storage protection, and redact quota errors.
Co-authored-by: Luchong <lu740528977@gmail.com>
* fix(accounts): retain GLM credential results during quota refresh
* fix(accounts): make GLM credential editing desktop-only
* fix(accounts): mirror the host GLM site in paired clients
* fix(accounts): report unknown GLM host details and split web settings tests
Apply the independently reviewed Accounts correction from697284a without the v2 adapter commits. Preserve the saved-key store and serialized write behavior.
* fix(zcode): ship required GLM account translation entries
* chore: record GLM reconciliation hook validation
* chore: validate installed GLM commit hooks
* test: complete GLM account fixtures and web API inventory
---------
Co-authored-by: Luchong <lu740528977@gmail.com>
* fix(native-chat): route transcript requests through shared SQLite worker
* fix(ci): prevent concurrent pnpm refresh during mobile typechecks (#24776)
* fix(ci): run mobile typechecks without concurrent dependency refresh
* test(ci): check effective Linux E2E package list
* test(ci): preserve the mobile production compiler barrier
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* test(terminal): restore the live fish fixture prerequisites (#24947)
A restored pane waits for the initial status replay before subscribing to
PTY output. This fixture never settled that replay, so fish printed its
mode-2031 arm before the renderer connected. Its PTY API also omitted the
reset-input listener required by the serializer, aborting attachment.
Settle and dispose the existing startup-snapshot registration and provide
the same reset-listener mock used by the other PTY tests. The real fish
child-stdin assertions and timeouts remain unchanged. No production change.
* fix(shortcuts): defer TUI editing chords in terminal-first mode (#24640)
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: f707cde14a
fix(shortcuts): defer TUI editing chords in terminal-first mode
Original-commit: 0c6348e49e
docs(shortcuts): describe deferred preview terminal chords
Original-commit: be62c1b6c6
Align worktree history shortcut metadata with terminal conflict policy
Restacked-from: be62c1b6c6
Restacked-onto: f7b1f9d8be
* Register supervised Qoder China and Qwen Code (#24616)
* Add Qoder session history and search with real CLI coverage
* Allow the real Qoder marker file to end with a newline
* Keep Qoder tool output out of history previews and search
* Keep Qoder search pages readable by older clients
* Verify persisted Qoder history after a real generated and resumed task
* Negotiate Qoder filters before searching an older execution host
* Combine search client imports for the CI plugin gate
* Keep the relay search oracle aligned with legacy agent filtering
* Register supervised Qoder China and Qwen lifecycle integration
* Cover Qoder China mobile assets and mixed-host resume gates
* Verify Qoder provider tags against the older released wire parser
* Verify China and Qwen keep independent Windows hook scripts
* Verify Qoder registrations against the installed older Windows release
* test(qoder): align search capability contracts and pin old-host fencing
* fix(qoder): rank exact picker identities and command aliases first
* test(qoder): preserve the regional CLI shared icon expectation
Keep the full bundled-asset and no-remote-image checks, with an explicit
shared-logo basename for Qoder China. The map also works with older
catalog type unions.
* fix(qoder): align China catalog entry with fallback order
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* Use the measured pnpm lookup policy automatically in hosted root CI (#24951)
* Select lookup automatically for the measured hosted root-install profile
* Record hosted automatic-mode cold cache publication proof
* fix(native-chat): keep OpenCode history usable at read limits
Continue past failed database probes while preserving discovery cancellation.
Verify rows displaced by a capped tail before deciding whether to replace history.
Represent oversized v1/v2 rows with the existing omission text and stable cursors.
* Continue Antigravity IDE and 2.0 history in new CLI conversations (#24692)
* feat(antigravity): bridge IDE history into new CLI conversations
* fix(antigravity): preserve fresh-launch model and environment for IDE references
* fix(antigravity): forward IDE history opt-in through desktop IPC
* fix(antigravity): rebuild remote IDE reference startup on its host
* fix(antigravity): register IDE continuation action labels
* fix(antigravity): confine IDE references and bound metadata reads
* fix(antigravity): localize IDE continuation badges
* Preserve scanner service cache assertions and refresh Antigravity opening metadata
* Preserve Antigravity opening joins and target folder runtime authority
* fix(opencode): retry timed-out SSH plugin updates (#24666)
Preserve bounded retry behavior and the current-main status-envelope fields.
Original-PR: #24124
Reviewed-source: 103144f9c5
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.
Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021
Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237
Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
---------
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
Co-authored-by: Luchong <lu740528977@gmail.com>
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(accounts): protect registered UUID case variants during removal recovery
Compare validated account UUID identities without changing stored IDs or filesystem paths. Protect registered originals and quarantines, including explicit retry variants, and accept equivalent UUID spelling in a valid removal backup. Retain the complete account component and published contributor ancestry.
Private source only; no index, ref, or public mutation.
* Drain removal fixture jobs before resetting and deleting their records (#24977)
* Reuse measured Electron preparation for current Terminal Perf refs (#24968)
* feat(jcode): add Jcode as a supported TUI agent with managed hooks
Ports PR #10521 onto current main: agent catalog, managed hook service,
agent-status listener, session resume, AI Vault parser, per-pane daemon
isolation, and Source Control AI support.
Co-authored-by: Neil <neil@stably.ai>
* feat(jcode): evidence-backed status pipeline (turn_start, live pre_tool, questions)
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): register vault fixture, dynamic model discovery, script refresher
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): keep finished-turn detail so completion notifications fire
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): show the prompt in agent rows instead of jcode's repainting title
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): pre-warm the per-pane daemon so a cold runtime dir cannot time out
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): stop the OSC color skip from crashing every pane connect
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* refactor(jcode): fold three reverse-scan copies into one, reuse the shared hook POST
The jcode journal reader, the Claude transcript reader and the Command Code
transcript reader each carried their own copy of the same reverse chunked line
scan; they now share one tested helper. jcode's managed hook script drops its
hand-rolled curl for buildPosixAgentHookPostCommand, which also gains it the
raw-JSON transport and the --noproxy guard the bespoke copy was missing.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): narrow dynamic reads with predicates, pin the Windows hook shape
CI's anti-slop audit rejects Reflect.get: parse dynamic input into a named type
instead. Adds Windows script-shape tests too, since Windows is the platform this
change could not be exercised on directly.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* test(jcode): measure the gate against the synchronous path, not the clock
The absolute 1s bound was the flake CI shard 3/8 hit: it is tight enough to catch
a synchronous gate on an idle laptop and too tight on a loaded runner. Measuring
the same POST both ways on the same machine makes the claim a ratio, which is what
the test is actually about. Mutation-checked: a synchronous gate reads 6120ms
against a 1517ms observer.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* test(jcode): drop the wall-clock gate assertion
The bound was machine-speed sensitive and flaked on CI shard 3/8 at 1525ms. The
structural assertions (detached gate branch, foreground observer branch) and the
no-hang stdin drain cover the same contract without a timer.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): address CodeRabbit review on #22539
- Windows posted no payload at all: the shared builder reads `payload@-` from
stdin, which the gate has already drained and observer hooks never receive, so
every Windows event was dropped. Write the env var to a temp file and pipe it.
- The daemon pre-warm never fired for daemon-host spawns, which is the default
local path; it now runs there too, and from the final env so the daemon gets the
hook port and token.
- A failed runtime-dir mkdir took down every local terminal, jcode or not.
- removeJcodeManagedHooks matched the raw line, so a user hook whose comment
mentioned the managed script was deleted; matching on Windows never worked.
- A managed entry left by a copied home or a platform switch is now repointed
instead of being reported as user-owned forever.
- The OSC colour skip only checked launchAgent, so a command- or telemetry-named
jcode pane still leaked the reply into its composer.
- `['hooks']` and a commented scalar are recognised, instead of appending a
second [hooks] table that makes jcode reject the whole config.
- A failed tool's error is marked as tool output rather than agent prose.
- The vault keeps a session's stored name, counts its tokens, and skips
background_task and [Scheduled task] turns.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): read the journal once per turn, key prompts by byte offset
The jcode prompt reader ran a synchronous bounded file scan plus a JSON
parse on every hook event. jcode blocks on pre_tool, so a turn that ran
four tools charged the user eight scans of latency it did not need — the
prompt cannot change inside a turn.
- Cache the journal read per pane, refreshed on the turn boundary that
can change it. The cache holds the whole evidence record, since
hasExplicitUserPrompt needs the transcript-evidence flag and not just
the text, and it joins the existing pane-scoped lifecycle (close,
rename, reset) rather than living in a module singleton.
- Key a journal prompt by its absolute byte offset instead of its
region-local line index. The backward scan windows the file from EOF,
so appending shifted every boundary and reminted the key for a prompt
that never moved; a repeated turn_end then slipped past the same-hash
dedupe as a second done event with duplicate telemetry.
- Pin the platform in the daemon pre-warm tests. The pre-warm is a no-op
off POSIX, so the dedupe and retry cases would have passed vacuously
on a Windows runner.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): quote the managed hook path, drop tools from patch prompts
Three fixes, all on paths this PR could not exercise locally.
The managed hook command was stored as a bare path. jcode tokenizes that
string shell-style before exec'ing it directly (parse_hook_command,
crates/jcode-terminal-launch/src/lib.rs): unquoted whitespace splits, and
every unquoted backslash is consumed as an escape. So on Windows
`C:\Users\me\.orca\agent-hooks\jcode-hook.cmd` reached exec as
`C:Usersme.orcaagent-hooksjcode-hook.cmd` and no hook fired at all, and a
POSIX home with a space split into two arguments. Store the path
single-quoted (verbatim, backslashes included), falling back to double
quotes for a path containing a single quote. Existing bare entries are
already repointed by the stale-key path, and getStatus accepts both forms
so the repair is not reported as a user-owned hook. The quoting helper was
previously dead code that only tests called; the three production sites
now use it. isJcodeManagedCommand also normalizes separators, since a
`/`-only needle never matched a Windows entry.
Commit-message generation feeds a staged patch to `jcode run` as the
prompt — attacker-influenced text — while jcode's default profile exposes
shell, read, write, and MCP. Pass `--tool-profile none`, which resolves to
an empty allowed-tool set in jcode's config (base_allowed_tools), matching
the read-only posture claude (plan) and codex (read-only) already take.
docs/reference/jcode-hook-events.md was never actually in this PR: the
repo ignores docs/** and tracks reference docs by allow-list only, so the
captured-payload evidence four source comments point at was silently
dropped. Allow-list it.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* test(jcode): pin the tool-profile flag in the generation plan too
The argv assertion lives in two places; --tool-profile none only landed in
one, so the plan test still expected the unrestricted argv.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(text-generation): refuse an oversized argv prompt on Linux too
The pre-spawn size guard only ran on Windows. Linux caps a single argv
entry at MAX_ARG_STRLEN (32 pages, 128 KiB on a 4-KiB-page host) and
execve fails with E2BIG past it, so an agent that delivers the whole
prompt as one argument — jcode, and the other argv-delivery agents —
failed on a large staged diff with an error the user could not act on.
The cap is per-argument and in bytes, which is why it is not the Windows
line budget: 40k chars trips Windows and is nowhere near the Linux limit,
so folding them together would have refused prompts Linux runs fine.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): register in main's remote-installer guard, drop our duplicate
Rebasing onto 853 commits of main surfaced two things the earlier branch
had hidden.
main already owns a guard for the issue-#7253 bug class
(`remote-hook-service-registry-coverage.test.ts`). This branch had added a
second, near-identical one — a parallel implementation of a test that
already existed, which is what AGENTS.md's reuse rule is about. Deleted
ours and registered jcode in main's, which is the one that has kept pace
with every agent added since.
Also fixes a missing separator in the mobile icon map. `pnpm tc` does not
cover `mobile/`, so only the session-route closure suite caught it.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): decompose the four files jcode pushed over max-lines
Adding an agent tipped four modules past their line budget. AGENTS.md
forbids a `max-lines` disable or a per-file bump, so each is split on a
real seam rather than silenced:
- agent-catalog.tsx keeps `AgentIcon`, which 70+ files import, and the
rows move out. The rows alone exceed the 300-line budget a `.ts` file
gets, so they follow the primary/secondary split this repo already uses
for commit-message agent specs.
- getAgentResumeArgv -> agent-resume-argv.ts, re-exported so the 18 call
sites keep one import path.
- isDiscoverableSessionFile/pathSegments -> session-file-discovery.ts.
- remoteCodexSources -> remote-session-scanner-codex-sources.ts; Codex is
the one remote agent with two CODEX_HOME roots.
Also:
- Records the readiness-census baseline jcode now needs. main added that
gate while this branch was out; the fixture is the recorded 74-case
matrix, not a hand-written one.
- Restores two entries a rebase resolution silently dropped from
config/tsconfig.cli.json (gitlab/project-ref-parser,
startup/shell-path-probe). Nothing to do with jcode; losing them was a
conflict-resolution mistake on this branch.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* feat(native-chat): open structured chats on the paired Orca server that owns the workspace (#24205)
* fix(native-chat): a host admits structured sessions by client capability, not its own chat setting
A host's experimentalStructuredNativeChat decided whether any paired client could reach
agentSession.* at all, and whether session.tabs.* showed it structured tabs. That setting is the
host user's own launch preference: whether a new agent opens as a chat or a terminal is decided by
whoever launches it. Using it as admission control meant a client whose own preference was
"structured chat" was refused on a host whose preference was "terminal", and chats opened while
the setting was on were withheld from mobile once it was turned off.
The gate now asks one thing: did the client advertise agent-session.structured.v1 (in-process
callers negotiate nothing and are always admitted). Tab projection and restore follow the same
rule. With the setting no longer gating anything, the separate cleanup gate (close, cancel,
unsubscribe, release), which existed only so those kept working after the setting was switched
off, is identical to the main gate and is folded into it. The settings listener that republished
tabs when the setting changed is removed, since projection no longer depends on it.
The host setting still picks the default for launches that start on the host itself
(agent.launch from mobile, orchestration worker-start).
* fix(native-chat): the desktop declares structured chat support to paired hosts
The desktop renderer advertised agent-session.structured.v1 (and the Claude, turn-item and
background-task capabilities that go with it) to its own main process but not to a paired Orca
server. The server therefore refused every agentSession.* call from the desktop and stripped
structured chat tabs out of the tab list it published to it, so a structured chat running on a
paired server never appeared on the desktop, even though the renderer already mirrors a host's
agent-session tabs and drives each one against the server that owns its workspace.
The same renderer reads structured chats on either host, so the remote Electron list now carries
the same structured-session capabilities as the local one, and the capability test pins that
nothing is advertised only locally.
* feat(native-chat): open structured chats on the paired server that owns the workspace
With the structured-chat default on, an agent launched in a workspace that lives on a paired Orca
server always opened as a terminal (or the terminal-backed chat view). Three things kept it off
the structured path: the launch check refused every host but this machine, a remote workspace was
handed to the host-published terminal path before the structured route was even considered, and
the structured launch pipeline sent create and every follow-up call to this machine's runtime.
A workspace's owning runtime is fixed, so the pipeline now derives it from the workspace instead
of assuming this machine (structured-agent-session-owner.ts, the same derivation the chat pane
already uses to read a session). The launch intent carries that target; the pre-create support
check, create, the publication check and fence read, the launch prompt send, held option picks,
the "focus this chat" marker, the placeholder tab's host, and tab close/purge all use it.
The launch check now accepts a paired server and asks that server's own capabilities (read from
the status the client already cached for it) rather than this machine's. An SSH workspace stays
terminal-backed: no Orca runtime runs there. The host still answers createSupport before
anything is created, so an older server that refuses shows the failure in the chat tab.
A chat the user closed before its create landed is now also retired on the paired server when it
publishes, as the local sync already does. Orchestration workers placed on another runtime are
unchanged: federation creates terminal agents only.
* fix(native-chat): negotiate client-chosen launch mode so released phones and old servers keep terminals
Hosts advertise agent-session.structured.client-launch-mode.v1: they admit
structured sessions by client capability alone. A remote client that does
not advertise it (phones released before agent.launch) asks createSupport
to pick the launch mode, so the host keeps answering that with its own
setting, exactly as before. Cleanup methods keep their own named gate so a
future admission condition cannot make close or cancel refusable.
* refactor(runtime): keep the Electron client capability list in its own module
protocol-version.ts is at its line budget; the list is what the desktop
advertises to paired hosts, not the host's own contract.
* fix(native-chat): the desktop declares it picks each launch mode itself
Paired hosts and the desktop's own main process then answer createSupport
by the workspace rather than by their own chat setting.
* fix(native-chat): pin each structured chat to the host it was launched on
- Route: a paired server opens a chat only when it advertises the
client-chosen launch mode; an older server keeps its terminal. Its
capabilities come from the store's host status, not the compatibility
cache that is empty after boot or reconnect.
- A launch command override is this machine's: the route applies it only
locally, and a host's createSupport refuses on its own override.
- The owning host is resolved once, from the same value the route used,
and carried on the launch intent, its persisted record (legacy records
load as local), the provisional tab and every mirrored chat tab. Close,
purge, retry and reload read it instead of re-deriving it from a
worktree id two hosts can share; an owner that cannot be named refuses.
- Cancellation tombstones record their host: only that host's
authoritative inventory retires one, restored cleanup closes it there,
and a paired host's tombstone expires after 30 days if it never answers.
- A paired server's frame settles launches it published, as the local
inventory already does for this machine.
* fix(native-chat): a paired server that declines a chat opens its terminal instead
createSupport only reads, so both of its non-answers are settled before
anything is created:
- A paired server that answers it cannot run the chat (a WSL repo, a
Claude account mismatch, its own launch command override) closes the
chat tab and opens the terminal the route would have chosen, with a
notice saying why. This machine's own decline stays a failed chat.
- A host that could not be asked closes the chat tab and leaves one
failure toast, instead of a lingering "could not confirm" chat.
* test(native-chat): a provisional chat carries its launch's host and hands pre-create failures on
* chore(native-chat): justify the two type assertions this change's lines touch
* test(native-chat): state why each staged test fixture is cast
* fix(native-chat): chats that already exist keep showing whatever the chat setting says
The structured chat setting decides only what new agents open as. With it
off, this machine's structured chats used to be hidden while the host,
which no longer reads the setting, still reported them to the workspace
activation gate, so a workspace holding only a chat opened empty. The
local chat mirror and its startup restore now run whatever the setting
says, the continue-after-restart offer follows the chats that exist, and
the setting's copy says it applies to new agents.
* fix(native-chat): the browser client keeps its host terminal on paired servers
A browser client whose own preferences turn structured chat on took the
structured route for every paired-server workspace, but its handshake
never says it reads structured sessions, so the server refused the chat
and the user got a failed chat tab where a host terminal used to open.
The route for a paired host now also asks what this client advertises to
it: the desktop's list does, the browser client's does not. Its handshake
list is now a named constant the route reads, so the two cannot drift.
The chat setting's copy now says it runs on paired Orca servers too;
WSL and SSH hosts still use terminal chat.
* fix(native-chat): a retried launch a paired server declines opens its terminal too
A launch restored after a reload settles only through its Retry, so a
declining paired server left a failed chat there while a first launch got
the server's terminal and a notice. The chat's Retry now hands the same
pre-create failures to the same replacement, carrying the prompt the
launch had staged.
* refactor(native-chat): a paired host's cancelled-chat record ends on its 30-day TTL
The paired census re-read a host's whole inventory after every
authoritative frame to retire tombstones, and a tombstone restored after
a reload needed a second such frame, so in practice it retired nothing.
A tombstone guards a random session id and is inert once stale; the chat
is already closed on its host whenever a frame shows it. The census, its
trigger in the mirror layer and its cleanup are removed; the owner-scoped
tombstones, close-on-sight, the TTL and publication marking from frames
stay.
* fix(native-chat): a chat's pane and status read from the host recorded on its tab
The chat pane and its sidebar status still derived the host from the
workspace id, which two hosts can share; a paired chat in a non-active
same-id workspace was read from this machine. Both now read the owner
stamped on the tab, as close, purge and publication already do.
* fix(native-chat): "Resume in chat" follows the terminal resume's host rule
Agent Session History offered "Resume in chat" for a conversation
recorded on this machine into a paired server's workspace, where its
transcript does not exist. A chat now resumes a conversation only on the
host that recorded it, as the terminal resume does, and that host is the
one asked whether it can resume history.
* fix(native-chat): the chat setting says older paired servers keep terminal chat
* test(native-chat): pin that a host advertises the client-chosen launch mode
* fix(native-chat): mirror this machine's chats only where it holds them
Round 1 ran the local chat mirror for everyone so existing chats show
whatever the setting says. That gave every desktop a permanent
session-tabs listener, which turns on the runtime's phone replication
paths, plus two full session-tab censuses at startup, and made the
browser client mirror its remote host a second time.
The runtime now says whether it holds structured chats: its structured
host is built only when saved chats were restored at startup or a client
created one here, and it announces the moment one is built. The mirror,
the startup restore and the continue-after-restart offer run only when
the setting launches chats or the host holds some, and never in the
browser client. A chat a paired client creates here with the setting off
still appears at once. The chat behaviour settings show wherever chats
exist, and the setting's copy says it picks what new agents open as. The
toggle-off teardown this made dead is removed.
* test(native-chat): route a paired-server launch over the capability lists both sides really advertise
* test(native-chat): record install listeners without a cast
* fix(native-chat): a paired server admits a chat before any of it exists here
The desktop opened a paired server's chat tab, launch record, queued
prompt and focus intent before asking the server, so a "no" needed a
replacement that undid and redid all of it, and every piece it missed
was a bug: the workspace deselected, the caller told "failed" while a
terminal ran its prompt, the caller's arguments and other queued prompts
lost, and a create whose reply was lost treated as never sent.
A paired launch now asks the server first and commits nothing until it
answers. Admitted opens the chat as before. Declined runs the caller's
own launch as the server's terminal, with the existing notice (a resume
fails instead, having no terminal equivalent). Unreachable opens nothing
and names the server in one toast. The new-tab launcher reports the
host's surface for paired workspaces, as it did before paired chats,
with the prompt delivery of whichever surface got the prompt. The
replacement and its error classes are gone, and the probe inside a
launch is back to its old meaning: a "no" is a failed chat with Retry,
and no answer leaves "Could not confirm" with Retry and the prompt kept,
here as on this machine.
* fix(native-chat): mirror this machine's chats only once it holds one, not once its host is built
Session history, resume preparation, terminal resume commands and replay-safe phone launches all
build the structured host for users who never had a chat, which turned on the chat mirror and the
structured-only settings rows until the next restart. The signal is now derived from the host's
records (or a records file still owed its import) and pushed when the first chat is restored or
created. A throwing listener no longer fails the install that fired it.
* fix(native-chat): a fork's reveal never seeds a terminal beside the surface the launcher opens
Forking into a paired-server workspace revealed it as if nothing would open there, so the reveal
created a blank host terminal beside the forked chat (and beside a forked agent terminal on main).
The launcher always opens the fork's surface itself, so the reveal now says so for every surface,
as the fix-checks launch already does.
* fix(native-chat): a declined direct launch keeps the caller's CLI args; an unreachable resume toasts once
When a paired server declines a "Fix checks" chat in a new workspace, the terminal that opens
instead now carries the recipe's saved CLI arguments, launch platform and launch source, as the
terminal route did. "Resume in chat" to a server that cannot be reached showed the admission's
"Could not reach" toast and the vault's generic one; the admission marks its failure notified and
the vault adds nothing.
* fix(native-chat): a declined background create opens its terminal without switching workspaces
Since #23974 a worktree create the user moved away from must not pull them onto the new
workspace. When a paired server declined that create's chat, the fallback terminal opened as a new
agent tab, whose host create selects the workspace. The create now opens its own agent terminal the
way main's background branch does: in place from the request's startup plan (so its CLI args carry),
without selecting the workspace. A create the user is still watching keeps the new-tab fallback.
* test(native-chat): name the launch's host in main's new outbox fence test
Main's new staging-failure test calls settleStructuredAgentLaunchPrompt without the target this PR
made required; it is a local launch, as in the sibling tests.
* fix(native-chat): a paired server's new chat shows no model until the server reports the one it started
A chat on a paired server starts with the server's saved model and options, but the picker showed
this desktop's saved selection (or the catalog default) until the server reported a model, and a
pick made in that window was remembered on the server under that guessed model. A paired launch
now carries no desktop seed, and until the server reports its model the picker names no model and
takes no picks. Local chats are unchanged.
* test(native-chat): seed the paired repo without a cast
The repo literal already satisfies Repo, so the changed-lines cast gate has nothing to excuse.
* feat(native-chat): createSupport reports the saved selection a new chat on this host starts with
A chat on a paired server starts with the server's saved model and options, which the desktop could
not read, so its picker showed a guess. createSupport's answer, which the desktop already waits for
before a paired launch, now also carries that seed as a new optional field (older clients ignore it).
Create and createSupport read it through one resolver so they cannot drift.
* fix(native-chat): a paired server's new chat shows the selection the server will start it with
The paired server now names its saved model and options in the admission answer the desktop
already waits for. That seed goes into the launch intent and its persisted record, so the picker
shows the server's model at once, stays pickable like a local chat, and remembers picks on the
server under that model; a reload shows the same. The locked picker remains only for a server too
old to name a seed.
Also moves host admission and launch-outcome tracking into their own modules: the latest main
merge left structured-agent-session-launch.ts over the max-lines limit.
* test(native-chat): expect the launch intent's new seed argument in exact-call assertions
* refactor(protocol): move the Electron remote client capability list into its own module
Merging main left protocol-version.ts one line over the max-lines limit on this branch. The list of
capabilities the desktop advertises to a paired host moves, unchanged, into
electron-remote-runtime-client-capabilities.ts, the module the next PR in the stack already uses
for it; importers point there.
* fix(native-chat): a paired chat with no saved server model is pickable; Retry shows the server's current seed
A server whose user never saved a chat model sends no seed, and the desktop showed a locked,
model-only picker for it, although that is the common case: no server that can admit a paired chat
predates the seed field. Such a chat now behaves like a local chat with no saved model: the CLI
default, pickable. The lock and its snapshot helper are gone.
Retry kept the first admission's seed while the create probe, which already runs on every attempt,
reported the server's current one and dropped it. The probe's seed now replaces a paired launch's
seed and the picker's, so a retried chat shows what its create will run.
* test(cross-version): stub the launch seed resolver createSupport now reads
* test(protocol): pin the desktop capability divergence against what a paired server receives
Every paired transport sends the shared remote base plus the Electron list, so the
divergence test now compares that union with the renderer's local list instead of
the declared Electron list. A capability added only to the shared base can no
longer slip past it. The two base-only capabilities it surfaced are recorded:
skills.install-result.v2 has no local caller; the authoritative-inventory label is
read by the local tabs sync but dropped by main, and is marked unsettled.
The turn-item and both background-task-stop capabilities were already sent through
the shared base, so the Electron list no longer repeats them. The wire set is
unchanged; this PR's real change on the wire is structured.v1, the Claude
structured capability and the client launch-mode capability.
* fix(native-chat): the desktop tells its own host it picks each launch mode, so retrying an existing chat works with the setting off
* docs(native-chat): name the real exit for the released-phone createSupport rule
* fix(native-chat): the route reads the capabilities a paired host actually receives
The renderer decided whether a paired host would admit a chat from the desktop's Electron list, but
every desktop transport sends that list plus the shared remote base. They agreed only because the
route's checks happened to sit in both. The route input is now built with the same
remoteRuntimeClientCapabilities the transports use (the browser client already sends its list as is),
and a test pins each against the real handshake.
* test(cross-version): a released client still gets the host-setting createSupport answer; a launch-mode client gets supported plus the seed
* test(native-chat): let main's child-records test resolve each chat's owner
Main's new test mocks worktree-runtime-owner with only the runtime environment id, but the status
projection in this PR also resolves each structured chat's owner from the worktree. The mock keeps
the module's real exports and overrides only what the test pins.
* fix(deps): take #24204's lockfile that the merge reverted
* test(native-chat): let the Codex child-approval e2e unit test resolve each chat's owner
Its worktree-runtime-owner mock exported only the runtime environment id, but this PR's status
projection also resolves each structured chat's owner from the worktree. The mock now keeps the
module's real exports and overrides only that id, as structured-child-records-switch does.
* test(native-chat): move the close-race launch cases into their own file
Merging main added launch tests on both sides and took structured-agent-session-launch.test.ts past
the 800-line limit. The three cases where a tab close races a launch move to
structured-agent-session-launch-close-race.test.ts, with the same setup the other split launch
suites copy.
* feat(cli): add --unread/--read to orca worktree set
Pass validated read/unread flags through the existing workspace metadata update.
Contributor attribution correction: the earlier Ctrl+M squash message placed its related link after the co-author footer, so GitHub did not recognize all contributors. Preserve that credit here for https://github.com/stablyai/orca/pull/24100 and its related contribution https://github.com/stablyai/orca/pull/24143 .
Related CLI link validation retained from https://github.com/stablyai/orca/pull/20036 .
Co-authored-by: Raz Shlomo <12373339+razshlomo@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Zhichang Yu <yuzhichang@gmail.com>
Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com>
* feat(cli): configure external worktree visibility per repo
Pass show/hide/inherit to the existing repository visibility update.
Related: https://github.com/stablyai/orca/pull/23025
Preserves CLI link validation from https://github.com/stablyai/orca/pull/20036 and read controls from https://github.com/stablyai/orca/pull/22714 .
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: KAPUIST <thsxornjs12@gmail.com>
* perf(git): reuse queries and stop canceled catalog scans (#24923)
* perf(git): reuse queries and stop canceled catalog scans
* refactor(git): check relay filesystem error codes
* Allow cold node-pty setup on Windows ARM CI
Keep a ten-minute bound only for node-pty rebuilt on Windows ARM CI hosts; all other node-gyp calls retain five minutes. Cold setup in two completed ARM jobs left compilation less than a minute.
* test(e2e): require exact Linux package tokens (#24995)
* Allow cold node-pty setup on Windows ARM CI (#24997)
Keep a ten-minute bound only for node-pty rebuilt on Windows ARM CI hosts; all other node-gyp calls retain five minutes. Cold setup in two completed ARM jobs left compilation less than a minute.
* fix(jcode): harden Windows hooks and negotiate remote history (#24998)
Redirect the managed Windows payload file into curl instead of starting
pipeline shells, register native Windows delivery coverage, and document
Jcode v0.89.0+ as the upstream launcher requirement for invisible hooks.
Negotiate Jcode history in both directions with mixed-version Orca hosts,
preserving supported search filters and old-client response compatibility.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
Co-authored-by: JianJia2018 <39438074+JianJia2018@users.noreply.github.com>
* fix(git): preserve SSH review context and worktree ownership (#24945)
* fix(git): preserve SSH arguments and guard background writes
* test(terminal): settle fish fixture startup readiness
* fix(git): preserve bare UNC SSH paths
* fix(git): unescape shell operators in Windows SSH paths
* test(runtime): settle removal writes before fixture cleanup
* test(git): skip optional OpenSSH probe when unavailable
* perf(git): skip equal-tip reads and bound relay discovery
* test(processes): ratchet the removed relay Git spawn
* test(shells): wait for initial zsh output before sending input
* Fix SSH review context and recover Git maintenance cleanup safely
* Keep relay Git compatibility fixtures outside shared client projects
* Fence superseded maintenance and preserve mixed-version session search
* test: model Git child termination and search catalogs
* test: retain catalog authority over history flags
* fix(opencode): keep shared-session TUI activity with its owning pane (#24962)
* fix(opencode): bind legacy attach status to its TUI pane
Adapt the official v1 TUI API to the existing pane reporter and learn structural session identity only after execution-host admission. Preserve known creators, gate new reports to capable hosts, and register the legacy TUI entry without changing user settings or overlay targets.
Credit werlang for the same-directory investigation in #22838 and #22847; no source from that PR is copied.
(cherry picked from commit d9eeb028a23a66b78b08538a5ebf6052499ec24f)
* fix(opencode): fence legacy TUI viewers before creator attribution
Use the execution host's existing admission policy on the physical TUI
pane and launch token before rewriting to a session creator or mutating
the launch-token cache. Preserve live viewers, frozen server stamps and
OpenCode 2 behavior.
Add HTTP regressions for retired panes, closed tabs, replaced tokens and
relay retirement, plus current-launch, alias and compatibility controls.
Follow-up to #22838 and @werlang's original #22847; preserves the original
intentional creator attribution without copying that PR's source.
* fix(opencode): preserve current plugin exports in legacy TUI wrapper
Keep the current server, setup, id and other default export entries when
adding the legacy TUI entry. Cover their preservation and seed the ACL
fixtures with the separate installed TUI/config bytes.
This completes the current-main port of the legacy identity replacement
for issue #22838, carrying @werlang's original PR #22847 contribution.
* fix(opencode): retry timed-out SSH plugin updates (#24666)
Preserve bounded retry behavior and the current-main status-envelope fields.
Original-PR: #24124
Reviewed-source: 103144f9c5
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.
Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021
Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237
Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
* Skip store notifications when refreshed work items are unchanged (#24530)
An identical forced refresh previously returned an empty Zustand patch, creating a new root state and notifying every subscriber. It now returns the current state after renewing the existing freshness timestamp. Real row or metadata changes still publish once.
* Read project activity once while sorting the sidebar (#24549)
Reuse the existing pure recent rank once per actual entry tuple within each synchronous sort; discard the lookup after sorting.
* Skip impossible link and tag matches in docs search results (#24646)
* Skip impossible HTML matches when formatting docs search results
After the existing link-removal phase, skip the unchanged HTML regex only when the current string has no closing delimiter.
* Skip impossible link and tag matches in docs search results
Skip the five unchanged link regexes when their protected-code input lacks ]; skip the unchanged HTML regex when its post-link input lacks >.
* Decode complete transcript lines without copying their bytes (#24546)
Decode each single owned Buffer synchronously; preserve concatenation for multipart records and remove a redundant tail array copy.
* Clear unused speech worker timers after errors and exit (#24924)
Call the existing idle timer cleanup when private current-worker state is cleared; keep current-worker guards, transcript/error order, deliberate warmth and stop deadline unchanged.
* Reuse documentation search excerpts during result navigation (#24574)
Memoize the existing pure excerpt renderer by complete raw text for the current result array, retaining original rendering as a miss fallback.
* Append subagent handoffs without recopying their message list (#24672)
Append each message reference to its private call-local scope list; retain the final merge and existing ordering.
* Cancel plugin retry timers when their fetch is replaced (#24700)
Reuse the existing timer clear block immediately after the fetch generation advances; preserve live retries and all state publications.
* Avoid rebuilding visible file paths for single-row selection (#24743)
Skip the visible path array only for keyboard replacement selection; the existing replacement branch never reads that order.
* Reuse prepared reads while searching saved agent conversations (#24535)
Use schema-complete session columns to enable the existing bounded statement cache without caching result rows.
* Reuse discovered mobile chunks during static traversal (#24774)
Iterate the existing live visited Set in FIFO discovery order instead of maintaining a second shifted queue; both actual callers own untouched esbuild JSON metadata.
* Skip unused image-size calculations in mobile web browser requests (#24941)
Use the existing mobile density budget only for mobile view; pass the existing constant to the existing assembler for web/default mode, where that argument is discarded. No cache, policy, request or native path changes.
* Skip diff analysis after a result has been discarded (#24711)
Move the unchanged pure render-limit calculation after both existing generation and section-token rejection fences.
* Skip late browser grab toasts after their surface closes (#24727)
Reuse the existing mounted-owner ref at the toast presentation entry while preserving all admitted extraction/screenshot and clipboard completion.
* Classify native chat waiting messages once (#24771)
Use the existing native filter traversal to populate a fresh waiting array, keeping the original predicate, policy, projection and output ordering while classifying each actual private slot once.
* Skip discarded Kanban pruning for an empty selection (#24782)
Guard only the existing open pruning branch when both its private selected Set and nullable anchor are empty; retain all original pre-guard projections and nonempty/anchor-only/public-helper behavior.
* Index discovered test files once during shard selection validation (#24532)
Verified selection plans previously validated each selected filename with a scan of all discovered files. One per-call Set now handles membership checks. Exact source/discovery checks, nonempty selection, fallback to all tests, shard balancing and manifests remain intact.
* Clear the watchdog benchmark heartbeat when CPU sampling fails (#24802)
Move the existing initial CPU observation and histogram enable inside the existing try/finally so their failures clear the sampler's owned heartbeat, preserving successful operation order and original errors.
* Reuse the parsed notification when leasing a push delivery (#24645)
* Reuse the parsed notification when leasing a push delivery
Pass the notification already parsed for the dismissal check into the existing private delivery builder.
* Reuse the parsed notification when leasing a push delivery
Pass the notification already parsed for the dismissal check into the existing private delivery builder.
* Update README downloads badge
* Let retired-cache GC observations settle across the existing six-turn budget (#24967)
* Collect test-selection evidence when full unit tests fail (#24955)
* Collect advisory unit-selection evidence from failed full runs
* Trigger checks after retargeting the evidence fix to main
* Check each project repository once while filtering mobile cards (#24540)
Reuse exact raw source/slug matching decisions within one project filter call, preserving the matcher, membership, negative matches, output order and identity.
* Skip renderer callbacks after their request has ended (#24631)
Reuse the existing pending-request identity check before starting its deferred renderer callback.
* Decode single-piece saved-session tails without copying them (#24632)
Decode the existing owned Buffer directly when the EOF tail has one piece; preserve the existing concatenation for multiple pieces.
* Avoid irrelevant scroll-action searches in Mac snapshots (#24731)
Check the current pure action name before searching for vertical scroll actions in the same immutable array.
* Stop copying every retained browser page before attachment (#24553)
Find the first page/generation match directly in the existing insertion-ordered Map instead of copying all page references into an array.
* Reuse event byte sizes when relay watchers notify several clients (#24558)
Store the existing grouped numeric byte-size array on the already call-local watcher batch sizing object, for reuse by later chunking clients.
* Stop building unused import candidates in the test planner (#24611)
Replace the existing ordered extension map/find with a loop that forms each candidate only when its existing lookup is reached.
* docs(terminal): explain local macOS/Linux shell startup files
Document the actual login/interactive shell startup-file order and existing shell setup behavior.
Co-authored-by: brynnclaw <261708852+brynnclaw@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(editor): recognize Ruby task and configuration files
Add Ruby task/configuration filenames to the existing generated language associations.
Co-authored-by: ggbdpq <ggbdpq@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* Speed up large Markdown Find and render oversized tables (#24948)
* Speed up large Markdown Find and render oversized tables
* Poll table preview geometry outside hidden renderers
* Preserve Markdown navigation through refresh and tab restoration
* Confirm Markdown restoration when refreshed content is ready
* Trace table refresh positions and update Unicode search reference
* Recognize queued measurement scrolls before restoring Markdown anchors
* Rebuild Markdown Find ranges after renderer components change
* fix(source-control): generate clean OpenCode messages locally and over SSH (#24613)
* fix(source-control): generate clean OpenCode answers on local and SSH hosts
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: 64bb15e3f2
fix(source-control): generate clean OpenCode answers on local and SSH hosts
Use configured models and JSON answer/error events, preserve run-first arguments, and handle the precise v2 variant rejection. Hydrate SSH execution-host PATH through the existing bounded login environment resolver before direct spawning.
Credits: andy-murr (PR #5197 SSH environment intent) and coelho-doti (PR #13065 argument-order intent).
Original-commit: 1b60ec5d11
fix(source-control): retry inline OpenCode model and variant options
Original-commit: 98fdecc7a3
Preserve OpenCode named errors without a data message
Restacked-from: 98fdecc7a3
Restacked-onto: f7b1f9d8be
* fix(ci): prevent concurrent pnpm refresh during mobile typechecks (#24776)
* fix(ci): run mobile typechecks without concurrent dependency refresh
* test(ci): check effective Linux E2E package list
* test(ci): preserve the mobile production compiler barrier
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* test(terminal): restore the live fish fixture prerequisites (#24947)
A restored pane waits for the initial status replay before subscribing to
PTY output. This fixture never settled that replay, so fish printed its
mode-2031 arm before the renderer connected. Its PTY API also omitted the
reset-input listener required by the serializer, aborting attachment.
Settle and dispose the existing startup-snapshot registration and provide
the same reset-listener mock used by the other PTY tests. The real fish
child-stdin assertions and timeouts remain unchanged. No production change.
* fix(shortcuts): defer TUI editing chords in terminal-first mode (#24640)
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: f707cde14a
fix(shortcuts): defer TUI editing chords in terminal-first mode
Original-commit: 0c6348e49e
docs(shortcuts): describe deferred preview terminal chords
Original-commit: be62c1b6c6
Align worktree history shortcut metadata with terminal conflict policy
Restacked-from: be62c1b6c6
Restacked-onto: f7b1f9d8be
* Register supervised Qoder China and Qwen Code (#24616)
* Add Qoder session history and search with real CLI coverage
* Allow the real Qoder marker file to end with a newline
* Keep Qoder tool output out of history previews and search
* Keep Qoder search pages readable by older clients
* Verify persisted Qoder history after a real generated and resumed task
* Negotiate Qoder filters before searching an older execution host
* Combine search client imports for the CI plugin gate
* Keep the relay search oracle aligned with legacy agent filtering
* Register supervised Qoder China and Qwen lifecycle integration
* Cover Qoder China mobile assets and mixed-host resume gates
* Verify Qoder provider tags against the older released wire parser
* Verify China and Qwen keep independent Windows hook scripts
* Verify Qoder registrations against the installed older Windows release
* test(qoder): align search capability contracts and pin old-host fencing
* fix(qoder): rank exact picker identities and command aliases first
* test(qoder): preserve the regional CLI shared icon expectation
Keep the full bundled-asset and no-remote-image checks, with an explicit
shared-logo basename for Qoder China. The map also works with older
catalog type unions.
* fix(qoder): align China catalog entry with fallback order
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* Use the measured pnpm lookup policy automatically in hosted root CI (#24951)
* Select lookup automatically for the measured hosted root-install profile
* Record hosted automatic-mode cold cache publication proof
* fix: bound remote generation setup and honor OpenCode option terminators
Count execution-host profile resolution inside the existing request deadline, cancel its waiter promptly, and pass only the remaining time to the child. Shared bounded profile probes keep their existing cache lifetime; the SSH transport margin is unchanged.
Read OpenCode output format from active final-argv options before -- so literal prompt arguments cannot select the JSON finalizer.
Fresh exact-source controls reproduce nine failures before; 139 related checks pass after, including primary/fallback delays, deadline boundaries, cancellation, parser metadata, and SSH lanes. Node typecheck and strict changed-file lint pass.
* Continue Antigravity IDE and 2.0 history in new CLI conversations (#24692)
* feat(antigravity): bridge IDE history into new CLI conversations
* fix(antigravity): preserve fresh-launch model and environment for IDE references
* fix(antigravity): forward IDE history opt-in through desktop IPC
* fix(antigravity): rebuild remote IDE reference startup on its host
* fix(antigravity): register IDE continuation action labels
* fix(antigravity): confine IDE references and bound metadata reads
* fix(antigravity): localize IDE continuation badges
* Preserve scanner service cache assertions and refresh Antigravity opening metadata
* Preserve Antigravity opening joins and target folder runtime authority
* fix(opencode): retry timed-out SSH plugin updates (#24666)
Preserve bounded retry behavior and the current-main status-envelope fields.
Original-PR: #24124
Reviewed-source: 103144f9c5
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.
Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021
Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237
Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
* fix(opencode): enforce deadline through executable startup
Keep synchronous Windows PATH resolution and child startup within the existing request budget.
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(persistence): preserve projectGroupOrder on restart for flat folder-scan groups
Retain saved project ranks when the project remains in its folder-scan group.
Co-authored-by: lurunzi <lurunzi@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(runtime): reclaim temp files orphaned by interrupted mobile store writes
Reuse the existing stale temporary-file cleanup policy when each mobile store opens.
Co-authored-by: LDH1103 <ldh517525@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(terminal): show actionable copy for missing folder workspace paths
Show the existing folder-path failure with concrete recovery instructions.
Co-authored-by: Wayn_Liu <wayntingliu@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* docs: reference local orca.yaml and .worktreeinclude
Document the existing workspace configuration, include/copy rules and sharing behavior.
Co-authored-by: Neil <neil@stably.ai>
* fix(orchestration): allow model selection for OMP workers
Pass an explicitly requested model through the existing OMP worker launch catalog.
Co-authored-by: Neil <neil@stably.ai>
* fix(cli): preserve primitive success results in JSON output
Check for an object before inspecting screenshot fields so primitive success results remain printable.
Related: https://github.com/stablyai/orca/pull/14735
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
* Show loaded file changes before deleting a workspace
Expose up to ten already loaded changed paths without altering deletion counts, hydration or authorization.
Related: https://github.com/stablyai/orca/pull/22778
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Michiel de Gooijer <mdgooijer@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(keybindings): record and match Option+digit shortcuts on macOS
Use the physical digit key for explicitly assigned Mac Option+digit shortcuts while retaining modifier checks.
Co-authored-by: marcuslannister <marcus@lannister.cc>
Co-authored-by: Neil <neil@stably.ai>
* fix(editor): don't throw closing a stale active file
Recover stale active-file selection within the owning workspace before choosing a surviving editor.
Related: https://github.com/stablyai/orca/pull/24715
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: Neil <neil@stably.ai>
* Fix Project Settings targeting for multiple local checkouts
Carry the selected checkout identity into the existing project Settings action.
Co-authored-by: fsmeier <1506919+fsmeier@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
* feat(documents): open CSV and TSV files from the OS
Extend existing OS document associations and delivery to CSV/TSV, preserving restoration and authorization.
Co-authored-by: Neil <neil@stably.ai>
* feat(cli): link GitHub and GitLab items on worktree create and set
Expose and validate the existing workspace link fields, preserving omitted values and provider identity checks.
Related: https://github.com/stablyai/orca/pull/22609
Co-authored-by: marco song <marco.song@mvlchain.io>
Co-authored-by: SongMarco <20613630+SongMarco@users.noreply.github.com>
Co-authored-by: Luca Critelli <lucacri@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* feat(sidebar): include folder workspaces in keyboard navigation
Use the rendered sidebar row order and host identity when cycling through folder and Git workspaces.
Related: https://github.com/stablyai/orca/pull/10555
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: JeongUk Park <jeongph.dev@gmail.com>
* fix(build): turn off MSBuild file tracking for Windows native rebuilds
Default Windows native rebuilds to TrackFileAccess=false while preserving explicit caller preferences.
Co-authored-by: B1nh M1nh <43268322+b1nhm1nh@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
* Fix Ctrl+M in Linux and Windows terminals
Keep the Minimize menu item but disable its accelerator registration on Linux and Windows.
Co-authored-by: Zhichang Yu <yuzhichang@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com>
Related contribution: https://github.com/stablyai/orca/pull/24143
* fix(startup): read a nushell login PATH from $env.PATH
Use Nushell login command syntax and preserve the actual login PATH value.
Related: https://github.com/stablyai/orca/pull/22677
Co-authored-by: Kh05ifr4nD <meandSSH0219@gmail.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(cursor): run local hooks through sh for non-POSIX login shells
Keep POSIX hook syntax inside one quoted sh command so the login shell can invoke it safely.
Related: https://github.com/stablyai/orca/pull/22663
Co-authored-by: Kh05ifr4nD <meandSSH0219@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
* Fix word wrap for both panes in side-by-side diffs
Forward wrapping to both diff panes through the existing editor option path and clean up listeners.
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(monaco): highlight Svelte block closers inside markup
Return Svelte block closers to the existing markup tokenizer state.
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(repos): keep active clone dialog open on outside clicks
Ignore accidental outside dismissal only while cloning; Escape, Close and Back still cancel.
Related: https://github.com/stablyai/orca/pull/24581, https://github.com/stablyai/orca/pull/23430
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>
* fix(editor): keep chat visible after closing Markdown tabs
Count remaining unified chat tabs before clearing workspace selection during editor close.
Related: https://github.com/stablyai/orca/pull/24273, https://github.com/stablyai/orca/pull/23760
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Honor typed starting numbers in chat ordered lists
Forward the existing parsed ordered-list start attribute to both chat Markdown renderers.
Related: https://github.com/stablyai/orca/pull/19765
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Frederic Barthelemy <git@fbartho.com>
* Normalize base source once while checking changed-code diagnostics (#24557)
Reuse the exact existing moved-code matching algorithm with run-local lazy normalization of immutable base source blocks across diagnostics.
* Support real OpenCode sessions in native Chat (#24647)
* Use bounded OpenCode context for vault session continuation
OpenCode database and synthetic row paths are not text transcripts. Use the
vault preview or captured pane context, preserving actual transcript paths
containing a hash and supporting both OpenCode lanes and Windows paths.
Adapted the intent of #11859 and extended it to actual installed v2 vault rows.
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
* Read real OpenCode sessions in terminal-backed native Chat
Reuse the bounded AI Vault SQLite worker for v1 and v2 session pages and live updates. Keep terminal input as the real execution path and pace OpenCode Stop through its two-Escape interrupt.
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
* fix(opencode): publish approval cards for permission requests
* Send OpenCode native approval through its Enter selector
* Resolve mobile Chat readability for folder workspaces
* Bound OpenCode part batches and preserve v2 image attachments
* Prefer live migrated OpenCode sessions over legacy copies
* Consolidate mobile Chat eligibility test imports
* Consolidate OpenCode SQLite protocol type imports
* Update native chat settings contract for both OpenCode agents
* fix(native-chat): reconcile bounded OpenCode transcript reads
* fix(native-chat): dispatch OpenCode questions safely
* fix(native-chat): keep native discovery and transcript windows current
* feat(accounts): link standalone GLM Coding Plans (#24618)
* feat(accounts): link standalone GLM Coding Plans
Adapt the reviewed GLM accounts contribution to current main, retain Antigravity behavior, guard late credential results, expose storage protection, and redact quota errors.
Co-authored-by: Luchong <lu740528977@gmail.com>
* fix(accounts): retain GLM credential results during quota refresh
* fix(accounts): make GLM credential editing desktop-only
* fix(accounts): mirror the host GLM site in paired clients
* fix(accounts): report unknown GLM host details and split web settings tests
Apply the independently reviewed Accounts correction from697284a without the v2 adapter commits. Preserve the saved-key store and serialized write behavior.
* fix(zcode): ship required GLM account translation entries
* chore: record GLM reconciliation hook validation
* chore: validate installed GLM commit hooks
* test: complete GLM account fixtures and web API inventory
---------
Co-authored-by: Luchong <lu740528977@gmail.com>
* fix(native-chat): route transcript requests through shared SQLite worker
* fix(ci): prevent concurrent pnpm refresh during mobile typechecks (#24776)
* fix(ci): run mobile typechecks without concurrent dependency refresh
* test(ci): check effective Linux E2E package list
* test(ci): preserve the mobile production compiler barrier
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* test(terminal): restore the live fish fixture prerequisites (#24947)
A restored pane waits for the initial status replay before subscribing to
PTY output. This fixture never settled that replay, so fish printed its
mode-2031 arm before the renderer connected. Its PTY API also omitted the
reset-input listener required by the serializer, aborting attachment.
Settle and dispose the existing startup-snapshot registration and provide
the same reset-listener mock used by the other PTY tests. The real fish
child-stdin assertions and timeouts remain unchanged. No production change.
* fix(shortcuts): defer TUI editing chords in terminal-first mode (#24640)
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: f707cde14a
fix(shortcuts): defer TUI editing chords in terminal-first mode
Original-commit: 0c6348e49e
docs(shortcuts): describe deferred preview terminal chords
Original-commit: be62c1b6c6
Align worktree history shortcut metadata with terminal conflict policy
Restacked-from: be62c1b6c6
Restacked-onto: f7b1f9d8be
* Register supervised Qoder China and Qwen Code (#24616)
* Add Qoder session history and search with real CLI coverage
* Allow the real Qoder marker file to end with a newline
* Keep Qoder tool output out of history previews and search
* Keep Qoder search pages readable by older clients
* Verify persisted Qoder history after a real generated and resumed task
* Negotiate Qoder filters before searching an older execution host
* Combine search client imports for the CI plugin gate
* Keep the relay search oracle aligned with legacy agent filtering
* Register supervised Qoder China and Qwen lifecycle integration
* Cover Qoder China mobile assets and mixed-host resume gates
* Verify Qoder provider tags against the older released wire parser
* Verify China and Qwen keep independent Windows hook scripts
* Verify Qoder registrations against the installed older Windows release
* test(qoder): align search capability contracts and pin old-host fencing
* fix(qoder): rank exact picker identities and command aliases first
* test(qoder): preserve the regional CLI shared icon expectation
Keep the full bundled-asset and no-remote-image checks, with an explicit
shared-logo basename for Qoder China. The map also works with older
catalog type unions.
* fix(qoder): align China catalog entry with fallback order
---------
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
* Use the measured pnpm lookup policy automatically in hosted root CI (#24951)
* Select lookup automatically for the measured hosted root-install profile
* Record hosted automatic-mode cold cache publication proof
* fix(native-chat): keep OpenCode history usable at read limits
Continue past failed database probes while preserving discovery cancellation.
Verify rows displaced by a capped tail before deciding whether to replace history.
Represent oversized v1/v2 rows with the existing omission text and stable cursors.
* Continue Antigravity IDE and 2.0 history in new CLI conversations (#24692)
* feat(antigravity): bridge IDE history into new CLI conversations
* fix(antigravity): preserve fresh-launch model and environment for IDE references
* fix(antigravity): forward IDE history opt-in through desktop IPC
* fix(antigravity): rebuild remote IDE reference startup on its host
* fix(antigravity): register IDE continuation action labels
* fix(antigravity): confine IDE references and bound metadata reads
* fix(antigravity): localize IDE continuation badges
* Preserve scanner service cache assertions and refresh Antigravity opening metadata
* Preserve Antigravity opening joins and target folder runtime authority
* fix(opencode): retry timed-out SSH plugin updates (#24666)
Preserve bounded retry behavior and the current-main status-envelope fields.
Original-PR: #24124
Reviewed-source: 103144f9c5
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.
Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021
Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237
Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
---------
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
Co-authored-by: Luchong <lu740528977@gmail.com>
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
* Drain removal fixture jobs before resetting and deleting their records (#24977)
* Reuse measured Electron preparation for current Terminal Perf refs (#24968)
* feat(jcode): add Jcode as a supported TUI agent with managed hooks
Ports PR #10521 onto current main: agent catalog, managed hook service,
agent-status listener, session resume, AI Vault parser, per-pane daemon
isolation, and Source Control AI support.
Co-authored-by: Neil <neil@stably.ai>
* feat(jcode): evidence-backed status pipeline (turn_start, live pre_tool, questions)
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): register vault fixture, dynamic model discovery, script refresher
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): keep finished-turn detail so completion notifications fire
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): show the prompt in agent rows instead of jcode's repainting title
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): pre-warm the per-pane daemon so a cold runtime dir cannot time out
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): stop the OSC color skip from crashing every pane connect
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* refactor(jcode): fold three reverse-scan copies into one, reuse the shared hook POST
The jcode journal reader, the Claude transcript reader and the Command Code
transcript reader each carried their own copy of the same reverse chunked line
scan; they now share one tested helper. jcode's managed hook script drops its
hand-rolled curl for buildPosixAgentHookPostCommand, which also gains it the
raw-JSON transport and the --noproxy guard the bespoke copy was missing.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): narrow dynamic reads with predicates, pin the Windows hook shape
CI's anti-slop audit rejects Reflect.get: parse dynamic input into a named type
instead. Adds Windows script-shape tests too, since Windows is the platform this
change could not be exercised on directly.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* test(jcode): measure the gate against the synchronous path, not the clock
The absolute 1s bound was the flake CI shard 3/8 hit: it is tight enough to catch
a synchronous gate on an idle laptop and too tight on a loaded runner. Measuring
the same POST both ways on the same machine makes the claim a ratio, which is what
the test is actually about. Mutation-checked: a synchronous gate reads 6120ms
against a 1517ms observer.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* test(jcode): drop the wall-clock gate assertion
The bound was machine-speed sensitive and flaked on CI shard 3/8 at 1525ms. The
structural assertions (detached gate branch, foreground observer branch) and the
no-hang stdin drain cover the same contract without a timer.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): address CodeRabbit review on #22539
- Windows posted no payload at all: the shared builder reads `payload@-` from
stdin, which the gate has already drained and observer hooks never receive, so
every Windows event was dropped. Write the env var to a temp file and pipe it.
- The daemon pre-warm never fired for daemon-host spawns, which is the default
local path; it now runs there too, and from the final env so the daemon gets the
hook port and token.
- A failed runtime-dir mkdir took down every local terminal, jcode or not.
- removeJcodeManagedHooks matched the raw line, so a user hook whose comment
mentioned the managed script was deleted; matching on Windows never worked.
- A managed entry left by a copied home or a platform switch is now repointed
instead of being reported as user-owned forever.
- The OSC colour skip only checked launchAgent, so a command- or telemetry-named
jcode pane still leaked the reply into its composer.
- `['hooks']` and a commented scalar are recognised, instead of appending a
second [hooks] table that makes jcode reject the whole config.
- A failed tool's error is marked as tool output rather than agent prose.
- The vault keeps a session's stored name, counts its tokens, and skips
background_task and [Scheduled task] turns.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): read the journal once per turn, key prompts by byte offset
The jcode prompt reader ran a synchronous bounded file scan plus a JSON
parse on every hook event. jcode blocks on pre_tool, so a turn that ran
four tools charged the user eight scans of latency it did not need — the
prompt cannot change inside a turn.
- Cache the journal read per pane, refreshed on the turn boundary that
can change it. The cache holds the whole evidence record, since
hasExplicitUserPrompt needs the transcript-evidence flag and not just
the text, and it joins the existing pane-scoped lifecycle (close,
rename, reset) rather than living in a module singleton.
- Key a journal prompt by its absolute byte offset instead of its
region-local line index. The backward scan windows the file from EOF,
so appending shifted every boundary and reminted the key for a prompt
that never moved; a repeated turn_end then slipped past the same-hash
dedupe as a second done event with duplicate telemetry.
- Pin the platform in the daemon pre-warm tests. The pre-warm is a no-op
off POSIX, so the dedupe and retry cases would have passed vacuously
on a Windows runner.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): quote the managed hook path, drop tools from patch prompts
Three fixes, all on paths this PR could not exercise locally.
The managed hook command was stored as a bare path. jcode tokenizes that
string shell-style before exec'ing it directly (parse_hook_command,
crates/jcode-terminal-launch/src/lib.rs): unquoted whitespace splits, and
every unquoted backslash is consumed as an escape. So on Windows
`C:\Users\me\.orca\agent-hooks\jcode-hook.cmd` reached exec as
`C:Usersme.orcaagent-hooksjcode-hook.cmd` and no hook fired at all, and a
POSIX home with a space split into two arguments. Store the path
single-quoted (verbatim, backslashes included), falling back to double
quotes for a path containing a single quote. Existing bare entries are
already repointed by the stale-key path, and getStatus accepts both forms
so the repair is not reported as a user-owned hook. The quoting helper was
previously dead code that only tests called; the three production sites
now use it. isJcodeManagedCommand also normalizes separators, since a
`/`-only needle never matched a Windows entry.
Commit-message generation feeds a staged patch to `jcode run` as the
prompt — attacker-influenced text — while jcode's default profile exposes
shell, read, write, and MCP. Pass `--tool-profile none`, which resolves to
an empty allowed-tool set in jcode's config (base_allowed_tools), matching
the read-only posture claude (plan) and codex (read-only) already take.
docs/reference/jcode-hook-events.md was never actually in this PR: the
repo ignores docs/** and tracks reference docs by allow-list only, so the
captured-payload evidence four source comments point at was silently
dropped. Allow-list it.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* test(jcode): pin the tool-profile flag in the generation plan too
The argv assertion lives in two places; --tool-profile none only landed in
one, so the plan test still expected the unrestricted argv.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(text-generation): refuse an oversized argv prompt on Linux too
The pre-spawn size guard only ran on Windows. Linux caps a single argv
entry at MAX_ARG_STRLEN (32 pages, 128 KiB on a 4-KiB-page host) and
execve fails with E2BIG past it, so an agent that delivers the whole
prompt as one argument — jcode, and the other argv-delivery agents —
failed on a large staged diff with an error the user could not act on.
The cap is per-argument and in bytes, which is why it is not the Windows
line budget: 40k chars trips Windows and is nowhere near the Linux limit,
so folding them together would have refused prompts Linux runs fine.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): register in main's remote-installer guard, drop our duplicate
Rebasing onto 853 commits of main surfaced two things the earlier branch
had hidden.
main already owns a guard for the issue-#7253 bug class
(`remote-hook-service-registry-coverage.test.ts`). This branch had added a
second, near-identical one — a parallel implementation of a test that
already existed, which is what AGENTS.md's reuse rule is about. Deleted
ours and registered jcode in main's, which is the one that has kept pace
with every agent added since.
Also fixes a missing separator in the mobile icon map. `pnpm tc` does not
cover `mobile/`, so only the session-route closure suite caught it.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* fix(jcode): decompose the four files jcode pushed over max-lines
Adding an agent tipped four modules past their line budget. AGENTS.md
forbids a `max-lines` disable or a per-file bump, so each is split on a
real seam rather than silenced:
- agent-catalog.tsx keeps `AgentIcon`, which 70+ files import, and the
rows move out. The rows alone exceed the 300-line budget a `.ts` file
gets, so they follow the primary/secondary split this repo already uses
for commit-message agent specs.
- getAgentResumeArgv -> agent-resume-argv.ts, re-exported so the 18 call
sites keep one import path.
- isDiscoverableSessionFile/pathSegments -> session-file-discovery.ts.
- remoteCodexSources -> remote-session-scanner-codex-sources.ts; Codex is
the one remote agent with two CODEX_HOME roots.
Also:
- Records the readiness-census baseline jcode now needs. main added that
gate while this branch was out; the fixture is the recorded 74-case
matrix, not a hand-written one.
- Restores two entries a rebase resolution silently dropped from
config/tsconfig.cli.json (gitlab/project-ref-parser,
startup/shell-path-probe). Nothing to do with jcode; losing them was a
conflict-resolution mistake on this branch.
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
* feat(native-chat): open structured chats on the paired Orca server that owns the workspace (#24205)
* fix(native-chat): a host admits structured sessions by client capability, not its own chat setting
A host's experimentalStructuredNativeChat decided whether any paired client could reach
agentSession.* at all, and whether session.tabs.* showed it structured tabs. That setting is the
host user's own launch preference: whether a new agent opens as a chat or a terminal is decided by
whoever launches it. Using it as admission control meant a client whose own preference was
"structured chat" was refused on a host whose preference was "terminal", and chats opened while
the setting was on were withheld from mobile once it was turned off.
The gate now asks one thing: did the client advertise agent-session.structured.v1 (in-process
callers negotiate nothing and are always admitted). Tab projection and restore follow the same
rule. With the setting no longer gating anything, the separate cleanup gate (close, cancel,
unsubscribe, release), which existed only so those kept working after the setting was switched
off, is identical to the main gate and is folded into it. The settings listener that republished
tabs when the setting changed is removed, since projection no longer depends on it.
The host setting still picks the default for launches that start on the host itself
(agent.launch from mobile, orchestration worker-start).
* fix(native-chat): the desktop declares structured chat support to paired hosts
The desktop renderer advertised agent-session.structured.v1 (and the Claude, turn-item and
background-task capabilities that go with it) to its own main process but not to a paired Orca
server. The server therefore refused every agentSession.* call from the desktop and stripped
structured chat tabs out of the tab list it published to it, so a structured chat running on a
paired server never appeared on the desktop, even though the renderer already mirrors a host's
agent-session tabs and drives each one against the server that owns its workspace.
The same renderer reads structured chats on either host, so the remote Electron list now carries
the same structured-session capabilities as the local one, and the capability test pins that
nothing is advertised only locally.
* feat(native-chat): open structured chats on the paired server that owns the workspace
With the structured-chat default on, an agent launched in a workspace that lives on a paired Orca
server always opened as a terminal (or the terminal-backed chat view). Three things kept it off
the structured path: the launch check refused every host but this machine, a remote workspace was
handed to the host-published terminal path before the structured route was even considered, and
the structured launch pipeline sent create and every follow-up call to this machine's runtime.
A workspace's owning runtime is fixed, so the pipeline now derives it from the workspace instead
of assuming this machine (structured-agent-session-owner.ts, the same derivation the chat pane
already uses to read a session). The launch intent carries that target; the pre-create support
check, create, the publication check and fence read, the launch prompt send, held option picks,
the "focus this chat" marker, the placeholder tab's host, and tab close/purge all use it.
The launch check now accepts a paired server and asks that server's own capabilities (read from
the status the client already cached for it) rather than this machine's. An SSH workspace stays
terminal-backed: no Orca runtime runs there. The host still answers createSupport before
anything is created, so an older server that refuses shows the failure in the chat tab.
A chat the user closed before its create landed is now also retired on the paired server when it
publishes, as the local sync already does. Orchestration workers placed on another runtime are
unchanged: federation creates terminal agents only.
* fix(native-chat): negotiate client-chosen launch mode so released phones and old servers keep terminals
Hosts advertise agent-session.structured.client-launch-mode.v1: they admit
structured sessions by client capability alone. A remote client that does
not advertise it (phones released before agent.launch) asks createSupport
to pick the launch mode, so the host keeps answering that with its own
setting, exactly as before. Cleanup methods keep their own named gate so a
future admission condition cannot make close or cancel refusable.
* refactor(runtime): keep the Electron client capability list in its own module
protocol-version.ts is at its line budget; the list is what the desktop
advertises to paired hosts, not the host's own contract.
* fix(native-chat): the desktop declares it picks each launch mode itself
Paired hosts and the desktop's own main process then answer createSupport
by the workspace rather than by their own chat setting.
* fix(native-chat): pin each structured chat to the host it was launched on
- Route: a paired server opens a chat only when it advertises the
client-chosen launch mode; an older server keeps its terminal. Its
capabilities come from the store's host status, not the compatibility
cache that is empty after boot or reconnect.
- A launch command override is this machine's: the route applies it only
locally, and a host's createSupport refuses on its own override.
- The owning host is resolved once, from the same value the route used,
and carried on the launch intent, its persisted record (legacy records
load as local), the provisional tab and every mirrored chat tab. Close,
purge, retry and reload read it instead of re-deriving it from a
worktree id two hosts can share; an owner that cannot be named refuses.
- Cancellation tombstones record their host: only that host's
authoritative inventory retires one, restored cleanup closes it there,
and a paired host's tombstone expires after 30 days if it never answers.
- A paired server's frame settles launches it published, as the local
inventory already does for this machine.
* fix(native-chat): a paired server that declines a chat opens its terminal instead
createSupport only reads, so both of its non-answers are settled before
anything is created:
- A paired server that answers it cannot run the chat (a WSL repo, a
Claude account mismatch, its own launch command override) closes the
chat tab and opens the terminal the route would have chosen, with a
notice saying why. This machine's own decline stays a failed chat.
- A host that could not be asked closes the chat tab and leaves one
failure toast, instead of a lingering "could not confirm" chat.
* test(native-chat): a provisional chat carries its launch's host and hands pre-create failures on
* chore(native-chat): justify the two type assertions this change's lines touch
* test(native-chat): state why each staged test fixture is cast
* fix(native-chat): chats that already exist keep showing whatever the chat setting says
The structured chat setting decides only what new agents open as. With it
off, this machine's structured chats used to be hidden while the host,
which no longer reads the setting, still reported them to the workspace
activation gate, so a workspace holding only a chat opened empty. The
local chat mirror and its startup restore now run whatever the setting
says, the continue-after-restart offer follows the chats that exist, and
the setting's copy says it applies to new agents.
* fix(native-chat): the browser client keeps its host terminal on paired servers
A browser client whose own preferences turn structured chat on took the
structured route for every paired-server workspace, but its handshake
never says it reads structured sessions, so the server refused the chat
and the user got a failed chat tab where a host terminal used to open.
The route for a paired host now also asks what this client advertises to
it: the desktop's list does, the browser client's does not. Its handshake
list is now a named constant the route reads, so the two cannot drift.
The chat setting's copy now says it runs on paired Orca servers too;
WSL and SSH hosts still use terminal chat.
* fix(native-chat): a retried launch a paired server declines opens its terminal too
A launch restored after a reload settles only through its Retry, so a
declining paired server left a failed chat there while a first launch got
the server's terminal and a notice. The chat's Retry now hands the same
pre-create failures to the same replacement, carrying the prompt the
launch had staged.
* refactor(native-chat): a paired host's cancelled-chat record ends on its 30-day TTL
The paired census re-read a host's whole inventory after every
authoritative frame to retire tombstones, and a tombstone restored after
a reload needed a second such frame, so in practice it retired nothing.
A tombstone guards a random session id and is inert once stale; the chat
is already closed on its host whenever a frame shows it. The census, its
trigger in the mirror layer and its cleanup are removed; the owner-scoped
tombstones, close-on-sight, the TTL and publication marking from frames
stay.
* fix(native-chat): a chat's pane and status read from the host recorded on its tab
The chat pane and its sidebar status still derived the host from the
workspace id, which two hosts can share; a paired chat in a non-active
same-id workspace was read from this machine. Both now read the owner
stamped on the tab, as close, purge and publication already do.
* fix(native-chat): "Resume in chat" follows the terminal resume's host rule
Agent Session History offered "Resume in chat" for a conversation
recorded on this machine into a paired server's workspace, where its
transcript does not exist. A chat now resumes a conversation only on the
host that recorded it, as the terminal resume does, and that host is the
one asked whether it can resume history.
* fix(native-chat): the chat setting says older paired servers keep terminal chat
* test(native-chat): pin that a host advertises the client-chosen launch mode
* fix(native-chat): mirror this machine's chats only where it holds them
Round 1 ran the local chat mirror for everyone so existing chats show
whatever the setting says. That gave every desktop a permanent
session-tabs listener, which turns on the runtime's phone replication
paths, plus two full session-tab censuses at startup, and made the
browser client mirror its remote host a second time.
The runtime now says whether it holds structured chats: its structured
host is built only when saved chats were restored at startup or a client
created one here, and it announces the moment one is built. The mirror,
the startup restore and the continue-after-restart offer run only when
the setting launches chats or the host holds some, and never in the
browser client. A chat a paired client creates here with the setting off
still appears at once. The chat behaviour settings show wherever chats
exist, and the setting's copy says it picks what new agents open as. The
toggle-off teardown this made dead is removed.
* test(native-chat): route a paired-server launch over the capability lists both sides really advertise
* test(native-chat): record install listeners without a cast
* fix(native-chat): a paired server admits a chat before any of it exists here
The desktop opened a paired server's chat tab, launch record, queued
prompt and focus intent before asking the server, so a "no" needed a
replacement that undid and redid all of it, and every piece it missed
was a bug: the workspace deselected, the caller told "failed" while a
terminal ran its prompt, the caller's arguments and other queued prompts
lost, and a create whose reply was lost treated as never sent.
A paired launch now asks the server first and commits nothing until it
answers. Admitted opens the chat as before. Declined runs the caller's
own launch as the server's terminal, with the existing notice (a resume
fails instead, having no terminal equivalent). Unreachable opens nothing
and names the server in one toast. The new-tab launcher reports the
host's surface for paired workspaces, as it did before paired chats,
with the prompt delivery of whichever surface got the prompt. The
replacement and its error classes are gone, and the probe inside a
launch is back to its old meaning: a "no" is a failed chat with Retry,
and no answer leaves "Could not confirm" with Retry and the prompt kept,
here as on this machine.
* fix(native-chat): mirror this machine's chats only once it holds one, not once its host is built
Session history, resume preparation, terminal resume commands and replay-safe phone launches all
build the structured host for users who never had a chat, which turned on the chat mirror and the
structured-only settings rows until the next restart. The signal is now derived from the host's
records (or a records file still owed its import) and pushed when the first chat is restored or
created. A throwing listener no longer fails the install that fired it.
* fix(native-chat): a fork's reveal never seeds a terminal beside the surface the launcher opens
Forking into a paired-server workspace revealed it as if nothing would open there, so the reveal
created a blank host terminal beside the forked chat (and beside a forked agent terminal on main).
The launcher always opens the fork's surface itself, so the reveal now says so for every surface,
as the fix-checks launch already does.
* fix(native-chat): a declined direct launch keeps the caller's CLI args; an unreachable resume toasts once
When a paired server declines a "Fix checks" chat in a new workspace, the terminal that opens
instead now carries the recipe's saved CLI arguments, launch platform and launch source, as the
terminal route did. "Resume in chat" to a server that cannot be reached showed the admission's
"Could not reach" toast and the vault's generic one; the admission marks its failure notified and
the vault adds nothing.
* fix(native-chat): a declined background create opens its terminal without switching workspaces
Since #23974 a worktree create the user moved away from must not pull them onto the new
workspace. When a paired server declined that create's chat, the fallback terminal opened as a new
agent tab, whose host create selects the workspace. The create now opens its own agent terminal the
way main's background branch does: in place from the request's startup plan (so its CLI args carry),
without selecting the workspace. A create the user is still watching keeps the new-tab fallback.
* test(native-chat): name the launch's host in main's new outbox fence test
Main's new staging-failure test calls settleStructuredAgentLaunchPrompt without the target this PR
made required; it is a local launch, as in the sibling tests.
* fix(native-chat): a paired server's new chat shows no model until the server reports the one it started
A chat on a paired server starts with the server's saved model and options, but the picker showed
this desktop's saved selection (or the catalog default) until the server reported a model, and a
pick made in that window was remembered on the server under that guessed model. A paired launch
now carries no desktop seed, and until the server reports its model the picker names no model and
takes no picks. Local chats are unchanged.
* test(native-chat): seed the paired repo without a cast
The repo literal already satisfies Repo, so the changed-lines cast gate has nothing to excuse.
* feat(native-chat): createSupport reports the saved selection a new chat on this host starts with
A chat on a paired server starts with the server's saved model and options, which the desktop could
not read, so its picker showed a guess. createSupport's answer, which the desktop already waits for
before a paired launch, now also carries that seed as a new optional field (older clients ignore it).
Create and createSupport read it through one resolver so they cannot drift.
* fix(native-chat): a paired server's new chat shows the selection the server will start it with
The paired server now names its saved model and options in the admission answer the desktop
already waits for. That seed goes into the launch intent and its persisted record, so the picker
shows the server's model at once, stays pickable like a local chat, and remembers picks on the
server under that model; a reload shows the same. The locked picker remains only for a server too
old to name a seed.
Also moves host admission and launch-outcome tracking into their own modules: the latest main
merge left structured-agent-session-launch.ts over the max-lines limit.
* test(native-chat): expect the launch intent's new seed argument in exact-call assertions
* refactor(protocol): move the Electron remote client capability list into its own module
Merging main left protocol-version.ts one line over the max-lines limit on this branch. The list of
capabilities the desktop advertises to a paired host moves, unchanged, into
electron-remote-runtime-client-capabilities.ts, the module the next PR in the stack already uses
for it; importers point there.
* fix(native-chat): a paired chat with no saved server model is pickable; Retry shows the server's current seed
A server whose user never saved a chat model sends no seed, and the desktop showed a locked,
model-only picker for it, although that is the common case: no server that can admit a paired chat
predates the seed field. Such a chat now behaves like a local chat with no saved model: the CLI
default, pickable. The lock and its snapshot helper are gone.
Retry kept the first admission's seed while the create probe, which already runs on every attempt,
reported the server's current one and dropped it. The probe's seed now replaces a paired launch's
seed and the picker's, so a retried chat shows what its create will run.
* test(cross-version): stub the launch seed resolver createSupport now reads
* test(protocol): pin the desktop capability divergence against what a paired server receives
Every paired transport sends the shared remote base plus the Electron list, so the
divergence test now compares that union with the renderer's local list instead of
the declared Electron list. A capability added only to the shared base can no
longer slip past it. The two base-only capabilities it surfaced are recorded:
skills.install-result.v2 has no local caller; the authoritative-inventory label is
read by the local tabs sync but dropped by main, and is marked unsettled.
The turn-item and both background-task-stop capabilities were already sent through
the shared base, so the Electron list no longer repeats them. The wire set is
unchanged; this PR's real change on the wire is structured.v1, the Claude
structured capability and the client launch-mode capability.
* fix(native-chat): the desktop tells its own host it picks each launch mode, so retrying an existing chat works with the setting off
* docs(native-chat): name the real exit for the released-phone createSupport rule
* fix(native-chat): the route reads the capabilities a paired host actually receives
The renderer decided whether a paired host would admit a chat from the desktop's Electron list, but
every desktop transport sends that list plus the shared remote base. They agreed only because the
route's checks happened to sit in both. The route input is now built with the same
remoteRuntimeClientCapabilities the transports use (the browser client already sends its list as is),
and a test pins each against the real handshake.
* test(cross-version): a released client still gets the host-setting createSupport answer; a launch-mode client gets supported plus the seed
* test(native-chat): let main's child-records test resolve each chat's owner
Main's new test mocks worktree-runtime-owner with only the runtime environment id, but the status
projection in this PR also resolves each structured chat's owner from the worktree. The mock keeps
the module's real exports and overrides only what the test pins.
* fix(deps): take #24204's lockfile that the merge reverted
* test(native-chat): let the Codex child-approval e2e unit test resolve each chat's owner
Its worktree-runtime-owner mock exported only the runtime environment id, but this PR's status
projection also resolves each structured chat's owner from the worktree. The mock now keeps the
module's real exports and overrides only that id, as structured-child-records-switch does.
* test(native-chat): move the close-race launch cases into their own file
Merging main added launch tests on both sides and took structured-agent-session-launch.test.ts past
the 800-line limit. The three cases where a tab close races a launch move to
structured-agent-session-launch-close-race.test.ts, with the same setup the other split launch
suites copy.
* feat(cli): add --unread/--read to orca worktree set
Pass validated read/unread flags through the existing workspace metadata update.
Contributor attribution correction: the earlier Ctrl+M squash message placed its related link after the co-author footer, so GitHub did not recognize all contributors. Preserve that credit here for https://github.com/stablyai/orca/pull/24100 and its related contribution https://github.com/stablyai/orca/pull/24143 .
Related CLI link validation retained from https://github.com/stablyai/orca/pull/20036 .
Co-authored-by: Raz Shlomo <1237333…
* fix(ci): wait for the fragment navigation policy report (#25017)
---------
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Brynn Bendixen <brynnbendixen@gmail.com>
Co-authored-by: brynnclaw <261708852+brynnclaw@users.noreply.github.com>
Co-authored-by: 小七 <ggbdpq@gmail.com>
Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
Co-authored-by: lurunzi <lurunzi@gmail.com>
Co-authored-by: Dongho Lee <126236161+LDH1103@users.noreply.github.com>
Co-authored-by: LDH1103 <ldh517525@gmail.com>
Co-authored-by: Wayn_Liu <115852642+Waynting@users.noreply.github.com>
Co-authored-by: Wayn_Liu <wayntingliu@gmail.com>
Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
Co-authored-by: Michiel de Gooijer <mdgooijer@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Marcus <70784566+marcuslannister@users.noreply.github.com>
Co-authored-by: marcuslannister <marcus@lannister.cc>
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: Florian Schwarzmeier <1506919+fsmeier@users.noreply.github.com>
Co-authored-by: SongMarco <snubflow@gmail.com>
Co-authored-by: marco song <marco.song@mvlchain.io>
Co-authored-by: SongMarco <20613630+SongMarco@users.noreply.github.com>
Co-authored-by: Luca Critelli <lucacri@gmail.com>
Co-authored-by: JeongUk Park <jeongph.dev@gmail.com>
Co-authored-by: B1nh M1nh <43268322+b1nhm1nh@users.noreply.github.com>
Co-authored-by: Zhichang Yu <yuzhichang@gmail.com>
Co-authored-by: Kh05ifr4nD <meandSSH0219@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Wooseong Kim <2222333+innocarpe@users.noreply.github.com>
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>
Co-authored-by: Frederic Barthelemy <git@fbartho.com>
Co-authored-by: mrcha033 <mrcha033@users.noreply.github.com>
Co-authored-by: xodmd45-ctrl <xodmd45-ctrl@users.noreply.github.com>
Co-authored-by: Luchong <lu740528977@gmail.com>
Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: razshlomo <razshlomo@users.noreply.github.com>
Co-authored-by: Raz Shlomo <12373339+razshlomo@users.noreply.github.com>
Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com>
Co-authored-by: KAPUIST <thsxornjs12@gmail.com>
Co-authored-by: JianJia2018 <39438074+JianJia2018@users.noreply.github.com>
* Keep large Markdown previews responsive
* Fix large preview review navigation and Find budgets
* Initialize preview scroll caches once and check viewport visibility
* Restore large previews after loaded rows are measured
* Refresh loaded Markdown rows after viewport changes
* Keep Markdown revisions visible and reuse bounded search text
* ci(daemon): gate PRs on daemon protocol crossing from the newest release
Lands daemon-protocol-facts.mjs from the Windows update diagnostic branch with a
stricter parser, and adds check-daemon-protocol-crossing.mjs (rule R1): the working
tree must attach the newest release tag's daemon. Rollback crossing is reported only.
Runs in the cross-version-wire job, which already has full tags; tag selection moves
to config/scripts/stable-release-tags.mjs so both use one rule.
* feat(persistence): run profile backups in the worker whenever its entry is bundled
* refactor(orcad): make profile and native preflight runtime-neutral
The profile preflight parser now takes the expected runtime identity from the
caller (shipped callers pass the pinned Bun identity), and the native
preflight is renamed to orcad-runtime-native-preflight with neutral wording.
* feat(runtime): pin the Node 24.21.0 server runtime with an offline CI check
Add src/shared/node-runtime-pin.ts (NODE_RUNTIME_PIN, SERVER_TARGETS,
NODE_RUNTIME_ASSETS for all 8 server targets plus the headers tarball),
generated by config/scripts/update-node-runtime-pin.mjs from the nodejs.org
and unofficial-builds SHASUMS. check-node-runtime-pin.mjs verifies, with no
network, that the pin tracks the locked Electron, matches engines.node's
major, and covers exactly SERVER_TARGETS; it runs in the static analysis job.
ORCAD_BUN_TARGETS consumers now read SERVER_TARGETS so there is one target
list; orcad's Bun runtime and build output are unchanged.
* test(persistence): skip plain-Node backup selection tests in the Bun profile suite
* fix(runtime): reject a pinned archive that belongs to another target
* ci(daemon): fail PRs that swap a runtime launcher and bump the daemon protocol
D7.1 R3: hosting orcad or the daemon on another runtime is not a protocol change,
so one PR must not do both. The launcher file list lives in the check script; the
allow-runtime-launcher-protocol-bump label overrides it.
* feat(orcad): select pinned-Node slots by a .runtime-node marker
D7.1 R5: a Node slot names its shared runtimes/node-<sha256>/node through
.runtime-node instead of .build-target, so Bun-era clients read it as a legacy
slot rather than exiting 78 on a missing bun-runtime. Nothing builds the marker yet.
* fix(runtime): load the Node pin without the typeless-module warning
check-node-runtime-pin.mjs now requires the pin and takes nodeDistArchiveName from
its own module, so it no longer loads the update script's build graph.
* fix(orcad): resolve Node slots to the design's runtimes/node-<sha>/bin/node layout
* feat(orcad): 8-slot node-pty prebuilds against the pinned Node headers at N-API 8
- build-orcad-prebuilds.mjs adds win32-x64/arm64 (conpty.node, the vendored
conpty.dll/OpenConsole.exe, upstream's N-API conpty_console_list.node), compiles
in a scratch copy against the hash-verified pinned headers (node.lib pinned per
Windows arch) with NAPI_VERSION=8, rejects post-8 node_api_* imports, and writes
a schema 2 manifest with per-file sha256, N-API level and the glibc need.
- --require-slots [slots] verifies files against hashes; --smoke loads the slot
under the pinned Node and spawns a PTY; --print-slot names the host slot.
- The slot installer gates on N-API, libc, arch, glibc and file hashes instead of
the exact NODE_MODULE_VERSION, and installs nested files (conpty/).
- bun-profile-tests.yml builds, verifies and smokes each runner's slot.
* fix(orcad): scope node-pty's glibc .symver pins to glibc on musl prebuild slots
musl's unversioned libc cannot satisfy openpty@GLIBC_* references at link
time, so the Alpine slot compile would fail. Pin the staged pty.cc guard to
__GLIBC__ and assert both musl transforms against the installed patch.
* feat(orcad): run orcad on the pinned Node instead of Bun
A packaged orcad slot now references the pinned Node 24.21.0 by its
executableSha256 (`.runtime-node`, `.server-target`) instead of carrying
bun-runtime, and ships node-pty from the slot's prebuild, only its own
ripgrep, and no Windows Bun PTY gate. The runtime lives beside the slots
at runtimes/node-<sha>/bin/node (node.exe on Windows, upstream name).
- build:orcad (build-orcad-node.mjs) builds the host slot's prebuild when
missing and places the pinned runtime; the template is schema 3 with
per-target files.
- handoffToBundledOrcad() resolves the slot's runtime reference and checks
process.versions.node against the pin; a host Node >= 18 still hands off.
Startup preflight keys on running as that runtime; callers expect 'node'.
- orcad and its daemon use node-pty (ConPTY + windows-pty-job on Windows);
the Bun PTY sources, gate entry and canUseBunPty branches are removed.
- SSH deploy uploads the official archive once per pin, extracts and
hash-checks it on the host, and self-tests it before publishing. Bun
slots stay launchable for rollback; Node slots never use host Node.
- The runtime materializer is generic over pinned assets; the Bun wrapper
remains only for the OpenCode vault reader (design Phase 2).
- Cross-runtime test: a profile DB written by Bun 1.4.2 (WAL left by
SIGKILL) opens and backs up under the pinned Node, and the reverse.
No daemon PROTOCOL_VERSION change (design D7.1 R3).
* docs(ci): name the headless lanes after the pinned Node, drop Bun shard timings
Design D10: ci-demand-rollout.md and ci-runner-efficiency.md follow the
bun-profile-tests.yml -> node-server-tests.yml rename; shard timings drop the
deleted Bun PTY tests and follow the renamed ones.
* chore(ci): count the runtime archive download as a runtime launcher path
* fix(orcad): pin the macOS C++ standard for node-pty prebuilds
The official Node headers' config.gypi sets clang: 0, so common.gypi skips its
gnu++20 xcode_settings and Apple clang 15 (macos-14 runners) compiles
node-addon-api as C++98.
* fix(orcad): resolve the preflight's slot through realpath, as the handoff does
A symlinked orcad.js handed off to its real slot's pinned Node, but the
startup and profile preflights read the symlink's directory, found no
runtime marker there, and silently skipped the readiness check.
* refactor(ssh): drop materializeCachedNodeRuntime, which nothing calls
Deploys upload the verified official archive (design D5); no client path
needs an extracted Node executable cached by digest.
* test(orcad): gate the Bun-to-Node upgrade and Node-to-Bun rollback with live terminals
Design D7.1 R1/R3/R4 and D7.2. The last Bun orcad and this checkout's Node
slot are installed side by side under ~/.orca-remote, launched and stopped
with the client's own deploy commands, and share one data root. Each
direction proves the incoming orcad adopts the outgoing runtime's daemon
(same PID, same shell, output continues), opens its profile database and
backs it up with its own shipped worker, and that GC keeps the slot the
live daemon was forked from.
The node-server Linux lanes provide Bun 1.4.2 and build that Bun orcad
from main, and run with --cross-runtime. --artifact and --cross-runtime
now make their tests fail on a missing input instead of skipping.
* ci(node-server): pin node:24.21.0-alpine by its multi-arch index digest
* test(ssh): name the runtime archive fixture after its role
* test(node-server): load node-pty from the packaged slot in artifact runs
The node-server lane installs dependencies without building node-pty, and
Linux has no upstream prebuild, so the real-PTY failed-I/O teardown test
(picked up by the pty-subprocess selector) could not load pty.node. In
--artifact runs, alias node-pty to out/orcad's shipped slot so the test
exercises the addon orcad actually runs under the pinned Node.
* fix(orcad): let the Windows profile preflight exit after its PTY probe
On Windows, node-pty keeps the conout worker thread and pseudoconsole alive
until kill(), even after the shell exits. The PTY health probe never killed a
cleanly exited probe, so the packaged preflight printed its readiness line
and then hung until the build's 30s timeout, reported with an empty stderr.
- The probe kills its PTY on Windows after exit and uses the bundled ConPTY
the daemon spawns with.
- The preflight exits once stdout is flushed; its owner reads to EOF.
- Preflight failures now report code, signal, timeout, stdout and stderr.
* test(node-server): load the slot's node-pty in the real-PTY test, not by alias
A vite alias redirected only ESM imports of node-pty; windows-pty-job and
local-pty-utils resolve it through require, so Windows loaded two conpty.node
copies and the Git Bash job-membership proof read an empty job. The failed-I/O
teardown test now loads node-pty through a fixture that picks the packaged slot
in artifact lanes.
The pty-subprocess selector was a prefix that also pulled in its POSIX-host
sibling unit tests, which pr.yml runs and which were never qualified on
Windows. Select the directory plus the two sibling files that belong here.
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* ci(daemon): gate PRs on daemon protocol crossing from the newest release
Lands daemon-protocol-facts.mjs from the Windows update diagnostic branch with a
stricter parser, and adds check-daemon-protocol-crossing.mjs (rule R1): the working
tree must attach the newest release tag's daemon. Rollback crossing is reported only.
Runs in the cross-version-wire job, which already has full tags; tag selection moves
to config/scripts/stable-release-tags.mjs so both use one rule.
* feat(persistence): run profile backups in the worker whenever its entry is bundled
* refactor(orcad): make profile and native preflight runtime-neutral
The profile preflight parser now takes the expected runtime identity from the
caller (shipped callers pass the pinned Bun identity), and the native
preflight is renamed to orcad-runtime-native-preflight with neutral wording.
* feat(runtime): pin the Node 24.21.0 server runtime with an offline CI check
Add src/shared/node-runtime-pin.ts (NODE_RUNTIME_PIN, SERVER_TARGETS,
NODE_RUNTIME_ASSETS for all 8 server targets plus the headers tarball),
generated by config/scripts/update-node-runtime-pin.mjs from the nodejs.org
and unofficial-builds SHASUMS. check-node-runtime-pin.mjs verifies, with no
network, that the pin tracks the locked Electron, matches engines.node's
major, and covers exactly SERVER_TARGETS; it runs in the static analysis job.
ORCAD_BUN_TARGETS consumers now read SERVER_TARGETS so there is one target
list; orcad's Bun runtime and build output are unchanged.
* test(persistence): skip plain-Node backup selection tests in the Bun profile suite
* fix(runtime): reject a pinned archive that belongs to another target
* ci(daemon): fail PRs that swap a runtime launcher and bump the daemon protocol
D7.1 R3: hosting orcad or the daemon on another runtime is not a protocol change,
so one PR must not do both. The launcher file list lives in the check script; the
allow-runtime-launcher-protocol-bump label overrides it.
* feat(orcad): select pinned-Node slots by a .runtime-node marker
D7.1 R5: a Node slot names its shared runtimes/node-<sha256>/node through
.runtime-node instead of .build-target, so Bun-era clients read it as a legacy
slot rather than exiting 78 on a missing bun-runtime. Nothing builds the marker yet.
* fix(runtime): load the Node pin without the typeless-module warning
check-node-runtime-pin.mjs now requires the pin and takes nodeDistArchiveName from
its own module, so it no longer loads the update script's build graph.
* fix(orcad): resolve Node slots to the design's runtimes/node-<sha>/bin/node layout
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Lands daemon-protocol-facts.mjs from the Windows update diagnostic branch with a
stricter parser, and adds check-daemon-protocol-crossing.mjs (rule R1): the working
tree must attach the newest release tag's daemon. Rollback crossing is reported only.
Runs in the cross-version-wire job, which already has full tags; tag selection moves
to config/scripts/stable-release-tags.mjs so both use one rule.
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* feat(runtime): pin the Node 24.21.0 server runtime with an offline CI check
Add src/shared/node-runtime-pin.ts (NODE_RUNTIME_PIN, SERVER_TARGETS,
NODE_RUNTIME_ASSETS for all 8 server targets plus the headers tarball),
generated by config/scripts/update-node-runtime-pin.mjs from the nodejs.org
and unofficial-builds SHASUMS. check-node-runtime-pin.mjs verifies, with no
network, that the pin tracks the locked Electron, matches engines.node's
major, and covers exactly SERVER_TARGETS; it runs in the static analysis job.
ORCAD_BUN_TARGETS consumers now read SERVER_TARGETS so there is one target
list; orcad's Bun runtime and build output are unchanged.
* fix(runtime): reject a pinned archive that belongs to another target
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* fix(emulator): take serve-sim 0.1.47 so the iOS simulator works on Xcode 27
serve-sim 0.1.40's helper binary hard-linked SimulatorKit at its pre-Xcode 27
path, so every iOS emulator start failed with a dyld error on Xcode 27.
0.1.47 replaces that binary with a napi addon that finds SimulatorKit in
either location, and runs the stream helper as a node process.
Match the new helper process (serve-sim.js ... --exit-on-simulator-shutdown)
while still matching legacy serve-sim-bin helpers left by an older Orca, and
mark the package's new standalone helpers executable.
* refactor(emulator): drop redundant serve-sim chmod; the package already ships helpers executable
serve-sim publishes its helpers as 755 and pnpm, fs.cpSync and electron-builder
all preserve the mode, so the executables list and every chmod over it are dead.
* fix(emulator): give the materialized serve-sim runtime its node dependencies
serve-sim 0.1.47's entry imports `ws`. Packaged macOS builds run serve-sim from
a copy under userData, and that copy had no node_modules, so every iOS emulator
attach failed with ERR_MODULE_NOT_FOUND. Dev builds were unaffected because
they run serve-sim straight from pnpm's node_modules.
Lay the copy out as <version>/node_modules/serve-sim and link each dependency
the package declares to the bundle's installed sibling, so transitive deps keep
resolving from the bundle. A runtime in the old flat layout, or one whose links
dangle because the app moved, is rebuilt instead of reused.
* ci: benchmark per-job Node compile caching on full unit shards
* ci: measure unit shards with three and four workers
* ci: benchmark localization extraction CLI patch
* perf(build): reuse identical relay bundles across platforms
* ci: compare Vitest 4 and 5 on complete ARM shards
* perf(ci): upgrade localization extraction to skip irrelevant syntax walks
* perf(ci): use all four ARM cores and remove benchmark workflows
* ci: preserve failures while capturing unit source revision
* fix(ci): preserve commented and escaped localization calls
* ci: remove corrected localization benchmark harness
Electron 43.7.0 threw 'Invalid guestInstanceId' from <webview>'s
disconnectedCallback for a loaded guest (electron/electron#53989), so a
webview React removed and re-inserted kept a dead guest id: the tab went
blank, reload did nothing, and the destroyed listener never fired.
43.7.4 (electron/electron#54097) returns early when the guest is gone.
Raises the runtime floor test to 43.7.4 so a downgrade cannot re-ship it.
Fixes STA-8757
1.65.0 decided "is this key a leaf" by scanning all extracted keys per key, so the
check cost ~13,200 squared startsWith calls. 1.74.1 replaced that with a prefix
Set. Extraction drops from 22.1-23.3s to 16.9-17.4s locally, and the gate from
~23s to 17.8s.
It also extracts 17 keys 1.65.0 missed, all of which are already committed in
en.json, so they move out of the orphan list rather than needing new
translations: extracted 13,191 -> 13,208 and orphans 1,567 -> 1,550. No key is
lost and no existing value changes, so no catalog is regenerated here.
config/oxlint-anti-slop.json turns every native oxlint category off and runs its
rules through jsPlugins, so oxlint's threaded Rust engine does no work and the
pass is one JS runtime per process. Measured, it does not scale with --threads:
11.68s at 4 threads against 12.38s at 16. Parallelism has to come from more
processes, so the audit now splits its file set across them.
Locally, 11.72s single pass against 3.83s at 4 shards (3.1x) and 2.91s at 8.
Sharding is sound because every anti-slop rule is a single-file analysis; the
only mutable module state is a WeakMap keyed on each file's own Program node.
Verified by running both shapes with all 18 rules enabled: 392,398 findings from
one pass and from the shard union, identical as sorted multisets.
Bundle a pinned, verified Bun runtime for headless Orca so existing Node launch commands can hand off before opening a profile. Keep desktop execution on Electron.
Add the Bun SQLite adapter and terminal backend, bounded shutdown, process inspection and cross-platform artifact qualification. Keep future managed SSH deployment separate from current production launch paths.
* feat(search): bundle ripgrep for local, WSL, and SSH search
Ship @vscode/ripgrep-universal's prebuilt rg for all six relay platforms in
every desktop artifact. Local and WSL searches spawn the bundled binary and
drop the git ls-files / git grep fallbacks; SSH deploys upload the remote's
binary once per ripgrep version and the relay prefers it over PATH rg.
* fix(search): address bundled ripgrep review findings
- Key the SSH ripgrep cache on the binary's content hash; a package bump is the only update step
- glibc verifier: read arch tokens below the slice root and accept static ELFs (arm64 release blocker)
- Ship ripgrep/PCRE2/musl license notices; bundle rg with orcad
- Packaged builds never spawn a bare rg; report fd pressure as transient
- SSH: install rg before sweep/GC, size-validate installs, back off instead of disabling on launch failure
- Scope Dependabot to @vscode/ripgrep-universal; revert unrelated lockfile churn
* chore(search): drop bundled-ripgrep reference doc; assert full packaging layout parity
* refactor(search): one entry point for spawning the bundled ripgrep
Local Quick Open, Quick Open path search, the Explorer name filter, and
runtime text search each repeated the same three steps: resolve the bundled
command, spread in the WSL distro, spread in the WSL shell expression. Fold
that into spawnBundledRipgrep so one place owns the rule that a bare 'rg'
must never reach spawn, and simplify the resolver's command/packaged checks.
Restore the AGENTS.md ripgrep rule dropped alongside its reference doc in
63f4dac, and note why the relay's availability probe may spawn a bare 'rg'.
No behaviour change; verified by the existing suites plus a new test that
pins the local, WSL-routed, and distro-routed-but-Windows-output cases.
* refactor(search): drop the local install-ripgrep path; enforce the rg rule
Bundling rg removed the local git/readdir fallback, so nothing can produce
the "install ripgrep on the host running the Quick Open scan" guidance any
more -- only a remote host an upload never reached still reaches the capped
listing. Drop the host parameter, the renderer's local branch and its
translation key, and the relay wrapper that existed only to pass 'remote'.
Add a ratchet test for bare 'rg' spawns, since the AGENTS.md rule alone had
nothing enforcing it. Its one allowlist entry is the relay's PATH probe,
which asks about PATH by definition. Verified the guard catches a planted
offender rather than passing vacuously.
Also stop chaining the remote cleanup sweep behind the ripgrep upload: on a
cold host that is a multi-MB transfer, and stale upload stages and
superseded version dirs were left on the remote for its whole duration. The
two touch different trees, so they now run concurrently.
* test(ssh): pin that the cleanup sweep does not wait on the ripgrep upload
* fix(search): derive rg spawn types instead of importing node:child_process
A type-only import still counts against the child_process ratchet, whose pin
and allowlist only ever shrink. Derive both types from wslAwareSpawn instead.
* fix(search): surface an unreachable WSL workspace instead of an empty result
Inside `bash -c`, a failed `cd` exits 1 -- the same code ripgrep uses for "no
matches" -- so a WSL workspace whose directory had gone away reported an empty
listing as a successful scan. main did not have this hole: checkRgAvailable ran
the same `cd` wrapper first and settled on `code === 0`, diverting to the git
fallback that this PR deletes. The WSL wrapper now takes an optional
cwdFailureExitCode; rg passes 97, and all four close handlers reject with a
clear error before the unavailable check can blame the install.
Also from review:
- Bound the fire-and-forget ripgrep upload with deploySignal. The controller
aborts only on the deploy timeout, never on success, so this cancels a
still-running upload when the deploy gives up.
- Run the stale-stage sweep before the installed check rather than inside its
else branch. Once rg was installed every later deploy took the PRESENT path,
so a stage orphaned by a dropped connection was never collected again.
- Note in orcad-remote-deploy.ts why wiring it up needs ripgrep work first:
build-orcad.mjs copies only the build host's rg, and orcad reports
isPackaged() === true, so a remote of another platform would find nothing.
ssh-relay-deploy.test.ts sat at the max-lines cap, so any edit to it failed the
gate. Split the four Windows named-pipe deploys into their own file (926 -> 737
+ 333); both are now well clear of it.
* fix(search): name the unreachable root in every handler, not three of four
Round-two review caught that the missing-cwd branch in scanRipgrepPaths sat
AFTER isRipgrepUnavailableExit, which classifies any code above 2 as a broken
install -- so for exit 97 it was dead code and Quick Open still told the user to
reinstall Orca. Reordered; all four handlers now check it first.
Also from review:
- A vanished workspace makes spawn fail with ENOENT, which read as a damaged
install on every local path. Confirm the cwd with isRipgrepSpawnCwdUsable --
the guard the relay already applies -- before blaming the binary. The async
continuation re-checks `resolved`, because finish() drops its argument once
settled and the rejected promise would otherwise go unhandled.
- bundledRipgrepCommand returned a bare 'rg' for an arch outside the bundled
set, bypassing the guard that exists so Windows cannot resolve a bare name
against the repo cwd. A packaged app now always names an absolute path.
Drop ci-shards/unit-assignment.json, a 9,425-line CI artifact swept in from
reproducing a shard locally, and gitignore the directory that produced it.
The "rg genuinely cannot start" test pointed at a synthetic /repo, which the
new guard correctly reports as unreachable; it now resolves to a real root so
it still tests what its name says.
* fix(search): let the error handler own the spawn-failure verdict
A failed spawn emits 'error' and THEN 'close' with a negative code. The cwd
check added in the error handler did not settle, so the close handler settled
first -- synchronously, with the reinstall message -- and won the race every
time. The branch was not merely flaky, it was unreachable in all four handlers:
it is guarded by pid === undefined, which is exactly the case that always
produces a following close(code < 0). Verified against a real spawn: 3/3 runs
give error(ENOENT) -> close(-2). The error handler now detaches 'close' before
the probe, so it owns the outcome.
The probe also had no rejection handler, so a probe that rejected left the
search unsettled forever -- a hang, not just a wrong message. It now falls back
to the prior verdict rather than inventing one.
Tests: filesystem-search-rg-timeout and orca-runtime-files-search already cover
error-first and close-first, but against synthetic roots that the new guard
correctly calls unreachable; they now resolve to a real root, keeping each
test's stated intent. Added a Quick Open case for the vanished-workspace path
and confirmed it fails with the old ordering.
* test(search): cover exit code 97 in all four ripgrep close handlers
Round-four review found the missing-cwd branch had zero handler coverage: no
test anywhere emitted close(97), only -2/0/1/2/127. Ordering was correct, but
guarded by source-line order alone -- and that exact ordering was wrong in
three of four handlers two commits ago. Each suite now drives close(97) through
its real handler and expects the unreachable-root message.
Verified the tests earn their place: neutering the missing-cwd check fails
exactly four tests, one per handler.
Also drop a Reflect.get the anti-slop gate rejects, in favour of `in` narrowing.
* docs(search): stop claiming the close handler always wins the race
The previous commit asserted close "would beat this threadpool round-trip every
time", from an n=3 sample that measured event ordering -- which was never in
dispute -- rather than probe-vs-close. Two later measurements disagree with each
other: 50/50 close-first here, 30/50 probe-first in review. Either way it is a
race on a sub-millisecond margin, and the detach is what makes the verdict
deterministic.
Why this wording matters: "close wins every time" is an argument for deleting
the detach as a guard against an impossible race. No test would catch that --
the suites emit error and close in the same synchronous tick.
* chore(search): ship the jemalloc and libunwind notices the Linux rg needs
The statically linked Linux builds carry jemalloc (BSD-2-Clause) and LLVM
libunwind (Apache-2.0 WITH LLVM-exception) in addition to PCRE2 and musl, and
both require their notice on binary redistribution. Confirmed with `strings`:
their symbols are present in linux-x64 and linux-arm64 and absent from the
darwin and win32 builds. Texts taken from the upstream canonical sources.
extraResources already copies the whole licenses directory, so these ship
without a packaging change.
* fix(relay): stop spawning a bare rg, name unreachable roots, collect old builds
Three gaps the reviews surfaced on the remote side, all pre-existing on main.
Bare `rg` on Windows remotes. Both relay spawn sites pass the user's repo as
cwd, and CreateProcessW searches the cwd before PATH -- the same hijack the
desktop side already fixes. The relay now walks PATH itself and spawns an
absolute rg.exe, skipping relative PATH entries because those resolve against
the cwd. No rg on PATH yields null, which callers treat as "ripgrep
unavailable" rather than handing spawn a bare name. POSIX keeps the bare name:
execvp never consults the cwd, so there is nothing to resolve and nothing to
gain. With the last probe converted, the bare-spawn ratchet allowlist is empty.
Empty results for an unreachable root. settleLaunchFailure resolved an empty,
successful-looking scan when the root was gone but PATH rg existed, and the
git/readdir chain never engaged because it only triggers on
RipgrepUnavailableError. Both relay paths now reject naming the root, matching
local workspaces. Missing-rg keeps precedence over a missing root, because only
that verdict engages the fallback chain -- two tests pinned that deliberately
and it would have been wrong to flip it.
Unbounded ~/.orca-remote/ripgrep/. Nothing collected this tree; the relay's
version GC only matches `relay-*`, so every rg bump left another ~5 MB per host
forever. The probe command now also drops sibling builds older than two weeks,
sparing the current one and live upload stages, on POSIX and PowerShell alike.
Two weeks because a client pinned to an older build may still be using it; the
cost of collecting one early is that client re-uploading once.
* fix(relay): probe the rg that failed, and close the drive-relative PATH hole
Five review findings against the previous commit, all reproduced first.
The launch-failure classifier probed PATH rg, but the spawn that failed was the
bundled binary. On the normal remote setup -- no rg on PATH, which is why Orca
uploads one -- the probe failed and a moved workspace was reported as a missing
ripgrep, telling the user to install what Orca already ships. So the fix was
inert on exactly the hosts the uploader exists for. It now takes a candidate
list and asks the binary that actually failed first, then PATH.
path.win32.isAbsolute accepts `\tools` and `/tools`: rooted, but carrying no
drive, so they resolve against whatever drive the process is on. The probe
would have validated one against the relay's drive while the spawn, running
with the user's repo as cwd, resolved it against the repo's -- the same
cwd-dependence this lookup removes, narrowed from directory to drive. A real
drive letter or UNC root is now required.
probeRipgrepVersion had lost the timeout's kill in the rewrite, leaking a live
process and a ref'd handle per launch failure -- for a hang, which is the very
case the bundled-rg back-off exists for. It also spawned without windowsHide,
which would flash a console; fixing that made an allowlist entry stale, so the
entry is gone and the pin ratchets down 63 -> 62.
`windowsPathRipgrep ??= …` never memoised a miss, because null is nullish. The
caching was inverted against cost: a hit stops at the first directory, a miss
stats every one, and only the miss was repeated -- per spawn.
The bare-spawn ratchet claimed "nothing in production spawns a bare rg", which
is false on POSIX. It now also matches PATH_RIPGREP_COMMAND at a spawn site,
and the comment states plainly what a textual guard cannot see: the POSIX bare
name reaches spawn as a parameter, and is safe because execvp ignores the cwd.
The drive-rooted predicate is tested directly rather than through the
filesystem -- a temp dir on a POSIX CI host has no drive letter to exercise
win32 semantics with, so the filesystem test could never have caught this.
* test(mobile): repin the session closure past #22452's two shared modules
Merging main brought the closure to 4220 against a pin of 4218. The two extra
modules are `src/shared/agent-turn-outcome.ts` and `src/shared/main-agent-status.ts`
from #22452, which the status projection this route already reaches import.
That change was src/shared-only, so the mobile job never ran on it -- the same
way the structured tool line slipped past, as the ledger above already records.
Repinned here because this PR's file set is what next made the job run, not
because this PR reaches either module. Verified: of the 28 source files this
branch changes, none appear anywhere in the route's 4220-module closure.
* fix(search): preserve remote binaries and complete runtime packaging
* test(relay): pin the probe's env now that it inherits the relay's PATH
8d6759a threaded the relay env into probeRipgrepVersion -- correctly, since the
probe decides whether a launch failure was the binary or the root and so has to
resolve the same rg the failed spawn would have. It left the assertion that
pins the probe's spawn arguments behind, which is what CI caught.
Asserting buildRelayCommandEnv() rather than loosening the match to any object:
under process.env the probe could resolve a different rg, or none, which is the
regression the change exists to prevent.
* feat(ssh): collect remote ripgrep builds by reference, not by age
Nothing collected `~/.orca-remote/ripgrep/`: the version GC matches only
`relay-*`, so every change to the shipped bytes left another ~5 MB on every SSH
host, permanently. The age window this replaces was the wrong instrument --
a directory's mtime is when it was written, not when it was last used, so it
cannot tell a superseded build from the one a live relay was launched against.
Deleting the latter is not graceful degradation: without a PATH ripgrep remote
text search rejects outright, and listing drops to the capped walk this PR
exists to remove.
So the question is reference. Each relay directory now records the build it
runs against in `.ripgrep-ref`, written only once that binary is confirmed
present, and the GC collects a build only when no installation names it.
The discipline is ssh-relay-native-deps-cache-gc.ts': anything the pass cannot
account for blocks the whole pass. A relay directory with no readable marker is
an older Orca's, possibly running right now against a binary it never recorded,
so the pass declines rather than guessing. Those directories are removed by the
version GC in time, which is what makes their builds collectable -- hence
running after it, not beside it. Deletion is the same tombstone, recheck under
the rename, then remove, so a deploy that takes a reference mid-pass gets its
tree restored. Windows has no pass yet, matching the native-deps cache's gate.
One test note: the first version of the "unaccountable blocks the pass" test
passed against a deliberately broken guard, because the tombstone recheck
masked its absence. The test now puts a readable recheck behind an unreadable
first scan, which is the only shape that fails when that guard is removed.
Recording the reference lives inside ensureRemoteBundledRipgrep rather than at
the call site: it is the same concern, and it keeps the deploy's ripgrep
surface to one call for the tests that mock it to protect their exec queues.
* feat(ssh): collect Windows remotes too, and ship the Rust crate notices
Three items previously left documented-but-open.
Windows remote accumulation. The cache GC was POSIX-gated, so the leak did not
go away -- it moved to the platform with the larger binary (rg.exe is 5.43 MB on
win32-x64, against 4.77 MB for linux-arm64). The PowerShell dialect now does the
same reference scan: entries and references carry token prefixes, because
PowerShell writes every uncaptured value to stdout and an untokenised listing
would feed Remove-Item whatever a cmdlet happened to emit.
Verified on a real Windows host rather than a mock: the listing emits its
ENTRY/LIST_OK tokens, a relay directory carrying a marker yields REF <entry>,
and a relay directory without one yields REFS_ERR -- the safety path, on the
real interpreter.
Rust crate notices. The crate set was read out of the shipped binary's symbols
and the licence identifiers taken from crates.io rather than assumed. Where a
crate offers the Unlicense, Orca elects it: a public-domain dedication carries
no notice obligation, and that covers eight of them. The four that do not offer
it get their MIT text reproduced. encoding_rs carries a BSD-3-Clause notice for
its WHATWG-derived encoding data that is joined by AND, not OR, so electing MIT
does not discharge it.
Release-only validation, corrected rather than repeated. Linux AppImage/deb/rpm
already runs in CI's package job on every PR, and Windows signing was already
rehearsed on this branch. macOS notarization is the only item a release must
still exercise, and the exposure is narrow: notarization requires signatures on
Mach-O binaries, and of the six bundled builds only the two darwin ones are
Mach-O -- `file` reports ELF for linux and PE32+ for win32 -- so signIgnore
excludes only files the notary never asks about.
orcad-artifacts.test.ts caught the new notice file missing from the standalone
runtime's shipped list, which is exactly the gap that test exists to catch: a
notice committed to the repo but never actually shipped.
* fix(search): protect relay cache references and handle failed spawns
* fix(ripgrep): close review gaps and repair deployment fixtures
* test(mobile): refresh merged session module census
* fix(ssh): preserve ripgrep caches with empty legacy references
* test(mobile): assert bundle boundaries instead of global module count
* feat(mobile): one build-time constant decides native or OTA, default native
EXPO_PUBLIC_MOBILE_SHELL is read in exactly one place, mobileShellBuildKind in
preferences.ts. Expo's babel preset inlines a literal process.env member
expression at build time, so a release bundle carries the answer as a constant
and anything but the exact string 'ota' — unset, empty, a typo — is native.
Every default build is therefore the native app, unchanged.
mobileWebShellFlagCanBeOn now answers __DEV__ or an OTA build, so the ability to
mount the page comes from the build and never from storage: a native binary
installed over an OTA one, same bundle id and same data container, still refuses
a stored 'true' without reading the key. An unset key reads on only in an OTA
build; a development build keeps its opt-in, and a stored 'false' wins
everywhere so the Troubleshoot toggle can switch an OTA build back to native.
That toggle now mounts wherever the flag can be on, which is the only way back
to the native screens in an OTA build, and its label names the build kind rather
than saying "(dev)". The bundle probe row beside it stays development-only: it
fetches.
The flag census gains two rules — one module reads the switch, in the member
form Expo inlines and not the bracket form, and one named function answers the
build kind — and the build-kind fence now lists the Troubleshoot route that asks
it. Docblocks that said a store build can never mount the shell now say it
mounts only when built for OTA.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* ci(mobile): one workflow input picks the shell, and no input means native
Both release workflows gain a `shell` workflow_dispatch choice, options native
and ota, default native, and hand it to the step that bundles the JavaScript as
EXPO_PUBLIC_MOBILE_SHELL. That is the Gradle assembleRelease step on Android and
the fastlane build_and_upload step on iOS; nothing else in either file sets it.
A tag push and a schedule carry no inputs at all, so `inputs.shell || 'native'`
yields native for them — the first OTA release is a dispatch with one field
changed, and every other run is the app we ship today.
Each build step prints the value it is about to build with, read back from the
same variable rather than from a second copy of the expression, so a run's log
cannot claim a shell the build did not use.
The new contract test evaluates that expression rather than matching its text:
absent, empty and 'native' all resolve to native, 'ota' to ota, and any
expression shape it cannot evaluate is a failure rather than a pass.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* build: the desktop packages the real page, and the placeholder is retired
build:mobile-web now runs the app builder and app verifier, and both take their
output root from MOBILE_WEB_BUNDLE_DIR in the packaging guard rather than each
carrying a constant of their own — one definition of where the bundle lives, so
a drift cannot leave electron-builder's beforePack looking at an empty directory
while the builder reports a tree it wrote elsewhere. build:mobile-web:app is
gone; it was the same two commands.
src/mobile-web/ and its two scripts go with it. What the app builder shared with
them is split into three modules named for what they hold rather than for the
bundle that used to own them: mobile-web-bundle-manifest.mjs (content types, the
canonical asset serialization, buildId, hashed assets, the protocol window and
the manifest write), script-entry-detection.mjs (isDirectInvocation, whose two
failure modes are Windows paths and symlinked entries), and
mobile-web-source-line-endings.mjs (the CRLF guard, now with a required
directory rather than a default pointing at the deleted tree).
The two suites that only needed *a* valid tree on disk — the beforePack guard
and the packaged-bundle guard — build one from mobile-web-bundle-fixture-tree
instead of bundling the whole mobile graph. It goes through the same manifest
writer the page does, so a manifest shape change still reaches them.
Also retired: the placeholder's tsconfig project and its typecheck lane, its
knip entry, its electron-builder exclusion and .gitattributes pins, and the
app-bundle test that asserted the shims stayed out of a builder that no longer
exists. pr.yml's page job builds the same bundle the package job ships.
Inert for native phones: they never fetch it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* style(config): one import of node:fs/promises in the entry-detection suite
The changed-code quality gate's focused plugins read the two as a duplicate
import; the readFile line was left over from the split.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs: the comments that still describe the retired placeholder bundle
The web entry said it was built by `build:mobile-web:app` into out/mobile-web-app
and shipped by nothing. That script, that directory and that fact are all gone:
it is built by `build:mobile-web` into the packaged bundle dir, and a phone
mounts it only when the binary was built with EXPO_PUBLIC_MOBILE_SHELL=ota.
Two Windows cache keys explained themselves by naming src/mobile-web and "the
two bundle builders"; config/** now covers the builder, the verifier and the
manifest writer, and the spike's key no longer waits on a Phase C flip that has
happened. The keys themselves are unchanged.
Three scratch directories in the app-bundle suites and one in the verifier still
spelled the retired output root. Renamed to mobile-web, which is what the build
writes; they are temp subdirectory names and nothing reads them.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat: add supervised Antigravity worker support
* fix: address Antigravity worker review findings
* fix: stabilize Antigravity readiness detection
* fix: allow Antigravity resume footer after readiness
* fix(antigravity): make agy reach worker_done as a supervised worker
Three defects each blocked `orchestration worker-start --agent antigravity
--worktree new-child` at the agent_readiness stage.
1. Readiness never fired. The composer check required the trimmed line to be
exactly one character, but agy 1.2.7 launches in accept-edits mode and paints
it into the caret row (`> Accept-edits mode: ...`). Widened narrowly to a bare
`>` or `> <name> mode:`; matching any `> <text>` would make every menu dialog
read as ready, since they all prefix their highlighted row the same way.
2. No trust artifact for agy. Added markAntigravityWorkspaceTrusted, writing
~/.gemini/antigravity-cli/settings.json under `trustedWorkspaces` — verified
empirically against agy 1.2.7, and distinct from the Gemini CLI's
trustedFolders.json, which agy does not consult. Trust is exact-path and not
inherited by subdirectories, so each child worktree needs its own entry.
3. The orchestration path skipped the preset. Orca has two trust dispatch
chains: the renderer's preflightAgentTrust and the main-process
markLocalWorktreeTrusted. worker-start only takes the second, which matched
cursor/copilot/codex and fell through for antigravity, so the trust write
never happened while renderer-side tests passed.
Verified live end to end: the dispatch settles `succeeded` with worker_done
carrying the right task and dispatch ids, and the worktree is appended to agy's
settings with sibling keys untouched.
Known gap: remote-agent-trust-presets.ts has no antigravity branch. The SSH
artifact path is unverified, so agy over SSH still stalls at agent_readiness.
Recorded in a comment there rather than guessed at.
* fix(antigravity): wire trust preset through preload safely
* fix: preserve Antigravity readiness across transcript tails
---------
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: LielinaH <lielinah@gmail.com>
* feat(terminal): inline images via @xterm/addon-image, perf-first
Add opt-in inline terminal images (SIXEL, iTerm2 IIP, Kitty graphics)
through @xterm/addon-image, designed to keep idle terminals unaffected.
Performance:
- The addon (base64-inlined wasm decoders + protocol handlers) loads off
the boot critical path via a deferred loader that mirrors the WebGL
addon: primed after first paint only when the setting is on, read back
synchronously at attach, with a 3-attempt cap so a transient failure
never disables images for the session and a missing chunk never
refetches per pane. renderer-boot-graph guards against eager import.
- enableSizeReports:false so the addon never sets windowOptions and
double-answers Orca's own CSI 14t/16t responder.
- Perf-tuned decode/storage limits (storageLimit, sixel/iip/kitty size
caps) in one place.
Correctness:
- Orca's DA1 handler wins over the addon's (last-registered-first), and
the default DA1 response never advertised Sixel (;4), so DA1-detecting
tools (chafa, img2sixel, viu, timg) never emitted it. The winning
handler now appends ;4 while the setting is on, resolved per query so a
live toggle changes the next DA1; idempotent against the ConPTY
response that already lists it.
- ORCA_IMAGE_PROTOCOL=kitty is exported to spawned shells (local, daemon,
relay/SSH) and forwarded across the WSL boundary, so image-capable
agents can pick an encoder. Unknown image sequences are swallowed by
xterm when the addon is detached, so this never garbles output.
- Settings toggle (default on) gates rendering and DA1 advertisement.
Cross-checked against community PRs #7775, #11706, and #19201 at the end;
credited below.
Co-authored-by: s546126 <s546126@users.noreply.github.com>
Co-authored-by: XRX193 <XRX193@users.noreply.github.com>
Co-authored-by: lmsh7 <lmsh7@users.noreply.github.com>
* fix(terminal): bound inline image memory and classify Kitty replies
* fix(terminal): bound image decode and release image resources on cleanup
* fix(terminal): address image addon review feedback
* test(terminal): stub setPaneInlineImagesEnabled in appearance manager fakes
* fix(terminal): evict unplaced kitty payloads before displayed images
Byte-budget eviction dropped the oldest transmitted blob regardless of
placement, so a new upload could erase a visible image while abandoned
blobs still held budget. Unplaced payloads now go first and displayed
ones only when that is not enough. The incoming image is always stored,
so an oversized one overshoots the cap by one payload instead of being
dropped after the protocol already acked OK.
* fix(terminal): gate DA1 Sixel on real addon attachment; claim SSH image spec in CI
- DA1 advertised Sixel from the setting alone, so a pane whose lazy addon
chunk was still loading (or had failed all three attempts) told
feature-detecting tools to emit DCS that nothing could render. Track the
attached decoder per terminal and require it before setting the ;4 bit.
- tests/e2e/terminal-inline-images-ssh.spec.ts was Docker-gated but claimed
by no lane runner, so pr-e2e-gate-contract failed and the spec would have
self-skipped green forever.
- Reject non-positive PNG IHDR dimensions before decode: they are parsed with
signed shifts, so a dimension >= 0x80000000 came back negative and slipped
past the pixel-limit comparison.
- One resolveTerminalInlineImagesEnabled() for the default-on setting; the
four call sites mixed '?? true' with '!== false', which disagree on null.
- One readInlineImageResources() walk of the addon internals instead of two
copies that could drift against the patched dependency.
- Isolate the deferred-attach drain per pane; make the zoom-invariance and
backing-storage e2e assertions fail when the feature is dead.
* refactor(terminal): one lazy xterm addon loader for webgl and image
terminal-image-addon-loader was a structural clone of the webgl one — same
memo, attempt cap, and .then(ok,err)-clears-memo recovery. Both now wrap
createLazyXtermAddonLoader; each keeps its literal import() specifier so the
bundler still splits the chunk (verified against a fresh build: addon-image
stays out of the boot graph).
* refactor(terminal): name openTerminal's addon flags; pin image addon limits
Two adjacent optional booleans could be swapped without a type error once
inline images added the second one.
* docs(terminal): state the real per-pane image ceiling; drop test ordering dependency
storageLimit:32 reads like the pane's budget but keys three pools — decoded
pixels, retained encoded Kitty blobs, and pending WASM decoders — so the worst
case is ~98 MB per pane with no cross-pane governor. Say so at the constant.
pane-inline-images.test.ts's deferred case needed to run first; it now takes a
fresh module instead, and the rest prime in beforeAll. Verified by running the
file with that test moved last.
* fix(terminal): satisfy rebased static analysis gate
* fix(terminal): complete casting gate cleanup
* fix(terminal): recover failed image addon loads
* fix(terminal): bound image decoder allocations
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: s546126 <s546126@users.noreply.github.com>
Co-authored-by: XRX193 <XRX193@users.noreply.github.com>
Co-authored-by: lmsh7 <lmsh7@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>