* 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
On case-insensitive or normalization-insensitive filesystems (macOS), Git
resolves `feature/TEST` or an NFD-composed branch name to the stored ref of
an already checked-out branch. The busy-branch check only compared exact
names, so creating a worktree for such an alias passed the check and then
failed in git. Fold case/Unicode for the comparison and only treat the alias
as busy when the filesystem does not store both spellings as distinct refs.
Co-authored-by: Neil <neil@stably.ai>
Pointer delivery accepted only run: and dispatch: mailboxes, so direct mail
to a pane with no Run, Dispatch, or remote attachment was stored but never
pointed. Accept the terminal's own handle as a mailbox and leave rows its
Run's coordinator or active Dispatch owns undelivered so routing can still
move them (runOwnsDirectMessageSql: alias OR active Dispatch, matching what
routing selects).
The deliverable-mail probe and a pointer-prompted direct check apply the
same exclusion, so probe, pointer, and check select the same rows and a
pane's check no longer consumes mail another Run's coordinator owns.
Fixes#29920
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: MsCNotes404 <269066445+MsCNotes404@users.noreply.github.com>
* refactor(ai-vault): group scanner modules by defining owner
* refactor(ai-vault): consolidate owner imports and isolate path absence
* Preserve mobile CI triggers for extracted absence predicate
Select each child's workflow state in the issue detail query and render
a colored state dot plus the state name on every sub-issue row. Rows
without a state (older servers, sub-issues created locally before a
refetch) render as before.
Fixes#41348
With showPinnedWorktreesInGroups enabled, a pinned worktree renders twice
(Pinned section + its natural group). Activating it from the Pinned
section fires the terminal-focus reveal, whose row lookup deliberately
preferred the natural-group copy, so the viewport scrolled away from the
row the user just clicked to a duplicate far down the list (#24852).
When the pending reveal has no explicit rowKey, the hook now checks the
mounted copies of the target workspace: a fully visible copy wins (the
lookup-preferred one when both are), then a partly visible copy, which is
nudged into view. Visibility is measured with the same card bounds the
reveal scroll uses (getRevealTargetBounds), so a lineage parent is judged
by its own card, not its children. Explicit rowKey reveals and the
no-copy-on-screen case keep the existing behavior.
Fixes#24852
Co-authored-by: Neil <neil@stably.ai>
GitLab's has_conflicts mirrors cannot_be_merged?, which also stays true
for merge requests with no commits, so freshly created empty MRs showed a
conflicts banner while gitlab.com reports "contains no changes". Gate the
has_conflicts and broken_status/conflict verdicts on changes_count, which
GitLab only sets once a diff with commits exists; null means there is
nothing to conflict over. glab's single-MR JSON serializes that null as
'', so getMergeRequest normalizes it before mapping.
Fixes#41186
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Media previews remounted at volume 100%, unmuted and 1x speed on every tab
switch. Volume and mute are now one shared preference applied to every
preview and updated on volumechange; a change still queued at teardown is
saved only when the element differs from what that preview last synced, so
an idle preview never overwrites a newer choice from another pane.
Playback speed is cached per clip alongside the restored position (loading a
source resets playbackRate, so it is reapplied on loadedmetadata). Saving is
gated on metadata having loaded (duration not NaN), so unfinalized WebM
recordings with Infinity duration still keep their speed.
Fixes#33884
Co-authored-by: Neil <neil@stably.ai>
The expect-wrapped login-session probe SIGKILLed only expect on timeout,
abort, or output overflow. login(1) ignores SIGHUP while in PAM, so the
closed PTY never stopped it: each inconclusive probe orphaned a
setuid-root login to launchd, spinning at 100% CPU.
Run expect through runProcess, whose stops send SIGTERM (with a bounded
SIGKILL backstop), and trap SIGTERM in the expect script to SIGKILL the
spawned login before exiting. runProcess's SIGTERM always lands before
expect's default 10s eof timeout, so the trap is always serviced.
Fixes#30806
Stopping a phone's screencast waited for any pending snapshot capture before
clearing the phone's device metrics. A guest that isn't drawing leaves that
capture pending until its 10 s timeout, so the tab stayed at phone size.
* fix(codex-hooks): name Orca's Codex hooks so Codex's hook review stops showing "Hook N"
Codex's hook review names a hook by its statusMessage and lists one without
it as "Hook 1", "Hook 2" and so on. Orca's nine Codex hooks had none, and a
name added by hand was stripped on the next reconcile, because Orca
replaces any entry that differs from its own.
Orca's entries now carry statusMessage "Reporting status to Orca". Codex
hashes the status message, so every trust entry Orca writes or recognizes
includes it: the local, remote and WSL installs, the status probe, and the
locally computed hashes. Approval cleanup still recognizes approvals made
for the unnamed entries.
The Codex hash memo was keyed by Codex version and command only, so after
this change it would have handed back the unnamed hashes as Codex's answer.
Records now store the status message, and a record without the current one
is ignored, which makes Orca ask Codex again. Old records still count as
known Orca approvals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(codex-hooks): name only form-2 hook commands and bump the form
Older builds share ~/.codex and rewrote the named entry back to unnamed on
every launch, since the command bytes were unchanged. Bump the POSIX command
form to 2 so they leave it alone, and name only commands that carry the form
marker. Windows, SSH and WSL commands have no marker and keep their frozen
bytes unnamed. Memo, stopgap and rollback stay command-keyed, which the form
bump already separates.
---------
Co-authored-by: Teddy Chan <1013507+teddychan@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Avoid refetching every linked Linear badge on window focus
* Preserve sourced badge updates without focus polling
* Use a fixed clock in linked issue refresh contracts
* Resolve linked issue refresh scope from its source
* Stream usage source caches without whole-document strings
* Keep the validated source-array type through migration
* Narrow migrated source records without an assertion
* Assert cache preservation without runtime-specific error copy
* fix(computer): stop echoing written text and secure read-backs in action results
* fix(computer): treat a truncated set-value read-back as unverifiable
* fix(computer): accept code-point truncated read-backs as unverifiable
* fix(workspace-cleanup): warn before deleting a workspace's ignored local files
git worktree remove deletes ignored files such as .env and local config,
and git status never reports them, so cleanup showed "Clean git" and the
delete dialog said nothing. Read them with a bounded ls-files listing,
skip regenerable output, symlinks and byte-identical copies of the main
checkout, and surface the rest as an approval-only cleanup blocker and a
warning in the delete confirmation.
* fix(workspace-cleanup): show an unchecked warning when the ignored-files check fails
* style(workspace-cleanup): format merged fact status list
* fix(sidebar): keep a user-set workspace name over linked task titles
The card title guessed whether a name was user-set by comparing it with the branch, so a pinned name equal to the branch lost to a linked issue or PR title. It now reads displayNameMode. The Workspace details dialog also pins the name when the field was edited, even if the text ends up equal to the branch.
* test(sidebar): drop title case that passes without the fix
* fix(mobile): exclude overlay permission and clear removed-host diagnostics
* fix(mobile): ignore late diagnostics for removed hosts
A closing client or a re-acquired open that fails with host-not-found could
append after removal and re-persist diagnostics for the removed host.
The cleanup scan ran only in desktop main over its own saved repos, so
workspaces on a paired server or an SSH host's orcad never showed up, and an
SSH host's retained source rows showed as "Remote unavailable" even while its
orcad served them.
Each host now runs the same scan over the repos it owns through a new
workspaceCleanup.scan RPC. The dialog merges every known host's answer,
names remote rows by this window's runtime host id so removal goes through
the existing worktree.rm route, cancels remote legs with their scan id, and
builds "Remote unavailable" rows from its catalog only when a host can't be
reached. Desktop main scans only the repos it shows.
The test reads the catalog at ref master but created it with plain git init, so
it failed wherever init.defaultBranch is main (e.g. Xcode's git on macOS).