mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 09:37:33 +00:00
36ed2363945a305b50722d76991306335c19f6dd
15083
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
36ed236394 |
fix(persistence): repo writes take the row's identity, never a bare id (#30616)
* fix(persistence): repo writes take the row's identity, never a bare id updateRepo(target, updates) now takes Pick<Repo,'id'|'connectionId'|'executionHostId'> (RepoHostTarget) and resolves the host from it, so a caller that holds the row can no longer drop its host and write a same-id sibling on another host. The id-only callers (project host setup controller and IPC handler, folder-to-Git upgrade watch, both clone paths, registration adoption, fork backfill) pass the row they resolved. Co-Authored-By: Claude <noreply@anthropic.com> * test: migrate the remaining Store.updateRepo callers to row targets Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
cb82bc80da |
fix(relay): never call a reserve cell's hosts stranded when it goes existing-only (#30609)
A reserve cell keeps no activity rows and its ledger stamps last_activity_at, so the stranded rule read its connected hosts as unserved and re-placed them. |
||
|
|
7dcb7d4439 |
Sleep stops a workspace's native chat agents (#28445)
* fix(sidebar): Sleep stops a workspace's native chat agents Sleeping a workspace stopped terminals and browsers but never its structured (native) chats, so their claude/codex process kept running, and Sleep was disabled for a workspace whose only content was a chat. Sleep now stops each chat's agent through a new agentSession.sleep (the host's close, keeping the tab), routed to the host recorded on the chat's tab; an older host without the capability fails Sleep with a clear message. The Sleep menu counts a chat. * Fix native chat Sleep ownership and delayed teardown * Stop native chat sessions when sleeping their workspace * Fix sleep teardown wake and host-qualified receipt races * Verify mobile Sleep sends the selected desktop host * Keep worktree Sleep RPC within method file limit * Use sleep intent as attempt identity * Gate workspace sleep on structured client support * fix(native-chat): preserve sleep stop receipts and send timing * fix(native-chat): keep superseded workspace sleeps quiet * fix(native-chat): ignore superseded failed sleep stops * Fix sleep lifetime test resolver types * Accept workspace Sleep for already ended chats * Sleep native chats through the shared no-toast sleep core Main split runSleepWorktrees into sleepWorktreesReportingFailures (no toast) and a toasting wrapper. Move the native chat stop, host routing and typed failures into the shared core so the WSL move's Sleep request also stops chats, and report every workspace not verifiably slept (an unconfirmed chat stop included) in failedWorktreeIds. Fold one ACP adapter field onto a line so main's file plus this PR's ownership check stays within the file length limit. * Import isStructuredTab from its shared module * fix(native-chat): leave chat rows to their host through Sleep Sleep's worktree status drop deleted the chat's row, which the host feed then rewrote, so the outcome depended on which write landed last. Open chat tabs own their rows (same rule as terminal-tab close), and a chat only makes a workspace sleepable while its host holds the agent's process. * fix(native-chat): judge the Sleep receipt by facts, not lane occupancy A running chat's stop queues the provider's own requested-close end report onto the chat's lane, so the lane-occupancy check read every proven stop as superseded and dropped its receipt. Later work now counts only once it exists: a moved fence, a child, delivery, child work, a pending submission, a pending create or attach, or an open dispatch. * test(native-chat): report a fake close's end only for an acquired child * Preserve canonical workspace identity when sleeping * Propagate optional sleep host through file command contract * Move the deliberate-sleep PTY tests to main's spec file Main grouped the PTY connection suites (#29193) and moved this file to a .spec.ts registered from pty-connection-spawn-and-startup.test.ts. Taking main's copy here keeps the following main merge free of a modify/delete conflict; this PR's test changes are reapplied after the merge. * Reapply Sleep's teardown waits to the moved PTY spec; keep acquisition under its line limit The deliberate-sleep spec moved in #29193; this carries the PR's changes into it: a pane mounted after wake waits for issued teardown, an unqualified pane waits for every same-id host's teardown, and a queued startup waits too. The merge left claude-structured-session-acquisition.ts over 300 lines. resolveClaudeAcquisitionError, which already builds the exit-unproven error, now decides whether to release the acquisition. * test: keep the Sleep receipt checks on the closed handle and kept record * fix(sidebar): a single-workspace Sleep failure says workspace, not workspaces * fix(sidebar): Sleep stops a workspace's chats on every host that has a copy * test: drop redundant casts from the Sleep flow test state * fix(sidebar): stop a workspace's chats after its terminals, leaving main's teardown owner check as is * fix: admit workspace chat stops before teardown waits * fix: retain browser teardown caller sequencing * fix: retire failed acquisitions after observed root exit |
||
|
|
978b207cc1 |
fix(agent-status): never drop a running helper for lack of room (#30416)
* fix(agent-status): never drop a running helper for lack of room Helper state (subagents, background tasks and commands) was bounded by counts and byte budgets that refused or evicted live records once hit, so the agent kept working while Orca listed it wrong. Every one of these records already dies with its session, pane or chat, so the count and size caps go; identity-history, transcript and transport bounds stay. * fix(agent-status): keep the status field limit, measured without the subagent roster The previous commit dropped the 256 KiB status check outright; it still guards every other field. Only the roster, whose rows are bounded individually, is left out of the measurement. * fix(agent-status): fit helper previews and walk lineage once * fix(agent-status): skip an oversized Pi row instead of stopping, and parse every envelope the store can write Review follow-up: one long row no longer hides later rows that still fit the request, and the transport envelope's reader no longer refuses a size its writer now produces. |
||
|
|
add1eccae3 |
fix(codex): a Codex chat keeps its Orca identity when the user's Codex config strips command environment variables (#30406)
* fix(codex): a Codex chat's commands keep its Orca identity under a restricted shell environment policy Codex rebuilds each command's environment from the user's shell_environment_policy, so inherit = "core" dropped the chat id, marker, launcher and instance pin Orca gives the app-server. orca orchestration then could not tell which chat was calling. Every thread start and resume now sends those values as shell_environment_policy.set overrides, which Codex applies after its filter and merges per key. * fix(codex): accept the launch's read-only delete list * test(codex): a chat without visuals gets no visuals config, identity overrides aside * refactor(codex): a chat's command env is what the seal wrote, not a second key list The seal now returns the session values it set (identity, a registered worker's own handle, the CLI launcher, the instance pin and the blanked remote selectors) as commandEnv, and the composed launch carries it. The Codex acquire sends that plus the chat's scratch folder from its own visuals. This deletes the hand-kept key list, the handle prefix test and the delete-list re-check, so a key later added to the seal reaches each command too, and an inherited worker handle the seal never named no longer does. * test(codex): wait for the shell command's turn to complete before the cold resume |
||
|
|
9ea5434371 |
fix(mobile): faster relay recovery after an app switch and on a late desktop data link (#30607)
* fix(mobile): recover a relay that died during an app switch in one short probe, and retry an attach timeout through the director once Refs STA-11130. Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): redial the same cell once on attach-timeout instead of re-resolving Aligns with the relay step 6 design: reason string attach-timeout, no director re-resolve (the director names the cell the host is seated on). Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): probe decisively only on a real background return, 4s, and jitter the attach-timeout redial Co-Authored-By: Claude <noreply@anthropic.com> * test(mobile): replace type assertions with a typed fake in the relay probe tests Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d893cf5548 | chore(docs): move site runtime and Node types to 24 (#30615) | ||
|
|
503d919e64 |
feat(native-chat): + menu shows chat, disabled chat and CLI rows (#29074)
* feat(native-chat): choose which agents open as a chat
Settings → Chat gets a Providers list. Each agent the host can open as a chat
has a switch; off opens that agent in a terminal instead. Only choices the user
makes are saved. Unchosen: Claude and Codex on, others on when their CLI is
installed on this computer.
The renderer's route and the host's launch-mode decision both read one rule, so
the tab menu, shortcuts, CLI, orchestration and relayed phone launches agree.
* fix: keep chat provider settings with the other chat settings types
* test: route input carries this computer's detected agents
* feat(native-chat): the + menu shows chat, disabled chat and CLI rows
With Chat UI on, each agent whose chat provider is on gets a chat row, disabled
with the reason where the workspace's machine can't run that chat (not
installed, SSH, WSL, not on this host). Every other installed agent gets a row
marked (CLI) that opens its terminal. With Chat UI off, or on an older server
that opens no chats, the menu is unchanged.
* fix: new tab menu rows use a launch variant instead of restyling the menu item
* fix: name every route blocker the menu rows map
* fix: menu row subscriptions render on the server too
* feat(native-chat): open a chat agent's CLI from beside its + menu row
A chat row whose CLI is installed on the workspace's machine gets a › beside
it. Its side menu offers '<Agent> (CLI)', which opens that agent's terminal for
this launch only; nothing is saved and the row itself still opens a chat.
* fix: keep new tab agent surfaces authoritative
* fix: handle unknown host support reasons
* fix(chat): resolve provider choices from their owning computer
* fix: include chat launch feedback translations
* test(chat): use checked native inventory fixtures
* chore: retrigger PR checks
* test: pin pending chat row behavior
* chore: retrigger PR checks after cancellation
* Fix launch preparation lifetime and execution host startup
* fix: keep agent menu choices bound to their launch
* Simplify agent menu support reads and preserve explicit surfaces
* Keep focused menu behavior coverage within its budget
* Keep menu host fixtures typed as execution hosts
* Assert notes payload on the current launch action
* Combine agent session launch type imports
* fix(chat): keep unchosen providers on without discovery
* fix(chat): use workspace installation for menu defaults
* test(chat): type provider routing fixtures against store contracts
* fix(tab-bar): keep + menu chat rows clickable while the host answers
Each menu open discarded the host's answers, so every open showed the chat
rows disabled with 'Checking chat support' until the host replied. Answers
now outlive the menu and refresh in the background, rows use this client's
facts until the host answers, and a slow host no longer reads as a refusal.
* test(chat): align shared launch fixture with private settings reader
* fix(tab-bar): keep known + menu refusals and never treat a silent host as one
Closing the menu dropped the workspace's agent list, which changed which agents
the menu asks about and threw away the host's earlier answers, so a reopen showed
a refused chat as clickable. A support read that could not answer was stored as
"No chat on this host", disabling chats the client knows are supported.
The menu now keeps the stored agent list while closed (only fetching waits for
it to open) and only a real host answer relabels a row. The workspace's
detection target is resolved where main already resolves it (QuickLaunchButton
and the tab-bar runtime model) and passed in, so no new focus read is added.
Tab-bar test mocks now stub the QuickLaunchButton exports the menu imports.
* fix: sync runtime English catalog for agent menu
* test(chat): give the structured-session RPC fixture the private settings reader
The host now reads saved chat choices through requireStore().getSettings(); the
shared RPC fixture only had getClientSettings, so the launch-mode reader saw no
settings and create support refused Claude. The fixture now follows each test's
client settings.
* test(chat): pin create support's options argument and kept decline reason
* fix(native-chat): + menu chat rows wait disabled for the host and share its answer across tab groups
A chat the host refuses showed clickable for up to the 10s host read, then flipped to disabled,
once per tab group. Rows now stay quietly disabled until their host answers (or the read times
out), and every tab group on the same workspace host reads the same answers.
* fix(native-chat): + menu rows also wait before the host's chat list loads, and on every reopen of a host that could not answer
Asking the host no longer waits for its chat list, so Claude and Codex no longer show clickable,
then disabled, while it loads. A read that times out releases rows for that open only, so the
next open waits again instead of flipping on a late refusal. Agents not installed skip the read.
* fix(native-chat): typed + menu search puts a launchable row ahead of a disabled chat
In a WSL workspace, typing an agent's name and pressing Enter highlighted its disabled chat row,
so nothing launched where main opened the CLI. Search now ranks rows that can launch first.
* fix: preserve provider choice and cached native inventory
* feat(native-chat): hosts publish each workspace's chat availability as one stream
A new negotiated stream, agentSession.subscribeAvailability, advertised as
agent-session.structured.availability.v1. The host keeps one in-memory resource per
workspace, shared by every reader, that asks the existing createSupport check for every
registered agent in that workspace and publishes only complete generations. It answers
again when a launch setting changes, after an explicit agent refresh, and (for answers
that could not be checked) when a workspace appears; the last reader leaving retires it.
A check that could not answer is unknown, never a refusal. agentSession.agents stays
static and unchanged.
* feat(native-chat): + menus read a publishing host's workspace availability
The renderer holds the workspace's availability stream while its tab bar is mounted
(not while a menu is open), shared by every tab group, with runtime and contact fences.
It exposes a readiness model, { ready: false } | { ready: true; byAgent }, from
useHostStructuredAgentAvailability and the menu hook. On a host advertising the
capability the menu reads its answers from that model and makes no per-agent
createSupport reads; an older host keeps the per-agent reads, answer cache and deadline
unchanged. Row building and presentation are unchanged.
* fix(native-chat): workspace availability checks only what the shown workspace needs
Only the shown worktree (or an open menu) holds its host's availability stream, and only with Chat
UI on. Display checks no longer force a fresh login shell for an agent that isn't installed, so
Claude and Codex answer without waiting on it. A change to the Windows runtime default or a project's
WSL choice re-checks, and a workspace still on its first check answers again when one appears.
* fix: keep main's bytes for the create-support file the merge reformatted
The pre-commit formatter rewrapped main's unformatted lines during the merge and pushed the file past its line limit.
* test: type the + menu rows hook mocks without assertions
* test: match the availability stream's types in three tests
* test(cross-version): declare the workspace availability stream in the agent-session surface
* feat(native-chat): + menu holds a placeholder until its agents are known, and learns them while the workspace is shown
* feat(native-chat): + menu reads one agent health snapshot per machine
The + menu no longer checks chat availability per workspace. Each Orca
runtime keeps one agent health snapshot: every agent's location rule on
this machine plus a typed observation of its installed release, probed
in the server's own home. It is taken on first use and replaced only on
a launch setting change, an explicit agent Refresh (after a fresh shell
capture), or a restart, and kept without readers.
- The version probe reports supported, unsupported or inconclusive; a
launch still refuses an inconclusive run, while the snapshot reads it
as unknown rather than a refusal.
- agentSession.subscribeAvailability takes no params and publishes that
snapshot; a replay that closes the transport releases its reader.
- The renderer keeps the snapshot per host, runtime and pairing, through
lost contact and across workspace switches on the same host.
- The menu hook drops the per-open createSupport display reads, their
deadline and answer cache; older hosts show client facts and the
click's admission, unchanged, states any refusal.
* fix: a failed shell recapture still publishes the agent health snapshot
* fix(native-chat): capture fresh shell facts for every health snapshot
* fix(tab-menu): share settled agent actions across menu surfaces
* fix(tab-menu): preserve loading row line height
* feat(native-chat): the whole chat row opens its CLI submenu
Hovering a chat row in the + menu, or pressing ArrowRight on it, now opens
the "<Agent> (CLI)" submenu to the right instead of needing a separate
chevron button. Clicking the row, or Enter/Space, still starts the chat
and closes the menu; hosts pass closeMenu since a submenu trigger never
closes the menu on its own.
* fix(tab-menu): line the CLI submenu up with its chat row
* feat(native-chat): a chat row's terminal button opens its CLI
Clicking a chat row in the + menu starts a chat. Hovering the row (or
pressing ArrowRight on it) reveals a terminal button at its right edge that
opens the agent in a terminal instead. This replaces the submenu, whose
chevron made the row look like it only opened a submenu. The button's click
bubbles into the row, so the menu closes as for any item and the hosts no
longer need a closeMenu callback.
* fix(native-chat): the chat row's terminal button handles drags, arrows and screen readers
A press on + dragged onto a chat row's terminal button and released there
now opens the CLI: the row reads whether the pointer or focus is on its
button instead of the click target, which a drag-release never sets.
Arrow, Home and End keys pressed on the focused button move between rows
as they do on the row. The row is announced as "Start a <Agent> chat"
instead of including the button's label.
* fix(native-chat): chat rows read "Start a chat with <Agent>"
"Start a OpenCode chat" was ungrammatical for agents whose name starts with a vowel.
|
||
|
|
f6cc8f67f5 | fix(layout): a pane main closed stays closed when its terminal starts late (dark, layout-owner build only) (#30560) | ||
|
|
f7ce68fb1b |
fix(ssh): relinking a stopped managed server keeps the project groups it holds, and later moves don't re-send them (#30608)
* fix(ssh): relinking a stopped server keeps the groups it holds, and later moves reference them Relinking found server-held groups only through the rows in them, so an empty group renamed on the server was re-imported and refused. Delta moves re-exported a linked group whenever a new row sat in it, so a group renamed on the server refused the next Move. Both now leave the server's group as is. * fix(ssh): delta moves keep the server's record of groups the first move imported * test(ssh): cover a deleted moved group and tidy the relink group fixtures * fix(ssh): resume an older build's delta with the groups its manifest exported * fix(ssh): take no group-ownership evidence from server rows on another host * fix(ssh): a relink records only the groups the server's own list holds, with their host stamps |
||
|
|
c68c84f157 |
fix(automations): headless reuse-session sends the prompt into the existing agent (#24278) (#30594)
* fix(automations): headless reuse-session sends the prompt into the existing agent (#24278) A headless host now continues the agent a previous run left idle, using the desktop's selection rule (moved to src/shared/automation-session-reuse.ts) and the launch's reused-terminal prompt writer; it launches fresh when no agent takes the prompt. Runs of one automation now claim, precheck, pick, prompt and record one at a time, and headless retention closes that automation's terminals in the same lane, so no two ever claim one pane. A pane belongs to its newest run by dispatch order. A run completes on a done reached after it was dispatched; an idle pane alone completes it only when the pane never reported agent status, measured from dispatch. * fix(automations): keep the shared reuse rule Hermes-safe |
||
|
|
d03d731ea6 |
refactor(renderer): one twin check, one worktree-copy scan, drop stale resolver mocks (#30579)
- isTwinWorkspace replaces four inline resolveOwner(...).kind === 'ambiguous' checks - someWorktreeCopy shared by isUnprovenServerSshRoute and hasHostEvidence - file-explorer owner: drop the unreachable second unresolved guard and the stale comment - native chat owner: drop a null ternary the helper already handles - tests: delete stale getRuntimeEnvironmentIdForWorktree mocks (29 suites), one shared local+ssh twin fixture, file:// open-target tests next to their module, reset mocks.twin |
||
|
|
21af18af35 |
fix(ssh): managed servers install through SSH gateways that cut long commands (#30491) (#30549)
* fix(ssh): preserve activation commands through SSH gateways * fix(ssh): fall back when a host cannot decode a long command, and read claims and snapshot verdicts through gateway noise On top of the gateway fix (#30503): - A host without base64 or gzip gets a long command plain: the compressed wrapper checks for its decoders before reading anything, exits 125 with a marker, and execCommand resends once with compressCommand: false and remembers that host. `base64 -D` covers macOS before 13, whose base64 refuses `-d`. - Snapshot capture, restore, presence and compare answers read the last line that is a verdict, so shell noise a stderr-merging gateway appends after it is ignored. - The machine claim reads the claim line by line, and every failed step now prints CLAIM_FAILED <step> <error> with the df line, so "orcad could not claim this host's .orca-remote" says what failed and how to fix it instead of exiting 65 with the raw command or failing with no reason. A failed write no longer leaves a partial file. Refs #30491 * test(ssh): size the simulated gateway cut from the command, not a fixed 10 KiB The snapshot command repeats its temp path ~40 times, so on CI's short /tmp it fell under 10 KiB and the truncation precondition failed. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: zzzpyyy <167217540+zzzpyyy@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
68ed9db434 |
fix(terminal): relay-era SSH panes the bridge serves check printed file paths on the workspace's server (#30240)
Co-authored-by: Neil <neil@stably.ai> Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
b331710b31 |
build: move cloud and docs/site to pnpm 12 (#30600)
The root moved to pnpm 12 in #17156 but these two isolated workspaces were left on pnpm 10, so CI and deploys carried a second pnpm and pnpm/action-setup. - Pin the root's integrity-checked pnpm 12.14.0 and regenerate both lockfiles (only the pnpm 12 package-manager document is added; the dependency graph is unchanged). - Move settings pnpm 12 ignores out of .npmrc and package.json#pnpm into each pnpm-workspace.yaml; onlyBuiltDependencies becomes allowBuilds, and cloud keeps esbuild's script skipped as pnpm 10 did. package-manager-strict is gone in pnpm 12, whose own version pinning supersedes it. - docs/site drops --ignore-workspace: under pnpm 12 it skips the workspace file, losing the overrides and failing the frozen install. vercel.json now pins install/build so a stale dashboard command cannot reintroduce it. - Cloud and docs workflows use pnpm/setup@v2 like the root; the relay unit-test step uses the root's pnpm instead of npx pnpm@10. |
||
|
|
2b3b64645a |
fix(editor): opening a quoted lettered list with an unquoted continuation no longer crashes (#30247)
* fix(editor): opening a quoted lettered list with an unquoted continuation no longer crashes Marked's blockquote re-reads a lazily continued list with its built-in list(), which rejects Tiptap's extended markers (a., iii)) and returns undefined; the caller then reads .raw and the rich editor hits its error boundary. A per-lexer tokenizer falls back to the registered ordered-list tokenizer, cut where Marked's own list would end at an outer quote line. * test(editor): guard extended-marker blockquote fallback against dependency drift Source re-review against Marked 17.0.6/18 and Tiptap 3.31.3: pin the override to Marked's lazy-list call site, build the quote-break regex once per item, and cover more item starts Tiptap accepts so a bump can't reopen the crash. --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
5f126af9f8 |
fix(watcher): flush file changes on a steady cadence instead of waiting for quiet (#30270)
* perf(watcher): flush file changes on a steady cadence instead of waiting for quiet Watcher batches used a 150 ms trailing debounce capped at 500 ms, so a worktree with agents writing continuously always waited the full cap. The git status nudge had no cap and could starve until the 60 s safety poll. Batches now flush 25 ms after the first change following a quiet period, then at most every 100 ms; new events join an armed flush instead of pushing it back. * fix(watcher): keep atomic saves in one batch and survive clock jumps Raise the first-flush delay to 75 ms so the native watcher's split burst lands in one batch, cap the flush delay at one interval so a backwards clock jump can't stall delivery, and widen the editor delete debounce to cover a split across one flush interval. * test: wait past the widened editor delete debounce * fix(watcher): keep markdown list refreshing under sustained writes and widen delete grace Markdown document refresh used a resetting 125 ms debounce that never fired once watch batches arrive every 100 ms. Delete tombstones now wait two batch intervals so slow stat calls (WSL, AV scans) can't split a save into a flash. * fix(watcher): bound watcher-driven reads and scans under sustained writes One watcher-triggered editor read and one markdown scan per target in flight, with a single follow-up; markdown scans honour the latest change time; the headless Windows watcher no longer resets its refresh timer on every event. * fix(watcher): make the faster remote cadence opt-in and harden watcher-driven reloads files.watch takes an optional batchCadence; hosts keep legacy trailing batching unless the client opts in. Coalesced editor follow-ups re-check the tab and replace a hung read only once; Markdown mounts skip pre-change scans; the Windows fallback refreshes the whole tree at most every 500 ms. * fix(watcher): stop restarting the remaining watch-driven timers Combined-diff section reloads and per-file editor reload dispatch no longer push an armed timer back; a queued editor follow-up for a tab that left the screen marks it stale instead of dropping the change. * fix(watcher): let combined-diff and worktree notifications land under sustained writes A combined-diff section discards an invalidated in-flight load at most once in a row; worktree base-directory notifications no longer restart their timer. * fix(watcher): never overwrite an edited diff section and unstick hung reloads A combined-diff load never commits over a section with unsaved edits; a reload queued behind a hung read starts after the stale limit without needing another change event. * fix(watcher): recover from hung markdown scans and keep evidence for edited tabs A Markdown scan that never settles is abandoned after the join window so later changes scan again; a queued editor follow-up for a tab edited meanwhile marks it changed-on-disk and stale instead of dropping the change. * chore(watcher): use a numeric markdown scan id * fix(watcher): don't let later events join a stale echo-verification read Payloads from the same burst still share the in-flight read; a payload from a later task gets exactly one fresh read issued after the current one settles. |
||
|
|
5c71a6cc6b |
fix(watcher): stop unwatched windows from being re-subscribed to local watchers (#30412)
* fix(watcher): stop unwatched windows from being re-subscribed to local watchers Two pre-existing races left a window subscribed after it unwatched: - a re-watch waiting out a cancelled install was not tracked, so its unwatch could not withdraw it and the retry subscribed it anyway - restoring after a failed worktree removal dropped the suspended owners up front, so an owner that unwatched mid-restore was still re-subscribed Adds a randomized lifecycle model test plus regressions for both sequences. * fix(watcher): keep a retried removal's owners when it overlaps the previous restore The restore now leaves its snapshot in place while it works, so a retried removal can add owners to the same entry. Stop restoring once the snapshot is forgotten or replaced, delete it only when empty, and merge failed owners into the current entry instead of overwriting it. Apply the same per-owner restore to SSH watchers, which dropped the saved list before re-subscribing. The lifecycle model now drives removals through the real coordinator and lets them overlap restores; adds local and SSH restore regressions. * fix(watcher): drop SSH re-watches unwatched while waiting out a cancelled install Same leak as the local fix: a watch that could not join an aborted SSH install waited untracked, so its unwatch had nothing to remove and the retry installed it anyway. Record those waiters, clear them on unwatch, and retry only if the sender is still recorded. * fix(watcher): keep reloaded windows and cancelled-install waiters across removals - An older restore checked a window's lifetime only after taking its entry, so a window reloaded under the same id lost its place in a newer removal's snapshot. Check the lifetime first (local and SSH). - Removal now suspends senders waiting out a cancelled install and drops their retry; previously the retry installed mid-removal and the close tore it down, losing the window. Waiters keep their WebContents for this. - Waiter maps live in watcher state: cleared on shutdown and per sender on cleanup. - Model: windows can reload (same id, new lifetime) and subscribes can be wedged (ignore abort). |
||
|
|
e7b60c155b |
refactor(orchestration): delete the unreachable pre-mailbox terminal delivery path (#30576)
OrcaRuntimeWithDeliverPendingMessages.deliverPendingMessages was only reachable from itself and settlePendingMessageDelivery, which was only reachable from it. Live terminal mail delivery goes through OrchestrationMailboxPointerDelivery (stage/submit), so this removes the dead mixin link, its settle method, its state fields (flights, parked redeliveries, probe dedupe, pointer watermarks), and the now-unused isCursorAgentOrchestrationTarget and RuntimeMessageWaiters.typeHasLiveWaiter. No behaviour change. |
||
|
|
243a08b851 |
fix(ssh): follow a relaunched managed server's port; match quoted Windows forward argv (#30583)
* fix(ssh): follow the managed server's port after activation and match quoted Windows forward argv An update, rollback or recovery that launches a managed server now rebuilds the tunnel so it follows the port the new server bound. Windows orphaned-forward recovery now parses the process table's quoted command line instead of comparing it to a space-joined argv, so a key path with spaces still matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ssh): keep a successful activation when the tunnel rebuild fails; strip one LogLevel pair Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(ssh): soften the tunnel rebuild failure comment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ssh): tokenize posix expected argv like the process table before normalizing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
75b6fa2201 |
fix(runtime): project removal and edits stay on the selected project's host (#30584)
* fix(runtime): project removal and settings writes keep to the resolved execution host - Removing a project now closes its native Claude/Codex chats on this host and retires their tabs. - A terminal spawn after removal is admitted only by a surviving row on the spawn's own host. - repo.update and repo.setBaseRef write the resolved row's host, not a same-id sibling. * fix(runtime): close project chats alongside the terminal stop, with their own floor - A failed or budget-spent terminal stop no longer skips the chat closes. - Spawn admission still refuses on any host once no row with the removed id survives. |
||
|
|
6d8e4cfef2 |
refactor(shell): one shell-argument-quoting module for the duplicated quote copies (#30564)
* refactor(shell): one shell-argument-quoting module for the duplicated quote copies Each former copy now calls the canonical function with its exact spelling; a table test pins byte-identical output for every copy over hostile inputs. Distinct spellings stay distinct functions: '\'' vs '"'"', the two plain-word sets, and the cmd.exe doubled quote. Co-Authored-By: Claude <noreply@anthropic.com> * style: keep the quoting import at the top of two import-less files Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
59b03cab00 |
fix(ssh): rehome a cold-start converted workspace the scan missed, and notify replayed background-turn completions (#30578)
* fix(ssh): rehome a converted workspace after a cold-start scan misses it, and notify replayed background-turn completions A cold-start conversion now lists the server's worktrees itself when startup's scan finishes without the destination row (the scan predated the server's projects or failed for them). A reconnect replay of a working row carrying a new turnCompletedAt is now observed instead of seeded; the completion observer already dedupes the stamps it handled. * test(ssh): drive the cold-start rehome retry through the server RPC |
||
|
|
e5184c4c78 |
refactor(renderer): fold browser creation, file actions and file preview off the deprecated owner resolver (#30562)
* refactor(renderer): fold browser creation, file actions and doc-preview callers off the deprecated owner resolver The creation-action policy and browser creation (#3, #48) fold together, so the provider the policy offers is the one creation uses. Browser panes, page lifecycle and conversion, tab duplication, the explorer tree pane, doc-preview actions and editor path moves (#21, #43, #45-#47, #49, #50) read runtimeEnvironmentIdWithLegacyFallbackUnlessTwin. File-explorer writes on a folder (#51) take the owner from the rows alone, never from the focused server. Deprecated importers 16 -> 6. * refactor(renderer): fold file preview off the deprecated owner resolver; Open in Orca Browser refuses a twin A project two hosts publish reads as no server for the doc-preview plan, but the file:// path now refuses it with its own message instead of opening this machine's copy (#7). Deprecated importers 6 -> 5. * fix(renderer): a twin's file preview and open-externally refuse instead of opening this machine's copy Codex P2 on the F7 folds: a folder twin read as no server, so the preview plan built a local file:// tab and open-externally handed the path to this machine's OS. Both now check the owner for ambiguity first. Also fix two test fixtures that broke the renderer typecheck. * refactor(renderer): move the file:// open target out of file-preview.ts file-preview.ts crossed its line limit after main grew it; the file:// target, its type and its two refusal messages move to workspace-file-browser-open-target.ts, which they never depended on file-preview for. * test: mock the replacement resolver in the quick-action availability suite (Codex P2) |
||
|
|
767b55b1b6 |
fix(terminal): client pane names reach a desktop host's window, and open panes follow the host's (#30496)
* fix(terminal): client pane names reach a desktop host's window, and open panes follow the host's - A host with its own desktop window ignored client pane-layout writes, so a client's pane rename never reached the window. updateMobileSessionPaneLayout now passes the client's pane names to the window one pane at a time through the set-pane-title path, and still leaves the window's geometry and the tab's name alone. A single pane named after its own tab is dropped, not forwarded. - Open panes on a client only read host pane names when they mounted. They now follow the host's published titlesByLeafId live. A local rename is held until the host echoes it, so a snapshot taken before the host saw it cannot undo it. No wire change: hosts already publish titlesByLeafId, and old peers keep their current behaviour. * fix(terminal): hold a client's pane rename over host snapshots until echoed, and fail a dropped window delivery * test(e2e): read the created host tab id without a type assertion * fix(terminal): only an explicit client rename reaches a desktop host's window, and local rename holds are bounded - A client push now names the panes it renamed (optional `renamedLeafIds`), and the host forwards only those. A resent title map, which older clients and every divider drag send, can no longer revert or clear a name set on the host. Older hosts strip the field. Older clients send none, so their renames stay local as they do today. - A held local rename now gives way to a newer host name, a failed push, or a 15s expiry, and expired holds are pruned. A host that never echoes (an older desktop host, or a single-pane name dropped as the tab's own) can no longer hide host titles forever. * fix(terminal): a failed rename push retries with its rename, and an earlier echo cannot undo a newer rename * fix(terminal): a pane rename bypasses the unchanged-layout push dedupe |
||
|
|
df7259e2ae |
fix(terminal): reattach a restored terminal while an older daemon cannot answer (#30486) (#30499)
* fix(terminal): reattach a restored terminal while an older daemon cannot answer (#30486) A window save erased the PTY incarnation main persisted for each pane, so after a restart every reattach lacked the generation proof the owner resolver needs. With any older daemon generation that could not answer, the inventory stayed incomplete and every restored terminal was refused as unverifiable, on every Retry. - keep each pane's incarnation across a window save that still binds it - accept an exact incarnation match from a reachable daemon even when another daemon's inventory fails (incarnation ids are unique per spawn) - drop a provably exited older daemon from the reattach inventory - offer Open new terminal beside Retry on the unverifiable toast * test(daemon): build the unanswering daemon endpoint with the platform socket path * fix(session): keep each pane's incarnation when a save carries only some of them * fix(daemon): judge a replacement legacy daemon's exit by its own pid * test(daemon): router fixture reports a pid that exists, so exit proof never sees a dead daemon * test(daemon): the wedged older daemon names a live pid instead of the shared fixture's arbitrary one * fix(daemon): a spawn treats an older daemon as exited only when its socket and pid are both gone |
||
|
|
d2bb60735b |
test(browser): document blocked popup compatibility gates (#30537)
* test(browser): expose native popup admission blockers with faithful app events * test(browser): probe Electron popup identity and inherited sandbox API limits * test(browser): handle requests without associated contents in popup probe * docs(browser): record blocked Electron popup compatibility gates and test routing * docs(browser): keep popup evidence in PR |
||
|
|
7395d30dc7 |
The host raises the shared unread dot whatever each client's Filters say (dark) (#30581)
* fix(sidebar): the host raises the shared unread dot regardless of Filters (dark) * fix(sidebar): tighten the shared-dot comment and tests after review |
||
|
|
6c40f9190d |
Editor files that share a path across two hosts: the tab lookup, the load report and the phone close match the right file (dark, layout-owner build only) (#30569)
* fix(layout): two owners' files at one path keep their own host, load report entry and phone retire (layout-owner build only) The bare-path tab's file lookup took the first record of its path, the load report keyed file records by path alone, and a headless host's phone retire found and removed records by path. * fix(layout): a path-id preview tab's flag stays on its own owner's record, and a stale phone id retires nothing (layout-owner build only) |
||
|
|
e8d7da029c |
An e2e test cuts the window's layout feed mid-session and checks it recovers and survives a restart (dark, layout-owner build only) (#30574)
* test(layout): an e2e ends the window's layout stream mid-session and checks the reopen and a relaunch (dark, layout-owner build only) * test(layout): the stream-loss e2e covers main's change during the gap deterministically and reuses the shared pane-tree and frame readers (dark, layout-owner build only) * test(layout): the stream-loss e2e names how long main's create took when it misses the reopen window (dark, layout-owner build only) |
||
|
|
3ef5a46061 |
Revert "fix(native-chat): Claude, Pi and ACP chats are Working only while the agent holds the send (#30134)" (#30572)
This reverts commit
|
||
|
|
f48123e89a |
feat(agent-status): show OSC 7501 program status in rows and tabs (#30262)
Builds on the program-status core: - A determinate progress ring replaces the working spinner when a program reports progress (rows and tabs; percent only in tooltip/aria), in the attention tone while blocked. The working color becomes the --agent-working token. - Non-agent programs (brew, terraform, …) get their first letter as icon. - The program's own one-line message shows as the row preview, a failed program reads "Failed: <message>", and a block such as sign-in that the row's state does not show raises attention. - A finished program's result outlives its exit until the next keypress or command in that pane, as the protocol asks; real agents are unchanged. |
||
|
|
07bf286c7b |
build: use the shared pnpm store on Windows by default (#30565)
Without the symlink privilege pnpm links the global virtual store with junctions, which standard users can create. Verified on Windows 10.0.26200 at medium integrity with no SeCreateSymbolicLinkPrivilege and Developer Mode off: the install, both native runtime checks, a ConPTY spawn and windows-process-tree all pass. ORCA_PNPM_GLOBAL_VIRTUAL_STORE=0 still opts out; CI stays off. |
||
|
|
3051b7b671 |
One background-process read of "Notify About Hidden Workspaces" for phone pushes and the host unread flag (#30571)
* refactor(notifications): one host read of Notify About Hidden Workspaces, used by phone pushes and the host unread flag * fix(notifications): folder chat phone pushes read the folder's name; skip the hidden check with no Filters switch on |
||
|
|
62975d6bfe |
fix(sidebar): run previous and next worktree shortcuts from a text field (#28891)
* fix(sidebar): run previous and next worktree shortcuts from a text field The worktree list swallowed every key while a text field was focused, so the bound shortcut did nothing in chat, markdown, and the code editor. * fix(sidebar): keep the default worktree chord with text fields Previous/next worktree now carries a text-field rule in its keybinding definition. In a text field the default Cmd/Ctrl+Shift+Up/Down keeps its select-to-start/end meaning, while a chord the user bound still switches worktrees from chat, markdown and the code editor. Option and AltGr chords that type a character stay with the field. * fix(sidebar): let user-bound Option arrow chords leave text fields On Mac only Option chords that type a character or dead key stay with a focused text field, so a bound Option+Down now switches worktrees from chat, markdown and the code editor. |
||
|
|
05688e7856 |
fix(git): stop starting Git in deleted project folders and for removed projects (#28822)
* fix(worktrees): skip Git sweeps for missing repo folders * fix(worktrees): skip missing roots in headless scans * fix(worktrees): check missing roots at Git listing boundary * test(git): treat branch removal fixture as existing repo * test(git): keep mocked worktree roots present for Git cases * test(git): await host preflight in scan sharing fixtures * revert(worktrees): drop the listing pre-check for missing repo folders Reverts |
||
|
|
8250c93f3e |
fix(sidebar): show the project name on ungrouped compact cards (#28890)
* fix(sidebar): show the project name on ungrouped compact cards Compact and new-card layouts kept the project in an icon chip, with the name only in a tooltip. The chip now shows the project name when the sidebar is not grouped by project. * fix(sidebar): show the project name on pinned chips too, muted and lowercase * docs(sidebar): describe repo identity placement, not the chip's contents * fix(sidebar): keep emoji project icons inside the chip |
||
|
|
80e2c68058 |
fix(mobile): one button style for the session header and tab strip; size tabs to titles (#29222)
* fix(mobile): put the session tabs in the top bar on a landscape phone A sideways phone is about 390pt tall and the session header spent two rows on it. On a landscape phone the tab strip now shares the top bar with the back button, title and panel icons. Portrait and tablet layouts are unchanged. * fix(mobile): give session tabs side borders and a filled active tab In the landscape top bar the tabs sat between the title and the panel icons with nothing marking where one tab ended. Each tab now has a 1pt border on its sides, running the full height of the bar, and the active tab gets the raised fill, as the desktop tab strip draws them. The same strip is used in portrait, so both orientations match. * fix(mobile): one button style for the session header and tab strip; size tabs to titles The header had three button sizes and the new-tab and quick-commands buttons were bare 16pt glyphs in a 40x36 area with no extra touch room, so "+" was hard to hit. Every header and tab-strip button now uses the same 36pt rounded button with an 18pt icon and a 44pt touch area. Tabs size to their titles (44 to 128pt) instead of a fixed 128pt, the strip row is 44pt, terminal tabs get a terminal icon, and tabs expose the tab role and selected state to screen readers. * fix(mobile): reset tab strip measurements on view attachment * style(mobile): format tab strip ref dependencies * fix(mobile): reserve independent header button touch targets * fix(mobile): type header button test against its component * fix(mobile): keep two header rows in landscape windows too narrow for top-bar tabs A short, narrow window (a ~390x350 web window) is wider than tall, so it counted as a landscape phone and moved the tabs into a top bar that has no width left for them. Top-bar tabs now also need at least 568pt of width, the narrowest landscape phone. * fix(mobile): give the landscape top-bar tab strip a fixed left edge so scrolled tabs never read as part of the title * chore: recompute mergeability after retarget to main |
||
|
|
d45894c280 | test(floating-terminal): mock the bulk-close confirmation the panel suite cannot render (#30568) | ||
|
|
e3c9370da4 |
feat(agent-status): read OSC 7501 program status beside hooks (#30261)
Programs can tell Orca what they are doing through the Program Status Protocol (OSC 7501); Claude Code, Codex, Grok, pi and oh-my-pi already do. Orca answers the protocol's feature-detection probe (visible xterm and the hidden-pane emulator), parses reports from PTY output on the execution host (src/shared/program-status/), and keeps two lanes: - Lifecycle state keeps one owner per pane. Hooks win; OSC 7501 drives the row when hooks are off, missing, or silent on a state the program has left (server/program-status/hook-precedence). - The program's own report (progress, its one-line message, blocks such as sign-in) is a live-only `programStatus` facet kept per pane (server/program-status/facet-store), stitched onto rows at publish time and carried to the renderer store, mobile, paired clients and `worktree ps`. Facet-only changes patch just the facet. Title-driven readers (idle edges, delivery, tui-idle, exit inference) classify by the program's OSC 7501 activity while it reports one and fall back to title glyphs only for agents that report nothing explicit, so they keep working when Claude stops painting its title glyph after the probe. |
||
|
|
70d00aa8c5 |
refactor(agent-status): one live agent transition detector in main, read by the sort stamps (#30532)
* refactor(agent-status): one live agent transition detector in main, read by the sort stamps Co-Authored-By: Claude <noreply@anthropic.com> * fix(agent-status): isolate transition readers from each other; doc touch-ups Co-Authored-By: Claude <noreply@anthropic.com> * chore: rewrap the stamps producer doc; restore the console spy in a finally Co-Authored-By: Claude <noreply@anthropic.com> * test(sort-stamps): name the title-to-hook handover gap the flip must close Co-Authored-By: Claude <noreply@anthropic.com> * fix(agent-status): forward every hook observation so the stamps hand title panes to hooks as before The stamps producer applied only the detector's transitions, so a pane its title showed as Working kept that tier when its first hook row was a finish, a stop, or had no workspace. The detector now reports each live look at a pane's hook rows; the producer applies those exactly as it applied store reads. One workspace type and comparison. Startup-test fakes gain the runtime's new members. Co-Authored-By: Claude <noreply@anthropic.com> * test(sort-stamps): pin the title-pane preconditions and a no-workspace row at attach; doc wording Co-Authored-By: Claude <noreply@anthropic.com> * refactor(runtime): move the agent transition wiring into its own runtime layer; one workspace type orca-runtime.ts was over the max-lines limit. The detector and attachAgentStatusStore now live in OrcaRuntimeWithAgentTransitions, and the sidebar layer overrides the attach to subscribe its producers first. Sort-stamp records use AgentPaneWorkspace. Co-Authored-By: Claude <noreply@anthropic.com> * chore(runtime): name the new base in sidebarFactsRuntime; accurate attach comment Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
c54e9fb497 |
fix(wsl): repair main typecheck after ssh-connection-utils split (#30566)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fc0d3d2f8f |
feat(native-chat): /resume in a structured chat opens Agent Session History (#28133)
* Give Session History's rows one decision for open and resume A session row decided "can ▶ resume this" twice: once for the button's disabled state and again inside its click handler, and the chat/CLI and switch-to-pane offers were each gated inline. A second caller of those offers would have had to copy the same conditions again. - One projection, resolveAiVaultSessionRowChatActions, now decides every open/resume offer a row makes; ▶'s greyed-out state and its target come from the same value. - The panel's host-scope menu and per-session resume lookups move into their own hooks unchanged, and the session-list request reads the panel's workspace through one selector. The sidebar behaves exactly as before. * Let Session History host a chat-bound panel with keyboard selection A second Session History panel (the /resume dialog that follows) needs to describe a chat's own workspace rather than the active one, and to let ↑/↓/Enter pick a row. Mounting it beside the sidebar also exposed a place where two panels stepped on each other. - AiVaultPanel takes an optional workspace, an initial query, a header action slot, a small search handle, and onSessionOpened, which also turns on row keys. A thin sidebar wrapper now owns Settings' "focus the search" request, so only the sidebar widens to All. - Keyboard rows store the chosen row, its session and the search text; a session listed twice no longer traps the arrows, and editing the search starts again at the top match. Enter takes the row's best offer: switch to it if open, else open it as a chat, else the row's own ▶, else show details. A clicked row moves focus to the panel, so the keys keep working, and a row scrolled into view stops below the pinned group header. - Switching to an open session reports whether it found something to switch to. - Mounted panels share one copy of the saved view options, so a filter changed in one is not undone by the other. * Make /resume in a structured native chat open Agent Session History Resuming a past session was only reachable from the Agents sidebar, so people in a chat had no way to find it. In a structured native chat, /resume [search] now opens Agent Session History for the chat's own workspace in a dialog, with the search filled in. - The dialog lives in the app's modal slot: a dialog rendered inside a chat would bubble its key and pointer events through the chat's own capture handlers. It closes once the tab on screen changes or a switch to an open session succeeds; a failed action leaves it open. Focus goes back to the chat unless the user was taken elsewhere. - /resume is an ordinary row in the structured chat's / menu, in place of an agent command of that name, or last when there is none. A skill named resume keeps its own row and a picked skill still goes to the agent. Typing it in any case opens the dialog; attachments stay. - Terminal-backed chats (SSH, WSL runtimes, custom start folders, reused terminals, older hosts) are unchanged: /resume reaches the agent as before. The phone is unchanged too. * Start /resume's Session History on the chat's own agent The dialog's agent filter starts on the agent recorded for the chat, the same one its tab icon shows, and stays local to the dialog: the sidebar's saved filter is never read or written by it. Sort, grouping and limit stay shared. An agent the filter doesn't list leaves every agent shown. * Start an OpenCode chat's Session History on both OpenCode names A structured OpenCode chat records its provider as opencode, but the scanner lists current OpenCode sessions as opencode2, so filtering on the recorded name alone hid every OpenCode row, the chat's own included. * feat(native-chat): /resume rows resume the conversation in place of the chat In the /resume dialog a row is now only a "resume here" target: clicking it or pressing Enter opens the picked conversation as a chat right after the chat /resume was typed in, and closes that chat once the new one is confirmed and the old one is idle. A conversation already open elsewhere is switched to, the chat's own conversation just closes the dialog, a closed chat of this workspace and agent is reopened in its place, and anything else only expands its details. A failed resume closes its own tab and leaves the old chat as it was. Dialog rows drop every other action (overflow and context menus, title drag, hover buttons, detail and subagent buttons); the chevron still shows the details. The dialog widens from 480px to 560px. The sidebar's Session History panel is unchanged. * test(native-chat): type the launch-plan mock in the /resume flow test * fix(native-chat): find the reopened chat by its tab, typed test settlement * fix(native-chat): /resume picker review fixes (R5) - Subagent lines in a picker row offer no View Log either (resume null switches them off). - An unsaved row in the picker drops the "open the log" hint, since the picker has no log button. - The sidebar title drag handler is restored, so a row whose resume is disabled still blocks text drags. - Only a failed resume closes its new tab; an unknown outcome keeps both tabs (the new one has its own badge and Retry). - The replaced chat closes only when its last turn finished, its composer draft is empty, and it is not pinned. - One pick at a time: a pick is ignored while an earlier one is still settling. * fix(native-chat): a new, empty chat is idle for /resume replacement A chat with no status entry closes when its launch has finished and its host, observed live, reports no message and no provider still starting. Still-starting and unobservable chats stay open. * fix(native-chat): /resume knows a stale row's open chat; no Copy in picker details - A row listed before its chat took the conversation over has no ownership link. The pick now also reads each open chat's own record (its host status feed's provider session, else the conversation its launch resumes): the chat /resume was typed in just closes the dialog, and another open chat is switched to, instead of trying the takeover again and failing. - The first prompt's Copy button is hidden in picker rows (same switch). * fix(native-chat): act on the host's ownership verdict for stale /resume rows Drop the renderer's own "which chat holds this conversation" lookup: it keyed on the record's head handle, which differs from the host's ownership key (Pi rows, older chain links). Instead, when a resume-here launch is refused because the conversation is held elsewhere, the refusal's owner decides: this chat closes the picker, another chat is switched to, the temporary tab closes, and no failure toast is shown. Rows with an ownership link keep the existing fast path. * refactor(native-chat): share the composer editor setters with the /resume hook Keeps NativeChatComposer under its line cap once current main is merged in. * feat(native-chat): /resume replaces its chat, asking first only when the agent is busy Picking a row now always closes the chat /resume was typed in once the picked conversation's chat is confirmed, pinned or not; the unsent-draft and finished-turn checks are gone. When that chat's agent is replying, waiting on an approval or question, or still starting, Session History closes and a small "Replace this chat?" prompt asks first; Cancel or Esc changes nothing. The prompt reuses the shared confirmation dialog with a new compact look matching the close prompts. * fix(ui): compact confirmation look through dialog title/description sizes The primitives own their typography, so the short close-prompt look is a size variant on DialogTitle and DialogDescription rather than class overrides. * fix(native-chat): command picker takes the full structured transport * fix(ui): compact confirmation width through a DialogContent size The /resume replace prompt matches the close prompts width (max-w-sm) via a compact size on the primitive; callers that pass no size render as before. * fix(editor): pan-zoom dialog props keep their own image size DialogContent now has a size prop; the pan-zoom dialog s size means the image, so it is omitted from the inherited props. * fix(native-chat): /resume asks when the chat's state is unknown; prompt ignores the opening gesture - R8-1: a chat whose host isn't observed live (old host without the status feed, lost contact, no host stamped on the tab) counts as busy, so replacing it asks. - R8-2: the confirmation dialog takes ignoresOpeningGesture: an outside press doesn't dismiss it, a repeated Enter doesn't confirm, and a double-click's second click doesn't press Replace. Other callers are unchanged. * Point the resume-command test at the moved picker-items module * Reopen a closed owner in place of the chat on a refused /resume A row listed before its conversation was adopted has no structured link, so /resume launches a new chat and the host refuses it, naming the closed chat that holds the conversation. That branch reopened the owner but never closed the source chat. Both the reveal path and the refusal path now share one reveal-in-place step; an owner open in another tab is still switched to and the source kept. * Put a reopened chat in the replaced chat's slot Reveal appends the reopened tab to the end of its group, so a /resume that reopened a closed chat left it after unrelated tabs. Move it right after the source with the existing anchor-insertion helper before the source closes, the way a new chat lands. * Keep the typed search when the /resume row is picked With the caret right after /resume and a search typed past it, picking the menu row opened Session History with an empty search. Read the search from the draft, as a typed send does. |
||
|
|
535e995565 |
fix(relay): log controls a cell closes after host proof but before wiring (#30559)
* fix(relay): log controls a cell closes after host proof but before wiring * fix(relay): log a pre-refusal hang-up as the hang-up and share printableCloseReason * refactor(relay): move printableCloseReason into its own module to break the import cycle * test(relay): assemble the fake pg URL at runtime so the secret scan passes |
||
|
|
9a662c3980 |
Native chat: rewind works at any chat length (#30394)
* fix(native-chat): rewind works at any chat length Rewind copied the kept part of the chat into its saved record and, for Codex, re-read and re-translated the whole provider history before and after the revert. Both were bounded (10,000 rows / 2 MB host, 1,024 entries / 2 MB Codex), so long chats were refused with "too long to go back in". The record now names the cut; the kept rows are derived from the journal, which keeps the old epoch until the rewind completes. Codex reads only the turns it undoes and trusts thread/revert; recovery proves an applied revert from the newest turn alone and never truncates otherwise. * test(codex): type the rewind fixture's page params instead of asserting them * fix(native-chat): rewind recovery reads the provider handle only from a record that exists * test: run the rewind cut test in the SQLite runtime project * fix(native-chat): omit rejected submissions from rewound history |
||
|
|
43a6cfe538 |
Confirm before bulk-closing tabs (#30551)
* Confirm bulk tab closes before closing tabs Close Others, Close Tabs to the Left/Right, and Close All Editor Tabs now show a confirmation dialog first, so one misclick or shortcut no longer closes a group of tabs at once. Pinned tabs are still skipped. Tabs are selected again after the user confirms. If a tab was pinned, moved, or removed while the dialog was open, it is left alone. Tabs created after the request are not closed. The shared confirmation lives in use-bulk-tab-close-confirmation.ts and is used by both the tab bar and the tab group. The tab group hook now reads the store directly instead of taking group and groupTabs as props. * Confirm bulk tab closes before closing tabs Close Others, Close Tabs to the Left/Right, and Close All Editor Tabs now show a confirmation dialog first, so one misclick or shortcut no longer closes a group of tabs at once. Pinned tabs are still skipped. Tabs are selected again after the user confirms. If a tab was pinned, moved, or removed while the dialog was open, it is left alone. Tabs created after the request are not closed. The shared confirmation lives in use-bulk-tab-close-confirmation.ts and is used by both the tab bar and the tab group. The tab group hook now reads the store directly instead of taking group and groupTabs as props. |
||
|
|
81730c0b3d |
Track usage of closing tabs to left and right (#30557)
* Add telemetry for directional tab close requests Measure how often people use Close Tabs To The Left and Right, without recording tab titles, paths, or workspace identifiers. - Sample a stable 10% of installs by hashing the install ID, so membership survives restarts. The app-open event carries the sampled flag so the group can be analysed. - Cap each direction to one event per app session, tracked in main, so repeated clicks or extra windows cannot inflate volume. - Count the request before the close flow runs, so cancelled save prompts still count. - Route both directions through one helper that counts eligible unpinned tabs in visible order. * Count settings opt-in sessions in tab-close sampling denominator Enabling telemetry from settings now fires `app_opened` for that session, not only when the opt-in came from the pending banner. Before, settings opt-ins never emitted `app_opened`, so those sessions were missing from the adoption denominator for directional tab close events. - `trackAppOpenedOnce` now marks the session as tracked only when `track()` accepts the event. An opt-out or environment consent override no longer uses up the session's denominator. - Tests cover sampling for settings opt-in and the `DO_NOT_TRACK` override. |
||
|
|
46be7e23ee |
feat(settings): sign Antigravity chats in from Accounts (#28705)
* feat(accounts): sign Antigravity chats in from the Accounts page
Adds an 'Antigravity chats' row to the Antigravity section of Accounts
(behind the native chat setting, this device only): Sign in starts the
host's chat sign-in, follows it with the Link poll, opens the Google page
once, offers Copy link and Cancel, and shows one status line per phase.
The saved line reflects the sign-in file only, never that it works.
* Keep Antigravity chat sign-in bound to the current view
* feat(settings): put Antigravity chat sign-in under its install row in Chat > Providers and Accounts
The chats' sign-in row now sits directly below the Antigravity install row,
in Settings > Chat > Providers and under the agy section of Accounts, with copy
that says it is separate from the agy account. Its button reads Sign in, Change
account or Retry sign-in by state, and stays visible but disabled while the host
has no runnable server; the install row above says why.
* fix(settings): both Antigravity chat sign-in rows share one sign-in per host and pick up one already running
Settings shows the sign-in row under Chat > Providers and under Accounts at once; each kept its own
flow, so starting one silently cancelled the other and a sign-in nobody watched could not be seen or
cancelled. One entry per host now holds the flow and the saved sign-in for every row, follows the
host's Link poll while a row shows it, and on mount adopts the running flow the host names. Sign in
also waits while an install or Remove runs, read from the same install status.
* fix(settings): a failed sign-in poll re-reads the host so a running sign-in stays cancellable, and only a Remove (not an Update) holds Sign in
* fix(settings): the sign-in rows follow the host's sign-in status through the same loop as install, so an idle page sees another device's sign-in
* refactor(settings): install and sign-in rows share one subscription store; Install and Remove wait for a running sign-in, and Sign in waits for the host's answer
Both stores now follow the host through one generic subscription store
(snapshot, then a frame per newer revision, resubscribe with backoff on loss),
replacing the sign-in store's long-poll of a method the host no longer has.
While a sign-in runs, Install, Update and Remove are disabled (Cancel stays).
The sign-in row keeps Sign in disabled until the host answers, says when the
status could not be read instead of showing signed out, and sends one start or
cancel at a time.
* feat(settings): paste the last sign-in page's address to finish an Antigravity chat sign-in
While a chat sign-in waits on the browser, its row shows a field for the address of the last page after signing in, for when that page doesn't load because the browser is on another computer than the host. Continue sends it to the host, which passes it on to Antigravity; the host's refusal shows under the field. With that, remote hosts offer chat sign-in on the Accounts page too, and the row reads "Finishing sign-in…" while the host completes it.
* test(settings): give each host its own fake sign-in status and stub the new subscription export
The sign-in row tests shared one fake host across every owner, so the first test's finished sign-in made the next row show "Change account" instead of "Sign in", and rows left mounted after that failure doubled the later tests' matches. Each owner now has its own fake host behind the subscription, and every test cleans up its render. The Antigravity accounts section test's client mock also lacked the sign-in exports the status store now reads at import.
* feat(settings): the Antigravity chat sign-in row follows the host's sign-in answer
The host no longer reports whether a sign-in file exists; it reports what the
server last answered (signed in, signed out, or not known yet), and a running
sign-in started by another client arrives without its id.
The row now reads that answer:
- "Signed in on {host}." or "Not signed in on {host}."; no line until the server
has answered, with Sign in still enabled. Change account shows only when the
server said signed in.
- A sign-in another client is running reads "Sign-in is in progress in another
client.", with Sign in disabled and no Cancel, copy link or paste field, since
only the client that started it can act on it.
- "Finishing sign-in..." also follows a browser sign-in while the host checks it.
- Cancel and paste send only this client's own sign-in; the host's refusals
show under the row.
* fix(chat): a signed-out Antigravity chat says to sign in under Settings, not what the agent printed
The notice read "Sign in to Antigravity. The agent asked to sign in in a
browser": the second sentence was Orca's own log text for the sign-in prompt the
agent printed, shown as if it were the agent's words, and the notice never said
where to sign in.
The connection now closes with a sign-in prompt error whose message stays in the
logs, so the chat shows only Orca's sign-in sentence. Antigravity gets its own
sentence, in the shape Claude's already has: "Antigravity isn't signed in. Sign
in under Settings → Accounts → Antigravity." Other agents' sign-in words, and
the agent's own words in a sign-in answer, are unchanged.
* fix(chat): a sign-in prompt at chat start keeps Orca's words out of the signed-out notice
A signed-out Antigravity chat still read "... The agent asked to sign in in a
browser". The connection closes with a sign-in prompt error whose message is
Orca's log text, but the request wrapper and session setup re-typed every
sign-in answer by copying its message into a new error. The copy lost the
sign-in prompt type, so its message was quoted as the agent's own words.
Re-typing now keeps a sign-in prompt close as it is, at both the request
wrapper and session setup, so the notice says only where to sign in.
* fix(settings): the Antigravity sign-in row shows the host's answer once a sign-in is cancelled or timed out
After a cancelled or timed-out sign-in the row kept reading "Sign-in
cancelled." even when the host had answered that this computer is not signed
in. A finished sign-in now only stands in until the host answers; then the row
reads "Not signed in on <host>." or "Signed in on <host>.". A running sign-in
still shows its step.
* fix(settings): a wrong pasted sign-in address shows Orca's reason, not the browser's URL bubble
The paste field is a URL input, so the browser's own "Please enter a URL."
check ran before Orca could explain what was wrong with the address. The form
now skips the browser's check and shows the host's refusal sentence.
* style(settings): the Antigravity chat sign-in row reads as a plain row like the install row
The sign-in row sat in its own bordered box, the same bright outline the install
row lost when it became a single grey status line. It now has no box: a
medium-weight "Antigravity chats" title, the description and the grey status
line under it (errors inline in the destructive colour), and its action at the
row's right end. Sign in and Retry are primary buttons, so they stand out like
Install; Change account stays outline and Cancel stays ghost. The paste-the-
address form sits under the row without a box. Copy, keys and behaviour are
unchanged.
* ci: run the PR checks against main after the retarget
* fix(settings): list every sign-in phase in the status line switch
* test(chat): name Antigravity as the chat's agent in its signed-out sentence test
* feat(settings): Antigravity provider row with a ⋯ menu and its own sign-in area
The Providers row's status line now says only the install state. Update,
Reinstall and Remove move into a ⋯ menu beside the switch, shown for a copy
on disk; Remove keeps its confirmation. Sign-in moves into a quiet inset
area under the row, shown once a copy runs, with one sentence and one
action per state. Accounts keeps its sign-in row; its install row gets the
same menu.
* fix(settings): a damaged runtime offers Reinstall as its row's button
A damaged copy's repair is the next step, so it sits where Install does instead of inside the ⋯ menu, which then offers only Remove.
|
||
|
|
24e218c101 |
refactor(shared): move orcad-migration-* into src/shared/orcad-migration/ (#30536)
Pure git mv plus import and path-literal rewrites. Two files #29339 edits stay put: orcad-migration-dormant-state-validation.ts and orcad-migration-manifest-contract.test.ts. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
829d9448f1 |
test(wsl): exercise native chat on real WSL2 hosts (#28973)
* test(wsl): exercise structured chat on real guest hosts * test(wsl): keep chat contract within the module size limit * test(wsl): use braced conditions in chat lane * test(wsl): configure the fixture through host launch settings * test(wsl): keep cancellation assertion at the RPC boundary * test(wsl): avoid assuming cancelled turn publication timing * ci(wsl): run guest chat coverage for Claude environment changes * test(ci): match the WSL lane's merged unit-test exclusion * ci(wsl): drop lane trigger paths that no longer exist * ci(wsl): run the WSL lane when the shared process-host package changes |