- 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.
- 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.
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.
* 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).
* feat(plugins): Featured and Newly added rows in the plugin catalog
Featured shows the most installed plugin per category; Newly added lists plugins
by the catalog's new entry-level addedAt (hidden when no catalog dates entries),
with a New badge for 24h and an Added this week filter. Snapshots move to a v3
cache key so older builds never read addedAt.
* feat(plugins): derive Newly added dates from release dates, as the website does
Drops the catalog-level addedAt field (schema, docs, v3 snapshot key). Main now
estimates a listing date for each non-official plugin from its oldest dated
release, and the Newly added row, New badge and This week filter read it through
shared helpers that mirror orca-site's pluginNew.ts: 30-day window, newest first,
dates more than an hour in the future ignored, "just now / N hours ago / N days ago".
* feat(plugins): show nine Newly added plugins, drop the counts release dates inflate
Listing dates come from each community plugin's oldest kept release, and
catalogs keep only recent releases, so almost every plugin looks new: 181
of 182 fall in the 30-day window and 142 count as this week. Remove what
that inflates rather than flag it: the "N this week" count and its See all,
the This week filter, and the 24h New badge.
The Newly added row now offers the website's nine in the catalog's normal
wrapping grid, the same columns and card widths as the catalog groups.
* fix(plugins): pick Featured winners by every kind a plugin belongs to
Featured used only a plugin's first filter kind, while the category filters
count every kind, so category order could change winners or drop a kind's only
candidate. Each plugin, walked by installs with a key tie-break, now fills one
unfilled kind it belongs to (the one with the fewest candidates left, then by
name). Also note why Featured is a capped one-row teaser and pin the cap.
* fix(plugins): hide Featured when Newly added is empty
Without Newly added, Featured sat directly on the Official catalog, whose
leading cards are the same popular plugins in the same columns, so back-to-back
rows repeated and read as a render bug. Featured now shows only alongside
Newly added. Leaving featured plugins out of their catalog instead would move a
card into the catalog the moment it's installed and skew the catalog's N of M.
Adds a PluginManager test that no plugin card shows twice in the empty state,
and page-level fixtures (390, 600, 1440, empty 1440) for screenshots.
* fix(plugins): cap Newly added at two full rows of the current columns
At phone width nine stacked cards pushed the catalogs about 3.5 screens
down. The grid now shows two full rows of its container-query column
count (8 at four columns, 4 at two, 2 at one), hides a partial last row,
and the pick renders at most eight. Hidden plugins stay listed in their
catalog group, so there's no See all.
* Correct plugin catalog fixture evidence
* Make plugin highlight tests match observable behavior
* perf(sidebar): share mounted row scans across review visibility checks
* refactor(sidebar): drop review-visibility parameters the hook no longer uses
The hook now gates on enabled and filters on-screen cards itself, so visibleReviewCardIds no longer needs enabled or isOnScreen.
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix(sidebar): keep the session name first on a monitoring agent row
Leading with "Monitoring background tasks" made every monitoring row in a
narrow sidebar truncate to the same text. The row's dot already draws the
monitoring glyph, so the status can trail the name like every other state.
* fix(agents): keep a monitoring child row's name first too
Sub-agent rows (desktop sidebar, chat strip and mobile) share one lead/trail
rule that also put "Monitoring background tasks" before the name.
* perf(sidebar): share mounted row scans across review visibility checks
* refactor(sidebar): drop review-visibility parameters the hook no longer uses
The hook now gates on enabled and filters on-screen cards itself, so visibleReviewCardIds no longer needs enabled or isOnScreen.
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix(sidebar): keep the session name first on a monitoring agent row
Leading with "Monitoring background tasks" made every monitoring row in a
narrow sidebar truncate to the same text. The row's dot already draws the
monitoring glyph, so the status can trail the name like every other state.
* fix(agents): keep a monitoring child row's name first too
Sub-agent rows (desktop sidebar, chat strip and mobile) share one lead/trail
rule that also put "Monitoring background tasks" before the name.
Monaco falls back to brackets for auto-closing, so a plugin bracket up to 128
characters was typed into the file as you type. The shared schema now fills in
autoClosingPairs with the brackets that pass the 8-character typed-pair rule.
* feat(tab-group): split pane landing as a command card
The empty split pane becomes one bordered card. The caret starts in the search field with the default-agent row highlighted; Enter opens it, arrow keys move the highlight, → opens the agent picker from the default row with an empty query, and Esc clears the query. Late agent and file rows re-highlight the first row only while the user hasn't moved the highlight since the last query change, and a picked row that disappears hands the lead back. Under a 300px card the field says "Search"; under 280px the shortcut column hides. Styling lives in an opt-in Command density="card" variant plus a scoped selected-row rule in main.css.
* fix(tab-group): keep landing search accessible on narrow cards
* fix(tab-group): track actual landing highlight moves
* feat(workspace-cleanup): mark merged and unchanged workspaces from git evidence
Squash-merged branches whose remote was deleted were flagged as unpushed
and needed a forced delete. The scan now reads what HEAD holds beyond the
workspace's base (ancestor, Git's squash proof, or a cached merged PR) and
reports merged/unchanged reasons; landed commits no longer count as at risk.
A gone upstream no longer hides unpushed commits. Renames idle-clean to
inactive and drops the dead tier, selectedByDefault and volatile-local-context.
* fix(workspace-cleanup): read HEAD once, skip a reused branch as its own base
Merge evidence now compares the cached PR and the base against the HEAD it
reads, not the listing's possibly stale one; a workspace opened on an
existing branch no longer counts that branch as its base; the proof
wrapper settles the WSL route before building its caches; a visit keeps
merged/unchanged reasons; landed rows stop sorting as unpushed.
* fix(workspace-cleanup): qualify base refs and trust the branch proof only for HEAD
Base refs resolve through the worktree base-ref qualifier so a tag cannot
shadow them and only the branch's own family is skipped, including the
repo default; SHA-256 pinned commits are not bases; the squash proof runs
only while the branch ref is the HEAD just read; expanded details stop
counting landed commits as unpushed.
* test(workspace-cleanup): build the repo fixture without a cast
* fix(workspace-cleanup): never take a tag as a workspace's base
* fix(cli): report connected agent hook status from the sqlite file
Status always said appliedBy offline and named orca-data.json, even when the app was connected and the setting lived only in profile-state.db.
* fix(cli): read agent hook status from the running app
When the runtime answers, status takes agentStatusHooksEnabled from
settings.get instead of the disk, so appliedBy: runtime is true. The
settings path rule lives next to the reader, and on/off report the
SQLite file when the runtime applied the change.
---------
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* fix(agent-hooks): keep the Claude session model on later hook status
SessionStart reports the model, but the Claude handler dropped it, so the
mobile composer chip stayed on "Model". Publish that model and retain it
when later lead and child events omit it.
* fix(agent-hooks): keep the Claude model across an Orca restart
Claude reports its model only on SessionStart. After a restart the listener's
lead state is empty, so the next hook dropped the saved model from the row.
* test: run the Claude model restart test in the node pool
* fix(agent-hooks): keep the Claude model when a terminal status row lands first
* refactor(agent-hooks): read the Claude lead model once to keep claude-events under the line limit
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* fix(tabs): reopen the final closed tab from the welcome screen
* fix(tabs): activate a reopened folder workspace through the workspace activator
History entries can be folder workspace keys, so welcome-screen reopen
activates through the same activator history replay uses. Adds a
regression test for the welcome-screen shortcut.
* fix(tabs): let the shortcut recorder capture workspace shortcuts and open reopened workspaces as user opens
---------
Co-authored-by: Vyacheslav Scherbinin <gentslava@mail.ru>
* feat(agent-picker): fold legacy models the same way in every model picker
Claude aliases whose versioned model is listed, and Codex models superseded
in their family or by a newer major line, fold under Legacy models in the
agent launch picker, text-generation pickers, and native chat on desktop and
mobile. The default and current model always stay listed.
* feat(agent-picker): expand legacy models in place instead of a separate list
* feat(source-control): fold legacy models in the action model menu
* fix(agent-picker): keep model rows keyed across sections; let launch search find legacy models
* fix(agent-picker): keep rows in place while picking; classify each model catalog once
Legacy membership no longer depends on the selected model; a picker opens the
legacy section when the current model is in it. Classification is one pass and
cached per catalog, so streamed chat renders don't reclassify an unchanged list.
Typing on a closed Select no longer swallows its next close.
* fix(agent-picker): reopen menus with the current legacy model shown; PTY chats classify their discovered list
* test(agent-picker): wait on the menu instead of sleeping
* fix(agent-picker): keep the Legacy models row last, expanding older models above it
* fix(agent-picker): label the folded section More models, matching mobile
* fix(ime): stop a commit key with no keyup from eating the next Enter
compositionend only scheduled the carry's expiry when it armed a new carry. A marked commit key (Process/229 with an unknown code) whose keyup never arrives left its carry armed forever, so the next bare Enter in the chat composer and every IME text field was swallowed while Ctrl/Cmd+Enter still submitted. compositionend now always schedules the expiry.
Refs #41072, STA-14827
* fix(ime): expire an unidentified commit key's carry on every composition end
Keep a carry armed by a key known to be Enter until its keyup, so a confirm Enter held across a frame still swallows macOS's redispatch. Route setComposing(false), which the chat composer, file explorer, quick commands and comment box use, through the same settle step as onCompositionEnd.
* fix(ime): expire an unknown-code commit key's carry outside a composition
* perf(orchestration): read released worker output from its original instead of a saved copy
* fix(orchestration): keep refusal codes on released chat reads and locate legacy remote transcripts
* test(orchestration): prune flaky-pattern baseline for removed archive tests; guard a cursor read
* fix(orchestration): keep locator fields when stripping copies; never let the optional receipt page block remote release
* test(orchestration): drop removed archive tests from the node-runtime test list
* refactor(orchestration): drop the background strip; old rows keep their saved copy and its reader
* fix(orchestration): give reference rows the empty copy fields older Orca reads, with a note
* test(orchestration): give the slow-exit release mock a stored archive body
* fix(orchestration): record a transcript reference only for agents the reader can open
The close actions swallowed refusals and transport errors, so a tap on
close did nothing when the host refused (for example while the desktop
window is still loading). Show a short toast instead, with a specific
message for runtime_unavailable.
* fix(orchestration): hide approve from help; approval follow-ups
- Hide 'orchestration approve' from group help; its own --help still works.
- Mark a terminal worker's approval reported only once its escalation was sent,
so a prompt whose report was skipped gets no stray closing line.
- Hold back a worker approval receipt older than every loaded chat message
instead of drawing it below newer replies.
* fix(orchestration): count a stored stop report as sent when its wake fails
Liquid's {% comment %} / {% endcomment %} were dropped by hosts and refused by
validate:plugin. Comment markers are only inserted by Toggle Comment, so they
share the 128-character printable one-line rule with brackets; typed pairs keep 8.