* perf(worktrees): give each repository's worktree list one owner
The sidebar and the runtime catalog each kept their own copy of a
repository's `git worktree list` result and their own staleness rule, so
the same change was often listed twice. One owner per repository, host
and Git runtime now holds the observation, its flight and invalidation;
each reader keeps its accepted age (sidebar 5 s, runtime 30 s plus the
admin-file verifier up to the 5-minute ceiling) and shares the scan.
Settled rows now go stale on Git-level mutations too, overtaken readers
follow one trailing scan, store hygiene uses the facts captured before
the scan that produced the rows (once per observation), and a cooled-
down watcher failure defers its catalog refresh instead of dropping it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): fence the catalog owner by runtime, single flight and hygiene claims
Sidebar reads age from scan start, so runtime verifications no longer
stretch them; probes only certify rows fingerprinted before their scan.
One flight per owner, with a single trailing scan after invalidation.
Switching a repo's Git runtime retires the old owner, and a reader whose
owner was retired mid-read returns non-authoritative rows. Only pruning
readers claim hygiene, failed scans keep the evidence gate's no-retry
claim, overflow invalidates at once, and the stopped-distro fallback
serves only current rows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): let only the live route own a repository's catalog
The catalog owner now asks the live registration which path and Git
runtime are current instead of trusting whichever request arrives last.
A request admitted for a replaced route runs no Git, retires nothing and
answers non-authoritatively; liveness is rechecked before a flight
starts and when it publishes. Route-changing writes retire obsolete
owners after they land. Adapters re-resolve the route after the WSL
admission check, a mismatching verification probe becomes the rescan's
fingerprint, and runtime scans share the strict true-empty-aware reader.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): bound live-route checks and recheck authority before pruning
Live-route checks read the repo registry once per synchronous pass
instead of hydrating every repo per check. WSL discovery answers that
change a repo's Git route retire obsolete owners like other route
writes, and desktop and headless pruning recheck the owner's authority
for the observation after their own awaits.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): revision the live-route snapshot and carry owner authority to publication
Route checks use one registry and project pass per route-input revision,
which repo, project-membership and runtime-setting writes advance
synchronously; WSL discovery answers retire obsolete owners at once.
Repair-state and SSH routes never acquire local Git authority. Metadata
pruning rechecks owner authority after its path probes, fleet lineage
claims require a current observation, and final desktop and runtime
listings stay non-authoritative once the owner has retired their route.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): keep remote-owned repos out of local Git routes
Registrations owned by another host never get a local Git route, WSL
discovery notifies after the distro list is written and isolates its
listeners, and tests cover idle retirement on route-input and discovery
changes plus pruning that loses owner authority mid-helper.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): cover catalog route authority at its production seams
Real repo-removal writer revokes the route at once, and desktop and
runtime listings never publish owner rows as authoritative after their
route was retired.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): prove catalog authority at the pruning helper and Git conversion writer
The runtime listing loses route authority during its own metadata prune
and stays non-authoritative; a real held path probe deletes nothing once
the owner retires the route; a folder converted to Git is admitted as a
live route as soon as the write lands.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): keep strict true-empty listing behind the mockable lister
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): start each worktree.ps case from an unobserved catalog owner
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* build(typecheck): list the WSL discovery change module in the web project
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): withdraw catalog authority from a replaced observation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(github): cancel image uploads when the draft closes
* test(github): use the runtime response metadata field
* chore(github): explain upload boundary type assertions
* fix(agent-picker): stop listing Claude short-name rows beside the versioned model they run
* fix(agent-picker): hide a short name only on reported proof; keep hidden rows searchable
* fix(native-chat): keep a pending hidden model pick listed
* Hide projects from the sidebar without stopping them
Adds Repo.hiddenAt, a per-host, view-only flag. A hidden project's
workspaces, empty header and project-filter entry leave the desktop
sidebar, the paired phone's list, the new-workspace picker and the
palette's project filter. Its terminals, agents and automations keep
running, and repo.list / worktree.ps still list it.
- Hide Project in the project menu (toast with Undo), a collapsed
"N hidden projects" list under the workspace list with Show buttons,
and a Hide from sidebar switch in Settings > Projects.
- repo.setHidden RPC ({repo, repoHostId?, hidden} -> {repo, hostId,
hidden}) and `orca repo hide|unhide [--host]`; `repo list` marks
hidden rows.
- Opening a hidden project's workspace (notification, CLI, deep link)
focuses it without unhiding it, lifting sidebar filters, or leaving a
pending sidebar reveal.
- repo.update echo check: a host that drops hiddenAt reports failure
instead of reading as hidden.
* Address hidden-projects review: multi-host hide, picker scope, mute, stale filters, shared predicate, restore list styling
* Hidden projects re-review: keep saved filters, Agents filter, composer seed, per-toast Undo, shared default host
* Hidden projects: address review threads (pre-feed, palette search, notices, dialog default, seed remap, row keys, a11y)
* Hidden projects: no implicit dialog default when every project is hidden
* feat(plugins): host API 1.9: agent.listSessions (history:read), resume past sessions, agents.list task titles
* test(plugins): host API 1.9 coverage; regenerate plugin artifacts
* fix(plugins): host API 1.9 review fixes
- agent.resume starts a past session where the desktop's Resume would: the same preparation
request and answer handling (now shared), and the transcript in the home it pins
- AI Vault list cache keeps four scopes, so plugin reads never evict the panel's list
- list fingerprints ignore scan id and issues, so an unchanged history revalidates
- workspace mapping resolves symlinked workspaces and prefers a project over a folder
- agents.list task only for open dispatches, read at most once a second
- running matches OpenCode versions; history reads reset when the vault list is invalidated
- truncation, consent copy and limitations documented
* fix(plugins): forced history refresh supersedes cached scopes; resume legacy Codex sessions from their recorded transcript
* fix(plugins): address host API 1.9 review threads
- a scan that began before a forced history refresh no longer caches its older result
- list fingerprints count which issues there are (not their wording)
- symlinked workspaces rank by their resolved folder
- a session-only status row is no live pane: resume prepares the session
- a failed task-title read leaves agents.list rows intact
- the symlink test uses a junction on Windows
* test(ai-vault): order the stale scan before the forced refresh
* feat(plugins): proposed tier for host methods not yet frozen
A method spec may set `proposed: true`: only development plugins may call
it (others get not_permitted), and the minor, frame/worker method lists,
typings and skill references omit it. The reference renders such methods
in a separate Proposed section; dev workers' host.has answers for them.
* feat(plugins): param capabilities, host confirmations, automation provenance
- PluginHostMethodSpec.paramCapabilities: a passed param can require a
second capability; executePluginHostCall and the mock host refuse it with
capability_denied before any audit or effect, and the reference shows it.
- confirmPluginAction generalizes the external-open prompt into a host-owned
confirmation, exposed as PluginHostServices.confirm (desktop only; absent
in headless serve). It re-checks the plugin's approval after the answer.
- Automations record createdBy { kind: 'plugin', pluginKey, name }, set by
the runtime from the in-process plugin caller (RpcContext.plugin), never
from params; the Automations detail shows "Created by <plugin>".
* feat(plugins): workspace:create, automation:read and automation:write capabilities
New closed-enum kinds (older Orcas refuse such manifests with the engines
message), worker-only, with consent copy that names what each allows;
release floor PLUGIN_HOST_API_1_8_SINCE (1.4.259) joins
PLUGIN_SCHEMA_NEWEST_SINCE. agent:launch copy now says it also resumes.
* feat(plugins): host API 1.8 methods: workspace.create, agent.resume, automation.*
- workspace.create (workspace:create; agent also needs agent:launch): a
worktree in a Git project, as orca worktree create does; with agent, the
CLI's agent.launch create-worktree path, so the agent starts after setup.
Setup is skipped or follows the project's settings; never run, an
approved hash or a startup command.
- agent.resume (agent:launch): terminal.ensureAgentSession by session id;
started is false when a terminal already ran it. agents.list rows and
agent.status.changed carry sessionId.
- automation.list/create/update/delete (automation:read/write): create
forwards only name, prompt, agent, target, schedule, time zone and
enabled; update/delete of automations another plugin or the person made
ask the person first and report cancelled or consent-unavailable.
* test(plugins): host API 1.8 coverage; regenerate plugin artifacts
Covers param capabilities (host and mock host), workspace.create payloads
and refusals, agent.resume keys and adoption, sessionId on agents.list,
automation projection, forbidden create fields, confirmation outcomes,
runtime-set provenance and capability access growth. Regenerates the
reference, schemas, typings, declarative runtime and skill references.
* fix(plugins): host API 1.8 review fixes
- Confirmations spend the frame dialog budget, allow one question per
plugin at a time and pause after a refusal (rate_limited); a worker's
question shows in the focused window, else the first visible one.
- Changing what an automation with extra agent settings runs (prompt,
schedule, resuming) asks first even for its creator, showing the prompt.
- Removing, disabling or blocking a plugin pauses its automations and ends
its ownership (createdBy.endedAt), so neither re-enabling nor a reinstall
under the key resumes or edits them silently; the remove dialog says so.
- workspace.create and agent.resume record the terminals they return as
the plugin's own; a launch that fails after its worktree exists names it.
- Setup-decision refusals are told by the runtime's error class (kept as
the in-process RPC error's cause); generated names see every worktree
of the project; resume transcripts match the workspace and host.
- Created by shows the plugin id beside its name.
* fix(plugins): end automation ownership only on uninstall, disable or block
- The ownership sweep no longer reads discovery: only an uninstall
(announced by the removal path), the disabled list, the kill list or
the plugin-system switch end it, and only once the service is ready,
so an unreadable plugins folder pauses nothing. An install under the
same key after a removal keeps what it creates.
- A new time zone on an automation with extra agent settings asks first.
- A failed launch names only a new worktree made from the requested name.
- Settings' plugins-off state and the CLI's plugin system off and plugin
disable say that plugin-created automations are paused and stay so.
* fix(plugins): address host API 1.8 review threads
- Confirmations close when the worker's call ends, freeing the plugin's
turn; they name the automation (short id, project, workspace,
schedule), show any new prompt, and are refused as `changed` when the
automation was edited while the person was asked.
- automation.create takes hostId (through the project's setup on that
host) and a creationKey scoped to the plugin; the runtime rechecks the
plugin's approval right before the write.
- agent.resume records only terminals it started; both it and
workspace.create recheck approval before recording.
- automation.list never cuts a schedule (null when too long) or fails on
an id too long to show.
- The Automations detail says when a creator plugin was removed or
disabled.
Windows hosts' Git Bash (and agents that shell through it) print drive
paths as /c/Users/..., which resolved as POSIX paths and never linked.
Map /<drive>/<segment>... to <DRIVE>:\... when the workspace lives on a
Windows drive and no WSL distro owns the pane; bare /c stays literal so
'cmd /c' is not linked to C:\. Moves the existing MSYS drive conversion
from the main-process shell args to shared so both sides reuse it.
Closes#41357
When a field accepted the AX value write but kept its old value, the
helper had already moved the caret, so the fallback keys or Cmd+V went
in at the wrong place.
The Used/Left column heading sat in a fixed 32px span with break-all, so
longer translations such as Russian 'Использовано' broke one character at
a time across three lines. Let the heading take its intrinsic width with
a 32px minimum so it stays on one line while its right edge keeps lining
up with the percentage column; the account heading truncates instead.
Adds Russian 390px fixtures for the Codex and Claude account lists.
Fixes#41111
Co-authored-by: Neil <neil@stably.ai>
Guard the automation editor's outside-dismiss once the user has edited the
draft, so a stray backdrop click no longer discards unsaved changes. The
baseline is captured at the first user edit (after opening effects fill
defaults) and compared by JSON snapshot plus selected host, so reverting an
edit by hand makes the dialog dismissable again. Escape and Cancel still close.
Fixes#29113
* feat(plugins): host API 1.7 — workspace ids, project/terminal list, terminal focus/close
Plugins that track workspaces and terminals across Orca had to read the private runtime
token or shell out to the CLI. Host API 1.7 gives them the same through consented methods:
- workspace.readContext (and workspace.changed) carry `workspaceId`, the id workspace.list
gives; workspace.created/removed and agent.status.changed add `workspaceListId` beside
their existing raw `workspaceId`, which keeps its meaning.
- workspace.list rows name their `projectId`; opt-in `includeStatus` joins each row's agent
state, unread flag, live terminal count and last activity from worktree.ps.
- project.list (workspace:read), terminal.list (workspace:read), terminal.focus
(workspace:write, desktop only, steers only this window) and terminal.close
(terminal:send, reports whether the process is known to have stopped).
- Runtime refusals (unknown or ambiguous workspace, stale or exited terminal) reach plugins
as fixed messages instead of runtime wording.
- Consent copy for workspace:read, workspace:write and terminal:send says what they now
allow; terminal.focus and terminal.close are audited by terminal id.
- PLUGIN_SCHEMA_NEWEST_SINCE covers the editor typography and theme video floors it missed,
and the provisional 1.7 floor, so development builds accept plugins requiring them.
* fix(plugins): narrow terminal.close and harden host API 1.7 from review
- terminal.close closes only terminals of the focused workspace (the check terminal.sendText
uses) or ones this plugin opened with terminal.create or agent.launch, recorded in a bounded
per-plugin registry that is forgotten when its worker stops or it is disabled or removed.
terminal.focus stays any-terminal; summaries and terminal:send copy say so.
- Lifecycle events carry the worktree's identity and host: from the new row on create, and read
host-qualified before the purge on every removal path. agent.status.changed looks up the
identity on the reporting host only. The global removed-identity map is gone.
- includeStatus shares one in-flight worktree.ps and a 1 s cache across plugins, and reports
statusTruncated when that listing stopped short.
- Stored remote contexts get their identity host-qualified; panels and editors never get a raw,
path-bearing workspace id from readContext or workspace.changed.
- Runtime refusals map to fixed messages (tab pinned, no window, tab gone, host disconnected);
any other runtime error or malformed answer is a fixed action_failed with the detail logged.
- hostId is `local`, `ssh:` or `runtime:` in both workspace.list and project.list; rows past the
id or path bounds are left out with truncated set instead of failing the list.
- The mock testing host answers terminal.focus and terminal.close by default.
* fix(plugins): keep shipped host API values and codes; honest terminal results
- workspace.list hostId keeps the values 1.3 shipped (recorded host, else null; folder rows
their executionHostId); project.list always names the host. Both summaries say which.
- Methods that shipped before 1.7 report every runtime refusal as action_failed, as before;
the unavailable/conflict mapping applies to 1.7 methods only.
- includeStatus shares a pending worktree.ps however long it runs; the 1 s TTL starts when it
lands.
- The opened-terminal registry records only for a call still approved and not cancelled, and is
pruned on disable or removal only, so event-driven workers keep close rights across restarts.
- terminal.close answers { closed: true }: a local close cannot tell whether the process exited.
terminal.focus answers focused: false when the request was superseded or not navigated.
- workspace.list and project.list cut labels to 512 instead of failing the list.
- Removal events from the runtime job and the IPC retry are tested to carry the identity read
before the purge; folder deletion carries its host.
* fix(plugins): name the status-listing owner type