Commit Graph
16131 Commits
Author SHA1 Message Date
5ce9d58b2d fix(agent-hooks): force C numeric locale on POSIX curl timeouts (#19600)
* fix(agent-hooks): force C numeric locale on POSIX curl timeouts

curl parses --connect-timeout/--max-time with locale-sensitive strtod, so comma-decimal locales reject 0.5 and every hook silently falls into the restart spool.

Fixes #19578

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(agent-hooks): neutralize LC_ALL before setting LC_NUMERIC=C on POSIX curl

Signed-off-by: siddiqui irshad <mohdirshad1306@gmail.com>

* style(agent-hooks): format scoped curl commands

* test(hooks): expect scoped curl locale in Muse install script

---------

Signed-off-by: siddiqui irshad <mohdirshad1306@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 02:17:08 -07:00
Neil 1e341b6edf fix(terminal): reject creates for durably closed tab identities (#32190) 2026-10-24 02:16:29 -07:00
Neil 1af7bc6c77 fix(runtime): preserve working hooks across idle title repaints (#32195) 2026-10-24 02:11:07 -07:00
Neil 23e5d99856 fix(mobile): remove eager regex lookbehind from paired web routes (#32906) 2026-10-24 02:07:47 -07:00
Yeji HanandNeil 4948524c84 fix(editor): enable Lean syntax highlighting (#31930)
* fix(editor): enable Lean syntax highlighting

* fix(editor): enable Lean syntax highlighting

Adapt #31930 onto current main and retain its bundled Lean grammar repair. Register the lazy TextMate provider and cover detection, proof syntax, nested comments, and bounded tokenization.

* test(editor): verify Lean highlighting in the full app

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-24 02:07:43 -07:00
Neil f6ae05898f refactor(renderer): group session tab activation and cancellation (#33100) 2026-10-24 01:58:30 -07:00
OrcaWinandOrcaWin 2a20d2de94 test: consolidate refusal cases and repair boundary coverage (#33098)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 01:52:04 -07:00
Neil b33ad1bb2c feat(remote): trace why each remote-runtime connection closed (#33094)
Client control-socket closes and server heartbeat reaps only reached the
console, which a packaged app never shows. Each control-socket close now
writes a remote-runtime.control-socket-close span (cause, close code,
error code, last keepalive RTT, socket and ready uptime). Each server reap
writes remote-runtime.server-socket-reaped. Both land in the main trace
file that Send app diagnostics collects. Keepalive pongs now measure RTT,
exposed as lastRttMs and lastCloseCause on the shared-control diagnostics.
2026-10-24 01:43:08 -07:00
OrcaWinandOrcaWin 9096344b8a test: consolidate image-send coverage and repair contract assertions (#33093)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 01:36:55 -07:00
Neil e496ea6f23 test(chat): respect frame ownership after creation protection expires (#33091) 2026-10-24 01:35:43 -07:00
Neil 6a2340b967 fix(remote): a missed wake probe marks a socket suspect instead of closing it (#33071)
On macOS wake and network online events every remote-runtime socket was
probed with an 8 s deadline and closed on a miss. Relayed or lossy links
still rerouting after a wake lost connections that would have survived.
A miss now re-pings and leaves the socket until the normal 25 s keepalive
window ends; the probe is skipped when data arrived in the last 3 s.
2026-10-24 01:29:10 -07:00
Brennan Benson 04f7ed24b6 Resume dialog: show workspace names, not internal ids, while workspaces load (#33038)
* fix(native-chat): name workspaces by folder in the resume dialog while workspaces load

On restart the resume dialog opens before the full workspace list loads, so
every row it could not look up showed its raw repo-qualified id. Fall back to
the folder name the id carries, as the status bar and Automations already do.

* fix: distinguish unknown resume workspaces with matching folder names

* refactor: extract resume workspace display derivation

* fix: distinguish resume workspace control names
2026-10-24 01:22:49 -07:00
OrcaWinandOrcaWin 0c06f1f082 test: consolidate duplicates and strengthen behavioral boundaries (#33088)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 01:22:18 -07:00
Neil 28258c0ba9 fix(terminal): keep host-retired panes out of every layout a paired client writes (#33083)
Exiting a paired host tab's original pane moved the host's active leaf to the
surviving split. The client's reconciliation effect followed it with
setActivePane, and that persisted every still-mounted pane, writing the retired
leaf (whose remote PTY had not ended yet) back into the layout. The leaf was then
no longer retired, so its pane stayed forever. Any other persist in that window
(insertions, clicks, title edits, divider drags) could do the same.

persistLayoutSnapshot now drops the leaves the host retired, using the
retireLeavesFromTerminalLayout main already uses, moved to shared. The retired
set it reads is the one the reconciliation effect tracks.
2026-10-24 01:08:08 -07:00
OrcaWinandOrcaWin 4138e5866f test: consolidate redundant checks and strengthen boundary coverage (#33082)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 00:57:20 -07:00
Neil 24069f0a97 ci: fail when a checked-in test file has no runner (#33080)
* ci: fail when a checked-in test file has no runner

Two node:test files under .github/actions lost their runner when #31792
trimmed cloud pretest, and the two monaco .bench.ts budgets were never run
by any CI job. Wire both back in and add a static check that every
test-like file is collected by a declared runner or imported by a
collected entry (the src .spec.* suite-module convention).

* ci: ignore type-only imports and quote the lease glob for cmd.exe

* ci: parse type-only imports, cover UNIT_EXCLUDE, route cloud runner edits

Type-only imports are now dropped with the TypeScript parser, so a type
alias before a runtime re-export no longer hides it. UNIT_EXCLUDE files
must be named by a pr.yml or unit-tests.yml step. A cloud-only diff that
adds a test or edits a runner config now runs the static job. The monaco
benchmarks stay manual (pnpm test:perf:benchmarks) until a CI runner
proves their wall-clock budgets.

* ci: require UNIT_INCLUDE names in dedicated jobs and erase typeof import()

* ci: resolve each runtime import to the one file Vite loads

The unit dependency graph keeps every plausible candidate so it can
invalidate conservatively, which cannot prove a suite runs: an import of
./case.spec counted both case.spec.ts and case.spec.tsx. Resolve with
Vite's extension order instead.

* ci: take runtime imports from transpiled output

Imports used only as types are erased without a type modifier, so
parsing for type-only syntax still followed them. Derive edges from
ts.transpileModule output, which erases what Vitest's transform erases,
and only transpile files that can reach an uncollected candidate.

* ci: fix the agent-status benchmark config and route all cloud code

The agent-status benchmark config inherited the unit projects, whose
includes override its own, so it ran the whole unit suite and never the
benchmark. Clear projects as the performance config does. Route every
cloud code file to the static job, since content-detected tests can have
any name.
2026-10-24 00:57:17 -07:00
Brennan Benson 4c2a2ac3d9 A Claude chat shows the turns made outside it (#31615)
* A Claude chat shows the turns made outside it

When a Claude chat's agent next opens, turns written to the same
conversation elsewhere (claude --resume in a terminal) are appended once,
in order, under the existing "Continued outside this chat." divider.

Streamed reply and thinking rows are now keyed by their source block
(message id and block index), which Claude also saves on each transcript
row, so the chat can tell which saved rows it already shows. The read goes
back from the end of the transcript only to the chat's saved turn-end leaf,
replays the rows through Claude's own translator in an isolated sink, and
commits them in one settlement that writes nothing if the chat moved.
Imported prompts are display-only and never settle a send; any failure
opens the chat normally.

* test(claude): name the transcript row shapes the catch-up test builds

* fix(claude): read a catch-up row's uuid once as a string

* fix(claude): parse transcript tail rows without type assertions

* refactor(native-chat): share the catch-up open runner across agents

The Codex catch-up's agent-neutral parts move to one module: the open-time
envelope (its own deadline and cancellation, never gating the open), the
bounded staged batch, the single settlement commit that re-checks the
journal before writing, and the "Continued outside this chat." divider.
Codex keeps only how it reads its saved turns and what the chat already
holds. No behavior change; the other agents' catch-up builds on this
instead of copying it.

* refactor(claude): catch up through the shared history catch-up runner

* chore: remove unrelated pnpm executable metadata

* refactor(native-chat): the catch-up runner reads the chat's view only when the agent asks

* chore: remove regenerated pnpm executable metadata

* fix(native-chat): the catch-up deadline also bounds waiting for its commit to be written

A journal write barrier that never settled after the import was admitted held
the provider's open past the catch-up deadline. The wait now ends with the
deadline or the open's cancellation, like every other step.

* test: type the history catch-up sink fixture without assertions

* fix(native-chat): bound and revoke history preparation

* test(claude): expect source block identity in stream cadence

* fix(claude): preserve final output and history coverage

* feat(native-chat): validate expected saved items during history catch-up

* fix(claude): integrate conditional history settlement

* fix(claude): use new content and provider prompt identities for catch-up

* fix(claude): retain qualified completed leaves through session exit

* fix(claude): preserve unresolved membership for recovered follow-ups

* test(claude): use fork epoch reason for rewind preservation

* fix(claude): consolidate translator sink type imports

* fix(claude): preserve source message times during catch-up

* fix(claude): narrow staged history mutations to items

* test: bound Claude retained-leaf catch-up work

* fix(claude): settle imported work at source boundaries

* test(claude): keep shared journal harness portable

* test(mobile): await editor listener cleanup before snapshot

* test: isolate retained slice measurements and accept closed socket EPIPE
2026-10-24 00:51:33 -07:00
Neil 9787f83639 fix(web): chat attachments from the browser's file picker and drops upload to the paired server (#33060)
* fix(web): chat attachments from the browser's file picker and drops upload to the paired server

In the web client the chat's attach button resolved no files and a dropped
file was refused as unresolvable, so neither ever attached. The browser now
opens its own file picker, and a drop on the chat hands over its File
objects, each held under a token whose last segment is the file's name. The
chat's upload reads the held file and streams it in chunks over the paired
server's existing agentSessionAttachment upload RPC, as the browser's image
paste already does.

* fix(web): release held browser files where an attach ends, never by eviction

An eviction cap could drop the files of a batch still waiting to upload when
another pick or drop came in. Every pick and drop now ends in the composer's
one delivery step, which releases whatever it left unclaimed.

* fix(web): release held browser files a disabled composer or detached drop owner never delivers

A pick that returns to a disabled or unmounted composer skipped the delivery
step, and so did a queued drop whose owner detached; both now release their
held files. The composer-workspace comparison moves beside the other
attachment ownership checks.
2026-10-24 00:49:26 -07:00
Neil 7190b835f1 test(e2e): stop restarted serve hosts with SIGTERM so the PTY daemon survives (#33067)
On macOS, orca serve vetoes an unmarked app.quit() (#15537), so Playwright's
close never finished and its fallback force-killed the whole process tree,
including the detached PTY daemon's terminals. The restart spec's fixture
process got SIGTERM and the spec failed on every macOS run.

Restart now stops serve the way a supervisor does (SIGTERM, a marked quit),
and the restart spec no longer shows or focuses its client window.
2026-10-24 00:48:05 -07:00
Neil b5ee026111 refactor: group Parcel watcher child process ownership (#33079) 2026-10-24 00:47:31 -07:00
Neil 4b8c59c400 fix(terminal): a split right after a paired-server tab opens starts on the server (#33078)
* fix(terminal): a split of a paired-server pane still attaching starts on the server

A pane of a server tab whose own attach is in flight has no transport id, so its split fell back
to a local split. Only the server starts terminals in its tabs, so the new pane stayed blank.
Split from the terminal the server already published for the pane, or wait for it.

* fix(terminal): tell the user when a held split never starts, and keep focus where they went

A split held for the server's source terminal now gives up when the pane, its tab or the pairing
goes, toasts when the server never publishes it, and lands without focus if the user moved on.

* fix(terminal): a held split runs only on the server the gesture targeted

Check the workspace still belongs to that server before reading the source binding, and accept
only a binding that names it, so a hydration swapping both cannot redirect the split.

* refactor(terminal): route an unattached server pane's split straight to the server

Drop the held split. The split uses the terminal the server published for the source pane, and
when there is none yet it says so at once instead of queueing a gesture that needs its own fences.

* fix(terminal): a server-tab pane never splits locally

Refuse every split the server cannot take for a pane of its tab, and say whether the host is
disconnected or the source terminal has not started yet.
2026-10-24 00:41:44 -07:00
Neil 06211d1406 fix(terminal): a new SSH terminal opened before sign-in waits and starts after sign-in (#33072)
* fix(terminal): a new SSH terminal opened before sign-in waits and starts after sign-in

A terminal created while its managed SSH host is still waiting for a typed
password or passphrase got main's sign-in refusal from terminal.create and
showed it as a fatal error. Signing in later never started it.

Route that refusal into the same parked retry #32275 gave restored panes, so
the post-sign-in control-ready revival replays the create. The pane shows a
'Waiting for SSH sign-in' banner with a Sign in action instead of an error.

* fix(terminal): clear the sign-in wait as soon as sign-in finishes

Publish the sign-in reason through one setter so clearing it always reaches
the banner, and clear it when the control socket proves sign-in finished.
The Sign in button retries only after a successful connect, so a rejected
password cannot re-dial into a second prompt. Share one banner layout.

* fix(terminal): keep a sign-in-waiting spawn pending until the replayed create lands

The pane's spawn finalization (launch config registration, startup delivery)
ran against an undefined result while the create waited for SSH sign-in,
then never saw the terminal the post-sign-in replay made. Keep the original
connect pending and settle it with the replay's result, or with nothing when
the pane is closed, re-attached or a fresh connect supersedes it.

* fix(terminal): a stale replay settles only its own sign-in wait

* fix(terminal): settle a sign-in-waiting spawn at the recovery state, not per replay

Codex and Opus found recoveries that finish outside a create replay (the
stream resubscribe after the create landed, an ownership refresh) and left
the original connect pending. Settle it at the two points every recovery
passes: healthy (with the bound terminal) and idle or disposed (with nothing).

* fix(i18n): clearer Spanish sign-in wait copy
2026-10-24 00:35:20 -07:00
55ed5f8757 fix(checks): hide stale merged PRs after workspace HEAD changes (#7815)
* fix(checks): stop stale-while-revalidate from flashing a stale merged PR

The synchronous fresh-return path guards on `staleMergedHeadRefetch` so a
worktree that advanced off a merged PR's head does not reuse the now-stale
merged review (added in #7277). The stale-while-revalidate fast path was
missing that same guard, so it served the stale merged card synchronously
(and kicked only a background refetch) whenever the parent-PR-checks overview
polled with `{ staleWhileRevalidate: true, currentHeadOid }`.

Result: a worktree that commits new work on top of a merged branch keeps
showing the old "Merged" card instead of going branch-only — persistently
while the background revalidation keeps failing (rate limit / gh error).

Add `!staleMergedHeadRefetch` to the SWR condition so the read revalidates
synchronously, matching the fresh-return path. Adds a regression test that
fails without the guard.

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

* chore(review): clarify stale review revalidation comments

* fix: avoid reusing hosted review requests from a different head

* fix(checks): track concurrent review lookups by Git head

* fix(hosted-review): narrow per-HEAD request fencing to a join guard

Replace the HEAD-keyed in-flight map with the original single slot per
request key, and skip joining an in-flight lookup only when it was started
for a different known HEAD. Keep the stale-merged guard on the
stale-while-revalidate path and the merged-HEAD guards on the readers.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:41 -07:00
Naveen ChatlapalliandNeil 36f093a08f fix: back off failed GitLab authentication probes (#26742)
* fix: back off failed GitLab authentication probes

* fix(gitlab): use short exponential backoff for failed host-discovery probes

Start at 15s and double up to the existing 5-minute negative TTL, and
clear the backoff on auth refresh (rememberGlabKnownHosts), successful
probes and forced preflight resets so recovered auth is seen promptly.
Keeps the positive host cache shape unchanged.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:37 -07:00
395aa755b4 fix(keybindings): report conflicts with shortcuts the main process consumes first (#10780)
* fix(keybindings): report conflicts between terminal shortcuts and chords that fire in terminals

Actions that reach a focused terminal now share a conflict bucket, and the
Shortcuts pane judges the bindings as written in the file instead of the
map the loader already stripped.

* fix(keybindings): flag only the ignored binding and say it was ignored

The other claimant keeps its own default and never stopped working, so a
warning on its row blamed an untouched action. Adds the e2e regression that
seeds a conflicting file inside the isolated E2E home.

* test(keybindings): capture the shortcuts pane at each stage of the conflict report

* test(keybindings): assert the untouched action carries no warning

Visibility alone would still pass if a regression warned on both rows.

* fix(keybindings): narrow the ignored-binding guard and tighten its negative test

Review follow-ups:

- Only warn when the file asked for a binding and the loader took it away.
  The guard now also requires the action to be absent from the active map,
  so an override that survived is never labelled "was ignored".
- The negative case used view.tasks, which has no default binding, so the
  conflict assertion passed without exercising the terminal bucket at all.
  It now uses worktree.quickOpen and asserts the definition exists, is not
  terminal-reachable, and has a non-empty default list.

* fix(keybindings): compare main-process shortcuts against every scope

- List the actions resolveWindowShortcutAction consumes in main and put them in every scope's conflict bucket, so a binding elsewhere on the same chord is reported instead of silently losing it.
- Browser bindings are compared only against the actions a focused guest forwards ahead of browser chords.
- Dictation stays out, since main lets its chord through while voice is off (the default).
- The two digit ranges may still share a modifier.
- Drop the allowInTerminal terminal-bucket rule.
- Explain overrides the loader drops on a later pass.
- Pin the list to the resolver and the guest order with tests.

* fix: report main-intercepted shortcuts in Settings without dropping saved overrides

Main-process interception now only feeds the Settings conflict report.
The loader, write path and web normalization keep main's scope-only check,
so existing keybindings.json overrides load exactly as before.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:17 -07:00
AnmolandNeil 162c11a92d fix(native-chat): show unknown HTML tags as text instead of dropping them (#30819)
* fix(native-chat): show unknown HTML tags as text instead of dropping them

Chat UI renders messages through CommentMarkdown, which parses raw HTML
and then sanitizes it. Sanitize unwraps any tag outside its allowlist,
so prose that mentions a tag lost it: "use a <se-tabs>" rendered as
"use a ".

Add a remark plugin that turns raw HTML containing a non-allowlisted
tag into literal text before sanitize runs. It reads the sanitize
schema's own tag list, so the two cannot drift, and runs before
remark-breaks so newlines inside it still break. Allowed tags
(<sub>, <kbd>, <details>, ...) and inline code are unchanged.

The behaviour is opt-in via a new `unknownHtmlAsText` prop and enabled
only for native chat; GitHub, GitLab, Jira and Linear surfaces keep
matching their host's rendering, which also strips unknown tags.

Fixes #30604

* fix(native-chat): preserve markdown parsing around literal HTML

* fix(native-chat): escape only unknown tags in mixed raw HTML blocks

A raw HTML block that mixes allowed and unknown tags keeps rendering its
allowed tags and comments; only the unknown tags are shown literally.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:11 -07:00
hwantageandNeil 1af3a3f32c fix(ui): size localized sidebar Send controls without overlap (#18474)
* fix(ui): prevent sidebar agent send labels from wrapping

- Keep localized send-target labels on one line inside the fixed-height sidebar control. Add a regression assertion for the nowrap utility.

* fix(ui): reserve space for localized sidebar Send controls

Keep Send labels on one line and size their flex slot to the button. Share remaining task/model space while selecting a send target so longer translations cannot obscure the primary label.

Co-authored-by: hwantage <hwantagexsw2@gmail.com>

* fix(sidebar): keep model badge sizing while Send is in flow

The badge keeps its natural width and only shrinks when the in-flow Send
button would otherwise overflow the row, instead of splitting the row with
the task label.

* fix(sidebar): keep model badge class order unchanged outside send mode

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:06 -07:00
f0a399d143 fix(linear): accept normalized Markdown save confirmations (#30076)
* fix(linear): tolerate documented Markdown normalization

* fix(linear): preserve fenced Markdown literals

* docs(linear): describe Markdown fence matching

* fix(linear): close nested Markdown fences

* fix(linear): tolerate documented Markdown normalization

* fix(linear): preserve fenced Markdown literals

* docs(linear): describe Markdown fence matching

* fix(linear): close nested Markdown fences

* fix(linear): compare only parsed list marker rewrites

* style(linear): satisfy save confirmation quality gates

* fix(linear): preserve list structure and load parser on demand

* fix(linear): bundle save Markdown comparator for every runtime

The comparator resolved unified/remark at runtime via createRequire, which
orcad's esbuild bundle does not ship. Import them statically from a lazily
imported module so Electron and orcad both inline the parser.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:01 -07:00
Max HsuandNeil fdb7e12b55 fix(agent-status): keep a hook question from a stale program working report (#32156)
* fix(agent-status): keep a hook question from a stale program working report

Pi reports no blocked status for ctx.ui.custom() dialogs such as ask_user, so
its OSC 7501 status stays working through the question. The 10-minute lease
renewal re-sent that working, and once the hooks had been silent for two
minutes it replaced the hook row's waiting.

A waiting or blocked hook row now yields to a program working report until
the program enters working after the hook event, mirroring the done/waiting
case.

* fix(agent-status): limit hook-question precedence to working that predates it

Only a program working that began before the hook question is stale; a later working
transition, or one without facet ordering evidence, keeps the existing silence fallback.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:33:51 -07:00
cbd24b2872 fix(agent-status): drop Claude teammates stopped with TaskStop from the sidebar (#32048)
* fix(agent-status): drop Claude teammates stopped with TaskStop from the sidebar

Named background agents run as in-process teammates. TaskStop fires no
lifecycle hook, and their background_tasks entries use ids unrelated to the
lifecycle agent_id, so an idle teammate row was kept as long as any teammate
task remained listed, even after that teammate was stopped.

Remember the description of the lead's Agent call that spawned each teammate
(Claude lists the teammate's background_tasks entry under it), and at a lead
Stop with a complete inventory drop a teammate row whose description is no
longer listed. Rows without that evidence keep the previous behavior.

Fixes #31818

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

* fix(agent-status): keep a teammate's spawn description when its spawn result lands first

Hook requests are handled unordered, so the lead's Agent PostToolUse can
arrive before the teammate's SubagentStart. Create the row as idle in that
case so the description is kept; SubagentStart then revives it to working,
and an idle row never gates the pane if the teammate's stop came first.

Moves the spawn recorder into claude-teammate-spawn.ts to keep
claude-events.ts within the file length limit.

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

* fix(agent-status): retain teammates with clipped task descriptions

Claude caps background task descriptions and appends a clipping marker. Match that visible prefix conservatively when reconciling a teammate's full spawn description, so an inventory update cannot hide a live teammate. Keep ambiguous descriptions and unknown inventories safe, with coverage through the production hook normalizer.

* fix(agent-status): escape clipping marker in daemon bundle

* fix(agent-status): retire stopped teammates only when the inventory is fully attributable

Claude registers a teammate task under the whitespace-collapsed spawn description, so
compare normalized descriptions. Infer a stop from absence only when every listed
teammate matches a recorded spawn; unknown or differently formatted entries keep rows.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:33:46 -07:00
f4a3860073 fix(runtime): retain completion subtitles for paired workspaces (#9930)
* fix(runtime): keep lastAssistantMessage when demoting idle agent status

Paired clients mirror the mobile/session terminal projection. Demoting a
stale working status under a neutral title dropped lastAssistantMessage, so
remote workspace cards lost the completion subtitle that local cards still
show from the in-process hook row.

Fixes #9914

* Format completion field projection consistently

* Preserve completion classification and bound interrupted verdicts

* Keep completed replies out of new live title intervals

* Restrict retained completion previews to completed title status

* Keep mobile session projection within lint file budget

* fix(runtime): keep only completed-turn replies in demoted mobile status

Mid-turn hook snapshots carry tool stdout as lastAssistantMessage; a shell or idle
title must not surface that as the final reply.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:33:41 -07:00
909dce0a0b fix(settings): reveal full shortcut titles on hover (#16806)
* fix(settings): show tooltip for truncated shortcut titles in shortcuts pane

- Wrap shortcut title with Radix Tooltip primitives in ShortcutCommandBlock.
- Enable full title visibility on hover when long command names are truncated.
- Add unit test in ShortcutCommandBlock.test.tsx verifying tooltip trigger attachment.

* fix(settings): show shortcut title tooltip only when the title is truncated

Reuse TruncatedSidebarLabel so untruncated titles keep no tooltip (STYLEGUIDE Tooltips).

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:31:12 -07:00
44c3847f24 fix(ai-vault): preserve transient panel state (#9419)
* fix(ai-vault): preserve transient panel state

Refs #9418

* fix(ai-vault): synchronize transient panel state

* fix(ai-vault): keep session expansion local to the virtual list

Restore expandedSessionIds inside AiVaultSessionVirtualList so the
history/search resetKey still gives each list fresh expansions; only
scope and collapsed groups are retained across sidebar remounts.

---------

Co-authored-by: just-zzp <264744947+just-zzp@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:31:07 -07:00
55b08b65df fix(source-control): preserve section collapse state (#9405)
* fix(source-control): preserve section collapse state

* test(source-control): strengthen remount regression

* fix(source-control): preserve section disclosure across panel remounts

Adapt just-zzp's #9405 fix to the current panel modules and existing bounded map, with runtime/host/occupant isolation. Part of #9403; Session History remains separate.

Co-authored-by: just-zzp <264744947+just-zzp@users.noreply.github.com>

* fix(source-control): key retained disclosure by active worktree id

Narrow the remount fix to the existing per-worktree reset: a bounded
renderer-session map keyed by activeWorktreeId restores section disclosure
after the panel unmounts, while a worktree switch still never inherits
another worktree's view.

---------

Co-authored-by: just-zzp <264744947+just-zzp@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:31:03 -07:00
Neil e9c80a4101 ci: use grep instead of rg in dependency audit prep (#33074)
GitHub's Ubuntu runners don't ship ripgrep, so the release dependency
scan failed with 'rg: command not found' before extracting lockfiles.
2026-10-24 00:28:59 -07:00
OrcaWinandOrcaWin fe3cb24a04 test: consolidate UUID coverage and verify input boundaries (#33073)
* test: consolidate UUID coverage and exercise dotfile boundaries

* test: verify skill selector boundary and describe actual fixtures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 00:28:32 -07:00
Brennan Benson 6059a5f7fa Native chat: a message is never marked not delivered after a crash, and reopening a chat stops reading the transcript (#30580)
* Native chat: never mark a message not delivered after a crash; reopening a chat stops reading the transcript

After a crash or restart, attach read a window of the agent's saved log and marked any
unconfirmed message missing from it as rejected "not delivered". Absence from a log Orca
does not own is not proof: the window's anchor moves every turn (STA-7416) and its text
filters drop tagged prompts (STA-7453), so messages the agent received were marked
undelivered and retyping them duplicated the question. The read also ran on every attach
(STA-7434).

Delete the inference: the provider history window and its adapter hooks (Claude window and
branch proof, OpenCode stored-messages reader), the submission matcher, and the attach step.
A send the crash boundary leaves unconfirmed stays an ordinary sent message; the interrupted
turn still ends through the existing settlement. Explicit live refusals are unchanged.

A queued card whose hand-off a restart left unconfirmed no longer counts as able to return
to the queue, so it stops keeping an idle chat loaded.

* test(native-chat): update restart fixture calls after history removal

* Hide rewind targets without a saved native boundary

* Run rewind boundary regression across host and renderer

* Keep incoming main package-manager lock unchanged

* Test recovered chat consumer outcomes without delivery inference

* Assign the complete cursor in recovered phone regression fixture

* Supply presentation capture in rewind boundary fixture
2026-10-24 00:27:01 -07:00
Brennan Benson c2241d44af fix: move native-chat adapter error classes out of the adapter contract file (#33046)
Main's static lint failed max-lines (303 of 300) on structured-agent-session-adapter.ts after #28051 merged. The adapter's error classes move to structured-agent-session-adapter-errors.ts and are re-exported, so every importer is unchanged.
2026-10-24 00:25:17 -07:00
c8db507932 fix(ui): dismiss notifications without closing modal editors (#32065)
* fix(ui): allow toast dismissal over modal panels

* test(ui): retain toast stacking and credential boundaries

* Preserve modal state when dismissing stacked toasts

* fix(ui): limit modal toast gate to dialog and sheet overlays

Radix menus and selects also set body pointer-events:none; gating on that
blocked toast clicks from closing an open dropdown.

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-10-24 00:25:04 -07:00
dd69e7dd80 fix(renderer): restore focus after floating tab rename (#31510)
* fix(renderer): restore focus after floating tab rename

* fix: limit one-shot chat composer focus to tab rename return

Other onlyIfFocusUnclaimed callers (e.g. floating terminal) keep main's
replayable composer focus request.

---------

Co-authored-by: MimeLyc <11363886+MimeLyc@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:24:59 -07:00
Neil 0399ffa2c6 fix(terminal): re-bind a host-exit-settled remote pane on the host's next snapshot (#33062)
* fix(terminal): hand a host-exit-settled remote pane back when the host binds a new process

After #31968 settles a host-session pane on the host's exit record, a host
restart can bind the new process just after the probe read the old one's exit.
The pane then stayed ended until the tab was reopened. Keep watching the host's
accepted snapshots after that settle; once one publishes a provably different
live process for the pane, remount the tab so it attaches to it.

* fix(terminal): let a new host process reopen recovery after a failed rebind

* fix(terminal): watch for a host rebind only on a leaf-addressed pane

* fix(terminal): catch a host rebind published while the exit probe confirms

* fix(terminal): keep the host-rebind remount under the recovery window cap
2026-10-24 00:20:12 -07:00
Neil a5a5a22c60 feat: reclaim stale dependencies and build output across worktrees (#32331)
* feat: reclaim stale generated files from sibling worktrees

* Include generated worktree cleanup in manual reclaim by default

* Preserve nested repositories inside generated output
2026-10-24 00:19:39 -07:00
OrcaWinandOrcaWin 70c4c55db9 test: remove duplicate coverage and strengthen boundary assertions (#33047)
* test: strengthen launch, subject identity, and release witnesses

* test: strengthen approval paths and prompt answer boundaries

* test: strengthen conversation names and mobile log persistence

* refactor: group background task stop contract with stop operations

* test: strengthen question, title, and release validation

* test: distinguish row text and generated dispatch titles

* test: verify mobile revival events and retry exclusivity

* test: reject incomplete child row output assertions

* test: verify copied modules and option selection contracts

* test: verify mobile host state and status retry boundaries

* test: observe connector refusals and real diagnostic delivery

* test: consolidate platform memory coverage and verify bounded work

* test: allow cached legacy failure reads within work bounds

* test: verify packaging boundaries with independent fixtures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 00:13:25 -07:00
Neil 156f3f4ef0 fix(ports): say direct SSH hosts don't support port detection yet (#33055)
The host-scoped port scan reported 'Remote connection dropped' for every
direct-SSH workspace even with a healthy connection: the relay has no port
scan, so the connection was never the problem. Name the real limitation
and point at an Orca server on the host, which scans its own ports.
2026-10-24 00:09:58 -07:00
Neil d9eabac536 refactor: colocate persistence settings and automation tests (#33058) 2026-10-24 00:07:10 -07:00
Neil 461d6b44c3 test: expect legacy failure reads to reuse the published display (#33061) 2026-10-23 23:55:54 -07:00
Neil 31fe3c7481 refactor: colocate orcad migration tests and fixtures (#33054) 2026-10-23 23:26:57 -07:00
Brennan Benson 8fd32eccd7 Show retained chat messages after reconnect without reopening (#32255)
* fix(native-chat): publish retained history corrections to capable clients

* test(native-chat): expose canonical reads in phone history fixture

* test(native-chat): migrate remaining history reader fixtures

* fix(native-chat): preserve expanded history on late acceptance

* fix(native-chat): share lazy correction metadata by journal source

* fix(native-chat): reuse raw legacy display selections
2026-10-23 23:19:26 -07:00
Neil c1d7a17008 refactor: colocate remote runtime socket cleanup (#33051) 2026-10-23 23:00:32 -07:00
Neil 92ccacd737 refactor: colocate structured session cancellation contract (#33048) 2026-10-23 22:47:30 -07:00