Commit Graph
13702 Commits
Author SHA1 Message Date
OrcaWinandOrca Worker 83056e5edb fix(skills): judge official skill updates by ownership, not snapshot matching (#27603)
* fix(skills): judge official skill updates by ownership, not snapshot matching

Orca decided whether an installed official skill was pristine by matching its
bytes against a shipped list of past snapshots, falling back to the skills CLI's
lock hash. Copies installed between releases, CRLF copies, and installs whose lock
recorded a sha256 hash all read "doesn't match the official version" and could not
be updated (#26841), and older content could be labelled newer and never offered.

Now an official skill is Orca's when the skills.sh global lock says it came from
stablyai/orca, and it is current only when its listed files equal the bundled
manifest (line endings and sidecars ignored); anything else is outdated. Install
and update are one pinned `skills add .../tree/v<appVersion>` command, used by the
in-app runner, `orca skills update`, and the Settings rails. The update dialog
preselects Orca-owned rows and lists same-named skills from elsewhere unchecked.

Removes the snapshot registry, release mapping, git-tree identity, lock trust and
convergence logic, and the release-cut provenance step.

* ci(skills): run the pinned-add round trip on a skills CLI that can fetch a commit

* fix(skills): let orca skills update target the shared folder on a bare host

Update became the same pinned add as install, so it inherited install's
refusal when no agent is detected. That broke the bare-host update the old
command guaranteed, and the built-CLI check (update --dry-run, no --agent) on
CI runners. The in-app runner already fell back to universal; both now share
one detectSkillsCliUpdateAgentKeys rule.

Also brings the CLI skills docs off `npx skills update`.

* refactor(skills): derive the update selection from the grouped rows

The selection hook took the live and the retained inventory, re-derived Orca
ownership from installations beside the grouping's own orcaOwned, and kept a
separate display count. Groups already carry both eligibility and ownership,
so the hook reads them once; the button's existing live-scan gate is the only
authorization check. The unselected line is inlined into the headline.

* refactor(skills): drop helpers the ownership design made redundant

- isOwnerManagedSkillScope only negated into the participation check once
  plugin caches stopped participating; fold it in.
- Eligibility re-checked resolvedPath/physicalIdentity, which observation
  already turns into 'inaccessible'.
- The manifest verifier probed access() before a readFile that throws anyway.
- orchestration-install-command.ts only forwarded one constant from the lib
  module its three importers already import.

* i18n(skills): stop naming npx skills update in four skipped-copy strings

The es/ja/ko/zh cantUpdateReason and tipDuplicate strings still named
`npx skills update`, which Orca no longer runs for its skills. Say 'the update
command' instead.

* refactor(skills): inline the single-use pinned source URL helper

orcaSkillsSourceUrl was exported but only called by the builder beside it.

* fix(skills): let an update choice lapse when the install source changes

The selection froze every checked name, Orca's preselection included, the
moment the user ticked anything. A re-check that then attributed a name to
another source kept it checked, replacing it without consent. Store only the
user's explicit choices, keyed by name and lock source, and default to
ownership otherwise.

* fix(skills): label the empty update action 'Update skills', not 'Update 0 skills'

The button stays whenever a row is updatable, so unticking every row (or a
dialog with only non-Orca copies outdated) doesn't reflow the footer. At zero
selected it stays disabled and reads 'Update skills' instead of a zero count.
The label lives beside the dialog's other count copy.

* fix(skills): let keyboard focus open the Not from Orca tooltip

The source of a non-Orca copy is only in the tooltip, and its trigger was a
plain span. Make it focusable like Orca's other tooltip-on-text triggers
(tabIndex 0, cursor-help, the row primitive's focus ring), as in the agreed
mock.

* test(skills): hold the round-trip add equal to the shared install builder

The CI round trip hardcoded its skills add argv, so it could drift from what
Orca runs. A .mjs script can't import the TS builder (extensionless imports),
so the argv moves into an exported roundTripAddArgs and a unit test asserts it
is the builder's unattended add with the tag swapped for the PR commit. The
script runs under the same main guard as the manifest generator.

* i18n(skills): translate the skill row's location count and duplicate reason

es, ko and zh still carried English for SkillUpdateRow.oneLocation and
manyLocations, and es and ko for SkillFreshnessRow.skippedReasonDuplicate.

* fix(skills): pin official skill installs to the build commit and skills CLI

`skills add .../tree/v<version>` only works for stable and RC builds: hourly,
daily and adhoc builds publish elsewhere, so stablyai/orca has no tag for their
version and both install and update failed there. Every build is made from a
commit that exists in stablyai/orca, so the build now stamps `git rev-parse HEAD`
and the one shared builder pins to `tree/<commit>`, which equals the bundled
skills by construction. Main, renderer, web and vitest get it as
ORCA_BUILD_COMMIT (replacing ORCA_APP_VERSION); the tsc-built CLI reads it from
the compiled out/package.json boundary. A missing or malformed commit fails the
build or the command with a clear error instead of building a bad URL.

The builder also pins the skills CLI (`npx skills@1.7.2`): 1.5.x cannot fetch a
commit, and a bare `npx skills` can reuse a stale cache or resolve to another
binary (#18687). The CI round-trip script reuses the same constant.

* ci(skills): run the round trip on the shared pinned skills CLI version

* docs(skills): document the commit-pinned skills add

* refactor(skills): keep the pinned skills CLI version in the install builder

skills-cli-version.ts existed only so the plain-Node round-trip script could
strip-load a .ts file, which needed --disable-warning in CI. The version now
lives beside the builder that uses it; the script keeps a literal, and the
existing round-trip test already asserts its argv, skills@<version> included,
equals the builder's.

* refactor(skills): validate the build commit once, where it is resolved

resolveBuildSourceCommit already refuses anything but a full hash, and both the
define and the CLI's stamped package.json come from it. The builder's second
check could only fire as a module-load throw in the renderer's command
constants. Also drops the resolver's unused cwd parameter.

* test: stamp a fixed build commit instead of running git in the vitest config

Tests only interpolate ORCA_BUILD_COMMIT, so they need not read HEAD (or fail
outside a git checkout) on every run.

* refactor(settings): match only skills add in the Windows npx preflight

Every official skill command is now the builder's skills@<version> add; no
rail emits skills update any more.

* fix(skills): fail an update that leaves a broken skill link behind

Eligibility already skips names with a broken link before the run, so one
afterwards is the run's own damage; report it instead of claiming success.

* ci(release): refuse to tag when the skill manifest is stale

The removed --release step implicitly checked the committed manifest against
skills/. Keep that guarantee with a verify-only run; it writes nothing.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-11 18:30:03 -07:00
Brennan Benson 4526d8bab4 native chat: queued-message actions and question answers accept through their own receipt (#26988)
* feat(native-chat): add a chat-owned command receipt store

Adds the agent_session_command_receipts table and an insert-if-absent
receipt write that runs inside a command's own journal transaction. A
receipt records the command's identity, canonical hash, status and a
pointer to the row holding its result; it dies with its chat record.
Nothing uses it yet: the existing operation ledger stays authoritative.

* fix(native-chat): key command receipts like today's ledger ids

Rows are keyed by caller and operation id; a send looks across callers and
other methods look up only their caller's row, matching the existing ledger.
Bad rows and newer table shapes read as unreadable instead of throwing.

* fix(native-chat): order receipt lookups by insertion and key by operation id

Global lookups now take rows in insertion order, so a read and an insert
agree on which receipt answers an id regardless of the host clock. The
primary key leads with the operation id, so its own index serves both
lookups.

* fix(native-chat): accept sends through their own receipt

Send (direct, queued, steer) and /compact no longer commit a separate
ledger reservation before the message is saved. Their command receipt is
inserted-if-absent in the same transaction as the submission or queued
draft; a repeated id replays from it, a changed request conflicts.
Operation ids are checked for syntax only, so age, clock skew, pruning
and the reservation's own storage can no longer refuse a send.

The ledger success row they already co-committed stays as a temporary
insert-if-absent write so families not yet switched keep seeing these
ids; it is never read on this path.

* fix(native-chat): answer a send's receipt one way, share /compact's id space with Send, wake delivery once

- After its commit, a send whose publication fails answers through the same receipt replay a retry takes, so one id gets one answer; the in-memory copy and journal reload are gone.
- /compact ids are global like Send's: a retry under a new connection replays instead of needing a second acknowledgment write.
- The receipt route calls its runner directly; the runner wakes delivery when a new message row commits.
- The compatibility ledger row expires from host time; each temporary site is labelled.
- Receipt read errors and post-commit failures are logged.

* test(native-chat): expect receipt answers where tests assumed the send ledger

Restart-continuation tests now model the case Send cannot vouch for (saved but
not loaded into memory) and keep it unconfirmed; a later throw after the message
is in the chat answers continued, as a resend would. Drop /compact's unreachable
legacy replay branch and fix stale comments.

* test: run the queued tests that open the journal database in the Node runtime project

* fix(native-chat): queued-message actions and prompt answers accept through their own receipt

Send Now, Delete, Resume and prompt answers move onto the command-receipt route
Send uses: the receipt is inserted with the command's own write. A no-op answers
only once its receipt commits (otherwise outcome unknown), and a prompt answer's
replay reads the exact revision it was accepted at.

* fix(native-chat): replay a queued action from what its receipt recorded

Send Now's acknowledgement points at the submission it handed off; a question
answer's receipt keeps the accepted resolution; a no-op that cannot save its
receipt answers outcome unknown and holds nothing in memory; classified refusals
pass through.

* docs(native-chat): receipt comments say what a replay answers from

* fix(native-chat): refuse a prompt answer on a newer Orca's journal with the update words

A prompt answer now runs through the same newer-Orca refusal the queued-message actions use, so a read-only journal answers agent_session_journal_unreadable instead of throwing journal_read_only.
2026-10-11 18:25:26 -07:00
Jinwoo Hong d0306a8e56 perf(daemon): yield between bounded slices of terminal output ingest (#27578)
* perf(daemon): yield between bounded slices of terminal output ingest

* fix(daemon): drain already-read stream output before handling a control close
2026-10-11 21:21:33 -04:00
OrcaWinandm4air e2b31cbc59 ci(e2e): one managed-SSH lane and routing regex; no relay build in e2e setup (#27640)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 18:18:59 -07:00
Jinwoo Hong 8dfa2b1875 test(e2e): mixed-version, two-client and phone layout baselines recorded on main (#27688)
* test(e2e): record mixed-version, two-client and phone layout baselines on main (P3)

* test(e2e): P3 review fixes: reconnect and I/O proof, restart order bounds, Linux-only baselines

* test(e2e): re-record Linux phone and serve-restart baselines

* test(e2e): P3: echo drawn in the desktop pane; bound the group selection after restart

* test(e2e): re-record serve-restart group selection

* test(e2e): name P3 baseline helpers for what they record
2026-10-11 21:18:16 -04:00
9d305f3ce4 refactor(settings): one settings host-scope type and label (#27629)
Settings had two identical provider-scope types, a host-choice type that
copied RuntimeClientTarget, a scope-to-environment-id getter that copied
runtimeTargetEnvironmentId, and the remote-server/local label written
three times. Keep one of each. getProviderAccountScope now reads the
default host through defaultScopeHost, the sanctioned reader; output is
unchanged (trimmed id, blank means this computer).

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:50:13 -07:00
OrcaWinandm4air c5224f70d8 refactor(providers): delete the SSH git and filesystem providers and relay-only wiring (#27644)
* refactor(providers): delete the SSH git and filesystem providers and relay-only wiring

* refactor(ssh): drop relay-protocol exports only the deleted stream readers used

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 17:48:43 -07:00
Brennan Benson c9097ac991 test: run a structured chat against the packaged headless server (#26527)
* fix: preserve module URLs in the headless server bundle

* test: exercise structured chat on the packaged Node server

* test(orcad): run the packaged chat check from the desktop's server template

The chat check loaded the server bundle directly, so a desktop build missing its
server template still passed. Materialize the slot from the built template the way
the desktop does, and replace the fixed streaming pause with a test-owned
acknowledgement so the partial-reply assertion can't race.

* Connect the packaged chat test through a paired connection

Main now sends remote runtime requests over a paired connection rather than a
pairing offer, so the test converts the server's offer once when it is ready.
2026-10-11 17:46:15 -07:00
OrcaWinandm4air cf58a13202 Revert "feat(mobile): verified https pairing links; custom scheme carries no secret (#27475)" (#27693)
This reverts commit 20f8cb02c1.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 17:45:02 -07:00
Jinwoo Hong 599a610a5a chore(relay): turn on the director Cloud SQL proxy sidecar and stall alert in production (#27697)
Staging proved both on image F (2026-10-11): the sidecar keeps the director's
readiness through a Cloud SQL outage, and the event-loop stall alert reads
eventLoopDelayMsMax. Production adopts the shape on its serving image first.
2026-10-11 20:44:20 -04:00
Brennan Benson ee34f729be Keep SSH chat providers alive after the desktop disconnects (#26473)
* fix: preserve structured chat work during automatic server stops

* fix: require stop-request support before automatic cleanup

* fix: retain unverified session owners until exit is proven

* fix: preserve unreadable work and retry expired prompt stops

* fix: give unheld reservations and queued sends retirement exits

* fix: clear empty recovery leases and preserve unverified owners in tests

* Keep managed servers alive while chat providers are loaded

* Recheck returning clients before managed idle shutdown

* test: run the managed idle exit chat provider test in the Node runtime

It builds on the native chat rest test rig, which opens a real SQLite database.
2026-10-11 17:24:25 -07:00
Brennan Benson cfb63dc6ca test(pty): pin launch race fixes on both spawn paths before merging them (#27187)
* test(pty): pin pane-reservation launch races on both spawn lanes

* test(pty): pin failure-cleanup, commit-admission and spawn-guard launch races on both lanes

* test(pty): name launch-race fixture types so declarations stay portable

* test(pty): pin the runtime lane's hidden-mark rollback on a post-spawn throw

* test(pty): reshape launch race pins as per-lane tables and pin the review's missing drifts

* test(pty): pin the window lane's wait for a host create in progress

* test(pty): restore joiner-setup and early hidden-rollback pins; reword size and reattach notes

* test(pty): pare launch race pins to the cases later spawn-lane PRs rely on

Merge the four IPC race files into one, reuse the shared daemon provider
double, fold the host createTerminal guards and the window's create-wait
into existing specs, and drop pins of duplicated, unobservable or
already-decided behaviour.

* test(pty): drop the host early-hold pin that the claim-arbitration test already protects

Breaking the runtime lane's early pane reservation also fails
'reuses runtime materialization when renderer focuses the same pane during spawn'.

* test(pty): pin which lane overwrites a remembered size while a failed start builds
2026-10-11 17:00:37 -07:00
Brennan Benson e5a4e0c96f test(launch): pin what each agent launcher produces on main today (#27189)
* test(launch): pin the window planner's quoting platform per workspace

* test(launch): pin the runtime a window pane sends on spawn

* test(launch): pin the host's quoting platform for repos and folders

* test(launch): pin the runtime assembler's shell per workspace

* test(launch): pin that a queued tab startup does not survive a restart

* test(launch): pin the quick composer's startup handed to workspace creation

* test(launch): pin the window-lane spawn assembler's shell, env and telemetry per launcher

* test(launch): pin what host-lane launches hand the terminal spawn

* test(launch): pin the spawn request desktop automations send, local and paired

* test(launch): pin typed-prompt, transcript-continue, OpenCode and paired new-tab launches

* test(launch): pin work-item quoting, create payloads, prompted paired tabs and the WSL cwd shell rule

* test(launch): type launch fixtures instead of casting them

* test(launch): drive both launch lanes to the provider from shared per-producer tables

* test(launch): pin rows 4, 5r, 7 and 8 through real producers and shared tables; fold duplicates

* test(launch): type the parity tables and harnesses

* test(launch): pin the pane identity keys main stamps on both lanes

* test(launch): drop a redundant cast and name the automation case fields

* test(launch): build every parity row as a real producer does; pin kitty, pane size, env values, hidden and paired-host spawns

Round-2 review fixes: local Windows ConPTY panes withhold the kitty flag, the pane's
measured grid reaches the provider, agent/caller env values and the hidden mark are
provider facts, rows 5r/7 run through the host's RPC handlers, SSH folder rows quote an
apostrophe, Windows clients use their real '' default shell, titled automations only,
the quick composer drives its real selection hook, and inputs no producer builds are gone.

* test(launch): drop the automation missing-folder row, which cannot fail

* test(launch): feed the composer hook its real eligible repos; type the paired RPC request

* test(launch): quick commands auto-submit, mobile agent.launch for row 10, real desktop caller, older-host seeding, local Windows folder composer, gone automation root

* test(launch): give each AI-button row the button that reaches its workspace kind

* test(launch): re-pin to main's execution-host platform: hosts report their OS, wsl$ and SSH folders quote for Linux, older paired hosts get the host's quoting

* test(launch): automations and window tabs share the execution-host quoting rule

* test(launch): give the test-only parity modules test suffixes

* test(launch): cut the parity tables to the rows a later plan PR checks against

Drop SSH rows (the SSH PTY provider is gone from main), rows whose behaviour is
already decided to change, rows existing tests already pin, and the hostAdmittedMembership
assertion main removed. Workspace-creation and paired new-tab producer tests move to C4.
Each remaining suite names the later PR that relies on it.

* test(launch): case runs pass full settings to the shared renderer fixture

The focus-read ratchet counts getDefaultSettings in a new test fixture; the suites
build the settings instead. Type the row 8 telemetry literal.
2026-10-11 17:00:10 -07:00
Brennan Benson 8cc779e576 Editor: Nim and Typst on the shared TextMate layer, so a long line can't freeze the window (#26987)
* 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
2026-10-11 16:54:08 -07:00
Jinwoo Hong 29dca8c031 ci: the layout oracle gates merges (#27600)
* ci: the layout oracle gates merges

An oracle-only job joins verify's needs. It runs only the two layout oracle
specs through the existing e2e workflow, routed by layout writer paths; a PR
outside the route skips it and verify treats the skip as a pass. The route is
widened to the terminal store, terminal pane, graph sync, frozen mirror and
compatibility folders while the layout-holder stack is open. The oracle specs
leave the advisory e2e job so a PR does not run them twice.

* ci: the layout oracle tests the merge commit and routes its harness and stack writers

Review fixes: run on the merge commit so a stale-base PR cannot pass on an old
main; route the oracle's own e2e harness and runtime client (a change there broke
main's oracle without running it), and the window-side layout writers the stack
edits; move the route into its own module.

* ci: route the remaining layout writers the stack edits to the layout oracle
2026-10-11 19:39:59 -04:00
Neil a944a37c8c [CI] Check real I/O progress during worktree deletion (#27620) 2026-10-11 16:26:37 -07:00
Kelvin AmoabaandBrennan Benson 641b9773cc fix(renderer): jump instead of smooth-scrolling under reduced motion (#26796)
* fix(renderer): jump instead of smooth-scrolling under reduced motion

A scripted scroll's explicit behavior ignores the reduce-motion setting.

* test(renderer): cover motionSafeScrollBehavior under reduced motion

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-11 16:11:10 -07:00
OrcaWinandm4air b02240639d refactor(renderer): one default-host read; one catalog fan-out (#27613)
* test(renderer): characterize all-host catalog settlement

* refactor(renderer): one default-host read; one catalog fan-out

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 15:57:41 -07:00
Brennan Benson 28df5197ac fix(native-chat): keep diagram source mounted and retire reveal history (#26690)
* fix(native-chat): preserve pending diagrams and retire reveal history

* fix(native-chat): keep diagrams through theme changes and key reveal history by message id

- MermaidBlock: a result goes stale only when its source changes, so a theme switch keeps
  the old diagram until the new one renders instead of flashing back to source.
- Reply reveals keep only loaded message ids (conversation rows plus subagent rows), since
  only message rows reveal; drops the separate row inventory and restores nativeChatSlotKey
  to the slots module.
- CommentMarkdownDocumentPre: drop the unreachable renderMermaid half of the fence check.
- Tests: Mermaid stays idle while streaming (waits past the async load), theme change keeps
  the diagram, held renders are settled after each test, a reopened subagent section does
  not replay its streaming reply through the real list, and retirement is tested by outcome.
2026-10-11 15:56:47 -07:00
OrcaWinandm4air df2f2a72de refactor(ipc): filesystem and repo handlers are local or runtime only (#27609)
* refactor(ipc): filesystem and repo handlers are local or runtime only

* test(ipc): drop two casts in the rewritten listing and import tests

* fix(providers): keep the default error name so IPC callers see the old text

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 15:41:56 -07:00
OrcaWinandm4air c9730df4c8 refactor(renderer): no rebound-settings owner pattern (#27607)
* refactor(renderer): no rebound-settings owner pattern

* chore: lower focus-read baseline for composer owner scalar

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 15:34:24 -07:00
OrcaWinandm4air 5e9fb931a1 fix(codex): copy from remote-runtime Codex sessions reaches the viewer's clipboard (#27605)
Orca-launched Codex on a headless runtime gets SSH_CONNECTION so Codex sends
OSC 52 to the viewing terminal instead of only the host's clipboard.

Fixes #23530

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 15:31:04 -07:00
Jinwoo Hong aa1eca1d11 chore(layout): delete the dev-only layout round-trip watch (#27597)
The layout oracle checks the saved profile's round trip at every settled step and the live layout's model rules on every published frame, which covers what this 1 s settle logger did.
2026-10-11 18:18:10 -04:00
OrcaWinandm4air 0663e27b35 feat(web): the browser signs in with a non-extractable device key, or pairs for this session only (#27552)
* feat(web): the browser signs in with a non-extractable device key, or pairs for this session only

- On a secure page with WebCrypto Ed25519, the browser registers a non-extractable key
  (stored in IndexedDB, create-exclusive across tabs) over a v2 token socket, then
  reconnects with key auth and replaces its stored pairing code with a placeholder.
  Requests on a keyed socket carry no token.
- On a plain-http page on another machine, or without Ed25519, existing stored pairings
  keep working unchanged (with a warning); new pairings stay in memory and, when the host
  supports it, register a session-only key.
- Only a sealed device_revoked naming this browser's key deletes it; a downgraded host,
  a bad proof or host storage trouble keep it.
- The shared handshake core gains an async device-key credential (v2 only).

* fix(web): mark web sockets, bind keys to their pairing, survive a broken key store

- Every web socket carries ?orca-client=web so hosts gate only the web bundle.
- A key record belongs to one pairing revision: a re-pair uses its fresh code.
- A code that never migrated connects without reading IndexedDB; a migrated
  pairing whose key store fails retries instead of going dead.
- A tab that finds the key already active forgets its code, and saving a stale
  copy of the same pairing never puts the code back over a placeholder.

* fix(web): a stale tab never deletes or overwrites a newer pairing

A key record from a newer pairing revision is left alone (the tab stays on its
code), and saving an older revision never replaces a newer stored pairing.

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 15:17:00 -07:00
Brennan Benson 220165366c fix(native-chat): offer resume after restart for chats whose only work was in the background (#27167)
* fix(native-chat): offer restart resume for chats whose only work was background

The quit started the chat teardown and then stopped the hook server, which
emptied the status store holding each chat's subagents and background
commands. The teardown reads that store to decide which chats were still
working, so a chat whose turn had finished but whose background command or
subagent was still running was never offered to resume after a restart or
update. The quit now stops hook ingress without clearing the store.

* test: preserve background-only chat resume across shutdown
2026-10-11 15:12:35 -07:00
OrcaWinandm4air cc04684acb refactor(ssh): SSH status copy lives in renderer/src/ssh and says what the build does (#27604)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 15:05:54 -07:00
Jinwoo Hong 3c84201608 test(e2e): open the oracle's headless layout stream with a paired connection (#27601)
* test(e2e): open the oracle's headless layout stream with a paired connection

The remote subscription now takes a paired connection rather than a pairing
offer; build one from the offer the way the CLI does.

* test(e2e): build the paired mobile client's connection from its offer

Same stale call as the oracle stream after the subscription and request
functions took a paired connection.
2026-10-11 18:00:55 -04:00
OrcaWinandm4air 6dafde48e7 refactor(renderer): no client SSH operation arms in project creation or native chat (#27593)
* refactor(renderer): no client SSH operation arms in project creation or native chat

* fix(add-project): show the start step when the remote step loses its server

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 14:56:50 -07:00
Neil c180ba6338 [CI] Narrow daemon fixture checks and advance coordinator test clocks (#27592) 2026-10-11 14:42:38 -07:00
OrcaWinandm4air bd54fc7fa3 refactor(renderer): delete the direct-SSH authority and reconnect coordinator (#27587)
No ssh: git provider is registered in production, so the reconnect
coordinator's catalog and lineage reads always came back non-authoritative
and its worktree refresh never merged. Its only live effect was clearing an
unconverted target's stale PTY bindings on each connected push and on wake;
the state bridge now does that directly.

Deletes the coordinator, preparation, worktree refresh scheduler, host
hydration, rollout flag and their product telemetry event, the scoped
direct-SSH reconnect/hydration options, and the now caller-less
repos:listForExecutionHost and worktrees:listLineageForHost IPC handlers.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 14:41:17 -07:00
OrcaWinandm4air fee083fa47 feat(runtime): session-only browser keys and opt-in for pairing from plain-http pages (host) (#27540)
* feat(runtime): session-only browser keys and an opt-in for pairing from plain-http pages (host)

- pairing.registerDeviceKey accepts ephemeral: true (advertised as
  pairing.device-key-ephemeral.v1). Such keyed rows are dropped when the host
  restarts and after 12 hours with no live socket; a sweep removes them and cuts
  any socket still bound to them, so later key auth gets device_revoked.
- The WebSocket upgrade Origin is recorded per socket. A first pairing (a
  never-used pairing code) from an http:// page on another machine is refused
  with a sealed insecure_origin error unless the operator sets
  ORCA_ALLOW_INSECURE_WEB_PAIRING=1. Already-paired browsers, https and loopback
  pages, and native clients are unchanged.
- Registry file reading moved to device-registry-file-read.ts to keep the
  registry under the size cap.

* fix(runtime): gate first pairings only for the marked web client, never phones

React Native sends an http Origin on every LAN WebSocket, so the Origin alone
refused first-time QR pairing from released phones. The web bundle now marks
its upgrade with ?orca-client=web; only a marked socket from an http page on
another machine is gated. Released hosts ignore the query string.

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 14:40:36 -07:00
Brennan BensonandKelvin Amoaba 79a597375b test(native-chat): keep #26586's path-link cases as regression tests (#26819)
Command-, route-, version- and host-shaped text stays plain unless the
host confirms the path, in chat and terminal alike, since #26675.

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-10-11 14:38:56 -07:00
Brennan Benson fc1686efd6 fix: preserve module URLs in the headless server bundle (#26450) 2026-10-11 14:34:38 -07:00
Jinwoo Hong 8e01e6d9f3 test(e2e): layout oracle reads the live runtime layout (#27560)
* test(e2e): read the live runtime layout in the layout oracle

The oracle now reads the runtime's layout from a layout.subscribe consumer
(the window's renderer, or a headless host's pairing offer) instead of
session:get or the saved profile. Model rules run on every published frame,
replacing the 300 ms settle read and the 100 ms rule sampler; disk-format
rules and the round trip run on the saved profile at each settled step.
Views are compared to the live layout exactly for every group and tab kind.
The known-on-main list is refilled from runs of this oracle on main.

* test(e2e): known-on-main differences do not hold the oracle's settle wait

A difference main is known to keep never converges, so a step settles once
only known-on-main differences remain. The orcad title-after-restart entry
is narrowed to the scenario a run on main reproduced.

* test(e2e): oracle settle counts frames across sleeps, pairs renamed groups, flushes saves

The settle wait carries the frame count across reads, so a frame during the
sleep restarts the quiet window. The client check pairs a group listed under
another id with the runtime group holding the same tabs in the same order,
so only that rename is known on main and a dropped group still fails. The
window flushes main's debounced save before the saved-profile read.

* test(e2e): refill known-on-main from Linux CI and post-flush runs on main

Removes the two-writers saved-profile entry (gone once the window flushes
main's save). Re-adds client-reorder tab order and the cold-relaunch client
list, and adds the cold- and live-daemon relaunch tab-bar entries and the
Electron rename-after-restart entry, each reproduced by Linux CI run
38170663248 or a local run on main's code.

* test(e2e): scope known-on-main entries to the OS that reproduced them

Entries reproduced on one OS only carry a platform and match only there.
The cold-relaunch restart entry now matches only a single group whose tabs
and order are unchanged, so a reorder still fails.
2026-10-11 17:29:35 -04:00
Jinwoo Hong 50b3d2e34c fix(mobile): accept a fresh terminal snapshot after a stream ends (#27539)
* fix(mobile): accept a fresh terminal snapshot after a stream ends

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): drop the native chat resume's duplicate snapshot-mark clear

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): drop the native chat stream's snapshot-mark wiring

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): drop the refit adapter's dead snapshot-mark argument

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-10-11 17:23:20 -04:00
Brennan Benson 05458416af Revert "+" new-tab agent launches to the window route (reverts #26489) (#27173)
#26489 started no-prompt "+" launches through the host lane that is built for
terminals no window shows, into a pane the window already owns. That lane
differs from the window lane in start size, env, telemetry, failure handling
and Windows runtime resolution. Restore main's window route until the two
terminal lanes are converged, then move "+" onto the single lane.

This reverts commit 7634bbfffe.
2026-10-11 14:15:02 -07:00
Brennan Benson c07b045934 Replace three copied new-terminal tab-order blocks with the shared helper (#26771)
* Keep browser tabs in place when the + button opens a terminal

The + new-terminal path kept its own copy of the tab-order placement and
left browser tabs out, so in a workspace with no saved order a browser tab
could land after the new terminal. Use the shared persistAgentLaunchTabOrder
helper instead.

* Use the shared tab-order helper for the remaining new-terminal paths

handleNewTab's shell-override fallback and the menu new-terminal IPC
fallback each kept an inline copy of the "put the new tab last" order
logic. Both now call persistAgentLaunchTabOrder. The one behaviour
difference: the shared reconcile drops ids repeated in a saved order,
where the copies kept them.

* Drop unchecked casts from the new tab-order tests
2026-10-11 14:14:01 -07:00
Brennan Benson e0090f9bda fix(worktrees): keep the 'worktree is ready' toast until dismissed (#27566) 2026-10-11 14:08:16 -07:00
OrcaWinandm4air 34e2e5f469 refactor(remote): clients connect through a paired-connection handle, not a pairing offer (#27533)
Desktop, CLI, web and runtime transports now take a PairedRuntimeConnection
(endpoint, pinned host key, credential) instead of a PairingOffer. Offers stay on
the pairing input path (codes, QR, links, environment add) and are converted with
pairedRuntimeConnectionFromOffer. The credential is token-only today; request
envelopes still carry the same deviceToken, so the wire is unchanged.

This lets device-key and ticket credentials replace the token later without
touching every connect call site again.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 14:02:03 -07:00
Neil d65e9bed9f [CI] Improve test clocks and dependency fixtures (#27574) 2026-10-11 14:01:07 -07:00
OrcaWinandm4air 8e536f9514 refactor(terminal): paste reads the live transport (#27572)
Paste and drop planning no longer re-derive an SSH connection from the
worktree. After C1 no pane transport carries a relay connection, so the
'ssh' paste kind, the worktree fallback and terminal-paste-ssh-platform.ts
are gone.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 13:50:54 -07:00
Jinwoo Hong 92f6c867d1 chore(relay): move the staging cells onto image F (#27570)
* chore(relay): move the staging cells onto image F

Staging c1, c2 and c4-c7 run image F (main 3c1141f2), so the director and
every staging cell share one digest for the c5-c7 promotion and the
six-cell dead-cell rerun. c3 is unchanged.

* test(relay): pin the staging C4 digest to image F
2026-10-11 16:48:40 -04:00
OrcaWinandm4air f0b310b1b1 refactor(store): delete direct-SSH terminal ledgers (#27569)
Panes stopped spawning on unconverted ssh: rows in #27489, so the retry,
live-binding and history ledgers were written but never read. Delete them,
the retry and settle actions, and the detach transfer. The disconnect and
new-authority clears keep nulling stale ssh: PTY ids exactly as before.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 13:42:25 -07:00
OrcaWinandm4air 47c93636af refactor(terminal): drop the relay-held error token (#27564)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 13:26:53 -07:00
Neil 127ab13f1c [CI] Freeze legacy journal partition receipts to reduce test work (#27559)
* [CI] Freeze legacy journal partition receipts to reduce test work

* [CI] Type readonly journal fixture callbacks
2026-10-11 13:24:52 -07:00
Jinwoo Hong 3c1141f233 test(perf): layout latency instruments and a main baseline (#27538)
* test(perf): in-app layout latency probe, event-loop delay on main and orcad, and a main baseline

* test(perf): plain report comment

* test(perf): name the latency report type

* test(perf): add the Linux CI baseline; routine runs take 10 samples

* test(perf): stamp close pane on the w key, keep probe work out of samples, label first drawn and Linux flood
2026-10-11 16:13:17 -04:00
Brennan Benson bedf6f5a7b Session History: resume folder workspace chats in their own folder (#27209)
* fix(session-history): resume folder workspace sessions in their own folder

Session History matched a session to its workspace only among git worktrees, so a
conversation recorded in a folder workspace had no owner: its subagent rows could
never resume, and the parent row only offered whatever workspace happened to be
active. Folder workspaces now join the owner list as their usual folder: rows.

* test(session-history): mock folderWorkspaces as the array the store holds

* fix(session-history): jump through the shared workspace activator without a second toast

Both Session History jumps and the resume activation now go through
activateAndRevealWorkspace, which owns the folder routing and its path gate.
A folder the gate refuses already shows why, so the generic 'no longer
available' toast is kept only for a workspace that is actually gone.

* fix(session-history): keep the jump toast when folder activation could not pick a folder

Folder activation only explains a refusal once it resolves the folder's
owner; an id present on two hosts returns before any toast. Suppress the
generic toast only when that owner resolves.

* test(session-history): type the folder fixture without an assertion

* fix(session-history): call folder workspaces folders, not worktrees

Folder rows now read 'Resume in Folder', 'Folder' (details heading),
'Current folder' / 'Folder' / 'Archived folder', 'Jump to Folder', and
'Folder workspace is no longer available.'. The kind is derived from the
row's folder: workspace id; git worktree rows are unchanged.

* fix(session-history): say Jump to Folder in the row menus for folder rows

Also shows a plain folder icon on a folder row's details heading and aligns
the Spanish and Japanese folder strings with their worktree counterparts.
2026-10-11 13:09:18 -07:00
Jinwoo Hong 9ad45591af refactor(layout): keep compatibility code in named folders behind one entry (#27556)
* 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
2026-10-11 16:06:05 -04:00
OrcaWinandm4air 8731ca9672 refactor(rpc): delete host-side terminal source-range acks (#27546)
The host side of the terminal source-range ACK protocol had one producer: the
relay-fed SSH output intake, deleted in #27515. Since then nothing installs the
runtime's source-range hooks and nothing passes source ranges into PTY output,
so every terminal.multiplex subscribe that offers ackOutputSourceRanges is
already answered without it and uses the byte-window ACK.

Delete the ledger, the per-connection registry, the consumer hook surface on
the runtime, the stream-level reservation/commit/rollback plumbing in flow
control, initial snapshot and cleanup, the source-range ACK frame schema, and
the range slicing in output chunks, batching and replay. The subscribe schema
still accepts the ackOutputSourceRanges capability, and the client keeps its
range-ACK support for older hosts. A new golden test pins that an offering
subscribe gets the same reply and frames as a byte-ACK one.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 13:04:34 -07:00
Brennan Benson 5b228ea71d One shared subagent tracker for Claude, Codex and Grok chat rows (#27228)
* test(native-chat): pin each agent's subagent row writes before unifying the rosters

* refactor(native-chat): one shared subagent tracker owns every agent's subagent rows

Claude, Codex and ACP (Grok) each kept their own subagent roster with drifted
rules. They now only report what they observe; one tracker decides grouping,
runs, names, state, sweeps, retention, row writes and restart inheritance.
The golden row-write tests from the previous commit show each rule change.

* fix(native-chat): one entry per subagent, derived lookups, ACP end stamped in its session-end write

Review round 1: a resumed child keeps its one entry and reopens it where it is
listed (the chat draws one section per child, anchored at the first row naming
it), every lookup is read from the rows, the no-turn row is never released,
and ACP's session end stamps its live children inside the session-end write
instead of writing rows ahead of it.

* fix(native-chat): derive the no-turn group, end a foreground run with the turn that started it

Review round 2: the row no turn owns is recognised from its id (a stored flag
was lost when an earlier run's row was first reached through one of its
children), and a turn's end settles the foreground runs it started wherever
their child is listed.

* fix(native-chat): count an inherited subagent row as settled history when a lookup loads it

A read-only lookup could load an earlier run's settled row that retention never saw, so it was never
released and a fresh settled row was released in its place.
2026-10-11 13:04:04 -07:00