Commit Graph
1148 Commits
Author SHA1 Message Date
Brennan Benson 923be35090 docs(settings): point the Chat or CLI choice at Settings → Agents (#36871)
* docs(settings): point the Chat or CLI choice at Settings → Agents

The Settings → Chat → Providers page was removed; the default and the per-agent choice now live in Settings → Agents.

Tracked in STA-13195.

* docs(settings): say the Chat | CLI choice is for supported agents on this computer
2026-11-01 16:07:45 -08:00
Kelvin Amoaba ac99f1cb39 fix(emulator): list only the worktree's attached emulators, iOS and Android (#37127)
* fix(emulator): scope emulator list to the worktree's sessions

emulator list ignored --worktree and returned serve-sim's global iOS helper list. It now lists the session registry's attached sessions (iOS and Android) for the selected worktree, or every worktree's for --worktree all.

* fix(emulator): drop stopped devices before listing sessions

* fix(emulator): point the no-active hint at emulator devices

* test(emulator): assert list results, not serve-sim calls

* refactor(emulator): prune stopped devices inside the registry listing
2026-11-01 21:34:03 +00:00
Neil af6dabd70b feat(plugins): orca plugin shot --scenario replays a check scenario; dev watch shot pendingRequests (#37090)
- shot <key|dir> --scenario <name> renders the panel in that check scenario through the check
  runner (a separate Node process on a mock host and mounted worker), sends the validated drawn
  tree to a new plugins.shotReplay, and captures it in a throwaway preview surface: no guest,
  no live host, no real storage or settings, no real worker, images as placeholders. Window
  hidden or minimized only; --act, --environment and bridged shells refused; an older host
  answers method_not_found (runtime_unsupported) rather than doing a live shot.
- dev watch's shot event carries pendingRequests.
2026-11-01 10:31:11 -08:00
guangyu-reflexioandKelvin Amoaba 5467bf3d25 fix: preview remote HTML outside registered workspaces (#36791)
* fix: preview remote HTML outside registered workspaces

* fix(terminal): keep HTML from hosts without re-grantable files in the editor

* fix(doc-preview): check outside-workspace reads by host-canonical path and page large workspace assets

* fix(doc-preview): check approved folders for workspace assets of outside documents

* test(e2e): read the preview guest without a type assertion

* test(e2e): explain the outside-workspace preview timeout

* test(doc-preview): type the pinned grant target in the share test

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-01 18:05:22 +00:00
Neil dfbaa2fc29 Plugin shots wait for the worker and restore after --act; terminal approval prompt (#37066)
* feat(plugins): dev watch asks for first-load approval in a terminal; acronym-aware names

- In an interactive terminal without --json, a first dev watch load that needs approval shows
  the grants (as plugin inspect prints them) and asks Approve and load? [y/N]; yes reloads with
  the host's fingerprint and keeps watching. --json and non-TTY runs keep the exit-3 flow.
- create keeps known acronyms uppercase when it derives a name from the id (pr-queue → PR Queue).

* fix(plugins): a declined dev watch approval prints only the re-run command

The person just read the grants in the prompt, so the exit-3 error no longer repeats them:
Not approved. To approve later, run: <command>.

* feat(plugins): shots wait for the worker; --act restores the panel; Table and Tree acts

- A shot settles once the panel has no plugin.request or host call in flight and no new tree for
  a quiet spell (capped at 3 s), so a cold worker's skeleton is not captured; pendingRequests
  reports what was still pending.
- After --act and the capture, the guest restores every plugin-written atom it had before (new
  ui.snapshot/ui.restore; persisted atoms re-persist) and Orca remounts its drawn controls;
  restored: true|false. Worker-side effects are not rolled back.
- --act gains the kit's slide, sort, selectRows, activateRow, loadMore, expand, collapse,
  selectItem and activateItem.
- Docs: the dev watch first-load prompt; shot settle, restore and the new verbs.

* fix(plugins): a shot waits for the theme repaint and retries a bare capture

Linux CI captured a dark --theme shot as a bare background (2 colors). The renderer now waits
until the document shows the asked color scheme and paints, adds a frame after a --width
resize, and main recaptures a panel image that is still a background and a border (two colors
or fewer) up to 8 times, 150 ms apart, before returning it. Html panels keep their guest
composite.

* fix(plugins): capture the main window with stayHidden

webContents.capturePage(rect, { stayHidden: true }) keeps a hidden window rendering for the
capture, as the components gallery screenshots do. The repaint wait stays, and the bare-capture
retry stays as a fallback.
2026-11-01 09:32:38 -08:00
Neil ce8ea081c0 Plugin check shows what drew; responsive Table columns; quieter Table and Stack (#37062)
* plugins: check shows what drew; responsive Table columns; quieter Table and Stack

- orca plugin check: each scenario result carries a summary (headings,
  callouts, empty states, table and tree sizes, the selected tab, loading
  placeholders, danger/warning text), printed under its line, and --json
  adds the drawn tree. dev watch's check event keeps only the summary.
  orca/testing gains panel.summary().
- Table columns take minWidth (floor for an auto column) and hideBelow
  ('sm' 360px, 'md' 480px), measured on the table itself; at least one
  column stays. Cut-off cells and headers show their text on hover. The
  kit's columnsOf(table, { width }) answers which columns fit.
- The active-row ring shows only while the grid has keyboard focus.
- A Stack with no align keeps buttons at their own width while fields,
  tables and cards still fill it; align='stretch' stretches everything.
- Typings, docs, the skill's workflow reference, regenerated references;
  the gallery's Kind column drops out in a narrow sidebar.

* plugins: check can render one scenario and return its drawn tree

For orca plugin shot --scenario (tree replay): checkPluginPanels takes
{ panel, scenario, drawnTree }, and with drawnTree returns each render's
validated tree exactly as Orca draws it (handlers as { $callback }),
re-validated on the CLI side. An unknown scenario is reported as an issue.
readPluginCheckScenarioNames(dir, entry) lists a panel's scenarios by
loading its .check file without mounting anything.
2026-11-01 09:07:14 -08:00
Neil 459745d9a4 fix(plugins): workspace.changed replays the current context on subscribe (#37044)
A panel that loaded before Orca focused a workspace showed nothing until the next change, and a
subscription made before the panel's push channel opened was never told at all. Now each new
subscriber gets the current context first: the panel bridge replays it once the channel is
open (or right away), and a worker's events.on('workspace.changed') reads it through
workspace.readContext. Unit tests for both, and the agents e2e checks the first event arrives
with no change happening.
2026-11-01 07:46:18 -08:00
Neil 2666bbddbe Plugin dev loop: dev request, shot --act, worker-panel template, output polish (#37049)
* feat(plugins): orca plugin dev request; --template worker-panel

- orca plugin dev request <dir|key> <name> [--data] [--panel] [--timeout-ms] calls a dev
  plugin's orca.panels.handle handler through the real panel bridge (plugins.devRequest) and
  prints the answer; handler errors become plugin_request_failed with the worker's code.
- orca plugin create --template worker-panel scaffolds a build-less trusted-worker plugin: a
  components panel, src/worker.ts, a shared src/protocol.ts contract, and panel and worker tests
  that pass under orca plugin test and type-check with tsc.

* feat(plugins): shot --act, shots in the plugin folder, quieter logs

- orca plugin shot --act '<json>' presses, types, selects and toggles in the live components
  panel (the test kit's queries and acts, through Orca's own controls and onEvent) before the
  capture, waiting for each to settle. Only that plugin's panel, only with Orca's window hidden
  or minimized; html panels refused; acts are not user gestures. Results in actions[].
- shot run inside a plugin folder defaults to its .orca/shots, like dev watch --shot.
- dev watch prints identical [frame] lines once per reload with a count; logged stacks drop
  <anonymous>/native frames; the floating workspace button hides during a capture.
- Skill: worker-panel template in the decision table, dev request, shot --act, and a gotcha
  that a worker save drops its in-memory state.
2026-11-01 07:31:10 -08:00
Neil 8503ecda71 plugins: check runs the worker and scenarios; RelativeTime (#37047)
- orca plugin check mounts the plugin's worker (main) on the same mock host
  as each panel, so plugin.request reaches its orca.panels.handle handlers
  as in Orca; a worker that fails to activate or deactivate is an issue
  named after its file. In orca/testing, a worker mounted on a host answers
  that host's panel requests unless `requests` stubs the name.
- Check scenarios: <entry>.check.ts (e.g. src/panel.check.ts) exports
  defineCheck({ name: { ...mockHost options, persisted, theme, events } });
  check renders the panel once per scenario and labels each result.
- RelativeTime (catalog 3, since 1.4.247) and { type: 'relative', at } Table
  cells: Orca ticks them (each second under a minute, then less often), so
  plugins never poll. Inline inside Text; test kit reads them as drawn.
- Typings, docs, skill guide, generated references; gallery shows it.
2026-11-01 06:56:19 -08:00
Neil a37a5b3e2d Plugin dev emit reaches panels; trim the plugin authoring skill (#37026)
* feat(plugins): dev emit also reaches the plugin's open panels

orca plugin dev emit now publishes through the panel bridge to that dev plugin's open panels
that subscribed with orca.on, under the same approval and capability rules as the worker, and
reports delivered.panels. The bridge's publishEvent takes an optional pluginKey filter and
returns how many panels heard it. A panel-only plugin no longer needs a worker; an event
nothing listens for is refused.

* docs(skills): trim the plugin authoring guide; details move to references

The main guide drops from 299 to 121 lines: the decision table, build-less rules, core loop
(create → dev watch --check --shot → read the PNG → test → check → release-entry), approval
and top gotchas. Test/check and orca/testing (panels and workers), dev watch events, shot,
open and dev emit move to a hand-written references/workflow.md; the panel/worker/data and
agent status recipes to references/recipes.md, each with a read-when row. The guide test now
also scans hand-written references for commands and caps the main guide at 150 lines.
2026-11-01 05:58:04 -08:00
Neil e5b87cb8e6 Components catalog 2 and worker tests: forms, tabs, table, tree, markdown, code, images (#36997)
* plugins: components catalog 2 (forms, tabs, table, tree, markdown, code, images)

Adds 18 components to TSX components panels, drawn with Orca's own
primitives and gated by ui.catalog 2 / ui.has() (Orca 1.4.247):
Textarea, Checkbox, RadioGroup, Slider, Field, Tabs/Tab, Collapsible,
Toolbar, Grid, a virtualized Table with sort, selection and pull-based
paging, a keyboard Tree with lazy children, Markdown (sanitized, links
through the checked open path), CodeView (Orca's highlighter), Image
(header-checked plugin files read by main), KeyValue, Callout and
Skeleton, plus a tooltip prop on Button and IconButton.

Value controls share the edit protocol; handlers get plain JSON event
args, including small objects ({ keys }, { key, expanded }). The test kit
gains matching actions and hides unselected tabs and closed collapsibles;
a components-gallery example and e2e cover the catalog end to end.

* plugins: worker test harness, host refusals in tests and check

- orca/testing gains mountWorker(main, { host, events, commands }): it calls
  the default-exported activate(orca) with the orca object Orca itself builds
  (createPluginWorkerOrcaApi, moved to shared so the kit reuses it), backed by
  mockHost. emit() checks payloads against Orca's event schemas and only
  delivers subscribed events; command(), request() (the worker's
  orca.panels.handle, as a panel's plugin.request), posts, logs, refusals and
  deactivate(). Storage and settings share the mock host's in-memory store.
- orca plugin test loads a worker main's module graph as Orca's worker host
  does (.ts/.mts transform, Node resolution, no runtime orca import).
- mockHost records refusals (capability_denied, not_permitted, ...) even
  when plugin code catches them; panel.refusals exposes them and
  orca plugin check reports them as warnings. Check dedupes repeated issues.
- Typings, docs and a generated references/testing.md skill reference; the
  reference generator reads the split orca typings.
2026-11-01 05:43:15 -08:00
Neil 1392c8e894 Plugin dev loop: orca plugin open/shot, dev emit, and dogfood fixes (#37008)
* feat(plugins): orca plugin open and shot; dev watch --shot

- orca plugin open <key> [--panel|--editor] shows a panel in the right sidebar or a file in the
  plugin's custom editor, by changing renderer state only (no focus or window raise).
- orca plugin shot <key> [--panel] [--theme] [--width] [--out] [--json] waits for the panel's
  current code to draw, then crops webContents.capturePage to the panel (html guests included),
  with background throttling off while hidden. --width and --theme are temporary in-memory
  overrides; the PNG returns base64 (5 MiB cap), so --environment works.
- dev watch --shot <dir> writes a PNG per panel after the load and each good reload.
- The authoring skill's loop is now dev watch --check --shot → read the PNG → plugin test.

* fix(plugins): shots never disturb a visible Orca

- shot --theme is skipped while the Orca window is on screen (themeApplied: false with a
  themeReason); the override applies only to a hidden or minimized window.
- dev watch --shot opens the first panel (or --shot-panel <id>) once, then shoots it with
  ifShown: if the user moved the sidebar away it emits one shot_skipped instead of switching.

* fix(plugins): dev loop dogfood fixes; orca plugin dev emit

- dev watch: approval re-run command drops watch-only flags and old --approve; a folder added
  by a refused load is owned by the next approved load (Ctrl-C unloads it); check events
  carry the plugin's status (no green while pending/errored); reloaded prints before that
  reload's logs; --json errors are one NDJSON line.
- Errors: log, banner and check share source-mapped positions (a line-1 prelude hands sucrase
  mappings to the components runtime); check dedupes issues and locates syntax errors;
  Orca runtime frames and the error module's bogus location are dropped from logs.
- .orca/types tsconfig sets preserveSymlinks (tsc 7 loaded typings twice under /tmp).
- Templates' READMEs use dev watch.
- orca plugin dev emit <dir|key> <event> [--data] sends a test event to a dev plugin's worker
  through the real delivery rules (plugins.devEmit).

* test(plugins): fixed timestamp in the dev emit test

* fix(plugins): shot captures an html panel's guest itself

On Linux the main window's capturePage leaves a <webview> guest blank. The renderer now reports
the guest's rect, and main captures the guest's own webContents and draws it over the window
capture at that rect (raw bitmaps, nativeImage only). Components panels are drawn by the host,
so the window capture alone stays right for them.

* refactor(plugins): keep plugin-service and main-process-plugins under max-lines

After rebasing onto #37013 both files crossed 300 lines. The guest log sink moves into the
desktop view binding (bindPluginDesktopViews), and plugin-service tightens emitEvent and its
event-delivery imports.
2026-11-01 05:04:51 -08:00
Neil ec0d3cedd1 Plugin panels talk to their worker; panel storage, settings and agent events (#37013)
* feat(plugins): panels talk to their own worker, read storage, settings and events

- plugin.request (host API 1.2): a panel or editor asks its plugin's worker, which answers with
  orca.panels.handle(name); the worker pushes with orca.panels.post. Same plugin only, JSON
  checked, 1 MiB answers, 256 KiB/200-per-10s posts, a deadline and the panel's lifetime.
- Panels and editors get storage.*, settings.* and secrets.* behind the same capabilities, and
  storage/settings onDidChange on every surface; events.subscribe delivers workspace and agent
  events to panels that hold the read capability.
- contributes.settings: typed settings (a JSON Schema subset) with defaults and value checks.
- Frames get orca.plugin/storage/settings/secrets; components panels import plugin, storage,
  settings, secrets from 'orca', and atom(key, init, { persist: 'storage' }) shares a value with
  the worker. Workers get orca.panels/storage/settings/secrets sugar.
- One typed contract: declare module 'orca' { interface PluginRequests / PluginPanelEvents }.
- Desktop push channel per open panel session (plugins:openPanelPush).

* feat(plugins): Orca Settings shows and edits a plugin's contributes.settings

The plugin page lists declared settings with Orca's own controls (switch, picker, text and
number fields, Reset to default). Edits are checked against the declaration in main, written to
the plugin's settings, and reach open panels and the running worker through onDidChange.

* docs(plugins): hello-tsx talks to its worker; authoring guide covers panels, workers and data

The example now uses one of each: plugin.request answered by orca.panels.handle, a worker post
heard with plugin.on, a persist: 'storage' atom the worker watches, two contributes.settings and
agent.status.changed in the panel, all typed by one src/protocol.ts. The build-less TSX e2e
drives the whole loop.

* fix(plugins): typed bridge overloads, panel call budget, settings IPC and catalog keys

- PluginBridgeApi/PanelsApi use overloads: declared names are typed (and complete), any other
  name carries JSON; no never-unions for the type-aware lint.
- The per-plugin panel call budget is 120 per 10 s, since a panel loading storage atoms,
  settings and its first requests makes a burst; the hostile fixture floods past it.
- Settings IPC builds its host per call; the settings section's strings are in en.json.

* test(plugins): the example's declared settings show under the plugin with the person's value

* refactor(plugins): keep hello-tsx the minimal starter; the bridge demo is hello-worker-panel

hello-tsx goes back to a sandboxed-ui panel with no worker or capabilities. The worker, protocol,
storage atom, settings and events move to examples/plugins/hello-worker-panel, with their own
e2e (plugin-worker-panel) and PR route.

* feat(plugins): test kit covers the panel bridge and plugin data

mockHost keeps the plugin's storage, settings and secrets in memory (seeded with storage,
settings, secrets; read with host.data()) and fakes the worker with requests, so plugin.request,
storage/settings/secrets and persist: 'storage' atoms work under mount(), writes reach
onDidChange, and panel.post(event, payload) is what plugin.on hears. Typed by PluginRequests and
PluginPanelEvents.

* test(plugins): focusInGuest waits for the guest and retries focusing as one step

Under load the custom-editor guest could still be attaching, so executeJavaScript threw
'dom-ready' before the focus poll began. Wait for waitForGuestReady, then retry focusing the
webview and the element together, tolerating a guest that is still attaching or remounts.

* docs(plugins): the authoring skill covers panels, workers and data; persistence leaves the renderer

- skill-guides/orca-plugin-authoring.md gains Panels, workers and data (plugin.request/on,
  orca.panels.handle/post, persist: 'storage', contributes.settings, panel events, test kit), and
  its generated references read orca-services.d.ts for shell, files, on and the bridge types.
- After the dev-loop rebase, persisted-atom wiring (queue plus storage sync) moves to
  createDeclarativePersistence so the renderer stays under max-lines.

* feat(plugins): panels show agents: agents.list, terminal ids on agent events, workspace.changed

- agents.list (agent:read, every surface): the focused workspace's agents with terminalId,
  title, agent kind, state and stateStartedAt, from the hook server's store.
- agent.status.changed carries terminalId and title: paneKey mapped through the runtime's
  terminal list (tabId:leafId) or the status's terminal handle; looked up only while a plugin
  listens, in arrival order.
- workspace.readContext terminals carry title; workspace.changed (workspace:read) fires after
  workspace session writes when the focused workspace or its terminals change.
- Event subscriptions are kept per panel session, so one made before the push channel opens
  still counts.
- shell.openExternal never prompts on a hidden or minimized window: it settles
  { opened: false, reason: 'consent-unavailable' } and says so in the plugin log, which also
  notes 'waiting for the person to confirm' while a prompt is up; the rate budget is spent only
  when Orca acts. e2e launches never open a real browser or mail client.
- A worker may export activate as default or by name; docs, typings and the authoring skill
  document the entry, the agent events and a live agent status recipe.

* fix(plugins): the plugin page names the workspace.changed subscription

* fix(plugins): workers activate only by default export, and say so when it is named

Orca calls the worker module's default export. A module with a named `activate` and no default
now fails to start with an error naming the fix (`export default function activate(orca)`),
instead of being called by a fallback; docs, typings and the skill say default export.
hello-worker-panel gains a picker setting (suggestionOrder) the worker reads.
2026-11-01 04:40:17 -08:00
Neil b17d408416 Plugin dev loop: TS workers, hot reload with state, orca plugin dev watch, authoring skill (#36982)
* feat(plugins): build-less TypeScript workers

A worker's main may be .ts/.mts. The plugin host installs module.registerHooks scoped to the
plugin folder: .ts files are transpiled with the frames' sucrase options (lines kept, inline
source map, source maps on), relative imports fall back to the frames' candidate files, and a
value import of the type-only 'orca/worker' fails with a clear error. Needs engines.orca
>=1.4.246.

* feat(plugins): components panels hot reload with their state

- In dev folders a save swaps only the hidden guest: keyed memory atoms are handed to the new
  guest (ui.retain → ui.hydrate memory), dropped when an atom's version changes.
- A save that fails to load keeps the last good render and shows an inline file:line:col
  error banner; the next good save clears it. A subtle Reloaded mark follows a reload.
- Transpile and import errors carry line and column; dev worker restarts keep storage.

* feat(plugins): orca plugin dev watch

One command for the inner loop: refreshes .orca/types, dev-loads the folder, follows its frame
and worker logs (plugins.getLogs gains an optional cursor), prints reloads (optional
devChangeCount on list entries) and status changes, and unloads on Ctrl-C. --json streams
NDJSON events for agents.

* feat(skills): orca-plugin-authoring bundled skill guide

A guide for agents to build Orca plugins end to end (decision table, build-less rules, the
dev watch loop, test/check, validate, release-entry, approvals, gotchas) with references
generated from the component catalog, host API table, event schemas, manifest schema and CLI
specs, checked for drift. Fixes the components template's Reset to set(count, 0).

* feat(plugins): orca plugin dev watch --check; test kit details in the authoring skill

- --check runs orca plugin check after the load and after each reload (one at a time; saves
  made meanwhile fold into one rerun) and prints each panel's render or errors inline, as a
  `check` event with --json. Local folders only, since it runs the plugin's code here.
- The orca-plugin-authoring guide's Test and check section now documents orca plugin test and
  check from #36969 exactly (JSON results, exit codes, test placement, the orca/testing kit and
  its common mistakes), and the loop uses dev watch --check.

* refactor(plugins): move the components renderer's timing and helper constants to their own module
2026-11-01 03:32:25 -08:00
Neil 531658f1d1 Plugin test kit: orca/testing, orca plugin test and orca plugin check (#36969)
* feat(plugins): orca/testing kit, `orca plugin test` and `orca plugin check`

- `orca/testing` mounts a components panel in plain Node (node:test, vitest, bun) with the real
  runtime and Orca's own tree validator: find by text/role/label/testId, press/type/submit/toggle/
  select that resolve once the panel settles, tree() snapshots, errors (with stacks) and warnings,
  persisted state (with stored() for migrations), emit/setVisible/rerender, and unmount running
  setup cleanup. It re-exports node:test and strict assert so tests type-check without @types/node.
- mockHost refuses what Orca refuses with the same codes (surface, capability from the manifest,
  params, a gesture method without a press), checks configured results against the host API, and
  records calls.
- `orca plugin test [dir]` discovers *.test.ts(x) and runs them with node:test through Node module
  hooks that resolve `orca` like the guest's import map and transpile/resolve sources exactly as
  Orca's build-less loader, or with the plugin's own vitest via a generated config.
- `orca plugin check [dir]` validates, then renders each components panel once and reports what it
  threw (file:line), layouts Orca would refuse, and props it would drop.
- The kit, hooks and register entry ship minified beside orca-runtime.mjs and are unpacked from asar;
  typings land in .orca/types/orca-testing. The components template and hello-tsx get sample tests.
- ui.error carries an optional stack; a new runtime starts every atom from its initial value; the
  hydration fallback timer is cleared once hydrated.

* plugins: run plugin test/check on a system Node or Orca's own, and warn they are unsandboxed

* test(plugins): run the shipped example plugin tests from the unit suite
2026-11-01 02:42:12 -08:00
Neil f29cc1aa82 feat(plugins): turn isolated plugin frames on by default (#36821)
Flips PLUGIN_ISOLATED_FRAMES_DEFAULT. ORCA_PLUGIN_ISOLATED_FRAMES=0 or the
experimentalPluginIsolatedFrames: false setting turns it off for one machine; reverting
this commit turns it off for everyone.
2026-11-01 01:52:57 -08:00
Neil 4ee788a725 Build-less TS/TSX plugins, typings and authoring tools (#36918)
* feat(plugins): build-less TS/TSX plugins, typings and authoring tools

- Plugin frames transpile .ts/.tsx/.mts/.jsx on load (sucrase on a shared worker thread with a
  2 s budget, cached in main), resolve relative imports to exact files, and refuse bare
  package imports by name. Components and html panel/editor entries may be .ts/.tsx.
- orca plugin types writes typings for the components orca module, window.orca, worker
  activate(orca), host.call and events into .orca/types, from one host-typings generator.
- orca plugin create --template components; templates omit publisher and target 1.4.246.
- orca plugin release-entry, dev inspect and restart; validate:plugin checks named files.
- Guest console errors reach orca plugin logs; dev workers restart on edits; the dev watcher
  ignores .git, .orca and node_modules; Git and folder installs cannot replace catalog installs.
- examples/plugins/hello-tsx, docs, and the plugin-buildless-tsx e2e spec.

* ci: run the Windows parity probes with one tsgo checker

tsconfig.node peaks near 22 GB with tsgo's default four checkers (each holds its own types)
and about 16 GB with one; the 16 GB Windows runner paged the native probe past its 900 s
timeout once this branch's package.json change triggered the job. One checker gives the
same diagnostics (verified locally with --parity-only); the timing benchmark is unchanged.
2026-11-01 01:28:35 -08:00
Neil f79b478c07 feat(themes): show the theme background image through native chat (#36796)
* feat(themes): show the theme background image through native chat

* fix(themes): keep unfocused chat splits undimmed over a theme image
2026-11-01 01:19:55 -08:00
Neil b30ab35992 fix(ui): toasts sit behind a modal's backdrop until pressed, instead of looking live but dead (#36888)
* fix(ui): let toast actions work above a modal instead of looking clickable but dead

Radix modals dismiss/refocus on outside pointerdown, so the Toaster already swallowed
pointerdown inside toasts; it additionally swallowed click for every control except the
close button, making actions like 'Update skill' look live while doing nothing. Keep the
pointerdown shield (modal stays open, focus/input intact) and let the click run.

* test(e2e): keep a toast mounted and close Settings after the over-modal action

* fix(ui): default-deny toast actions above a modal, visibly disabled, with a modal-safe opt-in

Restores #32065's lock (close always works; actions that navigate or change settings
don't bypass the modal) and fixes the clickable-but-dead look: blocked controls get
aria-disabled, a 'Close the dialog to use this' title, and disabled styling, updated
live as modals open and close. Background/toast-local actions opt in via
modalSafeToastActionLabel or data-toast-modal-safe.

* fix(ui): return focus to the modal draft after a toast-opened dialog closes

A modal-safe action that opens a trigger-less dialog (Update skill) left focus on body
after close, minimize or Escape. captureDialogReturnFocus records the focused element
before opening and restores it via onCloseAutoFocus.

Also harden Dialog Escape: Radix arms a just-opened sibling dialog's Escape a render
late, so an early Escape closed the dialog beneath and dropped its draft. A dialog now
ignores Escape while a later-opened modal is open.

* fix(ui): hide toast actions above a modal instead of leaving them clickable-looking but dead

Replaces the modal-safe opt-in design. Main's close-only capture gate stays as is; a
single CSS rule hides every toast control except close while a modal overlay is open,
so the message and dismissal remain and actions return once the modal closes.

* fix(ui): type the sheet fixture's effect cleanup

* test(e2e): dismiss an older star-nag toast over the dialog instead of re-raising one id

* fix(ui): toasts sit behind a modal's backdrop until pressed or newly raised

Older toasts drop behind an open Dialog/Sheet backdrop, dimmed and inert, so nothing looks
clickable that isn't. A press on one, or a toast raised while the modal is open, brings the
stack forward; close and actions then work above the modal with its focus and draft intact.
Actions run that way are an escape hatch and emit toast_action_over_modal.

* test(e2e): read recorded telemetry without a type assertion

* test(e2e): read recorded telemetry through a typed global

* test(e2e): check the raised Later action once by mouse; keep #32065's per-pointer Dismiss as on main
2026-11-01 01:54:07 -07:00
Neil 7df246b9d1 feat(plugins): components (TSX) plugin panels (#36814) 2026-11-01 01:32:36 -07:00
Jinjing c350f18083 Update WeChat community QR code for group 14 (#36943) 2026-11-01 01:19:53 -07:00
Neil abbbdd7714 feat(plugins): isolated plugin frames, off by default (#36813) 2026-11-01 01:03:13 -07:00
Neil 82c7b0c8b2 feat(editor): choose a code editor theme independently of the app theme (#36570)
* feat(editor): choose a code editor theme independently of the app theme

* fix(editor): paint the diff overview ruler with the editor theme

* chore(i18n): add editor theme strings to the English catalog

* fix(editor): editor-theme settings search and Orca line tints in a separate editor theme

Searching 'editor theme' now opens the Interface section that holds the rows.
Diff comment-range bands and the conflict base-section tint use the editor
theme's line-number color, so they stay visible when the app's tokens don't
contrast with a differently based editor background.

* fix(editor): an editor-only theme paints the diff colors it would paint as the app theme

Main's diff tokens reach Monaco only where a theme painted them, and omitted tints
come from main.css fallbacks. An editor-only theme now reads its tokens through the
same fallbacks, so its diff colors match the ones it paints as the app theme.
2026-11-01 00:00:48 -07:00
Neil 14d29b8d24 feat(themes): background images for app themes (#36589)
An app theme may declare one validated raster image (png/jpeg/webp, <=2 MB, >=640x360, inside the plugin) served through orca-plugin-image, painted as one static body layer under translucent terminals. Older Orca ignores the field; a bad image drops only the image.
2026-10-31 19:45:04 -07:00
NeilandClaude daa11df406 feat(themes): public tokens for diffs, git status, tabs, dividers, rows, links (#36578)
* feat(themes): public tokens for diffs, git status, tabs, dividers, rows, links

Adds 29 public app theme tokens (diff, git decorations, tab bar, split
divider, current workspace row, host badge, chat bubble, text link), each
falling back to today's built-in color or CSS chain so existing themes and
built-ins render the same. Monaco's diff editor now paints from the diff
tokens, with sharper default diff tints (from #34698) and a readable dark
ignored-file color. The contrast gate learns one-sided drops, translucent
surfaces checked over their base, and main.css chains for half-set pairs.

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

* fix(themes): check omitted diff tints, chat bubbles and dark host badges as painted

The contrast gate now derives an omitted diff tint from the theme's hue (and
drops the hue when its tint hides code), checks chat bubbles against chat's
mixed foreground on the chat canvas, and models the dark host badge at 80%
accent. The main.css fallback table moves to plugin-theme-css-fallbacks.ts.

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

* fix(themes): blame a diff hue only for tints it painted, and check chat diff rows

A diff tint the theme set itself no longer drops the theme's hue when it
fails, and chat diff rows (hue-mixed text on the line tint over the chat
canvas) are now checked too.

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

* fix(themes): new theme tokens only expose colors; unthemed surfaces paint exactly as before

Every new token now falls back to the exact value its surface used before:
- Diff hues default to the git decoration colors at the previous per-side
  percentages, and chat diff text keeps its 70% mix.
- Monaco's diff view, overview ruler and links keep Monaco's stock colors
  unless the theme painted a diff or link token. The colors go through the
  derived Monaco theme instead of a global CSS override.
- --text-link has no :root default; each link rule keeps its old literal as
  the fallback, including the markdown-light/-dark preview split and the
  always-#58a6ff Monaco doc link.
- Tab bar, tab text and indicator fall back per element (bg-card,
  text-foreground, ...) instead of through :root aliases.
- Dark --git-decoration-ignored goes back to #6e6e6e (#35910 ships separately).

The contrast gate skips checks that have no theme token to drop, so themes
that set only older tokens pay no extra probes.

* perf(themes): contrast check skips ring checks the theme didn't set and precomputes drop lists

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-31 19:20:51 -07:00
Neil a2a81cbf0a fix(theme): built-in color pairs meet WCAG AA and dark-mode surfaces stay themed (#36561) 2026-10-31 17:57:30 -07:00
小七andNeil 4d775bbc92 docs(plugins): theme and syntax entries take title, not label (#36706)
The manifest schema requires `title` (and rejects `label`) on theme and
syntax contributions, so the docs examples now match it.

Fixes #36696

Co-authored-by: Neil <neil@stably.ai>
2026-10-31 17:49:47 -07:00
OrcaWin 7467a4afc1 docs: clarify low-value test removal guidance (#36768)
* docs: clarify low-value test removal guidance

* docs: remove test removal preamble
2026-10-31 14:10:06 -07:00
Neil b69a5c6c7e feat(themes): per-folder-name icons in plugin icon themes (#36565)
* feat(themes): per-folder-name icons in plugin icon themes

Icon themes could only set one generic folder and folder-open icon, so
themes like Material Icon Theme could not give src, docs or tests their
own icons. Add VS Code's folderNames and folderNamesExpanded maps. They
use the same lowercasing, duplicate checks, own-key records and limits
as fileNames, and older Orca builds skip them. Every folder icon now goes
through FolderIcon: the explorer, Source Control, diff and conflict trees,
and the remote, sparse and symlink pickers.

* fix(themes): match the common icon-theme fallback for open folders
2026-10-31 04:21:10 -07:00
Neil 55507b87e9 fix(plugins): link community plugins to the author's repository, not the mirror (#36638) 2026-10-31 01:53:12 -07:00
Jinwoo Hong 47b7af097c Add --spec-file and one keep-open name to orchestration worker commands (#36545)
worker-start and task-create take --spec-file <path> (or - for stdin). worker-start --keep-open keeps a worker open after it finishes in one step, and worker-keep-open replaces worker-retain (kept as a hidden alias). Concise text output escapes terminal control characters in worker-written values.
2026-10-31 01:23:25 -04:00
Sandeep SinghandNeil d4a14acad3 fix(updater): skip apt's _apt sandbox notice in the Linux .deb install command (#35643)
The copied .deb install command now passes
-o Binary::file::APT::Sandbox::User=root to apt. The _apt sandbox user
can't read the package in the user's private ~/.cache, so apt already
fell back to root and printed an "unsandboxed as root" notice; presetting
that fallback for the file: method removes the notice without changing
effective privilege, and network methods stay sandboxed.

Fixes #35641

Co-authored-by: Neil <neil@stably.ai>
2026-10-30 20:46:53 -07:00
Jinwoo Hong 8138232c4f Print concise orchestration CLI output for agents, with --full for the full record (#36498)
Orchestration commands print small flat fields in text and --json by default; --full prints the previous output. Failures exit non-zero with error: <code>: <message> on stderr.
2026-10-30 23:11:56 -04:00
Neil c5f3499c46 chore(scripts): reclaim historical test caches from idle worktrees without --tests-stopped (#36532) 2026-10-30 19:11:50 -07:00
Kelvin Amoaba 01e03f9920 fix(browser): present Electron's own user agent on Google sign-in pages (#36519) 2026-10-31 02:03:50 +00:00
Brennan Benson 76cf164cf9 Chat UI leaves Experimental: each agent's Chat or CLI choice decides what opens (#36110)
* Chat UI leaves Experimental: each agent's Chat | CLI choice decides launches

Remove the experimentalNativeChat switch and every gate on it. The Chat
settings page, Default Agent Chat | CLI choice, + menu Chat/CLI rows,
source-control surface choice, Antigravity chat section and orchestration
chat-error switch are always present. What a new launch opens comes only
from the per-agent choice (no saved choice opens a chat) plus the existing
support checks.

- One lookup, nativeChatPreferenceFor, keyed by agent id so host-registered
  agents read the same way; unreadable settings still open a terminal.
- A relayed desktop stamp (nativeChatDefault) maps onto the launched agent;
  an explicit surface still wins. Old phones' createSupport keeps admitting
  by the host's per-agent choice.
- settings.get stops publishing the retired switch keys; nothing in the
  repo reads them from a host.
- Startup chat sync and hydration run when some agent can open as a chat
  or the machine already holds chats.
- The retired key is stripped on load. The one-time upgrade tip from the
  chat upgrade is retired with its audience record; a new record, captured
  once from the raw profile, marks which existing profiles had Chat UI off.
- The window-side AI-button tab path, which only ran with the switch off,
  is deleted.

* Keep profiles that had Chat UI off on the terminal

Existing users who never turned Chat UI on would otherwise see new agent
launches open as chats, since an agent without a saved choice now opens as
a chat. The one-time load step that records the new audience also sets
every known agent to CLI for those profiles, in the same save and only
when the record was absent, overwriting saved chat choices the old switch
masked. Profiles that had Chat UI on and new profiles are untouched.

Browser profiles keep their own settings, so the web client does the same
once: a stored switch that is not on seeds CLI choices, and dropping the
key marks the step done.

Agents first seen after this step open as chats.

* Agents without a saved Chat | CLI choice open as their CLI

An agent with no saved choice now opens in the terminal for everyone,
including fresh installs and agents first seen later. The one-time load
step carries what launches opened while Chat UI was experimental: with
the switch on, every known agent without a saved choice is saved as
chat; with it off, chat choices the switch masked are saved as CLI.
Existing profiles join the launch tip audience whether the switch was on
or off. Browser profiles get the same carry from their saved key.

The startup chat restore and host agent list now run only when some
agent is saved as chat or the runtime holds chats.

* Preserve owned terminal launches and local chat choices

* Clarify chat and terminal support

* Explain worker CLI integration timeout budget

* Use checked annotations for chat graduation test fixtures

* Store agent surface default separately from per-agent overrides

* Preserve explicit Chat intent across default changes

* Preserve current browser settings during host replies

* Correct browser settings factory and regression types

* Name exported launch fixture mock types and clarify agent defaults

* Preserve settings fallbacks and complete launch test lifecycles

* Check allocated tab identity before launch fixture interpolation

* Register combined integration project for compiler parity probes

* Use checked settings in the launch test fixture

* Preserve retired Chat preference data privately

* Align chat E2E fixtures and capture pane lifecycle evidence

* Use typed raw settings in retention contracts

* Consolidate transcript diagnostic imports

* test: align chat fixtures with live pane and native menu contracts

* test: capture relay tab selection loss diagnostics

* test: wait for owned shells before synthetic chat fixtures

* test: preserve all-host visibility in SSH observer

* test: bind synthetic transcript fixtures before provider seeding

* test: assert selected chat owner after narrow image preview

* ci: allow fifth compiler project to complete comparison

* Type the web-terminal fixture's chat map without an assertion

* Right-click the selection itself in the chat copy spec
2026-10-30 17:43:04 -07:00
Neil 2b00c57182 fix(agents): accept launch-time --model and --effort for grok (#36480)
* fix(agents): accept launch-time --model and --effort for grok

Grok's session option catalog already maps model and reasoning effort to
-m / --reasoning-effort, but it did not opt into worker launch
preferences, so orchestration worker-start rejected --model/--effort for
grok. Opt the catalog in and list Grok in the CLI help and docs.

Fixes #36199

* test(orchestration): use an agent without launch preferences in the federation rejection test
2026-10-30 16:48:26 -07:00
Neil 34f508fb11 fix(plugins): let an enabled plugin syntax beat Orca's guessed language (#36468)
A plugin syntax only applied to files Orca showed as plain text, so plugins
could not color MATLAB .m (Objective-C), OCaml .ml/.mli (F#), Julia .jl,
JSP .jsp/.jspf (HTML stopgap), or Haskell .hs and Erlang .erl/.hrl, which
Orca maps to ids it never registers a grammar for.

Now an enabled plugin syntax wins for any file name or extension it
declares, except files Orca opens as TypeScript, JavaScript, JSON, CSS,
SCSS, Less, HTML or Markdown (their language drives completions,
diagnostics or Markdown features). JSP stays overridable. Ties between
plugins go to the lower syntax id instead of load order. The editor and
both diff views share the hook, so they agree.
2026-10-30 14:57:23 -07:00
Jinwoo Hong a1f7f97671 Orchestration v3: coordinators end their turn and Orca wakes them (#36137)
* fix(orchestration): keep a terminal's mail from being consumed by another terminal, and let settled structured workers take further tasks

A plain orchestration check consumed the named terminal's mail whatever terminal ran it, so a
coordinator checking a worker's handle took the worker's message. A consuming check from a
terminal whose own live pane is a different terminal's is now refused; --peek and --all still read
it without consuming.

worker-start --terminal and dispatch --inject routed a structured worker (structworker_ handle) as
a terminal and failed with terminal_unsupported_for_agent_session. Any agent-session party now
takes its task as a send into its session, while a structured worker keeps the pane and process
authority its first Dispatch had.

* fix(orchestration): fence check by live terminal incl. acks; defer injecting structured workers

Review fixes for the mail fence and structured worker reuse:
- The check fence compares the caller's and the mailbox's live terminal process instead of
  recorded panes, so exited terminals keep the old behaviour and two processes sharing a
  recorded pane are told apart. It also covers --ack in every read mode. The caller lookup is
  shared with the lifecycle caller fence.
- dispatch --inject to a structured worker stays refused as before: an injected Dispatch takes
  no custody, so releasing the first Dispatch could close a session working on the second.
  worker-start --terminal reuse transfers custody and stays supported.
- worker-start decides pane-less chat authority from the resolved party and passes the session
  down, so delivery and next commands no longer re-derive it from the address spelling.
- Receipts name an agent session, not a chat.

* fix(orchestration): an exited caller's env pane names no caller

The caller lookup fell back to the env pane key whenever the env handle was not live, so an
exited terminal's pane resolved to whatever terminal now occupies it, and a tmux worker's own
check was refused. The pane fallback now applies only when the env handle is not proven exited.

* fix(terminal): keep surviving terminals streaming to main after a restart

Main now attaches every live local daemon session it adopts at startup,
not only ones a window pane or remote view is showing, and re-attaches
after a daemon endpoint drop. The attach snapshot seeds main's screen,
tail and title, and an attached-but-silent agent gets an output clock so
readiness can see it has gone quiet. Waits on a survivor main could not
attach fail at once instead of timing out.

* fix(terminal): restore only pane-less streams after endpoint loss; one attach state

- Endpoint loss re-attaches in main only sessions no renderer view reports; a pane restores its own.
- The attach hide skips views mid-attach and leaves a pane's own hidden claim alone on failure.
- One per-session stream state (attaching, attached, lost) replaces three sets and is forgotten on exit.
- Waits retry the attach before failing, including after a failed re-attach.
- The output clock starts wherever a reattach result is seeded, so pane reattaches get it too.

* refactor(terminal): attach survivors at inventory adoption only; waits on unattached survivors fail at once; one ptyId output clock

* test(e2e): scope the restart attach spec's terminal listing to the unopened workspace

* fix(terminal): narrow survivor attach: waits behave as on main, one quiet clock per generation, keep main's hidden mark on failed attach

* Teach the orchestration guide when to hand off and to end the turn after dispatch

* Rewrap a messaging reference line

* Keep the original placement default and skill trigger; keep the accuracy fixes

* Point DAG retries at --retry-of and drop the last blocking-wait instruction

* Check the served orchestration guide by its new worker heading

* Apply council review: best-effort wake, full completion rule, restored safety rules, fixed scope and settled wording

* Say when the orchestration skill applies, and prefer own subagents otherwise

* Name the polling, steering, checkout, and worker fan-out habits to avoid

* fix(orchestration): wake a coordinator whose turn ended while its program still reports working

Claude Code keeps its OSC 7501 report on working while an agent-teams
teammate is alive, even after the lead's own turn ends. The mailbox gate,
idle edges and tui-idle all read that report, so worker mail was stored and
never typed. A hook turn end newer than the program's working report now
counts as rest; hook rows re-classify the PTY so the existing idle edge
delivers. Agent Teams panes now join their claude hook rows.

* fix(orchestration): compare the lead's own turn end, not the row's receipt

A child's hook restamps the row's receivedAt while the lead's turn is still
the old one, which could mark rest under a newer program working report.
Use the lead's turn-end stamp on this runtime's clock, after the latest input.

* feat(orchestration): a consuming check marks the batch it returns as handled

* Say why watch scripts and open waits block the wake

* feat(orchestration): release settled workers automatically after a grace period

* fix(orchestration): use checked row reads for delivery cursors

* fix(orchestration): trust a live own-process turn end instead of comparing clocks

Drop programActivitySince and the turn-end clock mapping: replays and SSH
host clocks made the comparison unreliable. A Claude lead counts as at rest
under a program working report when its hook turn end is a live transition
from this pane's host and launch, past the existing input and respawn floors.

* test(orchestration): drop cast in ack no-op check

* test(orchestration): seed legacy outstanding batch for held chat nudges

* test(orchestration): built CLI check hands its batch over

* test(orchestration): annotate unchanged casts and stub the keep item in tab menu tests

* fix(orchestration): let real input, explicit release and host releases converge with automatic release

- Record worker takeover at the owning runtime when it accepts UI input, before the bytes reach the PTY.
- Run remote attachment releases through the same per-resource single-flight as local ones.
- Apply the confirmed-release home projection when host observation reports a host-side release, and carry the host deadline in fleet snapshots.
- Prove restart recovery with a file-backed database reopened by a fresh runtime.

* test(orchestration): classify the keep RPC's worker fields instead of counting methods

* fix(orchestration): checked delivery reads and observer and recovery help

* refactor(orchestration): end a check when it hands over a batch; wake rides the wait result

* fix(orchestration): refuse an automatic close after real input, join remote releases, converge the home

- An automatic close checks the terminal's last driving-input time right before closing and keeps a worker someone typed into after worker_done; typing itself does no orchestration database work.
- Remote releases share the per-resource single-flight with local release, so a home worker-release joins the host timer's close.
- Host fleet snapshots and federationShow report released and the pending deadline; the home applies the existing release projection inside its observation fence.
- Real restart tests: a file-backed database reopened by a fresh runtime releases a missed deadline once and finishes interrupted requests.

* fix(orchestration): receipt a shared wake batch on arrival and report the applied Run ack

* fix(orchestration): deliver the wake to a working coordinator; agents queue mid-turn input

The wake was typed only into a live idle pane, so a Claude lead whose program
status stayed working (teammate alive) never got it. Agents queue or steer text
typed mid-turn, so a pane takes mail when live idle or working; dialogs,
permission prompts, exited agents, unsettled panes and cold-parked Enter still
hold it. Drops the held-batch Enter deferral and the per-turn-end hook logic.

* fix(orchestration): record each remote release request before joining, publish archives only through the conditional commit

- Each remote release caller records its own request before joining a close under way, and the close decides on the current row, so an explicit release that overlaps the host timer clears the automatic deadline.
- Remote capture publishes its archive through commitWorkerTerminalArchiveForRelease, so a retain during capture leaves no stale archive for worker-read.
- Remote worker-show projects the fenced host deadline and its next action.
- The input guard lives in its own module instead of the scheduler.

* feat(orchestration): start a worker in one step; worker-start creates the coordinator's Run when none is bound

worker-start --spec now creates and binds a Run for a root coordinator with
no bound Run, through the same path run-create uses, and names the Run it
created or joined in every receipt. Workers on a Dispatch keep starting
helpers in their own Run and never get a new one. A new CLI against an older
runtime runs run-create and retries; old CLIs need no change.

* fix(orchestration): drop the older-runtime fallback; check the spec before a Run is created

The CLI fallback matched a refusal text the client decorates, could not
prove the caller was a root coordinator on an older runtime, and recovered
an inner run-create under the worker-start command. Older runtimes keep
their refusal and the guide says to run-create first there. The prompt
budget check now runs before worker-start can create a Run.

* Route report-back requests to coordinators, complete the steer command, and label the wake loop branches

* Say workers are done only when worker_done confirms, wait for helpers to settle, and let bare-shell workers exit

* Route report-back requests away from handoff and steer supervised workers through their mailbox

* docs(orchestration): teach auto-handled check, retry-request and inbox recovery in the guides

* Describe the mid-turn wake in the messaging reference

* docs(orchestration): say terminal workers are released 2 minutes after worker_done

- The guide's Completion section, the reuse recipe and the recovery reference now describe the automatic release and its autoReleaseAt deadline: reuse or worker-retain before it; explicit worker-release still works and is required for chat workers and workers without a deadline.
- The worker preamble says the terminal stays available until Orca releases it, about 2 minutes after worker_done unless retained.
- Regenerated the bundled guides.

* feat(orchestration): receipts say when to end the turn

* feat(orchestration): tell the coordinator when a worker stops without worker_done

* test+docs: worker stop reports

* fix(orchestration): judge the empty-check stop with execution-host verdicts

* test: drop casts from worker stop tests

* Say that report-back requests still mean end the turn

* Say that report-back requests still mean end the turn

* fix(orchestration): stop reports wait for children, keep their Dispatch, and relay intact

* fix(orchestration): a stop belongs to the Dispatch active when it was seen; restored stamps announce nothing

* fix(orchestration): never say to stop while a remote worker runs

* Shrink the orchestration guide to the end-state workflow

* fix(orchestration): stop reports quote the agent's last words, or the screen for errors and prompts

* fix(orchestration): withhold the stop while chat failure mail is off

* test: a clean finish quotes the agent's last words

* fix(orchestration): stop asking workers for heartbeats

Workers are no longer told to send periodic heartbeats. A heartbeat that
still arrives from an older worker or peer only records last_heartbeat_at:
it is stored already handled, so it never lands in a coordinator inbox and
never wakes a coordinator or a check --wait. Removes the stale-heartbeat
warning and the heartbeat interval plumbing that only fed the preamble.

* fix(settings): remove the worker heartbeat controls

The Worker heartbeats switch and Heartbeat interval field only changed the
worker preamble text, which no longer mentions heartbeats. Saved values are
dropped on load as retired settings.

* fix(orchestration): wake a coordinator that reports program status but never sets a title

Program status only re-classified a pane that had a title, so a titleless
agent (agy) never got a live status and the wake gate refused it.

* fix(orchestration): a new Run no longer cuts a coordinator off from its earlier Run

* test(orchestration): drop heartbeat-batch cases the handled heartbeat row removed

* fix(orchestration): redact capabilities from a stop report's quoted output

* fix(orchestration): never wake a mailbox for a heartbeat

One rule at the runtime notification entry replaces per-call-site read
checks, so mutation replay and federated control-mail import stay silent
for heartbeats too.

* Shrink the orchestration references and worker preamble to the end-state workflow

* Name the older-Orca bind refusal as worker-start prints it

* Owner-fence test: a refused foreign ack changes nothing, whatever the worker's own check already handled

* Integration: drop the duplicate consumingCheck after merging #34043 and #35293

* Integration: await the worker-start next receipt before naming its Run

* Apply review round 1: drain to zero, release without auto-release, retry after stop, Run before a DAG

* Apply review round 2: start ready DAG Tasks, block when no failure wake, replay sends by request ID

* Apply review round 3: one receipt-based wait rule for starts and wakes

* fix(orchestration): a worker stopped without reporting needs action, and stop keeps its output

worker-list projected a stop-reported attempt as live, in_progress, no action. It now
projects finished_unverified with a worker-read recovery until mail reaches the worker.
worker-stop closed the terminal before anything archived it, so release reported the
archive unavailable; stop now archives the output first.

* Revert "Owner-fence test: a refused foreign ack changes nothing, whatever the worker's own check already handled"

This reverts commit 336ff91737.

* Revert "Merge #34043 (terminal mail guard, chat worker reuse) into integration"

This reverts commit 9c01b1a06e, reversing
changes made to d4b0c6c5f7.

* Revert "Merge #33720 (restart: attach surviving terminals) into integration"

This reverts commit d4b0c6c5f7, reversing
changes made to 53f9735bfe.

* Integration: restore #35293's check-methods after removing #34043

* fix(orchestration): bound the pre-stop archive and keep it through retain

* fix(orchestration): a failed pre-stop archive never blocks the stop

* fix(orchestration): answering a question says to end the turn while the worker runs

* fix(orchestration): a settled worker whose terminal moved owes nothing

* Cut the orchestration guide to what Orca does not already enforce or explain

* Drop the terminal-send recovery the CLI already prints

* Restore a short never-do block: no polling, no terminal steering, no hand-made checkouts

* Trigger on named-agent review requests, define failure in specs, show the reuse commands

* Restore six guide rules from the cut review and drop the preamble heartbeat block

* Integration: drop the removed heartbeat interval from preamble tests

* Drop the outside-Orca wait and run-use lines; Orca's errors teach both

* Release finished chat workers like terminal workers unless the user touched them

A finished chat worker now closes ~2 min after its accepted worker_done, like a
terminal worker. Clients report a touch when a chat is on screen at rest (opened
idle, or its turn ends on screen); the owning host keeps a finished worker's
chat on that report, until its automatic release commits the archive and closes
in the same tick. Chat workers arm only while every connected client advertises
the touch capability; old clients keep today's manual release.

* Decide an automatic chat close in the session lane, recheck clients there, re-touch after reconnect

The automatic close now asks the database in the chat's session lane right before
Stop: a touch until then keeps the chat, and a client that cannot report touches
(joined during the grace or after a restart) sends it back to manual release.
Touch reports wait for a read chat, so a reconnect re-reads and reports again.

* Keep explicit chat releases eager and re-touch a desktop chat after lost contact

* Resolve a chat touch by durable worker identity, so a closing chat's hidden tab stays touchable

* Keep the mobile touch report free of route and app-state hooks: shown counts as seen

* List workerChatTouch's identity fields and the close's optional admission in their census tests

* Release a settled remote worker from the home when its host predates automatic release

* Replace Keep Worker Open with an auto-close setting, and tell agents when an older server needs a Run

The coordinator's runtime decides whether finished workers close: it gates local arming, the
settlement ack's autoRelease opt-in to a remote host, and the home deadline for older hosts.
worker-start and run-create name an older server's one-Run-per-coordinator behaviour instead
of failing or rebinding silently.

* Escalate a parent's prompt or error even while its helper is open

* Report a chat touch when a worker chat is opened while working, and on each turn edge

* Record worker takeover when the host admits a person's chat send, so mobile sends keep the worker

* End a reported worker stop when the worker resumes, not only when Dispatch mail arrives

* Recheck a chat mail notice at admission so mail a check consumed starts no turn

* Type the resumed-turn stop test's pane move directly

* Keep the stop decision in an empty check, report muted chat failures as stops, and bind a new Run only once a start passes preflight

A start or reply receipt now sends the caller to drain its mail with check instead of ending its turn, so a mid-batch answer cannot skip a later report and a replayed receipt carries no stale stop. With chat error mail off, a failed chat request still reports a plain stop through the existing stop report path. worker-start creates and binds its Run just before the Task and Dispatch, so a start refused in preflight leaves no Run behind. worker-release help no longer says chats never auto-release.

* Reuse a Run a parallel first start bound, refuse dependencies before creating a Run, and relay a paired-server worker exit home as a stop report

* Read a reported worker stop against live agent status instead of recording resume observations

A stop report is mail the coordinator reads once. Whether a worker shows as
finished now comes from its live status: a fresh working agent status (local
pane, chat session, or the execution host's fleet snapshot) outranks the
report, so nothing needs clearing on resume.

* Drop agent steps Orca already covers: start receipts say Orca wakes you, a check asks for another only when a capped batch leaves mail, workers get no mail-check or ack lines, and chat errors always report with the exact error

* Keep the home's remote auto-release deadline until the host answers, and let a retain win at the send

Claiming a due deadline postpones it instead of moving it to a releasing state, so a restart or
a failed attempt runs it again; the release send rechecks the deadline with no await before it.
A host that cannot be asked at settlement still gets the deadline, and a host upgraded since
settlement is released by the home, since it never armed its own.

* Close a finished worker automatically only if the user never opened it

Seeing a worker's terminal or chat now writes the user's Keep record (owned,
retained) on its host, at any point before its close is admitted, so a worker
seen while it worked is never armed and a pending automatic close is cancelled;
the coordinator can still release, stop or re-engage it. Desktop reports from
the on-screen attention scan for every on-screen terminal pane and chat once a
present user is on that workspace; mobile reports the tab a session opens on
and each tab switch. The weaker chat-only touch hooks are removed. A chat send
admitted after the automatic close committed its archive now keeps the chat.

* Show only live facts for a worker that reported a stop

A stop report is escalation mail that wakes the coordinator once. worker-list
and worker-show no longer project it as finished_unverified with a recovery
action, so the live-working override that cleared that label goes too.

* Report a paired-server worker's process exit home even while its ask or check --wait is still registered

* Report a seen worker until its host records the keep, including chats shown before custody and mobile auto-selections

The touch reply now says pending while a structured worker's custody row is
not yet recorded; desktop and mobile keep a subject unreported after a
failed or pending reply and retry on the next scan, snapshot or reconnect.
Mobile reports whichever terminal or chat is displayed, tapped or selected
by a snapshot, instead of only the first per workspace.

* Record a person's chat send or command takeover inside the session lane

Takeover moves from the RPC wrappers into the host's in-lane acceptance
of userSend sends, /compact and /clear, so a close queued behind an
accepted send cannot admit before the keep, including during /compact's
handover wait. Host orchestration sends never set userSend.

* Say which stop reports a deliberate wait suppresses

* Align merged tests with the shorter preamble and the fixed start hint

* Retry an unrecorded mobile worker touch when the phone reconnects

* Drop the removed worker check line from the CLI boundary test

* fix(orchestration): wake a coordinator that survived an app restart

After a restart, main typed the mail wake only into an agent whose status it
had seen live since starting, so a coordinator whose turn had already ended
never woke. At reattach, take the agent's own last hook report, which the hook
server restores from disk and from its offline spool, as that observation while
an agent still holds the surviving process.

* fix(orchestration): restore the lead's own turn, hold a restored draft, fence respawns

Read the lead agent's turn from the restored report so background work does
not hide an ended turn, keep the wake out of a composer that still holds text
typed before the restart, and drop a restore that a respawn has overtaken.

* fix(orchestration): restore a reattached status only from a current screen without a draft

The whole-screen accessor hides a model the provider still owns after attach, so a
draft there read as none. Read the screen the way wait gates do, provider image
included, and decline the restoration when it is unavailable.

* fix(orchestration): restore a reattached status without reading the screen

After a restart main has no record of typing, so the existing draft hold cannot
apply; a screen read here could miss a draft and, when slow, strand the
coordinator for good. A draft left before the restart now meets the same rule
as an abandoned one.

* fix(orchestration): let a slow foreground probe still restore a reattached status

A probe answering after the interactive two-second bound was dropped for good,
stranding the coordinator. Restoration has no caller waiting, so it takes the
real answer; the existing fences still guard application.

* test(orchestration): mark the slow foreground probe as fake-clock driven

* Report a terminal worker whose agent died while its shell lives

A killed agent sends no stop hook and its shell keeps the PTY open, so its
Dispatch stayed open and the coordinator was never woken. The shell's
command-finished already retires the PTY's launched-agent authority; the
panes it retires now go to the worker stop reporter as an 'exited' stop
(same mail and wake as a PTY exit, once per Dispatch, none once settled).

* refactor(orchestration): key mail, waits and settle checks by terminal

Reapplies #34056 on the integrated orchestration work: mail delivery, the
settle recheck and terminal waits are keyed by ptyId instead of the window
pane, deleting the pane-keyed leaf lane and the headless-only PTY lane.

Keeps #34706's delivery rule (a terminal taking input, idle or mid-turn,
gets the wake; dialogs, permission prompts and unsent drafts hold it; the
cold-park fence still defers a parked Enter) and #35274's multi-Run
coordinator mailboxes.

* fix(orchestration): keep best-effort delivery's catch; stage the remount e2e

deliverForHandle keeps the integration branch's catch: ownership resolution
still reads SQLite, and a notification queued before teardown must not throw.

The remount e2e now asserts each stage on its own (mid-turn mail before the
workspace opens, the wait across the remount, mail after it), since #34706
types mail mid-turn and the old single mail check could pass before the
remount happened.

* refactor(orchestration): settle a new terminal wait from its first reading

A wait now keeps the PTY record it resolved, and nothing runs between the
first evaluation and the waiter's registration, so the second evaluation
re-read the same evidence. Starts the poll from the first verdict instead.

Deletes the leaf wait-result builders the leaf lane left without callers,
and adds a unit test that a tui-idle wait survives its pane remounting
(the e2e cannot order the wait's registration before the remount).

* Report a dead worker agent only once its shell is proven alone

A command-finished marker alone does not prove the launched agent exited: a
nested interactive shell in a live worker emits the same marker. The exit
report now waits for the execution host's shell-alone proof and is fenced to
the Dispatch and launch seen at the marker, so a relaunch in the pane is not
reported by an older completion. It no longer hangs on launch-authority
retirement, so a nested completion that retires metadata does not stop a later
real death from being reported. The agent-exited fact feeds the same path for
shells without OSC 133.

The shell-alone report and a paired-server worker's PTY exit share one exit
entry point and key, so the Run home gets one report per process.

* fix(orchestration): resolve a terminal's mailbox across every pane that shows it

Two panes can show one PTY; ownership read only the first listed pane, so a
Run bound to the other was never woken. The mailbox terminal now carries
every pane alias and ownership, discovery and the delayed-Enter recheck
consult them all.

* Report a killed worker agent from its launch's exit receipt

The launch command now ends with a per-shell marker the shell prints once
the agent returns, however it ended. The execution host scans it before
thinning (main and daemon), checks it against the PTY's current launch and
incarnation, and reports through reportWorkerExit. A nested shell has no
receipt and a relaunch names a new one, so the shell-alone inference is gone.
A later local PTY exit reuses the same exit message id, so it settles the
Dispatch without a second report.

* Revert "Revert "Merge #33720 (restart: attach surviving terminals) into integration""

Reapplies survivor attach (72b3b79fd3, fe906ea2a2, f46777ba18) so the
#33720 merge that follows carries it; integration reverted it in 2f945fa715.
The output clock now feeds v3's terminal-keyed wait path; the leaf lane it
also fed is gone in v3.

* test(orchestration): give exit-path DB stubs the remote-attachment lookup

The paired-server exit relay now asks the Orchestration DB for an active remote
attachment on every local pty exit; partial stubs without it threw.

* Report a worker killed after a restart from its Dispatch's launch commitment

The exit receipt ID is now derived from the launch-token hash a Dispatch
persists, so a PTY reattached after restart (no launch token in memory)
accepts its receipt when the pane's active Dispatch committed that launch
in the same shell process.

* test(orchestration): stub main's session-dispatch presentation read in the stale-leaf DB

Main's projection scope now reads listSessionDispatchesForPresentation on every
projection; v3's hand-rolled stub predates it.

* test(orchestration): drop the stale-leaf stub key integration's merge now supplies

* test(orchestration): align main's new tests with integration's mid-turn mail, admitted close and nameless worktrees

Main's titleless-turn test assumed mail waits for Stop; v3 types it into a working agent.
Main dropped the --name requirement, so that preflight case now fails on the missing repo.
The evict close now forwards the session lane's admitClose.

* perf(orchestration): read the next federated release deadline through its partial index

The armed-state filter alone cannot use the non-null partial index, so every rearm walked retained federation history.

* perf(terminal): keep unviewed restart survivors to the daemon's live window

Main now attaches every surviving terminal at startup; each mirror took the full
5,000-row depth with no viewer. A survivor nobody views keeps the 1,000 rows the
daemon itself holds, and grows to full depth from those rows once a view subscribes.

* chore(orchestration): clear check:changed findings on the main merge

Format a listing statement, prune the flaky-pattern baseline, and justify or remove the
test casts the changed-code gate flags.

* Revert the unviewed-survivor mirror cap: it can lose desktop history after a restart

A capped survivor mirror is still served as complete history to desktop recovery, and a desktop pane
added after inventory never lifts the cap. Restart memory for unviewed survivors moves to a follow-up
that defers the screen model instead of truncating it.

This reverts commit 4ea934ca7f.

* Key main's new null-agent idle-wait test by terminal, as integration's waits are

* fix(orchestration): type mid-turn mail only into a recognized agent

An unrecognized terminal's working verdict may be a running program reading stdin,
so null-agent panes keep waiting for quiet before the pointer is typed.

* Remove killed-worker exit receipts from orchestration v3

The launch-bound receipt added a visible printf to every agent launch line. Owner decision: ship v3
without killed-worker detection (behaves as main does today) and revisit it later, e.g. by watching the
agent's hook-reported process.

This reverts merge b9a30b6e74.

* Stop re-arming a delivery recheck once the quiet window has passed

A refusal that outlives the quiet window (an unrecognized terminal still reading as working) is not
time-bound, so a 50 ms recheck spun forever. The next output, title or mail edge rechecks it.

* Keep retrying deliverable mail past the quiet window; gate mid-turn delivery on terminal agent identity

A refusal outliving the quiet window can wait on a hook final-frame deadline or a late foreground
recognition, neither of which emits a delivery edge, so poll slowly while that terminal still has
deliverable mail and stop once none is pending. Mid-turn delivery now asks whether the terminal agent
is recognized at all, not whether it has readiness rules, so a recognized reusable agent keeps it.

* Keep structured-agent-session.ts within the 300-line cap

* fix(orchestration): arm delivery rechecks only for actionable delivery work

The recheck predicate now counts persisted pointer reservations awaiting
recovery as well as selectable new mail, and gates every recheck (quiet
window, post-quiet, and holds) on it, using the canonical output clock.

* Check the served orchestration guide by a heading the v3 guide keeps

* test(e2e): launch the mail-pane fake under its agent CLI's name

v3 types mid-turn mail only into a recognized terminal agent, and a title alone no longer
recognizes one, so the fake runs as `node …/codex` (or cursor-agent) like an npm shim.
The hold-while-working case now models the unrecognized program that contract still holds for.

* test(e2e): align idle mail delivery spec with v3 auto-ack, mid-turn Enter and multi-Run coordinators

A consuming check now marks its batch handled (8fbb395a32), so the live
waiter's mail is pulled once returned and the held-batch case is seeded
as an older Orca leaves it. Agents queue mid-turn input (e0cb9a68d2), so
the held reminder's Enter is no longer withheld. A coordinator keeps its
earlier Run (#35274), so Run A direct mail is pointed with --run A.

* Wake a Run's coordinator through its pane when the Run's stored terminal handle belongs to the pane's previous PTY

* Make check --ack acknowledge only, so it never takes new mail

A check carrying --ack acknowledged that Delivery and then took (and,
since checks now acknowledge what they show, consumed) the next batch.
A caller that discarded the output lost that mail. --ack now returns
after acknowledging; newer mail stays unread, so the wake path still
fires and the next plain check shows it. The receipt says --ack is not
needed. Removes the ack-then-take receipts this made dead.

* Print every message body and payload in a plain orchestration check, since current hosts acknowledge the batch on read

* Keep acknowledge-then-take for check --ack --wait, and reject --ack on direct mail

A waiting caller reads what the wait takes, so --ack --wait acknowledges
and then waits for and takes the next batch, as before; --ack alone
stays acknowledge-only. Direct mail has no Delivery, so --ack there now
fails as a foreign delivery instead of consuming the mail.

* Keep a user-kept worker terminal kept when a new Dispatch reuses it, since its on-screen pane reports only once

* Tell a coordinator to check a remote worker itself when its host cannot report turn stops

* Find a Run's pane terminal for mail without re-issuing its handle mid-reload

* Keep the original ack-then-take path for check --ack --wait

Composing an ack with a second check lost the acknowledged consumer
generation and persisted subcall receipts. --ack --wait now runs the
original single-generation path unchanged; only --ack without --wait
returns after acknowledging, and direct mail still rejects --ack.

* Settle the e2e agent idle before newer mail so the mid-turn pointer cannot race the idle title

* Record a user's view of a worker terminal as user_seen so only it, not a coordinator's retain, survives reuse by a new Dispatch

* Let a worker ask, end its turn, and be woken with the answer

* Drop casts from the ask tests and the pending-question query

* Tell agents to run a plain check after acknowledging a held Delivery

--ack without --wait no longer takes newer mail, so the held-delivery
pointer now names the follow-up check for the new messages.

* Deliver Run and Dispatch mail to the pane's current PTY, issuing its handle only when none exists

* Wait for a PTY's exported handle before mail, and wake it when that handle is adopted

* Keep a cancelled ask from taking its answer, scope the asked-question stop mute to the asking turn, and end a waiting ask when its Dispatch settles

* Order the asked-question stop mute by message sequence, not same-second timestamps

* Wake a PTY's deferred mail whenever it gets a handle, by any path

* Make the headless Dispatch adoption test hold in both layout modes

* Mock the finished-worker strip states with neutral pane and chat notice fixtures

* Mock the temporary-worker strip with the owner's copy and the Allow auto-close tab menu item

* Mock chat workers with a goal-style composer strip instead of the composer notice

* Keep a worker open only by Keep open; hold its countdown while its agent works again

* Remove viewing and typing as implicit keeps on desktop and mobile

* Update the routing specs to acknowledge a held Delivery, then take with a plain check

* Show the temporary-worker strip with Keep open and its countdown, and Allow Auto-Close in tab menus

* Share the worker auto-close poll and key its read to the subject

* Re-read kept workers often so Allow Auto-Close shows its countdown promptly

* Hold auto-close through every turn, refuse stopping kept workers before anything changes, and read worker state only for known workers

* Keep a claimed close final, check the agent before claiming, refuse remote stops without a Keep open answer, and keep re-reading known workers

* Classify workerChatTouch caller fields and drop a test-only cast
2026-10-30 14:20:02 -04:00
Kelvin Amoaba dcaeaaa1fc feat(browser): typed snapshot flags, screenshots saved as files, and a guide that keeps browser work in Orca (#36257)
* feat(browser): typed snapshot flags and screenshots saved as files for agents

orca snapshot takes --interactive, --compact, --depth, --selector, --urls,
--delta and --full, passed to agent-browser on every route. The header
now shows the page title and URL. orca screenshot and full-screenshot save
the image where the CLI runs and print its path, in CSS pixels, with
--if-changed and --annotate. Flag help for exec, wait and download is
written for those commands, and the orca-cli guide sends web pages to
Orca's browser unless the user picked another tool.

* fix(browser): scale screenshots by the scrollbar-free axis and keep snapshot metadata behind SSH admission

* fix(browser): upscale low-DPR screenshots and wrap a torn-down tab's screenshot error

* fix(browser): give each screenshot its own file and keep typed screenshot errors

* refactor(browser): share the agent-browser screenshot reader between Electron and orcad

* refactor(browser): move snapshot and screenshot params under the line cap
2026-10-30 15:07:05 +00:00
Jinjing 9323738978 Revert "Allow conflict-safe private storage from plugin panels (#36273)" (#36316)
This reverts commit 5b505a6e94.
2026-10-30 02:24:24 -07:00
Neil e9be993f47 cli: add orca plugin dev load/unload/reload, list, logs, enable and disable (#36288)
* cli: add orca plugin dev load/unload/reload, list, logs, enable and disable

* cli: move plugin handler groups out of the manifest
2026-10-30 01:28:26 -07:00
Jinjing 5b505a6e94 Allow conflict-safe private storage from plugin panels (#36273)
* Support conflict-safe private storage in plugin panels

* Update frame and worker tests for host API 1.1

* Use required braces in storage conflict guard

* Update renderer handshake expectation for panel storage API

* Clarify null comparisons and exclude inherited storage keys
2026-10-30 01:02:21 -07:00
Neil f7ff1ee2ff docs(plugins): drop the donation-link and description-URL listing rule (#36291) 2026-10-30 01:00:34 -07:00
Neil 1963ac0ea8 cli: add orca plugin create, validate and inspect (#36280)
* cli: add orca plugin create, validate and inspect

* build: ship orca plugin validate's main checks as a CLI main entry
2026-10-30 01:00:15 -07:00
Neil 8e61c440df plugins: derive the publisher from where a plugin comes from, like a Go module path (#36281)
The manifest publisher is deprecated and ignored for catalog and Git installs:
catalog installs use the listing id, Git URL installs the URL owner, folders
'local' (or their still-declared publisher). Existing installs keep their key.
A format 2 manifest may omit publisher only with engines.orca >=1.4.246, so
older Orca reports 'requires Orca X' instead of failing to parse it. The v2
catalog documents an optional ownerId, which older Orca already strips.
2026-10-30 00:45:00 -07:00
Neil f78b5979ca docs(plugins): add plugin listing policy (#36275)
* docs(plugins): add plugin listing policy

* docs(plugins): include GitLab owners in publisher rule
2026-10-30 00:22:57 -07:00
Neil d6caa43719 feat(telemetry): drop the plugin_uninstalled event; install counts are cumulative (#36214) 2026-10-29 21:09:35 -07:00
Brennan Benson ca2148a30d Settings: choose Chat or CLI in each agent's row; remove Chat → Providers (#36185)
* feat(settings): choose chat or CLI per agent in the Agents list

Each agent's open-as-chat choice lived in two places: Settings > Chat >
Providers and a Chat | CLI control under Default Agent. The choice now
lives only in each chat agent's row of Settings > Agents > Installed,
writing the same per-agent setting. Agents whose chat runs without their
CLI (a downloaded chat runtime) are listed as installed with CLI
unavailable, and the downloaded runtime and chat sign-in move into the
agent's row. Search and the install card's hint point at Agents. Shown
only while Chat UI is on, as before.

* Preserve agent choices and setup across inventory changes

* Fix settings regression test type conformity
2026-10-29 20:07:00 -07:00
Neil a892fca5a1 Plugins settings: one list with Installed badge, inline switch, install-state filter, category chips and trusted-code access copy (#36100)
* Plugins settings: one list with an Installed badge, inline switch and install-state filter

* test(plugins): drive card enablement through the inline switch

* plugins: keep the installed-read error on Not installed; cover installed cards past the cap

* plugins: inline category filter chips with icons from one category-icon module

* plugins: single-select category chips, marketing icon table, Remote/VMs label, trusted-code access copy

* plugins: Not installed waits for the installed-plugin read

* ci: retrigger
2026-10-29 17:34:26 -07:00