* Give every Settings pane the same card structure
Settings cards that hold rows were built several ways (the non-grouped
SettingsSection px-7 card, shadcn Card, the Appearance accordion, one-off
bordered boxes), each with its own side padding and row inset. Every one
now goes through SettingsGroup: one 20px inset, rules between rows, title
outside the card. SettingsGroup gains an id, a collapse prop for the
Appearance groups, SettingsGroupSubsection, SettingsGroupNestedRows and
SettingsGroupDisclosure (the old Appearance advanced disclosure), plus
shared bleed classes for rows that span the card. The non-grouped
SettingsSection body is removed.
* Fix settings card typecheck errors
* Keep collapsible group headings valid and restore notification row padding
* Keep Chat names anchor on its group, toggle Appearance groups from the whole header, and steady the Machines row padding
* Show Appearance groups as plain cards and even the Computer Use card's top inset
* Restore the SettingsGroup subsection exports dropped with the collapse mode
* Scope workspace deletion shortcuts to selected sidebar rows
* Complete shortcut event typing and web bridge cleanup
* Use stable fixture rows and typed input elements
* Align sidebar deletion and navigation with eligible focused rows
* Pass explicit absent repo for selected row host lookup
* Describe selected options in keyboard test fixture
* Bound sidebar selection and deletion work and preserve host ownership
* Keep navigation imports within the file size limit
* Separate navigation type imports consistently
* Match selected workspace repositories by explicit host
* feat(computer): type into a named text field without focusing it on Linux and macOS
* fix(computer): report unconfirmed Linux text readback and cover element typing
* test(computer): send an element with benchmark text only for targeted typing
* test(computer): pick gedit's editor, not its hidden overview map, in Linux e2e
* docs(computer): confirm focus before the unindexed type-text fallback
* fix(computer): report unreadable element text readback as readback_unsupported
* fix(native-chat): preview GitHub PR links in chats without a GitHub repo
A chat whose workspace has no GitHub repo (a plain folder) sent every
GitHub link to the anonymous web page fetch. Private repos answer that
with a 404, so the card said "No preview for this page." Route the
lookup through another registered repo on the chat's host whose remote
is on the link's GitHub host; main already lets a preview read any repo
on such a host with that sign-in.
* fix(native-chat): prefer exact clones and repair attachment test import
* fix(native-chat): retain direct SSH ownership for GitHub previews
* fix(native-chat): invalidate routed previews when repo accounts change
* fix(native-chat): resolve current preview routes before cache reuse
* fix(native-chat): preserve preview authority across async lookups
* fix(native-chat): retain valid preview lookups across catalog updates
* Avoid coalescing retired preview requests
* Give terminal agents a lasting Orca Agent ID
Terminal agents now get an Orca ID (orca_session_id:<agent>_<uuid>) derived
from their own conversation id on the host that runs them, so it stays the
same across worktree sleep/wake, app restart and a hand-run resume in any
pane. The terminal pane menu gets Copy Agent ID beside Copy Session ID, and
worktree ps --json agent rows carry orcaSessionId. Messaging a terminal
agent by that ID is refused with a message naming its pane until terminal
delivery lands.
* Name terminal agents at row admission and fix review round 1
- Perf: a row's Agent ID is resolved once, when its provider key changes at
admission (legacy rows via the status adapter's prepareRow hook, tmux rows at
canonical commit) and carried on the row as runtime-only agentIdentity.
Snapshots, publications and internal notifications only copy it; no key
serialization, cache lookup or database access per walk. Drops the pane FIFO
cache.
- Refusal: the identity service keeps a reverse index id -> rows, kept exact by
the adapter's rowChanged hook; a terminal refusal reads only matching panes
and names only confirmed running ones (no resume seeds, exited or
unconfirmed restored rows).
- Retry: a read-only identity file is lookup-only and never mints; any store
failure backs off all SQLite work for 30 s; a Copy click retries once.
- Menu: Copy Agent ID shows for a live pane only when its host stamped an ID,
and for a sleeping pane only when this app owns it on macOS/Linux.
- SSH: a sleeping record keeps its connection; a foreign (non-WSL) one resolves
no ID.
- Renderer keeps an existing ID only for the same agent, owner and provider key
including the Pi transcript locator.
- tmux-selected terminal rows carry their ID at every snapshot seam and in Copy
resolution; structured chat rows stay excluded.
* Keep Agent ID work out of notifications and reach Copy wherever a host can answer
- Internal status/provider notifications walk rows without Agent ID work:
canonical (tmux-selected) rows are decorated only for outputs that carry
the id (snapshots, live emit, ps, Copy). The tmux publish body is now
publishTmuxSelectedStatus so tests drive the production path.
- A row this host can name but has not yet (failed or pending link) is
published with orcaSessionId: null; older hosts omit the field. The
renderer offers Copy Agent ID on a live row whenever the field is present,
and the click retries through terminal.agentId.
- Hosts advertise terminal.agent-id.v1. A sleeping pane offers Copy Agent ID
when its owner is this app on macOS/Linux, or a remote owner that advertises
the capability on a known non-Windows platform, and its record names no
foreign connection.
- "Running in pane" places require that this runtime observed the row; a
saved status, Done included, is history.
* Guard the pane menu subscription and stop offering Copy where it can never succeed
- A settled read-only miss (no link in a newer build's identity file) no
longer publishes orcaSessionId; only a resolved id or a pending failed link
does, so Copy Agent ID is offered only where a click can succeed.
- Render contract for the per-pane menu selection: another pane's or host's
status change re-renders no unaffected pane; a pane's own stamp flips its
availability.
* Move terminal agent identity SQL into a worker thread
Main's blocking-SQLite ratchet forbids the synchronous identity store, so
(per the decider ruling) agent-identities.db is now owned by one lazy
worker-thread entry per hook-server owner, desktop and orcad alike.
- The schema, open/version handling, mint/link transaction and read-only
behaviour move unchanged into agent-identity-worker-entry.ts; main keeps
only the key type, path and an async client over WorkerThreadRequestQueue.
- Admission stays synchronous: a new key is published pending (null), one
request per exact key is shared by every row and click, and the committed
answer re-stamps the current rows still pending on that key (legacy and
tmux-selected) as identity-only replays. Replies are fenced by store
generation, key and current subscription; failures back off 30 s and a
Copy click retries once.
- terminal.agentId and the terminal-ID refusal are async: Copy waits for the
pending lookup and revalidates the pane; send/task routing resolves the
terminal recipient (awaiting the store's has() on an index miss) before
any synchronous routing or effects.
- The renderer treats the host's row naming a conversation as authoritative
for its id, so a settled miss clears a pending null, while ID-less local
updates keep it.
- Packaged for desktop and orcad; registered as a worker entry, no baseline
growth.
* Own identity demand per key, publish settled ids as projections, recheck authority
Implements the second decider ruling for the identity worker:
- One owner-level demand table replaces the per-key promise backlog and the
settled-row scan: each exact key a current row or caller needs has one
record (identity, places, waiters); the pump sends the worker one operation
at a time, only for keys still needed, so a healthy burst completes and
detached keys are never sent. Operation deadlines start at dispatch; a
caller's wait expiring never drops a row's demand.
- One failure gate for lookups and membership: after a failure nothing is
sent for 30 s except one coalesced Copy attempt; a single owner wake
retries while rows still wait, and cancels with the last demand or close.
- The retirement barrier outlives a client: a replaced worker must exit
before another starts; a different store path is a different authority.
- Settlement re-stamps the stored row and publishes a projection only: window
readers get the row without its hook event (no agent-state record) and
session.tabs is woken by a row touch; no enriched subscriber, plugin event
or freshness heartbeat runs again.
- Copy and terminal membership check the owner generation after their waits.
A known chat is decided by the chat store alone; send, peer mail, reply and
assignee admission resolve the terminal recipient before reading the facts
they act on, recheck the sender after the wait, and group send reselects
its audience and mailboxes after each wait before writing.
* Scope identity answers to their store authority, expire Copy grants with callers, index provider aliases
- One synchronized authority token before Copy/reverse fast paths, compared after their awaits;
answers carry their authority and an ended store turns rows, tmux subjects and the reverse
index pending together. Status path changes sync the authority outside admission.
- A failed operation reschedules the pump so a queued click gets its attempt; grants belong to
live waiters and a click joining a running attempt earns none.
- Provider-native ids resolve through a commit-maintained alias index at the record store.
- Group fan-out no longer classifies authoritative candidates; @idle rereads statuses with
current candidates before the final synchronous selection.
- Hook-server unit fixtures use an in-memory identity client; a real worker load/stop failure
check covers the worker boundary.
* Pick a shared provider alias's owner among its owners only
The alias index keeps the store's listing order (readable, then unreadable) as derived ranks, updated from the draft's appended keys on commit, so a duplicate alias no longer walks the history per lookup.
* Ask a remote pane's host for its Agent ID by the host's own pane key
Copy Agent ID on a web-terminal surface sent the client's mirrored pane key, which the execution host does not know, so it answered null.
* Keep focus where the user moved, recover a missed identity re-stamp, ack peer-mail replays first
Copy Agent ID refocuses its pane only when focus is unclaimed or still in the pane; a carried pending row takes its key's settled answer; an already-delivered peer-mail retry is acknowledged before recipient preparation.
* fix(tab-group): track divider drags by pointer and keep guests click-through
When a divider drag's first move lands on a browser page, the guest's
process takes that event and every later one, so pointer capture never
activates and the divider stays stuck in is-dragging. Hold webview drag
passthrough for the whole gesture, as the pane-divider, tab and DevTools
dock drags already do.
Linux Chromium can report hasPointerCapture() false while moves still
arrive (#34287), so own the gesture by pointerId with window-level
listeners, and end it on a buttonless move when the release was missed.
Drawn from #35919 by alpenshtokk.
Co-authored-by: alpenshtokk <wawesandways@gmail.com>
* fix(tab-group): keep split dividers grabbable beside a narrow browser group
A squeezed browser toolbar overflowed its overlay slot and sat on top of the
divider to its right, so presses in the toolbar row never reached the handle.
Clip the overlay slot to its group like other retained pane hosts.
Adds a real-mouse e2e for a narrow terminal | browser | terminal row: the
divider must be uncovered along its whole height, follow a drag whose first
move lands on the page, and accept the next drag afterwards.
* fix(tab-group): end a lost divider drag on blur, hide or re-press
A release outside the window could be missed with no buttonless move to end
the gesture, leaving guest passthrough held and letting a later pressed move
resume the old drag. End the gesture when the window blurs or is hidden, and
let a new press of the same pointer replace a gesture whose release was missed.
Covers release outside the window, Linux false capture, multiple guest
surfaces and unmount mid-drag.
Co-authored-by: alpenshtokk <wawesandways@gmail.com>
* fix(tab-group): end a lost divider drag on the next press anywhere
Divider moves are tracked on window, but the same-pointer recovery only ran
when the next press landed on the stale handle itself. A press on another
divider or on pane content let later pressed moves resume the old drag, and
with two dividers both followed the new gesture. Listen for the active
pointer's next pointerdown on window in the capture phase and end the stale
gesture before anything handles the press.
* fix(browser): clip the narrow browser toolbar without making it scrollable
overflow-hidden still made the browser overlay slot a scroll container, so
tabbing to a toolbar control clipped in a narrow group scrolled the toolbar
and page sideways with no way back. overflow-clip clips the same way without
a scroll position.
---------
Co-authored-by: alpenshtokk <wawesandways@gmail.com>
* Chat UI settings: install an agent's chat runtime from its row
* Keep the install line while Orca installs over an external copy or can't read status
* Keep Removing… visible in the Chat UI install line
* Chat UI and new chats: show Antigravity sign-in as the next step after install
* Record NativeChatStructuredSession's cleared React Compiler bailout
* Keep the Chat UI row install-only; open sign-in on the chat's host and skip chats on the host's agy
* fix(settings): drop nested cards from Worktree hooks
The orca.yaml status, its parse-error callout and the local-scripts notice drew bordered panels inside the Worktree hooks card. Render them as flat rows; code blocks and inputs keep their borders.
* fix(settings): show healthy orca.yaml status as a collapsed row
Loaded and missing orca.yaml now render as a disclosure row under Command source instead of a tinted card inside the Worktree hooks card; the script preview or template is collapsed by default. Parse-error and update-available keep their warning card. Advanced content is indented to line up with its label. Restores the local-scripts notice card from main.
* chore(i18n): drop orca.yaml status strings the new row no longer uses
* fix(settings): keep orca.yaml row inside design-system lint rules
* fix(automations): follow-ups to the automation detail redesign
- Show the latest occurrence as a folded skipped run's last-run time. The
detail health band, list rows with loaded history, and the projected list
summary all read the retained run's first-occurrence timestamps, so a host
that kept refusing runs looked like it last ran days ago.
- Translate the new detail strings (Repeats, Missed-run grace, Execution,
Not run yet, the usage detail, and the prompt copy labels) in every
supported locale, reusing existing localized wording, and give the usage
detail CLDR plural forms again.
- Assert the prompt preview's eight-line boundary by behavior in the e2e
spec instead of the line-clamp CSS value.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(automations): zh copy-prompt wording
Use 提示词 in the new zh copy-prompt label so it matches the panel heading.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
It streams like Nemotron 3.5, so text shows as you speak, and scored
2.8% WER streamed (2.35% as one decode) on the 73 LibriSpeech bench
clips, against 3.7% for Parakeet v3. It sits ahead of Parakeet v2,
which measures the same, so one-click setup favouring accuracy picks it
for English.
* feat(native-chat): run Kimi Code in Chat over its ACP server
Kimi chats launch `kimi acp` with Kimi's own sign-in and KIMI_CODE_HOME.
Orca sets Kimi's own permission mode at every start: Ask is Kimi's
default, Approve for me is its Ask When Needed mode, and Full access is
its Never Ask mode. Without that, a default_permission_mode in Kimi's
config.toml would apply while the session reports default.
A Kimi dialect reads Bash results as output and exit code, drops the
streamed arguments a running call shows as text, names the command or
path on approval cards, and takes questions as forms so every question
reaches the chat.
* fix(native-chat): import Kimi session history helpers from their new home
* fix(computer-use): scroll, expand and set sliders on Windows without raising the window
* fix(computer-use): wait for Windows scroll and slider writes to settle before verifying
* fix(computer-use): reject NaN for Windows slider set-value
* test(computer-use): wait on the Windows fixture state with vi.waitFor and a schema
* test(computer-use): check activation per action against a holder window in the Windows fixture
* test(computer-use): log fixture window events for activation failures
* fix(computer-use): report foreground when a Windows app raises itself for a UI Automation action
WPF activates its own window for Scroll, ExpandCollapse, RangeValue and SelectionItem calls from any UIA client, so the runtime now compares the foreground window before and after a pattern action instead of assuming background. The Windows e2e checks delivery against the fixture's own activation count, with a holder window in another process.
* test(computer-use): skip an unstarted holder when closing the Windows fixture
* fix(computer-use): bound Windows pattern scroll paging and tighten slider read-back
* fix(computer-use): cap Windows slider read-back tolerance and run the UIA e2e only on CI
* fix(tabs): a new tab in a workspace whose layout outlived its groups becomes the layout (was invisible: empty strip, 0-size pane until a workspace switch)
* test(tabs): fixture for a new tab in a workspace whose layout outlived its groups
* feat(dictation): optional AI cleanup of dictated text, with a correction word
Off by default. When on, a dictation's text goes through the user's agent
CLI (the AI text actions' runner, Haiku by default for Claude) before it
lands: the pill says "Cleaning up…" and the text is inserted once. Failure
or a 10 s timeout inserts it as heard with a warning; the pill's X skips
cleanup. A consent dialog asks before text first goes to an agent. The
correction word ("scratch that") survives filler removal for cleanup to act
on. The microphone test gets "Try cleanup", and phone dictation the computer
transcribes is cleaned on the host.
* fix(dictation): keep finals that arrive during cleanup and tighten cleanup edges
Nemotron 3.5 was cut at Silero pauses and decoded per phrase like an
offline model, so it showed no text until a pause, paid a decode per
pause, and lost context across cuts. A model that reports streaming now
gets every chunk fed into one transcribe-cpp stream. Its text shows as
it grows and is committed when the speaker goes quiet, with coverage set
inside the quiet so a CPU replay after a GPU crash starts clear of words
already posted, and at stop. Past 20 s uncommitted the stream restarts
at the next pause Silero hears (25 s at the latest), keeping the CPU
fallback's backlog bounded. A second dictation started beside a live
one stays pause-cut and decodes only to commit, since the model serves
one stream at a time. Offline models are unchanged.
* test(speech): cover the recognizer's span commits, coverage and cancel with a fake transcribe-cpp
* test(speech): share one typed fake VAD stream between the segmenter and recognizer tests
* fix(native-chat): let a chat continue on the account you switch to after a usage limit
A usage-limit reset belongs to the account that hit it. After a switch the
host kept holding the chat until the old account's reset, and desktop and
phone hid Continue until then, so Switch Account on the card did nothing.
The host now releases the hold once the chat would start on another
account, letting only that account's own reading hold it, and publishes the
earlier continue time that both clients read.
* fix(native-chat): save the account a usage limit hit, per cut
The fallback to the chat's founding account could report a switch that
never happened for a chat that had already moved, releasing it before the
new account's reset. Save the cut's account while the child that ran it is
live, use only that, and key the switch time by the cut it belongs to.
* fix(native-chat): record the account each chat runtime launched on
record.accountHome names only the founding account, so after a switch
nothing said which account a usage limit hit. Owner proof now records the
account each runtime fence launched under, every host cut carries its
turn's fence, and the hold reads the cut's account from that pair. This
replaces the sweep's saved copy, which a fast child exit could miss.
Spending the new account showed before a switch now holds the chat until
its own reset; only a reading taken after the switch releases it.
* fix(native-chat): restart the switch time when the chat moves to yet another account
A second switch for the same cut kept the first switch's time, so a reading
of the newer account taken before it was chosen could release the chat.
* fix(browser): keep guest input bound to its webContents
* test(browser): reject unresolved guest input routes
* test(browser): keep text on the guest when the root frame probe fails
* fix(browser): insert guest text only into a confirmed focused frame
Electron's webContents.insertText crashes the guest renderer and never
settles when no local frame holds focus (a fresh tab, or focus in a
cross-site frame after a root probe timeout). Use it only when the focus
probe confirms a focused element or same-site frame, refuse otherwise,
and reject promptly if the renderer dies or stops answering.
* test(browser): explain the busy-page e2e timeout
* fix(browser): expire guest text edits at the renderer mutation boundary
* fix(browser): preserve focused local frame contexts for text edits
The agent already has full thread data from selectedCommentGroups. Report
only the host-resolvable thread count in the human-readable section to
avoid duplicating thread bodies in the JSON payload.
* fix(computer): verify macOS type-text and paste-text sent as keys by reading the field back
When text can't be written through AXValue (Chromium/Electron fields, fields
that refuse the write), type-text and paste-text send keys or Cmd+V and always
reported unverified. Now the helper predicts the focused field's value from its
value and selection before sending, then reads it back until it settles:
verified on a match, unverified (text_mismatch) otherwise. Secure fields and
fields without a readable value and selection stay unverified as before, and
the result never repeats the typed text or the field's contents.
* fix(computer): keep the caret put after a refused macOS text write and skip unprovable readbacks
A refused AX value write no longer moves the caret before falling back to keys, so the keys and the prediction both use the user's selection. Text with control characters, a value the field already holds, and a field still filling at the 2 s cap stay unverified rather than verified or text_mismatch. The quiet window starts after the first read answers.
* fix(computer): bound macOS text readback size and say a mismatch can mean keys went elsewhere
* fix(computer): treat a macOS text readback past the cap as undetermined
Selection checked only the account's identity file, so an account with
.claude.json intact but no usable login in its Keychain item or
credentials file passed, and every chat that follows the selection failed
its next start as signed out. Select now makes the same credential read
the usage meter makes for the account; an unreadable store still allows
the switch.
* fix(computer): refuse quit, log out and lock shortcuts unless explicitly allowed
* fix(computer): close Linux keysym bypasses in the system shortcut guard
* fix(computer): refuse Linux keycode, L1/L2 and VT-switch keysym bypasses
* fix(computer): catch control characters in Linux keycode tokens and point recovery at the stated effect
* feat(computer): show where snapshots were cut and read one element's subtree
* fix(computer): validate subtree targets against the fresh walk and keep scope for focused input
* fix(computer): return whole-window action results after subtree reads
* fix(computer): count kept records toward the macOS limit and index subtree reads against the latest cache
* fix(computer): index Linux/Windows subtree reads against the snapshot they started from
Capture the terminal screen, output position and unfinished parser state together on the existing output queue so restored output continues at the correct byte. Retire reconstructed partial escape state on Reset.
Tracked in #34435 and STA-12466.
Co-authored-by: liaoxingyu <3114147163@qq.com>
* perf(renderer): paint modal backdrops, sticky headers and pane overlays solid instead of blurring
Dialogs, palettes and sheets blurred the whole workspace, and sticky headers,
pinned bars, the mobile-driver scrim and markdown table controls re-blurred
whatever scrolled or changed beneath them. The workspace now stays sharp under
the existing dim with a solid dialog panel at today's color, and each sticky or
pinned bar paints the opaque equivalent of its tint over the surface it sits on.
Reduce Visual Effects keeps its behavior; the CSS-value-only blur e2e spec is
removed and its onboarding recovery test moves to the onboarding spec.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(fixtures): describe the sheet fixture's dimmed, unblurred workspace
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf(renderer): narrow to the mobile-driver overlay; keep every other blur as on main
Brennan 11-12 kept modal blur, and only the mobile-driver overlay met its
measurement rule. Restore every other surface, the e2e blur spec and the
fixture copy to the synced base.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>