Commit Graph
4363 Commits
Author SHA1 Message Date
Brennan Benson 42bdeffbe4 Move chat readers to the newest saved step after a no-op save (#37118)
* fix(chat): move forward readers to the covered tip after a no-op save

A write whose fold changes nothing stores no outcome record, so its
sequence never appears in a forward read. Readers placed themselves at
the last returned row and stayed behind the live cursor, so open chats
re-read an empty page and recomputed the prompt projection on every
later delivery until the next real save.

Forward journal reads now report the highest sequence they covered.
History pages move there when the read was complete, prompt reuse and
native responses compare against it, and a read cut short by its limit,
byte budget or damage still stops at the last returned row.

* fix(chat): append unlimited outcome reads without spreading, and report coverage from the mobile journal fake

* fix(chat): skip the retention scan for batches that change no journal items
2026-11-01 12:48:36 -08:00
Kelvin Amoaba 78484aac31 feat(mobile): one next-step bar under both Source Control tabs (#37104)
* feat(mobile): one next-step bar under both Source Control tabs

Changes and Pull request are the only segments. A bar at the foot of both
shows the branch's one next action: the commit box, push or pull, create
the review, resolve or fix with the workspace's agent, merge when ready,
merge, or why it can't. Commits opens as a pushed screen.

* fix(mobile): keep Merge reachable and show bar failures on both tabs

* test(mobile): expect the two Source Control segments in the page render test
2026-11-01 20:17:11 +00:00
Brennan BensonandClaude Opus 5.5 ad864a6ec7 Show a card in the chat when its agent hands work to another agent (#36848)
* Show a card in the chat when its agent hands work to another agent

An agent that hands work off with 'orca agent launch' from a native chat
now leaves a 'Handed off to agent' card there, linking to the new chat or
terminal, the way a supervised worker-start leaves 'Started worker'.

The CLI names its chat on agent launches (same-host only); the host records
a display-only agent-handoff item after a successful launch with a submitted
prompt, once per launch operation. Nothing about the card can refuse or delay
the launch.

Tracked in STA-13209.

* Keep orca commands working in a native chat that has no terminal handle

The launch-caller evidence read the host stamp off an absent evidence object, so every orca command from an ordinary native chat threw before sending. Adds CLI tests for which requests name the launching chat, restores the lockfile to the base, and fixes the Korean heading.

* Read a card's heading from its creation flags together

A caller passing only the started flag drew a handoff as Started worker; the heading now takes the message's flags as one argument.

* Show the handoff card for every agent a chat launches

A launch without a delivered task now records the card too, with no task text, as the common pattern records every agent a chat creates. The handoff tests now run the real chat host and journal: one card per launch operation, none on replay, a removed card stays removed across a restart, and the clock is fixed.

* Head a launch card Started agent when no task reached the new agent

Handed off to agent now appears only when the card carries a task; a card for an agent started with no task (no prompt, a draft, or a prompt that wasn't delivered) reads Started agent. The heading is derived from the card's text, so nothing new is saved. Also drops a self-launch check the launch path already refuses first.

* Show the handoff headings on the phone's agent cards

The phone card passed only the started flag, so with the new heading rules it would draw every creation card as a plain message. It now passes the message and has a heading for every case, so a missing one fails the build. The heading function now requires the message's creation facts, so no caller can leave them out. Adds the phone strings in every language, and the Japanese "Started agent" now uses the catalog's term for agent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-01 12:08:34 -08:00
Jinjing c24d916f31 Route hosted review operations to the owning host (#36003)
* Route hosted review refreshes to their owning host

* Route remote review operations to their owning host

- Refresh the correct hosted review after remote git operations
- Scope comment mutations and AI acknowledgements to the owner cache

* Route legacy paired SSH reviews through their server owner

- Recover proxied runtime owners for older SSH worktree listings
- Preserve direct and ambiguous host routing safeguards

* Preserve review ownership across remote workspace operations

- Route checks, comments, and linked-review clears through the selected repository owner
- Reject ambiguous or mismatched host context to prevent cross-machine mutations

* Keep check detail tabs scoped to their remote owner

* Preserve remote worktree ownership across reconstructed check tabs

* Preserve direct SSH check tabs when proxy listings overlap

* Scope PR refresh ownership state and safely roll back comment mutations

- Preserve linked-review clear scopes beyond cache eviction and clean them up on expiry
- Handle failed or refused comment reactions and thread resolutions without stale optimistic UI

* Preserve remote review ownership when SSH copies overlap

* Fix review routing for overlapping remote repository hosts

- Preserve server-owned review refreshes when a direct SSH copy exists
- Fail comment mutations and refetches safely when ownership becomes invalid
- Resolve check and sidebar data through the workspace’s owning host

* Relax cache-sensitive test expectations

* Route check refreshes to runtime owners and isolate restored requests

* Scope reconstructed check detail tabs by execution owner

* Fix review refresh ownership for shared remote hosts

- Prefer explicit execution owners when matching worktrees.
- Avoid repeated tab scans when restoring check details.

* Fix hosted review polling for workspaces on multiple hosts

* test: trim redundant review ownership scenarios
2026-11-01 12:57:45 -07: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
Jinwoo Hong 225db8cfb2 Hide no-action attention in concise worker views; say kept-open workers stay open (#36963) 2026-11-01 13:12:20 -05:00
Kelvin Amoaba 9ecf57545e feat(github): let mobile turn a pull request back into a draft (#37000)
Adds github.markPRDraft (gh pr ready --undo) on the host, allowlisted for
paired phones and advertised with mobile.github.markPRDraft.v1 so older
hosts simply hide the action.
2026-11-01 17:51:43 +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
Kelvin Amoaba d95e2324dc fix(notifications): announce finished only when the agent is fully done (#36992) 2026-11-01 16:03:29 +00:00
Kelvin Amoaba a83115ffd8 fix(native-chat): keep Claude's reply visible when a Stop hook sends it back (#36944)
* fix(native-chat): keep a Claude reply visible when a Stop hook sends Claude back

Claude writes a blocking Stop hook's reason as an injected user turn, which
both Claude decoders dropped. The settled-turn fold then took the post-hook
continuation as the turn's only answer and folded the real reply away.

Both decoders now turn that feedback into a 'Stop hook feedback' row, and
the fold keeps the prose before each such row as an answer too.

* fix(native-chat): keep the Stop hook row when catching up a Claude chat transcript

* refactor(native-chat): one row table for hook outcomes

* fix(native-chat): import journal types from their new home

* fix(native-chat): import moved translation and status-block modules from their new homes
2026-11-01 15:51:47 +00:00
Kelvin Amoaba ba0ebfa010 fix(native-chat): show images agents read, and markdown images on mobile (#36953)
A Claude Read of a PNG returns only base64; the path is on the call. A shared
rule turns each finished file-read of a raster image into an image-ref under
the tool run header on desktop and mobile. Mobile chat prose now draws
https, data and absolute-path markdown images inline, every image in a
paragraph, through the existing host image read.
2026-11-01 15:50:29 +00:00
Kelvin Amoaba 0d724378df feat(notifications): separate agent needs-input alerts from finished alerts (#36941) 2026-11-01 15:46:46 +00: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 b29e7f5316 refactor: group Claude structured session owners (#37036) 2026-11-01 06:33:21 -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
OrcaWinandOrcaWin 3a983d06fd test: remove 278 low-value declarations and 31 suites (#37007)
* test: remove 278 low-value declarations and 31 suites

* test: prune timeout baseline after test removal

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-01 12:37:19 +00:00
Kelvin Amoaba 0dcc811e07 fix(notifications): leave dispatched workers' finished turns to their coordinator (#36968) 2026-11-01 12:09:15 +00:00
OrcaWinandOrcaWin ed3e82a2e7 test: remove 699 low-value test declarations and 116 suites (#37002)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-01 11:34:06 +00: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 89f9ce08de refactor(shared): organize session contracts and projections (#36979)
* refactor(shared): organize session contracts and projections

* chore: tighten dropped-path flaky pattern allowance

* docs: preserve current agent instructions with valid formatting
2026-11-01 03:14:46 -08:00
OrcaWin 90a5374349 test: remove 205 low-value test declarations, including 30 suites (#36975)
* test: remove 205 low-value test declarations, including 30 suites

* test: update timeout-pattern baseline after removals

* docs: fix heading spacing required by formatting check
2026-11-01 10:51:42 +00: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
Kelvin Amoaba 4e2fcfe99c fix(mobile): send iPhone library photos as JPEG named .jpg, not HEIC named .png (#36989)
* fix(mobile): save iPhone library photos as JPEG named by their bytes

The iOS photo picker handed back the asset's HEIC bytes as-is, and the host
named every pasted image .png, so agents read a HEIC file as a PNG and failed.
Ask the picker for the compatible representation (JPEG), and name pasted
images by their signature so the extension always matches the bytes.

* fix(mobile): keep unknown pasted bytes on the .png name

A .heic name falls outside the shared image MIME table, so chat previews
and agent prompts would drop the image. The picker now sends JPEG, so
leave HEIC detection out.

* refactor(mobile): keep the pasted image extension list module-private
2026-11-01 10:32:11 +00:00
OrcaWinandOrcaWin 321574af87 fix(native-chat): steer async question answers instead of queuing duplicates (#36917)
* fix(native-chat): stop async question Submit from queuing duplicate answers

When the agent is mid-turn, answering a Codex async question queues the
answer as an ordinary message. The card stayed up until a transcript row
retired it, which a queued message never posts until the turn ends, so
every Submit queued another copy. Hide the occurrence once its answer is
queued, on desktop and phone.

* fix(native-chat): steer async question answers into the running turn

An answer to the agent's own mid-turn question belongs to that turn.
Queuing it behind the follow-up setting held it until the turn ended,
after the agent had already proceeded without it. Send it with the
steer intent whenever the follow-up resolver would queue a plain send,
on desktop and phone. Stop and a waiting command still queue, and the
queued fallback still hides the card.

* fix(native-chat): hold async questions while a queued answer waits

Replace the mount-scoped hide on a queued answer with one derived from
the queue: a question stays hidden while a queued card quotes it, across
remounts, and returns if that card is deleted or edited away. Lock
Submit until the send settles, and keep a returned answer out of the
composer draft.

* fix(native-chat): keep structured session under the line cap

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-01 01:43:26 -08:00
Neil 4b4c833e10 feat(window): working background opacity and blur on macOS, Windows and Linux (#36580)
* feat(window): working background opacity and blur on macOS, Windows and Linux

One window material model (opaque / translucent / blurred): macOS vibrancy without transparent, Windows 11 22H2+ acrylic, Linux transparent windows. Terminals paint their translucent background once, the transparent WebGL frame clears before drawing (#6491), and Reduce Visual Effects drops modal/backdrop blur and honors prefers-reduced-transparency.

* perf(window): drop hidden macOS vibrancy while a theme image covers the window

* refactor(settings): move Reduce Visual Effects into its own setting and search files
2026-11-01 01:41:07 -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 57454e6d7e refactor(agent-session): group lifecycle records and direct owners (#36909) 2026-11-01 01:24:37 -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
Kelvin Amoaba 715a6944ea feat(git): branch-only history pages with pushed state and paging (#36962)
git.history gains optional branchOnly and skip params. A branch-only page
lists only commits the base lacks, with per-commit stats and whether the
upstream has each one, plus ahead/behind/unpushed counts. Older clients
send neither param and get the same reply as before; older hosts ignore
them, so clients detect support from the echoed branch and skip fields.

Mobile may now call git.remoteCommitUrl to open a commit on its host.
2026-11-01 09:19:09 +00:00
OrcaWin 30242d1759 test: remove 185 low-value test cases, including 30 suites (#36916) 2026-11-01 09:04:00 +00:00
Neil a14fe3c589 Show child activity counts inside the children toggle (#36951)
* Show child activity counts beside the existing children toggle

* Keep child activity counts inside the children control

* Reuse seeded settings in the child activity preview
2026-11-01 01:02:52 -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
Neil d10ab00f3c feat(themes): Match Terminal becomes a real app theme for the whole window (#36567)
* feat(themes): make Match Terminal a real app theme painted at :root

Match Terminal used to be an inline override on three left-column
elements, so the tab bar, right sidebar, status bar, titlebar, menus and
editors kept the built-in palette, and its fixed color mixes became
unreadable on low-contrast palettes.

It is now a light/dark app theme slot value synthesized from the active
terminal palette (read through resolveTerminalPalette, so each mode's
color overrides apply) and painted through the plugin theme pipeline
(derivation, contrast gate, startup cache, Monaco theme). The document
mode follows the terminal background, including onboarding's restore.
The legacy leftSidebarAppearanceMode='match-terminal' value fills the
empty slots on load and on writes (desktop and web), and it is kept
mirrored while a slot matches the terminal so older Orca still matches
its sidebar. The pickers mark the option with a muted "(dynamic)" label.

* fix(onboarding): sync the painted-theme ref in an effect, not during render

* feat(themes): use a lightning icon for Match Terminal's dynamic label

* perf(themes): reuse the Match Terminal theme when its content is unchanged
2026-11-01 01:19:38 -07:00
Neil abbbdd7714 feat(plugins): isolated plugin frames, off by default (#36813) 2026-11-01 01:03:13 -07:00
Kelvin Amoaba 92fc8bc26d fix(accounts): restart running Claude sessions when the account is switched from the phone (#36933)
A switch over runtime RPC (the phone's Accounts screen) changed the
selection but never reached the window's running-session handling, which
only the status bar and Settings called after their own IPC switch. The
RPC path now reports the lane and outgoing/incoming account to the main
window, which runs the same handler. The web client handles its own
switches and opts out.
2026-11-01 08:00:08 +00:00
Neil 25a8418018 feat(skills): silently update unedited Orca skills per skill; prompt only for the rest (#36889)
* feat(skills): auto-update unedited Orca skills; ask only for edited copies

Planner already hashes each installed SKILL.md against every shipped release
digest (legacy installs included). When a plan's only writes replace such
hash-verified copies, the freshness nudge now applies it silently. Edited
copies and repairs still go through the consent toast; edited copies now raise
it too instead of only showing attention in settings.

* feat(skills): decide silent skill updates per skill and per location

Add skills.autoUpdateBundledOrca (capability-gated): the host plans the named
installed skills under their locks and applies only hash-verified released-copy
updates, re-checking each location before writing. The nudge auto-updates each
unedited skill and names only edited, unknown or repair-needing skills; the
try-once guard is keyed per skill and observed state, so one failure falls back
to the toast for that skill alone.

* fix(skills): surface preserved edits, share run ownership, re-show retracted nudges

- A silent update that moved a racing edit to a backup now shows a warning
  naming the skill and backup path instead of passing as success.
- Silent updates run under skill-update-run-store's per-target ownership, so
  manual and automatic runs never interleave and the dialog shows busy.
- Programmatic toast retraction releases its session marker; only a user
  dismissal suppresses the nudge.

* fix(skills): report stranded edits, isolate silent runs, bound attempt memory

- An old copy whose late edits cannot be backed up now fails the op with
  strandedPath naming where the edits remain; the silent caller warns with that
  path, and the next plan's backup cleanup is nudged, never deleted.
- Silent runs never start over a running or unacknowledged foreground result
  and never surface in the status bar.
- Auto-update attempts are keyed per target and pruned to the states the
  current scan observes; unchanged states stay suppressed.

* fix(skills): warn when a silent update's result is lost

Any rejected or timed-out auto-update request may follow a committed swap
whose racing edit went to a backup. Show a persistent warning naming the
target and its skill backup directory, and settle the attempt so the same
state is handed to the nudge instead of retried.

* test(skills): guard version skippers; ledger also walks release tags

Audit of every v* tag and HEAD history found no SKILL.md bytes missing from
released-digests.json. The generator now also walks release tags (falling back
to HEAD) so a release cut off-branch can never ship unrecognized bytes. Add a
real-disk test that installs the oldest shipped orchestration (v1.3.24-rc.2) in
the npx-skills and agent-folder layouts and auto-updates it silently.

* test(skills): type the nudge mock's error without an assertion

* chore(i18n): add the silent skill update warnings to the English catalog
2026-11-01 00:31:23 -07:00
NeilandClaude 4eccb84167 Chat UI launch (#36891)
* feat(telemetry): A/B holdout for the Chat UI launch tip

Freeze a 50/50 chat_ui_launch_tip_v1 cohort for profiles still due the tip on
official builds with telemetry not disabled; control never sees the tip. Emit
chat_ui_feature_tip_exposure in both arms and agent_default_surface_changed
from the tip and from Settings -> Agents.

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

* fix(telemetry): hold the Chat UI launch tip until the telemetry banner resolves

Enroll only when consent is enabled. While the banner is pending, the tip is
skipped for that launch without being marked seen, so its exposure is not
spent unmeasured and the arm cannot influence the consent choice.

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

* test(telemetry): document best-effort Chat UI tip exposure on rejected sends

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

* test(telemetry): pass the exposure test through a hoisted mock instead of in-test importActual

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

* test(telemetry): justify the fake Store assertion in the telemetry harness

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-01 00:08: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
OrcaWin b10a6fe827 test: remove 253 low-value test cases, including 28 suites (#36907)
* test: remove 253 low-value test cases, including 28 suites

* test: remove empty archive predicate suite
2026-11-01 06:37:00 +00:00
Neil c1c04700bc refactor(agent-session): group failure modules and use direct owner imports (#36906)
* refactor(agent-session): organize failure owners and direct imports

* test: retain historical release paths in literal baseline
2026-10-31 23:16:07 -07:00
Kelvin Amoaba fddfd7b024 refactor(github): move inline PR comment line placement to src/shared (#36904) 2026-11-01 05:43:51 +00:00
Neil c0190ff9fa perf(mobile): lighter liveness probes, preview-free terminal lists, cheaper terminal gzip (#36903)
The phone's liveness probe read a full status.get (~12 KB, ~2.3 KB gzipped)
and threw it away on every idle and foreground probe. It now sends
`status.get {probe: true}` and the desktop answers `{runtimeId}` alone,
skipping the machine-name wait. status.get keeps accepting any params, as
when it declared none; older desktops ignore the flag and answer the full
status, which still proves liveness.

terminal.list takes `includePreview: false`. The phone's session inventory
and automation pane lookup never read the 300-char output tail, so they ask
the desktop to drop it; older desktops strip the unknown key and keep
sending it.

Compressed terminal binary frames now use gzip level 1. On recorded-style
terminal output it keeps 93-99% of the level-6 saving (57-95% of bytes
saved) at 1.2-4x less main-thread CPU; text replies keep the default level,
where level 1 costs ~45% more bytes on the wire.
2026-10-31 22:43:17 -07:00
Neil 64021b0f47 fix(mobile): guard catalog snapshots against late replies and trim the snapshot paths (#36902)
* fix(mobile): guard catalog snapshots against late replies and trim the snapshot paths

A slow unchanged or delta reply that predates a client switch (A→B→A)
could re-point the paired-server snapshot token at rows it no longer
matched; the next row delta then rebuilt onto the wrong base. Pending
reads now carry the token they asked with, and admit drops any non-full
reply whose token is no longer held.

Single-flight reads with one trailing join move into
WorktreeCatalogSnapshotClient.read(), so paired-server rows get the same
protection home had; home's own copy goes away. Server rows use one read
path for old and new desktops and drop snapshots for servers that leave.

The host screen asks for a first page only while nothing is shown and
the full read is not failing, and drops it on an invalid reply. The
shell no longer seeds the page with a status proven on a connection
that has since dropped.

Cleanup: drop exactActivity (unreleased; home only ranks by coarsened
activity and its exact rows defeated delta reuse), fold the legacy opt-in
check into the snapshot resolvers, remove unused worktree.ps result
types and a duplicate page subscription, and re-record the affected
RPC goldens.

* fix(mobile): keep server-row snapshots per client and skip first pages for loaded empty hosts

A shared per-host snapshot let a slow read on a replaced client reclaim
ownership from the new one, discarding the new client's reply. Snapshots
are now kept per client, and a delta rebuild retry runs only for the
current owner. An empty host that already loaded no longer asks for a
first page on every poll.
2026-10-31 22:43:13 -07:00
Neil 7d76028a5c refactor: group agent session journal and request modules (#36882) 2026-10-31 22:33:23 -07:00