* fix(cli): check the runtime source before sending
A terminal Orca launches now carries which Orca launched it
(ORCA_RUNTIME_SOURCE_ID = the profile's installation id,
ORCA_RUNTIME_SOURCE_INCARNATION = the launching runtime id). Before a
request is sent, the CLI compares that with the runtime the profile's
runtime record names. Same runtime: sent as before. Different runtime:
a read-only status.get asks which profile it serves; the same profile (a
restart or update) is accepted, anything else is refused before the
request reaches it.
The vars cross into WSL through WSLENV. The SSH bridge stamps the
bridging runtime itself rather than trusting the remote env. The
existing orchestration compatibility stamp is untouched.
* fix(orcad): drop an inherited runtime source at startup
* fix(cli): an explicit switch to another profile is not fenced
The stamp also carries the launching runtime's userData path. A CLI that
dials a different profile than the one that launched its terminal, and
not the platform default an unpinned terminal falls back to, was pointed
there on purpose (orca-dev from a packaged Orca's terminal), so the
fence does not apply.
* refactor(cli): reuse the shared userData path comparison
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Repos, project groups, project host setups and folder workspaces fetched
from this app or a paired server were each stamped with their owner by a
separate function. They now go through adoptFromEndpoint, one adapter with
a per-kind arm. Behavior is unchanged; a characterization test was run
against the old stampers before they were deleted.
Co-authored-by: m4air <m4air@Mac.localdomain>
* refactor(hosts): host authorities and owner resolution
Adds HostAuthority {endpoint, at} with authorityKey (existing host-id
spellings kept; nested 'SSH target behind server' gets an in-memory key no
host-id parser accepts), HostRoute {target, at}, and resolveOwner, a
focus-free owner resolver over the existing route resolvers. Today's
focus-reading answer is kept behind resolveOwnerWithLegacyFocus, which
counts every answer focus changed. No caller migrates in this PR.
* fix(hosts): resolveOwner keeps nested SSH places and strict active selection
The active workspace's host now resolves through the strict path, so rival
servers proxying one ssh:t stay ambiguous. A folder or repo row on a paired
server that names an SSH connection resolves to that nested target instead
of the server's own machine.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* refactor(wsl): move the WSL guest bundle sources out of src/relay
The WSL hook relay and WSL browser network relay are built from src/relay but are
not part of the SSH relay. Move them, and the 46 relay modules the hook relay
bundles, to src/wsl-guest so the SSH relay can be deleted on its own. Pure
renames; the built bundles are byte-identical.
Adds a boundary test so nothing outside src/relay imports it except relay tests.
* fix(ci): exempt renamed files' existing lint debt in the changed-lines gate
* test(relay): keep the cast suppression on the wrapped line
* test(relay): only parse files that mention relay in the import boundary check
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(mobile): agents for workspaces another runtime owns show why
The paired host no longer lists its own agents for a workspace another Orca
server owns: preflight.detectAgents refuses with workspace_on_other_runtime,
and the phone shows 'Agents on Another Server - Pair that server directly'.
The phone also recognises an unambiguous runtime-owned repo row itself, so an
older host is never asked.
* fix(mobile): a shared repo id with a runtime-owned row defers to a refusing host
Rows on several hosts can share a repo id, and the first row's SSH connection
could name the paired host's own target. When any row is runtime-owned the phone
now asks a host that advertises preflight.other-runtime-refusal.v1 to decide by
worktree, and shows the other-server row on an older host.
* fix(mobile): keep the agent-loader change inside the web bundle budget
Move the refusal code beside its capability in protocol-version, read the
capability in the loader, and test the runtime: prefix directly: importing
execution-host into the loader split a web bundle chunk (129 > 128 assets).
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* feat(pairing): re-pairing the same server keeps its environment
The web client minted a new environment id on every runtime-scope pairing link,
even for the same server key, so existing terminals kept the old id and failed
as ambiguous. A key-matched re-pair now keeps the id (bumping the pairing
revision) and pins the server's host descriptor; a same-key server with a
different installation gets a new id, and a downgraded server never replaces
the pin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): a reply from the replaced pairing never overwrites a same-server re-pair
Re-pairing now keeps the environment id, so fence on the pairing revision too:
a request captured before the re-pair can neither reopen a client with the
replaced token nor save the old pairing back over the new one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(codex): keep running subagent groups in long chats
* fix(codex): remember superseded settled child turns
* fix(native-chat): retain running Claude subagent groups
* Keep repository detection off the main IPC thread
Share repository discovery decisions across synchronous compatibility callers and the existing asynchronous Git runner. Await discovery in project registration, nested imports, and folder upgrades, and recheck folder ownership after discovery completes.
* Make repository detection fixtures work on native Windows
* Await asynchronous Git checks in folder upgrade fixtures
* Prevent duplicate project registration after async discovery
* Check authoritative registration identity before renderer convergence
* Narrow repository responsiveness to detection and registration
* Keep one local project identity after concurrent registration
* Run registration SSH coverage in the existing Docker lane
* Keep folder upgrades responsive and recheck pending project ownership
* test(git): cover classifier failure stages and literal path identity
* test(git): use the current process host package
* test(repos): use current process host in folder upgrade fixture
* test(repos): use current process host in registration fixture
* Keep repository detection off the main IPC thread
Share repository discovery decisions across synchronous compatibility callers and the existing asynchronous Git runner. Await discovery in project registration, nested imports, and folder upgrades, and recheck folder ownership after discovery completes.
* Make repository detection fixtures work on native Windows
* Await asynchronous Git checks in folder upgrade fixtures
* Prevent duplicate project registration after async discovery
* Check authoritative registration identity before renderer convergence
* Narrow repository responsiveness to detection and registration
* Keep one local project identity after concurrent registration
* Run registration SSH coverage in the existing Docker lane
* Await Git discovery when scanning and importing nested projects
* test(git): cover classifier failure stages and literal path identity
* test(git): use the current process host package
* test(repos): use current process host in registration fixture
* Keep repository detection off the main IPC thread
Share repository discovery decisions across synchronous compatibility callers and the existing asynchronous Git runner. Await discovery in project registration, nested imports, and folder upgrades, and recheck folder ownership after discovery completes.
* Make repository detection fixtures work on native Windows
* Await asynchronous Git checks in folder upgrade fixtures
* Prevent duplicate project registration after async discovery
* Check authoritative registration identity before renderer convergence
* Narrow repository responsiveness to detection and registration
* Keep one local project identity after concurrent registration
* Run registration SSH coverage in the existing Docker lane
* test(git): cover classifier failure stages and literal path identity
* test(git): use the current process host package
* test(repos): use current process host in registration fixture
* Let a Grok chat list and stop the commands it left running in the background
A command Grok backgrounded (a dev server, a watcher, a monitor) showed only as a
transcript line: it never reached the background-tasks card above the composer, so
it could not be seen as running or stopped from Orca. Grok's subagents and Claude's
and Codex's background commands already list and stop there.
The ACP lane that publishes Grok's subagents as child records now also publishes
its background-task rows: a running task as a live background command or monitor,
an ended one as removed, derived from the rows the journal admitted. A Stop for one
of them goes through the same adapter route to Grok's own per-task kill
(x.ai/task/kill); Grok's completion notice ends the row, and a task Grok no longer
holds is removed at once. A refusal reports the task as not stopped; no answer or an
unreadable one leaves the outcome unknown.
Each Grok process is checked once at start with a no-target kill, beside the
existing subagent check. Only Grok naming the method unknown hides Stop.
* Settle a Grok task's transcript row when Stop finds it already gone
When Grok answered a Stop with already_exited or not_found, the card row was
removed out of band while the transcript row kept saying it was running, and a
later restated running notice could bring the card row back with Stop. The
answer now settles the transcript row through the translator, like the
subagent Stop, so the card row leaves because its row settled, the existing
no-relight rule keeps it gone, and a completion Grok still sends after
already_exited writes the real result. Also covers /clear being refused while
a Grok background command runs.
* feat(ports): host-scoped port scan and stop on the server
Add workspacePorts.scanHost/killHost, addressed by workspace and resolved on the
server to the workspace's own execution host (this machine or one of its SSH
hosts). Stop is bound to the host its row was scanned on and is refused on SSH
hosts, where ownership cannot be proven. Advertised as
workspace-ports.host-scoped.v1.
* fix(ports): only this host's workspaces authorize Stop; a failed relay scan is not empty
Port ownership probes now come from host routing instead of the repo row's
connectionId, so an SSH repo that carries only executionHostId (or a runtime-
hosted repo) can no longer make a local listener look workspace-owned.
The relay's ports.detect now fails when it could not read the socket table
(unreadable /proc/net/tcp, or neither Windows scanner answering) instead of
answering an empty list, so a host scan reports unavailable rather than none.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* feat(hosts): servers publish a host descriptor
A paired server now reports a stable installation id, created once per
profile, plus an optional machine binding (HMAC keyed by that id over
the machine id and profile path) in status.get and in runtime pairing
offers. Optional fields only: older clients ignore them and older hosts
omit them. Clients start using it in the follow-up.
* fix(hosts): serialize installation id creation so concurrent starts never fork it
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Preserve active graph lanes through unrelated root commits and share node/reference colors.
Isolate real-Git fixtures from inherited Git settings and use the existing public process-host import after the current-main package migration.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* fix(repos): deleting a project with live terminals warns and needs --force
orca project setup-delete now refuses a repo-backed project that still has
terminals not known to have exited or saved workspace details, unless --force.
The sidebar remove dialog says how many open terminals will close.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(repos): count a sibling host's host-qualified workspace metadata in the delete guard
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): cover the remove dialog's open-terminal warning
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): type the dialog mock state without assertions
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
#27164 made an old host without a known non-Windows platform ask for an update
instead of answering its default list. The cross-version journey mocked the
capability probe but not the status read, so no request ever reached the wire.
Co-authored-by: m4air <m4air@Mac.localdomain>
Every add/re-pair path goes through the environment store; it now compares the
offer's public key with this profile's own runtime key before saving.
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: s546126 <s546126@users.noreply.github.com>
* fix(shell): OS open actions follow the file's owner, not the focused server
Every shell open request (openPath, openInFileManager, openInExternalEditor,
openFilePath, openFileUri) now carries the path's owning host. Main allows
only a local owner, plus the SSH editor launch when the owner is that same
SSH target, and no longer consults the focused server. The renderer reveal
and Open-in checks drop their focused-server check to match.
* test(e2e): name the owner on the local editor control open
* fix(explorer): Open in follows the catalog owner for folder and host-only SSH workspaces
The explorer toolbar and the source-control Open in menu took their SSH
connection from the repo row, which a folder workspace or a host-only SSH
repo does not have, so the route read as local. Both now derive the route
from the catalog-confirmed owner.
* fix(sidebar): Open in keeps the SSH target of folder and host-only SSH rows
The row resolver now returns the SSH connection it resolved alongside the
runtime owner, so a folder workspace or host-only SSH repo with no repo
connection no longer reads as local.
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* fix(pairing): advertise the key and a fallback port the host can actually serve
* test(runtime): use a consistent keypair fixture now that the advertised key is derived
* fix(pairing): keep dev's pinned 6769 out of other instances' fallback ladder
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* fix(agents): an old Windows or unknown-platform host asks to update instead of answering its default list
A host without preflight.workspace-scoped.v1 answers a workspace's agent
request with its own default list. On a Windows host that list misses a WSL
workspace's agents, and a host that does not report its platform may be
Windows. Only a known non-Windows host's default is still used; the others
record an empty list plus an update notice, shown in the quick-launch menu.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agents): decide the old-host fallback from a fresh status
Reading the platform from the shared capability cache could see a probe
another caller had just started (no status yet) and flag a Linux host. A
fresh status is race-free and also catches an in-place upgrade.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(agents): name the served host status for its role
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: Claude <noreply@anthropic.com>
pi-subagents never sends a description, so every child row fell back to
the agent name and two children on one agent read the same. Workflow
children do carry workflowKey, a bounded slug, so use it as the label.
Fixes#26908
* feat(relay): per-cell switches for the rejection fence and the lost-reply margin, and report ignored keys
rejectionFence (default on) turns the unhandled-rejection fence off without a roll.
readTimeoutMarginMs (1-60 s) replaces the boot margin per query through pg's query_timeout.
The applied log line and flagsApplied name keys this image does not know.
* feat(relay): runtime-status names the switches and values this image accepts
The flag tool checks a write against it, so it can refuse ticketCheck=enforce (or any key)
on an image that would void or drop it.
* fix(mobile): a new pairing scan supersedes a stale relay pairing journal
* fix(mobile): a late recovery cannot overwrite a newer pairing; only a relay-hello 4401 abandons a journal
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
The flag channel recorded a generation as seen before reading its body, so a
read that failed mid-body sent ifGenerationNotMatch for it on every later poll
and got 304 forever: that write was stranded until the object changed again.
The generation now counts as seen only once its body has been read and parsed.
* fix(cli): orca file open and diff open name their owner
The desktop's own catalog resolves the worktree for files.open/files.openDiff,
so the renderer notification now says so (null) instead of leaving the owner
unset, which let the focused server claim a tab the renderer could not place yet.
* fix(cli): keep a paired server's ownership on diff open
A catalog row stamped runtime:<env> names that server; null would route its diff through this desktop's git.
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* fix(relay): wait through a scheduled reconnect and name why Relay is not ready
* fix(relay): add the new mint-failure notice strings to the English catalog
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
The agent status recovery snapshot was rewritten whole, synchronously on the
main thread, after a 250 ms trailing debounce on almost every hook event
(about 53 new files a minute on the reporter's Windows machine).
- Leading-edge throttle: the first change after idle is written on the next
turn; a stream gets at most one write per 5 s and cannot postpone it.
- Changes to persisted launch-token authority skip the window, since spool
replay and restored-terminal attestation read them after a crash.
- Writes run off the event loop through the stats snapshot writer, now
generalized as AtomicSnapshotWriter. Quit and hook-disable still flush
synchronously; the file format is unchanged.
Fixes#26720
isLocalPathOpenBlocked now takes the path's owning host (ExecutionHostId,
an ExecutionHostOwnerMatch, or 'unresolved') and allows only 'local'. The
settings parameter is gone, so every caller names its owner. Markdown link
callers resolve the document's host from the catalog instead of falling back
to the focused server; reveal keeps the focus check explicitly because the
main process still refuses reveals while a server is focused.
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* Verify pinned transcript reads and stream Codex usage aggregation
* Resume Claude usage scans with compact token checkpoints
* Harden transcript snapshots and reject incomplete discovery
* Keep Codex race fixtures inside their temporary data home
* Verify pinned transcript rotation with portable file moves
* test: keep Claude usage mock registration in test suites
* Compress saved Claude usage without dropping history
* Keep Claude cache writes bound to their original request
* Register Claude contracts without conflicting with chat contracts
* Assert unchanged Claude sources skip the actual cache writer
* fix(mobile-pairing): preselect LAN for a signed-out desktop; sign-out clears the Relay region cache
* fix(mobile-pairing): give the changed Relay sign-in copy its own localization key
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* Add CLI commands to manage and find workspace references
* Fix reference review findings: source-context twins, legacy link collapse, closed chats, old-host errors
- Keep same-URL links with different source contexts separate; CLI add/remove no longer drops a twin
- Restore merging a bare legacy link into its single matching URL link (no duplicate PR rows, no resurrection on older-client removal)
- Carry absorbed data through collapse chains; resolve repoId conflicts deterministically
- Identity: ignore www., case-insensitive GitHub/GitLab/Gitea paths (stored URL keeps case), no Jira match on git hosts; find accepts self-hosted issue URLs
- reference find: skip closed native chats, require a session id for linked, include an explicitly named archived workspace, warm-cache fallback for --worktree
- Selector parity: collapse same-host duplicate paths, issue: ignores unlinked rows
- CLI: clear update-host error for hosts without reference.*, local flag checks before host probe, help text fixes
* Fix CI: add reference rm alias for verb policy; expect linkedItems in worktree ps summary
* Simplify reference CLI: share identifier/legacy-match helpers, drop redundant dedupe
Add Copy Tab ID to terminal, editor and browser context menus.
Copy the clicked tab's existing workspace ID as "orcaTabId: <id>"
so agents can distinguish tab references from content IDs.
Keep inactive and pinned tabs identifiable without activating,
renaming or closing them. Reuse the native clipboard API and show
translated success or failure feedback after the write completes.
Cover clipboard failures, inactive and pinned tabs, separate editor
views, and browser content IDs that differ from workspace tab IDs.
Co-authored-by: Neil <neil@stably.ai>