Commit Graph
11850 Commits
Author SHA1 Message Date
Neilandstevelliu 347d0e46a1 feat(cli): set the title of one terminal pane (#30485)
* feat(cli): set the title of one terminal pane

Adds `orca terminal set-pane-title --terminal <handle> --title <text>`; `--title ""`
clears it. The runtime resolves the live handle to its saved leaf and asks the
desktop window to name that pane through a new optional notifier hook
(`ui:setPaneTitle`). Mounted panes apply it through the same local setter the GUI
uses; parked panes get it in the saved layout their next mount reads. The tab's
own name is never touched.

Rebuilt on current main from #24755.

Co-authored-by: stevelliu <stevelliu@tencent.com>

* fix(renderer): a main-owned layout takes a CLI pane title before the window shows it

* fix(renderer): a CLI pane title reaches a never-mounted tab with a rootless layout

---------

Co-authored-by: stevelliu <stevelliu@tencent.com>
2026-10-18 19:48:36 -07:00
NeilandClaude 139bac88b6 refactor(ssh): move the ssh-connection and ssh-target cluster into ssh/connection/ and ssh/targets/ (#30479)
* refactor(ssh): move the ssh-connection and ssh-target cluster into ssh/connection/ and ssh/targets/

Pure git mv plus import and path-literal rewrites. ssh-connection-utils stays put; the next PR splits it.

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

* test(e2e): keep the release-checkout import at the v1.4.218 path

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 19:23:21 -07:00
Neil bf866505e8 refactor(renderer): fold the right-sidebar, hook, native-chat and doc-page readers off the deprecated owner resolver (#30478)
Thirteen read-routing callers (#4, #5, #10-#20 of the round-3 caller list) read the owner through
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin. A workspace two hosts publish is no longer
searched on either host. The focus-read ratchet now counts the legacy-fallback adapter, which
reads the focused runtime inside an object literal its discovery skipped; the five F1 entries
that blind spot pruned come back. Deprecated importers 53 -> 40.
2026-10-18 19:01:49 -07:00
Jinwoo Hong 613c5986e3 Editor records keyed by owner, phone active file, and a bound on frame holds (dark, layout-owner build only) (#30473)
* fix(layout): editor file records keyed by path and owner, so two hosts' files at one path stay apart (layout-owner build only)

Opening a runtime-environment file at a path a local file already held replaced the local record,
the stored-session patch and unsaved drafts matched by path alone, a move took every owner's record
of the path, and closing a bare-path tab removed both owners' records.

* fix(mobile): a windowless host's editor tab under its owned id shows as active, and retiring it clears the active file (layout-owner build only)

* fix(layout): a layout change's tab and pane holds end 15 s after it was sent when main never answers (layout-owner build only)

* fix(layout): main's view reads and prunes keep an active file under its owned id (layout-owner build only)
2026-10-18 21:34:37 -04:00
Jinwoo Hong 8ffd3742ed The terminal server and desktop finish the two-pane terminal repair before startup continues (dark, layout-owner build only) (#30429)
* refactor(orcad): move startOrcad's option and handle types into their own module

Move-only: frees room in orcad-entry.ts for the layout-owner load repair.

* fix(orcad): run main's duplicate-terminal pane repair at orcad startup (layout-owner build only)

orcad builds its own runtime and never called the load-time repair, so an
orcad host kept a terminal drawn in two panes.

* test(orcad): type the hoisted test state without an assertion

* fix(orcad): await the startup pane repair so it lands before reconcile and RPC

* fix(startup): desktop awaits the duplicate-terminal repair before the window and RPC start (layout-owner build only)
2026-10-18 21:25:53 -04:00
Neil 1ad8755fa8 refactor(renderer): fold four lib callers off the deprecated runtime-owner resolver (#30458)
Open-in owner, execution-host platform facts, doc-preview grants and the activation PTY inventory
read the owner through resolveOwnerWithLegacyFallback. Open in now refuses a twin instead of
launching it as this desktop's checkout; the others keep reading a twin as not server-owned. A row
whose only repo row is a server proxying its SSH target routes through that server. The
creation-action policy stays until browser creation folds with it. Deprecated importers 57 -> 53.
2026-10-18 18:09:07 -07:00
NeilandOliver Mee cf460aba5a fix(browser): switch client-hosted profiles by staging the replacement guest (#30452)
* fix(browser): switch client-hosted profiles with staged guest replacement

Squashed from #30351 by @oliver-mee onto the current main.

Fixes #30350

* fix(browser): fence staged profile guests, release switch grants, carry host profile metadata

- A fence during automation retirement now revokes and stops the staged guest.
- A failed or late-settling switch releases its execution-host grant so the page can be recreated.
- Staging cleanup failures before handover stay visible to executor close.
- createPage/restorePage carry host-owned profile metadata to profile-switch-v1 desktops,
  so recovery and restore can rebuild a switched page.

* fix(browser): roll back a switch whose replacement died, and report a lost old guest

* fix(browser): resolve client-hosted pages before the server-local target path when switching profiles

* fix(browser): a staged profile-switch guest owns downloads it starts while loading

* fix(browser): keep the original placement until a timed-out switch settles

* fix(browser): rebind the original guest before releasing a rolled-back staged guest

* test(browser): type the harness guest release mock

* fix(browser): release the replaced generation's download transfers when a switch commits

* fix(browser): admit file transfers from a staged profile-switch guest while its switch is pending

* fix(browser): release only the replaced generation's download transfers after a switch

* refactor(shared): import BrowserNetworkExecutionHost from its own module, no re-export

* fix(browser): adopt a profile switch the client finished after the caller timed out

---------

Co-authored-by: Oliver Mee <102673257+oliver-mee@users.noreply.github.com>
2026-10-18 17:48:57 -07:00
Neil a8e90ddc30 refactor(renderer): one owner adapter for the deprecated-resolver folds, pinned by an acceptance table (#30451)
* refactor(renderer): one owner adapter for the deprecated-resolver folds, pinned by an acceptance table

resolveOwnerWithLegacyFallback answers as resolveOwner for a named host and for rows that carry a
host stamp, and keeps the deprecated answer (single focused runtime included) for missing and
unstamped rows. A full-owner acceptance table pins all matrix cases and asserts the only changes
from the deprecated resolver are the designed ones: twins and contested SSH hosts become ambiguous,
and a stale hub stamp routes through the server that names the target.

* refactor(renderer): the fold adapter shares resolveOwner's named-host rule

Clean-code pass: one namedHostForOwnerRef for both resolvers, floating counts as host evidence,
and the acceptance test drops non-null assertions.
2026-10-18 17:36:53 -07:00
OrcaWinandOrca Worker 5df4fef917 fix(windows): codex and claude get a prompt's double quotes intact in PowerShell (#30424)
* fix(windows): codex and claude get a prompt's double quotes in Windows PowerShell

Windows PowerShell 5.1 (and pwsh before 7.3) does not escape an embedded
`"` when it builds a native program's command line, so every quote in a
launch prompt was dropped, and quotes could split the prompt into extra
arguments. Orca's PowerShell codex and claude wrappers now escape their
arguments at runtime for the PowerShell that is actually running, through
one shared __OrcaNativeArgs function. pwsh 7.3+ already escapes, so it
gets the text untouched.

* fix(windows): escape agent args for .cmd shims under pwsh 7.3+ too

pwsh 7.3+ defaults to the Windows argument mode, which still passes
.cmd/.bat targets the Legacy way, so a codex or claude .cmd shim kept
dropping quotes there. The function now takes the program it escapes
for and names the modes that escape by themselves, and anchors the 5.1
trailing-backslash fix at the true end of the text (`$` also matched
before a final newline). Tests now run the real codex wrapper with a
quoted prompt, cover pwsh's Legacy mode and the newline case, and pin
the claude wrapper's call.

* fix(windows): strip comment lines from the PowerShell bootstrap payload

The shared escaping function pushed the encoded bootstrap to 27,324 of
the 28,000-character budget, leaving almost no room for a startup
command to ride in -EncodedCommand, so short commands fell back to
stdin (the path #7236 broke). Comment lines document the source but
only spend that budget in the payload; dropping them frees 3,732
encoded characters, more headroom than main had.

* fix(windows): quote agent args with "" where cmd.exe or Windows PowerShell reads them

Escaping embedded quotes as \" let pwsh 7.3+'s .cmd quoting expose a
prompt's `&` to cmd.exe, which then ran the rest as a command. Where
cmd.exe reads the line, or Windows PowerShell counts quotes naively,
the function now quotes the argument itself and writes each quote as
"", as process-host's windows-command-line.ts already does. That also
delivers text 5.1 used to strip, so its special cases go away.

The codex and claude wrappers now come as one fragment with the
function they call, the bootstrap test pins the startup-command budget
instead of restating the strip, and the cwd-restore comment leaves the
payload too.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-18 17:29:54 -07:00
Kelvin Amoaba d4dfcf2694 fix(mobile): page GitHub tasks by number and drop pages from an old selection (#30140) 2026-10-19 00:23:10 +00:00
Jinwoo Hong 3ad036551e fix(native-chat): clicking Send or Stop keeps focus in the message box (#30456)
* fix(native-chat): clicking Send or Stop keeps focus in the composer

A click focused the Send/Stop button; when it disabled (empty draft after the
turn, or while a Stop is in flight) Chromium dropped focus to <body>. A pointer
press on it no longer moves focus, matching Enter. Adds an e2e regression with
a turn-capable Claude stub. Fixes STA-10897.

* Tighten comments
2026-10-18 20:21:59 -04:00
Jinwoo Hong 062e06d982 feat(sidebar): the owning host raises the workspace unread flag (dark) (#30450)
* feat(sidebar): the owning host raises the workspace unread flag (dark)

* fix(sidebar): phone session open clears and spares the flag; refresh views; blur rescan

* fix(sidebar): throttle failed view retries, resend failed empty views, hold presence one refresh longer

* test(sidebar): switch-on window writes neither the unread flag nor a stamp
2026-10-18 20:01:28 -04:00
Jinwoo Hong f8a5d3e284 fix(usage): price Claude long-context requests per request, and price Haiku 5.5 (#30427)
* fix(usage): price Claude long-context requests per request, and price Haiku 5.5

Haiku 5.5 bills a whole request at higher rates once its prompt (input plus
cache reads and writes) exceeds 100K tokens. Claude usage was only kept as
daily totals, so that could not be priced and Haiku 5.5 stayed unpriced. The
existing Sonnet 4.5/4 200K tier had the opposite problem: it was applied as a
marginal threshold to each day's totals, so a day of short requests could be
surcharged and a single long request was under-charged (the Claude side of
STA-8117, fixed for Codex in #22360).

Each turn is now classified when it is aggregated, against its own model's
threshold, and daily and per-location rows carry the long-context subset as
an optional `longContext` field. A streamed turn that grows across the
threshold moves its whole share in the resume correction. estimateCostUsd
takes one token object and bills that subset at the long-context rates and
the rest at base, replacing the per-bucket tiering.

Saved per-file projections carry a projection version; files from an older
version are reparsed on the next scan, so history picks up the split without
touching the signed cache schema.

* fix(usage): keep the projection version out of the integrity hash

Hashing it made older inline reports fail validation and show the
'could not be validated' error; the scanner's version check alone decides reparse.
2026-10-18 19:55:00 -04:00
Jinjing 966832793b Check Claude sign-in duplicates against the listed account identity (#30446)
* Check sign-in duplicates against the listed account identity

A managed account folder's current Claude login can differ from the identity saved for it. The account list shows the current login, but the duplicate check compared the saved identity, so a sign-in could be missed or wrongly flagged. Duplicate detection now uses the same listed accounts as the account list, and the duplicate helper accepts account summaries. Tests cover a folder whose login email changed, and one where only the organization changed.

* Tolerate invalid Claude account rows during sign-in
2026-10-18 16:50:06 -07:00
Jinwoo Hong 04ce3d3891 fix(usage): price Claude models by exact version, add Sonnet 5.5, flag unpriced models (#30421)
normalizeModelForPricing was an ordered chain of regexes in which a new point
release fell through to its major: claude-sonnet-5-5 was billed as Sonnet 5
(cache reads at $0.20 instead of $0.10), and any future opus-5-x / fable-5-x
would be too. claude-haiku-5-5 matched nothing and silently added $0 to a
total that showed no caveat.

The resolver now parses family + major + optional minor (family-first and
legacy version-first IDs) and only accepts an exact MODEL_PRICING key, so an
unknown version is unpriced rather than borrowing another model's rates. The
deliberate opus-4.x -> 4.8 and sonnet-4.x -> 4.6 fallbacks go with it.

Adds Sonnet 5.5 and Mythos 5/5.1 rows. Haiku 5.5 stays unpriced: it reprices a
whole request above 100K prompt tokens, which daily totals cannot express.

The Claude summary now carries hasUnpricedModels (as Codex has since #22073),
and both panes share one unpricedCostLabel helper for the caveat.

Fixes #30338
2026-10-18 19:44:51 -04:00
Jinwoo Hong c4730d692f fix(cloud-auth): show that a sign-in is in progress, with copy-link and cancel, on every Orca sign-in button (#30410)
* fix(cloud-auth): show the browser sign-in wait and offer its link from Relay sign-in

Clicking 'Sign in for Relay' handed the sign-in page to the OS browser and
then showed nothing, so a browser that never came forward looked like a dead
button, and the 5-minute wait ended silently. The Relay sign-in row now shows
the wait with a Copy sign-in link button, and a wait that times out toasts.

STA-10940

* fix(cloud-auth): show the sign-in wait on every Orca sign-in button

The waiting text and Copy sign-in link now come from one shared notice used by
Mobile Relay, Orca Account, Artifacts (settings, page and publish popover),
Share Skills, the unexpected sign-out card and dev tools. The copyable link is
dropped as soon as the browser answers, and a copy with no waiting link
reports a failure instead of doing nothing.

STA-10940

* refactor(cloud-auth): main owns the waiting sign-in link; renderer only mirrors it

The notice now shows exactly while main holds a live link: main announces the
link when the browser wait starts and clears it the moment the browser
answers, so 'Waiting for sign-in in your browser' no longer outlives the wait
during the token exchange. Only the newest attempt may publish its link, so an
older attempt reporting late cannot replace or later wipe it. The renderer's
separate pending flag and its resets are gone.

STA-10940

* fix(cloud-auth): tolerate a sign-in link read that returns no promise

The app-wide IPC tests stub every bridge method as returning nothing, and the
one-time link read called .then on that result.

STA-10940

* fix(cloud-auth): a torn-down link subscription ignores its late snapshot; restore auth bridge tests

A window's one-time sign-in link read could land after its subscription was
disposed and overwrite a newer value from the replacement subscription. The
auth-status refresh tests the earlier rewrite dropped are back, alongside the
link cases.

STA-10940

* feat(cloud-auth): sign-in buttons show Signing in… with copy-link and cancel links

Every Orca sign-in button greys out to 'Signing in…' with a spinner until the
sign-in ends. While the browser has not answered, a line offers 'Copy sign-in
link' and 'Cancel'; Cancel aborts the browser wait so a lost tab never locks
the button. Main owns the in-flight sign-in (waiting on the browser, then
finishing) and every window mirrors it.

STA-10940
2026-10-18 19:28:04 -04:00
Neil 39314834be fix(open-in): open a managed server's workspace in VS Code over Remote-SSH (#30441)
* fix(open-in): open a managed server's workspace in VS Code over Remote-SSH (#30332)

* fix(open-in): only open a server workspace over SSH when its files are on that host; validate the SSH link; let process-host quote code.cmd

* test(open-in): expect the workspace ref in the source-control Open in payload

* fix(open-in): pin an SSH-access server's editor host to the settings it was verified on
2026-10-18 16:24:26 -07:00
Neil 859e359e35 fix(renderer): a created or lineage-refreshed worktree replaces only its own host's copy (#30447)
applyCreatedWorktree merged a create into any same-id row, so a local create could inherit a
server twin's stamp (or wipe it). Lineage refresh stamped a server's reply with the focused host and
replaced every same-id row, overwriting the local twin. Both now match id and host, and the lineage
reply is stamped with the transport it came from.
2026-10-18 16:20:26 -07:00
Neil 30a66ac083 fix(browser): a remote viewer larger than the host's browser pane sees the whole page and clicks land under the cursor (#30437)
* fix(browser): a remote viewer larger than the host's browser pane sees the whole page and clicks land under the cursor

Fixes #30341

* test: note why the screencast double is cast

* fix(browser): drop guest frames until the fitted size is applied; forget the cached CSS size when the frame size changes

* fix(browser): keep a press's release and markup inside the letterboxed remote frame

* fix(browser): resume live frames once fitted metrics land; remeasure the page after its frame size changes
2026-10-18 16:02:49 -07:00
Neil 9c14e0d450 fix(renderer): restored SSH session placeholders carry the host a listing would stamp (#30445)
A tab restored before its worktree catalog loads gets a placeholder row. SSH placeholders had no
host, and a server's nested-SSH placeholder carried only runtime:E, so an ssh: host selection found
no matching owner and routed the terminal as a direct dial from this desktop. Stamp the SSH target,
and the server proxying it, from the repo row; leave a repo id that lives on several hosts unstamped.
2026-10-18 16:00:46 -07:00
Jinjing b66c98fd72 update prompt logic (#30444) 2026-10-18 15:48:42 -07:00
Neil 7a9170cf77 refactor(main): import filesystem-auth, markdown-documents and preflight seams from their defining modules (#30440)
The three ipc modules re-exported symbols owned elsewhere. Importers and test mocks now name the
defining module, and the re-export lines are gone (no barrels).
2026-10-18 15:41:29 -07:00
Jinwoo Hong 687328ae41 A CLI split whose pane is closed before it shows answers at once (dark, layout-owner build only) (#30439)
* fix(layout): a CLI split whose pane main closes before it mounts answers at once (dark, layout-owner build only)

* test(layout): the closed-before-mount split fails because of the close
2026-10-18 18:40:32 -04:00
Jinjing 792c141fed Open file links beside their source tab's group (#30399)
* Open file links beside their source tab's group

Clicking a file link in a terminal or agent chat now opens the file in an editor next to that tab, not in whichever group happens to have focus. A new "Open in this group" row pins the file to the source group when that group has neighbors.

- Capture the source tab at click time, so focus changes during path resolution don't redirect the file.
- Rank candidate groups by adjacency: a neighbor already showing the file, then a neighbor showing an editor, then any neighbor, then the source group.
- "Open in this group" never falls back to a browser, system app, or other group. If the group is gone or the target is not an editor, it shows a toast and does nothing.
- Line reveals target the exact view that was opened, and are dropped if that view closes before the editor applies them.

* rm scratch files

* Preserve empty split panes during tab reconciliation

Reconciliation removed every empty group, so an empty split pane the user had made beside a tab was collapsed on the next pass. A file link opened from that tab then landed in the source group instead of the empty pane. Reconciliation now removes only groups that stale-tab cleanup emptied, and keeps panes that were already empty. Tests cover terminal and agent-session sources in both worktree and folder workspaces.

* Preserve runtime owner for local file links
2026-10-18 15:36:29 -07:00
Jinwoo Hong 7884f6e738 chore(telemetry): remove the three Smart sort telemetry events (#30435) 2026-10-18 18:34:50 -04:00
c71652977c fix(drops): name extensionless dropped images by their bytes (#30379)
* fix(drops): name extensionless dropped images by their bytes

A file dropped onto a remote workspace with no extension (some drag sources
hand images over as `noname`) now gets the extension its PNG/JPEG/GIF/WebP
signature declares, so the agent sees `noname.png`. Repo icon detection
reuses the same signature check.

Fixes #30378

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(drops): sniff extensionless images only for .orca/drops and chat attachments

Explorer imports into a user-picked remote folder keep the name, matching
local imports. Chat attachments on a paired server now use the staged name
so they get the extension too.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-18 15:30:55 -07:00
Neilandwangshifeng 34e03046b7 fix(terminal): host's saved pane titles reach terminal.list, and clearing the last one sticks (#30433)
A client's pane rename already lands in the host's saved layout (titlesByLeafId),
but terminal.list/show never read it, and clearing a remote tab's last pane title
sent no titles at all, which every host reads as 'unchanged'.

- terminal summaries carry an optional paneTitle from the host's saved layout;
  the CLI list prints it ahead of the terminal title. Older hosts omit it.
- clearing the last pane title of a remote tab pushes titlesByLeafId: {}, which
  old and new hosts both persist as cleared.

Supersedes #30333, which renamed the whole tab via terminal.rename.

Co-authored-by: wangshifeng <shifeng.wang@magic-shield.com>
2026-10-18 15:25:48 -07:00
Jinwoo Hong ecfc13889b test(layout-owner): pin that a paired server's workspace never gets layout.apply (#30430)
Every renderer sender of layout.apply (sendLayoutApply, recordLayoutApply, the activation
terminal create and the live terminal adoption) returns before the call for a worktree or
folder workspace a paired server owns, and a source census pins that no other renderer file
sends layout.apply.
2026-10-18 18:24:52 -04:00
4940654793 fix(cli): --host and --project accept the names host list and project list print (#30438)
* Accept host aliases and infer the host for a single-setup project

`orca worktree create --project scalp-it` failed with "Project is not set
up on the selected host" even though the project has exactly one ready
setup, and `--host trader-local` was rejected as an invalid value even
though `orca host list` prints that name. Three separate causes:

- `--project` was matched against the stored `projectId` only, which is a
  provider-scoped string (`github:owner/repo`). The display name shown by
  `orca project list` never matched, so the lookup found nothing and the
  failure was reported as a missing host.
- With no `--host`, the host fallback existed but was unreachable, because
  the project lookup above had already filtered to zero candidates.
- `parseExecutionHostId` accepts only `local`, `ssh:<id>` and
  `runtime:<id>`, so a bare alias could not parse at all. `ssh:<label>`
  already resolved labels; only the unprefixed spelling was missing.

Now:

- `--project` takes the project id or the display name, resolving through
  `project.list` and falling back to the setup's own display name for
  hosts that predate that method. Two projects sharing a name are refused
  rather than guessed.
- `--host` is only required when the project is set up on more than one
  host. A single setup is used directly; an ambiguous one fails with every
  host and the flag to select it.
- `--host <alias>` resolves against both axes: paired servers by name
  before routing, SSH targets by label once a client exists. An alias that
  names a machine on both is refused with the prefixed flag for each.

Errors now echo the flag the caller typed instead of an `ssh:` spelling
they did not use, and carry the hosts to retry with in `nextSteps`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Refuse a host alias that collides with duplicated SSH labels

assertAliasNamesOneHost asked findSshTargetByName whether the alias also
named an SSH target, but that function returns undefined for a label two
targets share — deliberately, so an ambiguous label never resolves. Read
as a boolean it says "no SSH target by that name", so an alias naming one
paired server and two same-labelled SSH targets looked conflict-free and
resolved to the server.

The opposite direction was already covered: a unique SSH target against
two servers sharing a name fails through assertEnvironmentNameUnambiguous.
Fall back to ambiguousSshTargets so both axes refuse, and list every
colliding target in nextSteps rather than only the first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Only fall back to setup display names on a server without project.list

findProjectsByName caught every error from project.list and returned an
empty list, so a network failure, a permission error, or a genuine server
fault was indistinguishable from "no project by that name" — and the
caller was then told `Project is not set up on the selected host` for a
project that exists and is set up.

Narrow the catch to method_not_found, the only code that means the weaker
fallback is warranted, matching callProjectHostSetup and listSshTargets in
the same layer. The function now returns null for that version gap and an
empty array for a real miss, so the two cases stay distinguishable at the
call site.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Refuse a legacy display-name match that spans two projects

The pre-project.list fallback matches ProjectHostSetup.displayName, which
is the repo's folder name rather than the project's name, so two projects
holding repos with the same folder name both match. selectSetupOnHost
would then have taken the first candidate — targeting a project the caller
never named.

Reject when the matched setups span more than one projectId and name each
one, the same rule the project.list path already applies to duplicate
display names. A single project with several setups is unaffected; the
host selection below still decides between them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Spell the --host placeholder as <host> in the project setup usage lines

The worktree create usage was updated to <host> when aliases became
valid, but the four project setup-* lines kept <host-id> in both the
command specs and the root help text they mirror. Those commands accept
an alias too, so the narrower placeholder hid it from anyone reading help.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Document host names and the optional --host in the CLI docs

The published CLI reference described `--host` as taking only `local`,
`ssh:<target-id>` or `runtime:<environment-id>`, and the automations page
showed `--host <hostId>` as required alongside `--project`. Both now
understate what the commands accept, which is how the original report
started: `orca host list` prints a name, the docs never said you could
type it, so the generated id looked mandatory.

Spell out that `--host` takes the bare name from `host list` on either
axis, that a name resolves only when unique and is otherwise refused with
the prefixed flag for each candidate, and that `--host` is unnecessary
when the project has a single setup. Also note `--project` accepts the
display name from `orca project list`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Name the runtime host id as the paired-server tie-breaker in the docs

The host-selection page told readers to fall back to ids when a name is
ambiguous but only ever showed `--environment <server-name>` for paired
servers — and that selector is exactly the one that cannot separate two
servers sharing a name. Spell out the pair the resolver actually emits in
its nextSteps: `--host ssh:<target-id>` and `--host runtime:<environment-id>`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Spell the automations create usage as --project <id-or-name> [--host <host>]

`automations create` resolves --project and --host through the same
alias-capable resolver as `worktree create`, and its own notes already say
--project takes a display name and --host takes a bare host name. The usage
line still read `--project <id> [--host <id>]`, the last spot left on the old
spelling (flagged by CodeRabbit after the main merge).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give the fake runtime client cast in the host alias tests its SAFETY note

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): accept every name host list and project list print, everywhere --project/--host is taken

Extends the alias work to the commands it did not reach and the names it
missed:

- `project setups --project <display name>` filtered by projectId only.
- `project setup-existing-folder|setup-clone|setup-create --project <name>`
  now send the project id that name resolves to (pass-through for ids and
  for servers without project.list).
- `--host "this machine"` (the local row's name) resolves to local, and is
  refused if an SSH target or paired server shares it.
- One pair of wrapping quotes is ignored, since cmd.exe passes single
  quotes through literally.
- With --environment/--pairing-code, a bare --host name is a machine the
  routed server reaches, not a different locally paired server.
- An unknown project is reported as a project miss, not a host miss.

Co-authored-by: Younghwan Chae <chyohw97@gmail.com>

* fix(cli): keep exact project ids over names, and refuse an unverifiable host name

An exact --project id whose only setup is not ready no longer falls through
to a project whose display name equals that id. A bare --host name that
matches a paired server now routes there only once this machine's SSH
targets were actually read, and under --environment/--pairing-code a bare
name always means a machine the routed server reaches.

Co-authored-by: Younghwan Chae <chyohw97@gmail.com>

---------

Co-authored-by: younghwan <chyohw97@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-18 15:22:37 -07:00
Brennan Benson ecd83b7b48 Resume terminal conversations in native chat without waiting for History (#29150)
* fix(native-chat): identify the owner when resume create is refused

* test(native-chat): consolidate transcript refusal cases

* test(native-chat): keep ownership coverage at the launch boundary

* test(native-chat): update adoption fixture for early ownership admission

* test(native-chat): align resume admission assertions with host ownership

* test(native-chat): keep RPC coverage focused on ownership proof

* test(native-chat): assert blank request presence before optional source

* fix(native-chat): admit the actual CLI source during chat creation

* fix(native-chat): satisfy source admission lint checks

* test(native-chat): provide the waiting hook prompt

* test(native-chat): isolate source admission fixtures from host homes

* test(native-chat): canonicalize adoption fixture directories

* test(native-chat): preserve early ownership admission in source stack

* Resume CLI conversations directly in native chat

* fix(native-chat): preserve resume refusal behavior and honest transcript feedback

* fix(native-chat): retain import read evidence in typed result

* Use the header test fixture record type

* Consolidate launch owner imports and correct feedback assertion

* Keep existing resume translation in runtime catalog

* fix(native-chat): preserve admitted source and prepared execution home

* fix(chat): retain source evidence for dispatched adoption

* fix(chat): keep source capture within module limits

* Keep deferred chat publication in the shared launch owner

* test(chat): allow host evidence in create routing

* Satisfy launch publication checks and scope slot cleanup

* Preserve canonical tab membership on projected reorders

* Use recorded launch ownership for surface recovery

* Reserve deferred chat order through existing group actions

* Pass the active layout owner into launch cancellation

* fix(native-chat): admit an unsaved transcript under a linked account folder

A missing transcript stayed unresolved while its account root resolved through
symlinks, so containment failed and a not-yet-saved conversation showed a load
error instead of the unsaved notice.

* refactor(native-chat): move the newest-item import window into its own module

Keeps journal-legacy-import.ts under the line limit after the source
identity observer landed on top of the streaming import.

* fix(native-chat): restore the refusal error import lost in the main merge

* test(native-chat): pass the layout owner in the layout-owner cancel test

main's new test predates launch cancellation taking the active layout owner.

* fix(native-chat): admit Pi transcripts against the root Session History walks

A PI_CODING_AGENT_DIR that already ends in sessions, or any custom folder,
made admission expect <dir>/sessions while the lookup searched <dir>, so a
found conversation was refused.

* refactor(native-chat): admit and look up CLI transcripts in one store per account

Each agent's import described where an account keeps its conversations twice,
once for the path check and once for the lookup, and the two could disagree
(the linked-folder and Pi custom-root refusals). The import now names one
conversation store per account; the create path resolves it once and both the
hook-path check and the fallback lookup judge that same store. OpenCode's
store is its exact database.

* fix(native-chat): keep a History resume that joins a terminal resume alive when the terminal closes

A History click that joins a pending terminal-tab resume now takes the
launch over: the terminal hand-off runs at join time and the create
resumes by conversation id, so closing the terminal before create no
longer abandons the shared launch.

* refactor(native-chat): move the launch source hand-off into its own module

* fix: pick the chat that adopts a header resume the way History does
2026-10-18 15:22:17 -07:00
Jinwoo HongandClaude 7df5eaf5f7 feat(cleanup): the owning host records last terminal use for cleanup's last active (dark) (#30431)
* feat(cleanup): the owning host records last terminal use for cleanup's last active (dark)

Owner decision Q2: with ORCA_SIDEBAR_INPUTS=main, the host that owns a
workspace writes lastTerminalUseAt when a terminal takes input (window, phone
or CLI, through the one input funnel) or a phone attaches, at most once a
minute per workspace. Output never counts. Cleanup's last active reads it
next to the Recent stamp, so a workspace used only from the phone never looks
idle. Never a sort key.

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

* fix(cleanup): count only typed input, skip paired-server terminals, share record routing

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 18:17:57 -04:00
8c3775f8d6 fix(shortcuts): Cmd+C no longer pastes on remapped Latin keyboard layouts (#29587)
* fix(shortcuts): Cmd+C no longer pastes on remapped Latin keyboard layouts

On macOS the main-process paste intercept matched `code === 'KeyV'` OR
`key === 'v'`, so on Turkish F (where the C letter sits on the physical
KeyV) Cmd+C was routed to ui:appMenuPaste. Trust the produced key when it
is a Latin letter or digit and only fall back to the physical code for
non-Latin layouts (Cyrillic etc.), matching the shared keybinding matcher.

Fixes #29574

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

* test(shortcuts): cover Dvorak, Korean and IME composition for the macOS paste intercept

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(shortcuts): match the macOS paste intercept by produced key token, in its own module

The produced key token wins on every Latin layout, punctuation included (BEPO puts . on KeyV); non-Latin and IME keys fall back to the physical key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: İzzetEmre <emreizzet@hotmail.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 18:13:55 -04:00
Jinwoo Hong bad74e1b7c test(sidebar): the desktop window and a phone reading the feed draw the same list (dark) (#30426)
* test(sidebar): the desktop window and a feed reader draw the same list (dark)

* fix(sidebar): a feed reader lists a paired server's folders and nested workspaces as the window does (dark)

* fix(sidebar): the paired-server shadow compares the lineage the server's check keeps (dark)

* fix(sidebar): paired-server rows carry the unread flag Smart Done reads (dark)
2026-10-18 18:01:45 -04:00
Brennan Benson 4298ba998d Mark completed Codex reviews Done when Stop is absent (#28860)
* Settle Codex review turns from their session completion

* Preserve interruption relay metadata when settling Codex reviews

* Keep the Codex turn id across status writers and settle through the existing host verdict path

The turn id that matches a rollout turn end was dropped by every lead writer
that did not pass it, so a failed turn after a child update never settled.
Drop the synthetic Stop label: the unlabelled poll update already carries the
host's main-agent verdict, including a failure, to current desktops. A remote
desktop now takes the host's settled verdict for a late hook, and a rollout
settle retires hook-only children like Stop does.

* Retire Codex children through the poll and start a fresh turn on any non-compaction session start

A rollout-settled turn deleted the child roster, so the local reconcile
re-seeded the child from the last row as running and the pane stayed Working.
Clear it instead, only from the poll (a child hook settling the root must not
drop that live child), and let a remote desktop clear its roster when the host
reports a done turn with no children. A resumed or forked session no longer
inherits the previous turn id. Also drops accidental pnpm-lock churn.

* Replay a Codex rollout turn end as the root Stop it stands in for

Codex fires no Stop for internal subagents such as /review, so the rollout's
task_complete was settling the main agent through a second path that every
other status writer (local and remote reconcile, child hooks, polls) had to
learn about. The transcript poll now replays that end as an ordinary root Stop
through hook normalization, and a root hook for an already ended turn is
classified as that Stop. A failed end carries its failure outcome and is
published unlabelled like a cancellation, since desktops derive a labelled
Stop's outcome as success. The separate settle path, the remote host-verdict
adoption and the child-hook rollout reads are gone.

* Keep an ended Codex turn retired across compaction, and keep WSL polls alive

A compaction SessionStart queued before the next prompt carried the previous,
already ended turn id, so the poll replayed its Stop: a Done flash and a
second "finished" notification. A compaction now keeps the turn only while
it is still running and its rollout end is unrecorded. The WSL guest
rescheduled its transcript poll with the update instead of the stored row,
so the poll stopped after its first update and a later turn end never
replayed there. A Stop also clears the child roster instead of deleting it,
so a reconcile cannot re-seed retired children from the previous row.

* Restate an ended Codex turn's hook in provider dispatch

Keeps the shared hook normalizer within its size limit after main's merge;
the Codex dispatch returns the event it normalized, which the envelope uses
for the published name and prompt classification.

* Type the rollout record helper in the Codex turn-end test

* Accept Codex's turn_complete name and keep only replayed Stops labelled

The rollout reader also records turn_complete, the name the existing
interruption reader already accepts. A poll row that is not a replay no
longer restates the last hook's event name, so only a replayed Stop can
carry the Stop label.

* Drop a delayed Codex hook for an ended turn the lead has moved past

A late root hook for an older finished turn was restated as Stop and marked
the newer running turn Done; passing it through would instead rewind the
lead's turn id so the poll replays the old end.
2026-10-18 14:49:21 -07:00
Jinwoo HongandClaude d3026df586 feat(sidebar): Smart Done means finished and unread (dark) (#30425)
* feat(sidebar): Smart Done means finished and unread (dark)

Owner decision Q1: with ORCA_SIDEBAR_INPUTS=main, a Done row holds its tier only
while the workspace unread flag is set, so opening the workspace or sending the
next prompt ends it. Reuses the existing flag; no new state or timer.

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

* test(sidebar): reading a workspace drops its Done row in the live list

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 17:39:48 -04:00
Neil 3dbb804f3e fix(macos): the TCC prompt watcher's log stream dies with Orca, and old orphans are reclaimed (#30148)
* fix(macos): the TCC prompt watcher's log stream dies with Orca

log stream only writes when its predicate matches (about never), so it never
gets SIGPIPE when Orca dies without running stop() (crash, SIGKILL, e2e
teardown, force-quit dev builds) and lived on under launchd. One dev machine had
126 orphans holding 566 MB and ~40% CPU. A /bin/sh wrapper now holds a
never-written stdin pipe from Orca and kills log stream on EOF.

* fix(macos): reclaim TCC log streams that older builds orphaned

The wrapper stops new leaks, but users upgrading still have every log stream an
earlier crash or force-quit left under launchd. Ten seconds after launch, once
per process, Orca terminates launchd-parented processes running exactly the
watcher's old command line. A live watcher's log stream is parented by its
wrapper, so it never matches.

* test(macos): document the fake log stream cast

* fix(macos): arm log stream wrapper trap before fork; verify orphan executable

* fix(macos): re-read orphan identity before SIGTERM; disable inherited errexit in wrapper
2026-10-18 14:25:39 -07:00
Jinwoo Hong eaa1ee131d A CLI-started terminal shows once in its workspace, and a pane close main has no pane for closes in the window (dark, layout-owner build only) (#30391)
* fix(layout): a workspace only main held shows main's tab on the window's first visit (dark, layout-owner build only)

* fix(layout): a live terminal no pane shows goes through main, which names the tab it holds; a pane close main has no pane for closes here (dark, layout-owner build only)

* test: no type assertions in the CLI-first tests

* refactor: the switch-off build keeps today's live-terminal mint as it was

* review: frame sync under the line limit; the first-visit test names exactly main's tabs

* fix(layout): adopt takes the recorded pane only while it is unbound

* fix(layout): a refused live-terminal adoption is declined, not counted as shown
2026-10-18 16:25:49 -04:00
Kelvin Amoaba b0a7d2eb48 fix(mobile): keep typed commit messages, restore failed shortcut removals, name icon buttons (#30142) 2026-10-18 20:05:28 +00:00
Kelvin Amoaba cf7423c8f4 feat(mobile): redesign the chat composer with file attachments and inline skills (#30359)
* refactor(native-chat): move the slash picker builder and upload chunking to shared

Mobile builds the same skill and command list as desktop.

* feat(mobile): redesign the chat composer with file attachments and inline skills

* fix(mobile): the composer field owns its input style

The page's font-size census cannot follow a style passed in as a prop.
2026-10-18 20:03:26 +00:00
Jinwoo HongandClaude 91b696b6cf refactor(sidebar): main keeps sleeping retention only, no order of its own (dark) (#30404)
* refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark)

With ORCA_SIDEBAR_INPUTS=main the window's sort hook is a pure read of the
rows' stamps: no settled-epoch store listener, no Smart latch, no saved Smart
order writes, no class telemetry over the old attention map. Switch off: the
existing hook, unchanged.

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

* test(sidebar): stamp sort hook reads no live sources; stronger switch-on tests

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

* test(sidebar): clear the order-save mock between tests

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

* wip: main keeps sleeping retention only

* fix(sidebar): stamp sort reads a shared empty-sources constant, not a renderer layout literal

The layout-writer ratchet counts layout field keys written in the renderer;
the empty sources object now lives beside SmartSortSources in shared code.

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

* refactor(sidebar): main keeps sleeping retention only; no order of its own (dark)

Clients sort by the owning host's stamps, so main's temporal owner loses its
settle window, Smart latch, create grace and catalog-listing classifier, and
the shadow check stops comparing order. Retention, its clock and its shadow
comparison stay.

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

* fix(sidebar): every retention listing shares one sequence; publish after the first

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

* test(sidebar): the first listing bumps the retention revision

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 15:51:52 -04:00
Jinwoo HongandClaude 63f0cb9570 refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark) (#30401)
* refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark)

With ORCA_SIDEBAR_INPUTS=main the window's sort hook is a pure read of the
rows' stamps: no settled-epoch store listener, no Smart latch, no saved Smart
order writes, no class telemetry over the old attention map. Switch off: the
existing hook, unchanged.

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

* test(sidebar): stamp sort hook reads no live sources; stronger switch-on tests

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

* test(sidebar): clear the order-save mock between tests

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

* fix(sidebar): stamp sort reads a shared empty-sources constant, not a renderer layout literal

The layout-writer ratchet counts layout field keys written in the renderer;
the empty sources object now lives beside SmartSortSources in shared code.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 15:31:39 -04:00
Kelvin Amoaba df460b8338 fix(native-chat): move a chat visual's Open and Share buttons above it (#30356) 2026-10-18 19:08:48 +00:00
Kelvin Amoaba 9189e0fbe1 feat(native-chat): pan and zoom chat image previews like mermaid diagrams (#30154) 2026-10-18 19:07:36 +00:00
Jinwoo HongandClaude 3af9c22cc9 fix(cleanup): count visits as last activity once terminals stop stamping Recent (dark) (#30397)
* fix(cleanup): count visits as last activity once terminals stop stamping Recent (dark)

With ORCA_SIDEBAR_INPUTS=main, terminal start and exit no longer write
lastActivityAt, so a workspace used only through plain shells could look idle
to workspace cleanup and old in the jump palette. Both now read the later of
the Recent stamp and the last visit. Switch off: unchanged.

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

* fix(cleanup): stamp the workspace you leave; space table reads visits too (dark)

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

* fix(cleanup): every reveal stamps the workspace you leave, host-aware (dark)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 15:01:08 -04:00
Kelvin Amoaba d378f49f17 fix(mobile): show a renamed terminal tab's name on the phone (#30163) 2026-10-18 18:46:53 +00:00
Kelvin Amoaba e43f3e7e04 feat(opencode): open OpenCode 2 in the chat view and list 2.x models before the first message (#28424) 2026-10-18 18:42:23 +00:00
Brennan Benson b6325ef951 style: format the resume and transcript files main left unformatted (#30137)
Two merged changes left lines past the formatter's 100-column width in
structured-session-create-support.ts, structured-agent-session-create-adoption.ts
and transcript-opencode-sqlite-query.ts. Any branch that merges main gets them
reformatted by the pre-commit hook, which pushes two of them past the 300-line
limit and fails CI. Format them, and keep them under the limit by moving the
bounded row readers and the resume adopt payload into the modules that own them.
No behavior change.
2026-10-18 11:17:20 -07:00
Jinwoo HongandClaude 3a2f162911 feat(sidebar): sort Recent and Smart by the owning host's stamps, hold order under the pointer (dark) (#30284)
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)

* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's

* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows

* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear

* chore(sidebar): merge origin/main; locality helper moved to shared

* feat(sidebar): sort Recent and Smart by host stamps behind the sidebar-inputs switch

With ORCA_SIDEBAR_INPUTS=main, the window, main and the phone order Recent by
lastActivityAt and Smart by smartTier then smartAt (Needs you, Done, Working,
then the rest by Recent). The window holds its displayed order while the
pointer is over the list or a drag runs. Switch off: today's sort path.

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

* chore: restore files the merge commit's formatter rewrote

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

* fix(sidebar): stamp-order review fixes behind the sidebar-inputs switch

The window writes no Recent stamps with the switch on (terminal start and
exit, unread marks, comment edits), the hover hold covers only Recent and
Smart and ends with a drag, main follows only explicit metadata saves, and
the phone picks its sort from the unfiltered rows.

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

* test(sidebar): folder writers and a Manual drag under the stamp switch

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

* test(sidebar): name the stamp type in the ps summaries test

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 14:16:34 -04:00
Jinjing a8d7151b49 Require a full hover delay before each tab preview (#30388)
* Require a full hover delay before each tab preview

Tab previews opened almost immediately after a previous preview had been shown, so sweeping across the tab strip caused a flurry of popups. Raise the tab tooltip delay to 800 ms and drop the warm-start chain: the shared open-state context, the 300 ms skip delay, and the slide animation that carried position between cards. Every tab now waits its full dwell time, and keyboard focus still shows the preview immediately.

* Test that tab hover cards wait a full dwell on every hover

The tab hover card no longer slides instantly between neighboring tabs. Each hover now waits for the full delay before showing a preview. The e2e test still expected instant sliding, so it was rewritten to match.

- Remove the frame-recording helpers and the slide and reversal assertions.
- Check that no card appears before the dwell, and that the card appears after it.
- Check that leaving a tab cancels a pending preview, and that time spent on other tabs does not count toward the dwell.
- Check that re-entering the same tab requires a fresh dwell.
2026-10-18 11:14:08 -07:00
Jinwoo Hong bde20afb1d Only the tabs the window just changed wait for main's answer; the window-wide layout hold goes (dark, layout-owner build only) (#30384)
* chore(layout): the drift check's ignore comment names every main-only kind (dark, layout-owner build only)

* fix(layout): a quit stage retried after a failed one still sends the resume records (dark, layout-owner build only)

* feat(layout): the window's local layout stream reopens after a loss, and its snapshot redraws every workspace (dark, layout-owner build only)

* fix(layout): a command whose target main's copy lacks is a logged refusal, not the window's own change (dark, layout-owner build only)

* feat(layout): frames keep each tab the window drew, closed or moved as drawn until main answers; the global frame hold goes (dark, layout-owner build only)

A refused draw-first terminal create is closed (its terminal stopped) at the refusal. The
activation create no longer draws main's tab itself while the stream is down: the reopened
stream's snapshot does.

* fix(layout): a tab move or drag-out the frame already drew is not placed again by the window (dark, layout-owner build only)

Also holds the group sizes the window drew until main saves them.

* test(layout): with frames flowing, a pin promotes a preview tab once and tab-change bookkeeping runs once (dark, layout-owner build only)

Also states that the held-tab overlay builds frame slices only.

* test(layout): read the rename's title without a cast

* chore(layout): the drift check's header lists the same ignored records as its inline comment
2026-10-18 14:13:15 -04:00