* fix(sidebar): keep the session name first on a monitoring agent row
Leading with "Monitoring background tasks" made every monitoring row in a
narrow sidebar truncate to the same text. The row's dot already draws the
monitoring glyph, so the status can trail the name like every other state.
* fix(agents): keep a monitoring child row's name first too
Sub-agent rows (desktop sidebar, chat strip and mobile) share one lead/trail
rule that also put "Monitoring background tasks" before the name.
* feat(agent-picker): fold legacy models the same way in every model picker
Claude aliases whose versioned model is listed, and Codex models superseded
in their family or by a newer major line, fold under Legacy models in the
agent launch picker, text-generation pickers, and native chat on desktop and
mobile. The default and current model always stay listed.
* feat(agent-picker): expand legacy models in place instead of a separate list
* feat(source-control): fold legacy models in the action model menu
* fix(agent-picker): keep model rows keyed across sections; let launch search find legacy models
* fix(agent-picker): keep rows in place while picking; classify each model catalog once
Legacy membership no longer depends on the selected model; a picker opens the
legacy section when the current model is in it. Classification is one pass and
cached per catalog, so streamed chat renders don't reclassify an unchanged list.
Typing on a closed Select no longer swallows its next close.
* fix(agent-picker): reopen menus with the current legacy model shown; PTY chats classify their discovered list
* test(agent-picker): wait on the menu instead of sleeping
* fix(agent-picker): keep the Legacy models row last, expanding older models above it
* fix(agent-picker): label the folded section More models, matching mobile
The close actions swallowed refusals and transport errors, so a tap on
close did nothing when the host refused (for example while the desktop
window is still loading). Show a short toast instead, with a specific
message for runtime_unavailable.
Holding Cmd/Ctrl swapped the round Send/Stop button for a wide labeled pill,
pushing the model picker, context ring and mic left. The preview now keeps
Send's round button and only swaps its icon (CornerDownRight for steer,
ListEnd for queue); the label stays as the aria-label. With Steer queued,
the head queued card's Steer button also fills as the primary.
The phone composer had the same pill and now follows the same rule.
The site now lives at orca.dev; the old onorca.dev and www.onorca.dev hosts
redirect there. Linking to orca.dev directly skips that redirect.
Only page links change. Service hosts, sign-in and relay endpoints stay as they are.
* chore(endpoints): call orca.dev for feedback, plugin lists and update notes
The site moved to orca.dev. Feedback, connection diagnostics, plugin install
stats, the plugin kill list, update nudges and the changelog feed now use
orca.dev, as do the diagnostics token URL baked into builds and the release
download mirror (downloads.orca.dev). The old hosts serve the same paths,
so released builds keep working.
Skill share links on share.orca.dev are accepted, and the mobile scanner
recognizes the orca.dev Android download code. The old hosts stay accepted.
* chore(ci): leave the release JavaScript benchmark workflow unchanged
Editing it triggers the benchmark, whose parity check currently fails on
arm64 because max-quality brotli output for the mobile page differs by
architecture. The benchmark only needs some diagnostics URL, and the old
host still serves it.
* fix(orchestration): label follow-up worker cards, gate remote-owned start cards on ready
A send that continues a finished or closed worker starts a follow-up Dispatch,
and the coordinator's chat showed it as "Started worker". The agent-start item
now carries the Dispatch it followed and whether the conversation reopened, and
the card reads "Reopened worker" or "Sent follow-up to worker".
worker-start --on a server that owns its workers wrote the card for any parsed
answer, with no agent kind. It now passes the started agent and writes the
card only once the server readied the worker, as local and attached starts do.
* test(native-chat): assert follow-up heading kind, not its copy
* feat(mobile): show only the open tab, with a tab count and a sheet of every tab
The session bar shows the open tab at full width, then a tab count cell and a + cell. The count opens a sheet of every tab grouped Needs you / Working / Everything else; tapping the tab opens its actions; swiping it steps between tabs; + opens a new tab sheet with agents as tiles. The header subtitle shows the branch. Brings back #40592 on current main.
* fix(mobile): don't switch tabs when a bar swipe is interrupted
* fix(native-chat): hold an automatic continue until the chat's account has usage again
The automatic continue after a usage limit sent at the cut's reported reset
without looking at the account, so it could send into a still-spent weekly
window. Each sweep pass now attributes the chat's launch account (the host's
launchAccountHome) to Orca's polled reading taken in that same home, and holds
the send until the latest spent window's reset (+15 s). The hold is derived
each pass, never saved, only grows for one cut and account, and waits up to two
minutes for a reading taken since that reset before falling back to the reset
it has. A spent window with no known reset stops the automatic continue for that
cut; on System Default, where Orca can't tell which login a reading saw, a fresh
reading with room releases both. Rows on desktop and phone show the held time
through an optional continueAt; older clients keep the cut's time.
* fix(native-chat): attribute usage holds once per account and retire them with their scope
- C1/X7: launch home resolved once per provider and reading once per (provider, home) per pass; narrow usageReading accessor replaces getState().
- X8: forget() drops the hold; hidden-chat caches are pruned while the setting is off; readState stores nothing for an unattributed chat.
- X3/C3: launch and reading homes compared in canonical form (~, dot segments, async realpath, bounded cache).
- X4/C2: a hold belongs to its scope (canonical home + billing kind) and retires when it changes.
- X2: a launch whose provider has no reading yet asks once and waits within the two-minute window.
- X5: attribution runs before the overdue check, so a restart keeps the account's later reset.
- X6: the launch account is re-attributed right before each send; beforeRun refuses a changed attribution.
- C5: cold chats publish when their continue time moves.
- X1/C4: WSL launches keep the cut's own reset (no hold).
* fix(native-chat): gate held continues on an account scope token and stop reviving stale cuts
- C6: a hold extends only with a spent window that had begun by the cut's reset.
- X6-R2, X10, P1-1: a synchronous account scope token (selection, launch env, reading home and
provenance) is captured at attribution; send() re-resolves only when it moved, memoized per
token, and beforeRun compares live and captured tokens. Opening a chat drops only cold-cut caches.
- X11: production tick() test at N=300 and 10N=3000 counts resolver and reader calls.
- X2-R2: the bootstrap wait starts when a pass first sees the due send without a reading.
- X9: canonical homes are resolved once per account per pass; the unbounded-lifetime cache is gone.
- C5-R2: unloaded chats have no status push path, so the cold continueAt publication is removed.
* fix(native-chat): separate continue eligibility from current spending and bind sends to their attribution's sources
- X12: a cut's eligible anchor (its reset, an established hold, or a covering window) takes the
24 h overdue test; once eligible, every currently spent window holds the send.
- X13/P1-2, X6-R3: the account owner returns attributions with a synchronous source witness
(settings snapshot identity, cached login-shell capture revision, reading-source identity);
Claude billing comes from the launch's own env composition. A result whose sources moved while
it resolved is discarded; send() and beforeRun bind to the attribution's own witness and
supersede without spending an attempt.
- CI-1: the scope-token type assertion is gone with the token.
* fix(native-chat): keep a covering hold beside spent windows of unknown reset
- X12-R4 / C7: rebuilding the covering anchor skips spent windows whose reset, and so start, is
unknown; the eligible branch still refuses on them.
- E1-R4: the preparation-change and suspended-attribution race tests run on a Claude chat
through the real host send, mutation and accepted settlement.
* refactor(native-chat): move the usage-limit continue status into its own wire module
The merge with main put agent-session-wire.ts one line over the 300-line limit.
* fix(native-chat): read the shell capture revision lazily so module mocks without it still load
Tests that main added mock login-shell-environment without the revision accessor, so binding it at module load failed.
* fix(native-chat): release a usage hold on fresh room for any account without stranding the cut
- QA-1: room in a fresh attributed reading newer than the last spent evidence (spentSeenAt)
releases the hold and clears an unknown reset for every account; the System Default-only
branch and identityKnown are gone.
- QA-1b: the release pulls heldUntil down to that reading instead of clearing it, and an
unknown-reset hold anchors on its last evidence, so a release over a day after the cut sends.
* fix(native-chat): release an expired usage hold from its room reading and ask for readings while a reset is unknown
- R6-1 / QA-1b-R6: a release sets heldUntil to the room reading itself, so an expired finite
hold followed by an unknown reset no longer strands the cut.
- R6-2: the usage poller idles while Orca is unfocused with no viewer, so an unknown reset asks
for a non-forced reading at most every 10 minutes through the existing refresh.
* fix(native-chat): bound unknown-reset readings per account and honor the failure backoff
- R7-1 / X-R7-1: the rate-limit service owns the automatic-read cadence: a read waits while the
provider's last reading or admitted automatic read is within the asked interval, so held chats
on one account cause at most one unknown-reset read per 10 minutes.
- R7-2: automatic reads wait out the poller's failure backoff (shared failureRetryDue) and start
its clock; forced refreshes are unchanged.
* fix(rate-limits): scope automatic usage-read admission to the selected account
- R8-1 / X-R8-1: an admitted automatic read counts toward the cadence only for the account whose
reading source the service held then, so a switch never lets one account's read delay another's.
- Prunes the flaky-test-pattern baseline: service-refresh-orchestration no longer reads the wall clock.
* fix(native-chat): keep a usage-reading request retryable until the service starts it, and show the last known reset while one is unknown
* fix(native-chat): show no automatic continue while a chat waits on an unknown reset, since fresh room still releases it
* fix(native-chat): publish no continue time for a cancelled or stopped cut while its account's reset is unknown
* fix(native-chat): refuse a stale startup-dialog card choice and add a card for the multi-server MCP dialog
* fix(native-chat): drop casts from the startup-dialog guard
* fix(native-chat): refuse a startup card choice until the screen model has applied all received output
* fix(native-chat): refuse a clipped MCP server list and guard restored startup cards
* fix(native-chat): allow one startup card choice per screen so a second client's keys can't hit the next dialog
* build(mobile): upgrade to Expo SDK 58 and React Native 0.88
Expo 58 drops Zod from @expo/cli, which clears the last Zod 3 advisory
(GHSA-mrhx-c7xc-m3w5) in mobile along with its local patch and override.
- Expo 55 -> 58, React Native 0.83 -> 0.88, React 19.3, gesture-handler 3,
webview 14, reanimated 4.7, worklets 0.13 (SDK 58 bundled versions,
pinned below today's batch to respect minimumReleaseAge).
- Port the react-native and react-native-webview patches unchanged; drop
expo-constants, expo-notifications, @expo/cli, expo-router, zod and
query-string patches plus the zod/query-string/xcode>uuid overrides,
which upstream fixed or no longer need.
- iOS minimum rises to 16.4 (Expo 58 / RN 0.88 requirement).
- Migrate to RN 0.88 strict types: instance ref types, NamedStyles,
AppState.currentState, list header/footer nulls.
- Replace removed runtime APIs: InteractionManager -> idle callback with a
macrotask fallback; StyleSheet.absoluteFillObject -> absoluteFill;
FileHandle.readBytes -> readBytesSync (it became async).
- Resolve react-native/asset-registry for shared assets in Metro and alias
the old registry path for react-native-svg in the desktop mobile-web bundle.
- Reduce the iOS scene plugin to the reconnect override; Expo 58's template
now ships the scene delegate itself.
* fix(mobile): bridge react-native-svg web imports and libsodium runtime for RN 0.88
- Share react-native-svg web shims between the mobile-web build and its render
checks: map the old asset-registry path to react-native-web's registry and stub
the Android-only touch-sound TurboModule.
- Patch react-native-libsodium 1.7.0 to read the JSI runtime from the bridge proxy;
RN 0.88 removed RCTCxxBridge.runtime.
* fix(mobile): use Expo 58's legacy promise resolver for audio permissions
* fix(mobile): skip null handles in the RPC recording timer shims for RN 0.88 types
* style(mobile): format MobilePRSidebar after rebase
* fix(mobile): name RN 0.88 text input refs for declarations; allow queued back in stack render check
- Type text input refs with an exported interface so declaration emit can name hook results
(RN 0.88 aliases TextInputInstance to an unexported class).
- Expo Router 58 drains back() after a commit; check the interrupted push's exit from the
frame it starts, bounded to three frames of lag.
* chore(mobile): justify the text input handle interface for React Doctor
* chore(mobile): use a nameable intersection alias for text input refs
* fix(mobile): use sync expo-file-system writes, copies and moves on Expo 58
Expo 58 made File.write/copy/move and FileHandle.writeBytes return promises.
Our callers never awaited them, so failures escaped their try/catch and the
store's tmp-then-move and PDF writes could race their close or rename.
* build(mobile): raise the page bundle budget to 10 MiB for Expo 58
Expo 58 adds about 310 KB to the page: expo-router (+258 KB, now carrying
React Navigation, net about +110 KB) and Reanimated 4.7 (+152 KB). Main had
85 KB of headroom left. 10 MiB still stays within the 10 MiB per-asset contract.
* fix(mobile): drop the window-metrics seed RN 0.88 removed from Android
RN 0.88 no longer holds window metrics: DeviceInfoModule reads the activity's
WindowMetrics on demand and setWindowDisplayMetrics is gone, so MainActivity
no longer compiled. Layout scale still comes from the screen metrics we reseed.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
A keyboard's mic button switches to a separate voice IME, which closes the keyboard and opens the voice panel about 250 ms later. The page read every Android keyboard close as Back and blurred the field at once, so the voice IME's text had nowhere to go. Blur only once the keyboard stays closed for a second; a reopen within that window keeps focus.
* test: remove 60 low-value cases and orphaned conversion fixtures
* docs(ci): list supported Windows SSH host cells
* test: recognize direct Windows orcad server entry
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(native-chat): answer Claude/Codex folder-trust dialog from a chat card
* fix(native-chat): show a chat card for Claude/Codex startup menus
* fix(native-chat): keep startup-menu cards holding Send; drop hand-typed identify retries
Startup-menu cards now hold the composer like the folder-trust card, so collapsing one can't free a Send whose Return would pick the highlighted choice.
The hand-typed agent probe retries and agentless hold are left out; Orca-launched panes already know their agent, and hand-typed native Claude identification predates this fix.
* fix(native-chat): read the whole screen for a tall startup dialog's heading
* fix(native-chat): drop the unreachable cwd prompt from startup-menu reasons
* fix(native-chat): show startup-menu warning text and give its choices no default
A startup-menu card now carries the dialog's body (Claude's bypass, API key, MCP and import warnings) under its heading, and its choices render as equals on desktop and mobile so Orca never nudges a consent choice. Long mobile option labels wrap inside their button.
---------
Co-authored-by: Kooooojun <edisonhuang1122@gmail.com>
* fix(mobile): show Loading for a new Tasks search instead of the previous search's rows
A new filter or search with no kept rows kept the same provider's rows on screen until the host's read landed, so a slow host looked like search did nothing. Only a re-read of the same list now stands on its rows; desktop already shows its load skeleton here.
* test(mobile): re-record Tasks list goldens for the new-filter spinner
* fix(mobile): hold Tasks paging while a new filter loads
* test(mobile): type the new-search spinner test's GitHub row fully
The phone's stale-resize filter took the binary scrollback frame's seq as
its layout high-water mark, but that seq counts output bytes, not layout
changes. A Desktop/Phone toggle on a terminal with little output then
looked older than the mark and was dropped, so the phone never resized
and kept showing the old mode. Seed the mark from the subscribed frame's
layout seq and leave scrollback out of it.
Co-authored-by: zhangxaochen <3416677+zhangxaochen@users.noreply.github.com>
* fix(native-chat): show session skill argument hints in the picker
Claude Code reports argument-hint on skill commands. The native chat skill row kept only the name and description, while built-in commands in the same picker still showed their hint.
* fix(native-chat): keep a skill's hint before Claude classifies its catalog
Before the first stream init, Claude lists skills as unclassified commands.
A scanned skill of the same name replaces that row and dropped its hint.
Fold the three hint tests into one shared-layer test and show a skill hint
in the picker fixture.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* feat(mobile): pick the PR glyph from PR state
Workspace rows always drew the open-PR glyph and only changed its color,
and the PR tab's state badge had no glyph. Both now show merged, closed and
draft glyphs, from one prStateIcon mapping that the details sheet and task
rows also use.
* feat(mobile): show the PR state on the session header's source-control button
The button always drew a branch glyph. When the worktree's catalog row has a
linked PR it now draws that PR's state glyph in its state colour, read from
the host list's worktree.ps query in its own component.
* fix(mobile): read the header's PR state from the catalog cache only
Opening a session must not request worktree.ps before its first paint, so the
source-control button reads what the host list last loaded instead of
fetching it.