* feat(mobile): GitHub threads, viewed sync and line comments on the diff screen
Committed files of a GitHub pull request now show its review threads under
their lines with Reply and Resolve, take their viewed state from GitHub and
sync the checkbox and Viewed · next file back through setPRFileViewed. A tap
or a long-press drag picks lines and opens a sheet that sends the note to
the workspace's agent through the existing send path, or posts it as a
review comment. Threads and GitHub comments appear only while the local
branch is at the PR head, the same contents desktop shows them on;
otherwise the file says to push. openDiffForFile takes an optional line for
a thread's Open in diff.
* test(mobile): use a type import for the line-mutations recording adapter
* fix(mobile): name the staged or unstaged side in the jump sheet's file labels
The Commits list now stops where the branch left its base. It groups
commits into Not pushed (hollow dots, with Push) and On origin, ends at
"Branched from <base>" with Update branch, and loads more past 50. A
commit opens its own page: message, author, date, pushed state, files
with +/-, and Copy SHA / Open on GitHub in the menu.
Older hosts ignore the new params, so the list falls back to the full
history without groups or the base marker.
- 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.
Reading an agent on the desktop rebuilt notification ids for turns that
never alerted (every chat pane, silenced workers) and forwarded each one
to the phone as a silent dismiss push plus a dismissal-store record.
Only forward a clear when the store holds a live delivery for that id.
* Add Dashboard terminal HTTP link actions
* Preserve Dashboard HTTP link targets and route refusals
* Await Dashboard link fixture operation completion
* Keep nested remote Dashboard links on system destinations
* Preserve preview link defaults and correct composition fixtures
* test: verify dashboard link resource ownership and composition
* fix: align preview link and fixture type contracts
* Complete preview callback types and popout copy feedback
* Keep popout terminal links on their existing default path
* Remove unsupported role query option from preview fixture
The review screen becomes the one diff screen from the approved design.
It walks the Changes section the file came from (uncommitted, staged or on
the branch) or one commit's files, so 'N of M' and next/previous stay in
that list. The title opens a jump-to-file sheet built on the shared
source-control tree, with search and viewed boxes. Unchanged runs fold the
way desktop's diff does. The bar's main button is Stage · next file for
uncommitted files and Viewed · next file otherwise; Discard and Unstage move
to the menu. Filter chips, hunk jumps and Stage Reviewed are gone.
openDiffForFile also takes { kind: 'commit', commitOid, filePath }, read
through git.commitCompare and git.commitDiff.
* 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>
* fix(browser): stop the paired tunnel refusing a heavy page's connections
The client capped a tunnel at 32 streams and the server refused opens past
16 pending connects, so ja.aliexpress.com (77-105 concurrent connections)
lost requests with ERR_SOCKS_CONNECTION_FAILED. Size the stream cap to
Chromium's 256-socket pool on the client, server and SOCKS listener, drop
the pending cap the stream cap already bounds, and raise the open rate to
1024 per 10 s above the measured 302.
* fix(browser): fail only the stream that overflows the tunnel receive budget
More streams per tunnel make the shared 32 MB receive budget reachable, and
a denied claim closed the whole tunnel as if the server sent invalid data.
Fail just that stream, as the server session already does.
* fix(browser): release a failed stream's claims before reporting it, update tunnel gate text
* fix(browser): keep room in the tunnel memory budget for a failed stream's Error frame
Application claims now stop 64 KiB and 64 claims short of the host and
process caps, so the Error frame for a stream failed by a full receive
budget can still be queued instead of closing the whole tunnel.
Source Control rows used to open a file three ways: the review screen, a
desktop editor tab the session then switched to, or a text-only branch
drawer. Every row now calls one openDiffForFile entry point that pushes the
review screen. The drawer, the session tab reveal and their handlers are
gone; Open in Session stays in the review screen's menu.
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.
Main's page code is 9,414,296 of 9,437,184 bytes, and the Source Control
redesign branches add 7-31 KB each. The phone contract allows 10 MiB per
asset and 32 MiB in total, so the fence was our own budget, not a phone
limit; 9.5 MiB keeps it under the per-asset ceiling.
* 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.
Every floating browser pane reported the 'owned-target' shortcut scope, and a
page only takes the keyboard on activation in the focused split, so the
keyboard stayed in the hidden main-area terminal. The overlay layer now marks
the floating pane in the focused split 'owned-target-focused': it still answers
only chords from its own overlay, and it takes focus like a focused split.
* Keep pending chat visible until the new worktree tab opens
* Cancel agent launches when their chat tab is refused
* Keep workspace sidebar mounted during chat creation handoff
* Preserve the first chat message when layout refuses the tab
* Make worktree creation handoff test timestamp deterministic
* Avoid assertion in pending chat test
* Add pending chat opening translation
On an orcad server whose browser is an external Chromium, fill, clear,
select, type and select-all went straight to agent-browser, which reports
success without checking the element. Run desktop's element functions on
the exact element behind the ref, reached through agent-browser's own
`get attr` call, and type with real key presses.
* 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.
* 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
The queue card's invisible full-text line-count probe overflowed its two-line
row and stretched the queue's scroll range far past the real rows. Clip it to
the card text.
The queued-message editor drew Save and Cancel labels inside 38pt round icon
controls, so translated labels spilled over each other. They are now pills that
hug their label and truncate only when the row runs out of room.
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.
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.
* fix(mobile): quote expo-constants iOS build paths so checkouts with spaces build
Backports the expo-constants 58 quoting of the app.config build phase
(podspec bash -l -c argument and basename $PROJECT_DIR) to 55.0.17.
* fix(mobile): quote react-native-xcode.sh call in the iOS bundle phase
Expo SDK 55's template leaves the command substitution unquoted, so the
Bundle React Native code and images phase splits a checkout path with a
space. Expo's SDK 58 template applies the same quoting.
* fix(mobile): scroll wide tables in the rich Markdown editor instead of squashing columns
* fix(mobile): keep CJK table columns from shrinking to one character
* 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.
* fix(mobile): keep Android split screen from restarting or squeezing the app
One UI gives a split-screen pane its own density. Android recreated
MainActivity on that change, and React Native seeds its display metrics
from the application context, which keeps the full-screen density, so
the pane was laid out at the wrong scale. A config plugin declares
density in configChanges and re-seeds the metrics from the activity.
* fix(mobile): catch React Native's rotation re-seed before the frame draws
A one-shot listener added on configuration change missed a cold start or
a 180-degree flip at a non-zero rotation, where React Native's root view
re-seeds from the application context without a configuration change.
* fix(mobile): publish re-seeded display metrics in one step
Text measurement reads the holder off the UI thread, so build the
pane-density screen metrics before publishing them.
* fix(mobile): show workspace images in session Markdown tabs
* fix(mobile): show workspace images restored by undo or paste
* fix(mobile): keep session image loader current and inside the workspace
* fix(mobile): classify entity-encoded image URLs by their decoded scheme
* refactor(mobile): classify editor images with the shared Markdown href router
* test(mobile): expect workspace image markup in the code-block selection check
- 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.
* 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.
* 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.
* 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.