Commit Graph
14171 Commits
Author SHA1 Message Date
Neil d3fc80292b test: isolate inactive browser pane leaves (#28918) 2026-10-14 18:53:16 -07:00
Neil 8df0f67c82 ci: skip redundant PR platform warmers (#28917) 2026-10-14 18:48:13 -07:00
Neil eeb4311b81 test: narrow editor model lifetime fixture (#28916) 2026-10-14 18:40:32 -07:00
Neil 64c63062c4 Fix WSL upload readiness fixture transport (#28911) 2026-10-14 18:30:46 -07:00
Neil ab8d33be8e [CI] Reuse automation publication test imports (#28910) 2026-10-14 18:24:42 -07:00
NeilandClaude d1bb7fec80 [CI] Reduce repeated test imports and fixture overhead (#28627)
Co-authored-by: Claude <claude@anthropic.com>
2026-10-14 18:23:35 -07:00
Brennan Benson e2fa611916 feat(native-chat): chat scratch folders expire after 30 days unused (#28394)
* fix(files): never write through a dangling symlink

A path that doesn't exist yet was authorized by walking up to the nearest
existing folder. A dangling symlink fails realpath with ENOENT, so the walk
stepped over it and approved the link's own path; the write then followed
the link and created its target outside the allowed folders. The walk now
continues from the link's target, and returns that real destination.

* refactor(native-chat): rename the chat visuals folder to the chat scratch folder

The per-chat folder agents write into is the chat's scratch space, not only
a visuals folder. Its root becomes <state>/chat-scratch/<hash> (the hash is
unchanged) and the agent variable becomes ORCA_CHAT_SCRATCH_DIR. The old
ORCA_CHAT_VISUALS_DIR is never set and is still stripped from what every
agent child inherits, Pi included. The visuals skill names the new variable
and says the folder is temporary.

* test(native-chat): keep the delivery test to the rename

The launch parity snapshot already pins that every agent drops the
pre-rename variable.

* feat(native-chat): chat scratch folders expire after 30 days unused

A chat's scratch folder was removed only when its record was gone or its
local workspace was proven deleted, so the folder of a long-idle chat
lived forever.

The sweep now expires a chat's folder once its newest journal row and
the newest entry anywhere in the folder (complete, no-follow scan) are
older than 30 days, unless the host holds a running agent, a delivery or
an open dispatch for the chat. The final recheck and a rename to
<hash>.expired run in the chat's session lane and then a per-folder lock
that Orca scratch saves (desktop IPC and host RPC) also take; the delete
runs outside both. A save that settles after the age scan marks that
expiry stale. Leftover .expired folders and the pre-rename
native-chat-visuals root are removed.

The workspace-removed rule and its catalog verdict are deleted.

* fix(files): refuse any dangling symlink in the missing-path walk

Following a dangling link by hand resolved relative targets against the
lexical parent, not the link's real folder: through a folder link the walk
could loop forever or authorize the wrong file. Refuse instead: realpath
can't say where a dangling link leads, but a write would follow it.

* test(files): drop the loop case that duplicated the dangling-link refusal

* refactor(native-chat): trim the scratch expiry and test the real live-work veto

- Remove the other-profile catalog options (running profile id, never-written
  profiles) that only the deleted workspace verdict used; history cleanup's
  reader is unchanged.
- The sweep keeps its original remove-and-warn block; an unused chat's
  folder is parked by one function. Drop the unused `now` option and the
  candidate identity check.
- The live-agent case runs against the host's real runWhileExecutionIdle.

* style(native-chat): keep the session host within its line limit after the main sync

* fix(native-chat): every scratch save and every overlapping sweep share the folder lock

Binary (base64) runtime saves now take the scratch folder lock like text saves, and each folder
keeps a set of expiry candidates so one sweep finishing no longer hides a save from another.
2026-10-14 18:22:39 -07:00
Kelvin Amoaba 095399aa4d fix(mobile): render markdown with markdown-it instead of a hand-written reader (#28858)
* fix(mobile): size markdown table columns to their content

* fix(mobile): always show the copy button under chat messages

Revealing it on tap shifted the transcript.

* fix(mobile): read markdown with a CommonMark parser

markdown-it rather than desktop's micromark: about 8x faster on the phone's JavaScript engine.
2026-10-15 01:22:13 +00:00
Neil f23fa8ca76 Prepare Windows natives with scoped compiler settings and measure servicing (#28881)
* Scope Windows native compiler setup and measure ARM inbox preparation

* Add a controlled Windows native preparation comparison

* Test native preparation through an injected process runner

* Guard reviewed Windows native batch runners
2026-10-14 18:18:24 -07:00
Jinwoo Hong 3b11b7ac94 fix(mobile): phone server rows follow the desktop's other-client and sleeping filters (#28819)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters

A phone created through a desktop now records whom it acted for, so the
desktop's Hide other-client workspaces filter keeps the user's own phone
workspaces (locally and on servers) and hides only other people's.
The desktop relay marks each server row its sidebar would hide, and the
rows its own browser or chat tabs keep awake; the phone applies both.

* test(e2e): dedupe sidebar row ids in the visibility spec

* fix(mobile): address review: owner naming, old-server relayed phones, scope trim

* refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag

A phone now records its owner as the creator (this host for a directly
paired phone, the relaying desktop for a delegated one), so the shared rule
is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved
browser/chat tabs into hasHostSidebarActivity, so the phone's status dot
follows too, and local worktree.ps counts the same signals. The old-server
fallback is removed.

* test(e2e): poll the phone and the sidebar together in the visibility spec
2026-10-14 21:18:01 -04:00
Neil 222b780276 Reuse real launcher builds and guard unused runtime RPC imports (#28905) 2026-10-14 18:17:14 -07:00
Kelvin Amoaba 0905ad4139 feat(native-chat): copy chat selections when Copy on Select is on (#28737)
* feat(native-chat): copy chat selections when Copy on Select is on

The terminal's Copy on Select setting now also copies text selected in a
native chat transcript, at the point the Add to chat offer already treats
a selection as finished. It writes the same text right-click Copy does,
skips the composer and other fields, and leaves the setting key as is.

* fix(i18n): translate the Copy on Select description and search keywords

Settings and Settings search showed English to es, fr, ja, ko and zh users
after the description moved to new keys. Extend the old terminal-only
translations to cover chat, and translate the new highlight and chat search
keywords.
2026-10-15 01:16:13 +00:00
Kelvin Amoaba fbb93b7385 fix(native-chat): send typing after a click to the chat's composer (#28728)
* fix(native-chat): send typing after a click to the chat's composer

The default Claude/Codex chat never wired the root typing redirect, so a
click in the transcript, on a toggle or link, on the chat's own tab, or on
blank app chrome left typed keys with nowhere to go. The chat-view-over-TUI
had the redirect but treated every button and link as a key owner.

Both views now share one hook that runs the reveal focus, focuses the chat
on blank clicks, routes keys typed inside the chat (but not in overlays it
portals out), and, while it is the only chat visible in the focused group,
routes keys typed with focus on the page body. Buttons and links pass
letters but keep Space and Enter. Re-activating an already visible chat tab
re-runs the composer claim, as a terminal tab refocuses xterm.

* fix(native-chat): refocus the composer when its active tab is activated again

Re-activating an already visible chat tab re-ran the ordinary reveal
claim, which stops as soon as focus is anywhere inside the pane. With the
transcript or a code-block Copy button focused, the tab shortcut left focus
there and Space kept scrolling or pressing the button. The activation now
moves focus to the composer from inside the pane too, unless a text field
such as the find input holds it; an ordinary reveal still keeps the user's
focus.
2026-10-15 01:15:23 +00:00
Kelvin Amoaba c7cb5d154a fix(mobile): open Create Workspace and Close issue from Tasks on iOS (#28855)
Each Tasks sheet was its own native modal, and iOS refuses to present one
over another that is still showing. They now share one modal and stack by zIndex.

Fixes #21003
2026-10-15 01:02:38 +00:00
Kelvin Amoaba 6d519f7835 fix(mobile): keep content clear of the side insets in landscape (#28749)
In landscape the iPhone sensor housing and Android's side nav bar are left/right
insets, which no screen or over-the-air page padded for.

Fixes #21190
2026-10-15 00:57:56 +00:00
Neil 67a3aa492d feat(wsl-mover): shell history moves with the project to the WSL host (D1b-6c-1) (#28902)
* feat(wsl-mover): shell history moves with the project to the WSL host (D1b-6c-1)

* fix(wsl-mover): moved WSL history lands under the host's login shell (D1b-6c-1)

* fix(wsl-mover): history follows the shell a pane would actually run, fallback included (D1b-6c-1)

* fix(wsl-mover): history installs follow the real pane shell, wait for live shells, and mark what they installed (D1b-6c-1)

* fix(wsl-mover): history installs wait until live shells can be checked (D1b-6c-1)
2026-10-14 17:56:14 -07:00
Jinwoo Hong 88accb2e4a feat(relay): log where the sticky lane's slot time goes, per pooled connection (#28908)
* feat(relay): log what the sticky slot spends its hold on, per pooled connection

* fix(relay): time every sticky round trip at the PostgreSQL client, pool wait apart

* docs(relay): say which round trips the sticky slot tally covers
2026-10-14 20:55:18 -04:00
Kelvin Amoaba c056cfc140 feat(native-chat): add a "New" line above unseen messages (#28333) 2026-10-15 00:55:06 +00:00
Brennan Benson d78363ab36 fix(agent-launch): find a repo's project the way the window does (#28237)
* fix(agent-launch): find a repo's project the way the window does

The host now also takes a project named after the repo as that repo's project, as the window
does, so both pick the same project row and runtime even when a repo was moved out of the
project named after it.

* refactor(agent-launch): share one repo-to-project match with the window

The host's agent-launch lookup and the window's launch preflight now call one projectMatchesRepo
instead of each writing the match out. No behaviour change on either side.
2026-10-14 17:52:41 -07:00
Brennan Benson 11aad143f8 fix(app-shell): let users close the profile storage delay warning (#28900)
The warning sticks until profile saving catches up, but it turned off the
close button every other Orca toast gets from the app toaster.
2026-10-14 17:48:08 -07:00
Neil 1f50bd305f Reduce unrelated setup in attention and mailbox fixtures (#28897) 2026-10-14 17:42:56 -07:00
Jinwoo Hong 6e46d5ef33 feat(relay): watch a reserve cell and flip it back on the first step 5 stop rule (#28698)
* feat(relay): watch a reserve cell and flip it back on the first step 5 stop rule

A manual, inert-until-dispatched workflow samples one reserve cell every minute for up to
300 minutes against the step 5 §9 stop rules, logs one JSON line per sample, and on the
first trip (or N unreadable samples in a row) runs the flag tool's own admitMode=db write
under the flag workflow's concurrency group. dry-run logs the flag tool's plan instead.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): keep the reserve watch's flip back out of the flag group, and baseline 503s before the flip

- The flip back holds no concurrency group: GitHub keeps one pending run per group and a
  newer one cancels it. The flag tool's CAS orders it instead; a generation conflict is
  re-read and retried (5 tries, backoff) while the switch file still says reserve.
- The 503 background comes from the 10 minutes before a required flipped-at, or an explicit
  value; neither is refused.
- A failed or cancelled write-mode watch flips back too. An unreachable cell fails with the
  exact break-glass inputs.
- Missing 503 data (empty series, unpublished minute, unparseable time) is unverifiable.
- Seat-behind-row key adds the director instance and cell; six segments share one command.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): read the reserve watch's 503 publishing front from every response code

A quiet fleet has no 503 series, so the watch read it as unverifiable and flipped a healthy
cell back within minutes. The 503 read now groups request_count by response code in one
query; the newest minute any code has a point for is how far the series is published, so a
503 minute at or before it with no point is zero and minutes past it are left out.

Also: the flip-back job's steps run on always(), flipped-at ages against the watch's start
in later segments, a pending pre-flip background no longer adds to the unverifiable streak,
the flip back retries runtime-status before calling for break glass, and the concurrency
rationale lives once in the docs.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): state what a cancel during the flip-back lease wait can leave, and the 503 publish-skew assumption

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): fail a segment closed on a corrupt watch state file, and keep no checkout token on disk

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): alarm on a failed flip back, refuse a watch under a rollout lease, wait for reserve to apply, and control the duplicate read

- A flip back whose write or plan never finished (no ID token, a failed login, a silent cell)
  raises an ::error:: annotation and a FLIP BACK FAILED summary, as the clock-skew monitor does.
- The first segment reads the Cloud SQL rollout lease through the lease action's own reader and
  refuses to start while any run holds it, or when it cannot be read.
- A cell not yet reporting reserve is waited on for up to 10 minutes, judging nothing, before the
  watch ends refused without a write.
- The duplicate-seat read takes the per-minute ledger summary in the same query and window; with
  no fresh summary in it the sample is unverifiable, not zero duplicates.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): page on a refused reserve watch or a failed flip back

A refused watch can leave a cell in reserve and unwatched, the same as a flip back that did not
land. Both now raise an escaped ::error:: annotation through one `alarm` command, and in write
mode write orca_relay_reserve_watch_alarm with gcloud logging write. A new log-based metric and
alert policy page on any such line, per cell, through relay_alert_notification_channels, and the
deploy identity gets roles/logging.logWriter in production to write it.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): name the log-writer risk, check the alarm's project, and say a failed login cannot page

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): note a force-cancel can skip the reserve-watch flip-back write
2026-10-14 20:42:27 -04:00
Jinwoo HongandClaude Opus 5.5 48c1d4d86e refactor(tabs): move tab drag preview selection writes into tabs-slice actions (#28893)
The drag preview's set-and-restore of the active tab per group now goes
through previewDragActiveTab, restoreDragActivationSnapshot and
restoreSourceGroupActiveTabAfterDrop instead of setState from a component.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:37:30 -04:00
Jinwoo HongandClaude Opus 5.5 2036c59509 refactor(store): route worktree, repo and SSH layout writes through tabs-slice transitions (#28892)
Worktree removal, purge, rename, activation wake and SSH target cleanup now
build their tab/group/pane layout changes with named transitions in
tabs/worktree-layout-lifecycle.ts, folded into the same single store update.
Project removal drops its layout writes: purgeWorktreeTerminalState, run just
before in the same call, already cleared every one of them.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:36:54 -04:00
Jinwoo HongandClaude Opus 5.5 9195d2aff5 refactor(terminal): park and quit capture keeps the stored pane layout (#28882)
* refactor(terminal): park and quit capture keeps the stored pane layout

The capture that runs on quit, sleep and park rebuilt the whole pane tree
from the DOM. The tab's stored layout already follows every pane change, so
the capture now writes only what it alone knows onto that layout: the
scrollback buffers, the pruned scrollback refs, and the live PTY bindings
with the focus repair that goes with them.

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

* fix(terminal): keep a stored leaf's scrollback and binding while its pane is unmounted

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:36:01 -04:00
Jinjing a140cc4b89 chore(i18n): address review threads on the translation scan
- ko Worktree Jump Palette: render "actions" as 동작 so it is distinct from
  the tasks entry (작업) in the same list.
- zh DevTools cookie instruction: clarify the filter phrase to
  `任意包含 remains 的请求`, keeping the literal `remains` request name.
2026-10-14 17:33:58 -07:00
Jinjing a46dfac35e chore(i18n): translate new keys to es/fr/ja/ko/zh
Scan 2026-10-14: fill target-locale coverage for PRs merged since the
2026-10-13 scan (#28486). Adds the missing keys (es 98, fr/ja/ko/zh 125
each), updates the two changed WorktreeJumpPalette strings, and fixes
pre-existing mistranslations: the ZCode missing-TUI notice (English in
all five locales), fr settings search keywords (procuration/indigene),
zh workspace-cleanup browse MT, 6 zh keys that still used Latin "Agent",
and the zh AccountsPane credential-help cluster (leftover English and a
mangled __Host-console_session token).

Leaves the 11 pinned provider-credit-balance keys plus the 3 pinned Chat
UI keys as English fallback by design. Records the scan in
config/update-translation.txt.
2026-10-14 17:33:58 -07:00
d5324c96e3 fix(ssh): the SSH password dialog no longer closes itself and comes back (#28883)
* fix(ssh): the SSH password dialog no longer closes itself and comes back

A host that drops an unanswered login no longer replaces the open prompt
with a new one, a background dial no longer replaces a connection whose
reconnect is asking the user, one Cancel closes every open prompt for that
host, and a paired client's automatic connect stays automatic.

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

* fix(ssh): a typed password its reconnect has yet to use is not replaced by a background dial

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

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-14 17:21:11 -07:00
Neil 39815a086f Reduce CI import, frame-capture, teardown, and shard costs (#28879)
* Avoid unused module imports and trim cosmetic test coverage

* Capture hidden popup frames and attach CDP before guest creation

* Overlap relay test projects within the existing worker budget

* Consolidate native preparation and add an opt-in transform cache comparison

* Release inherited debugger sockets during final E2E teardown

* Refresh unit scheduling weights from all five qualified ARM shards

* Avoid retaining unasserted RPC call history in the delivery model

* Use the supported unconditional background wait syntax

* Overlap unconditional output upload with native preparation

* Verify relay integration stays enabled for ordinary unit callers
2026-10-14 17:10:55 -07:00
Jinwoo Hong 8a6cc6c138 fix(terminal): bring each pane back after a terminal service restart when it is shown (#28880)
* fix(terminal): bring each pane back after a terminal service restart when it is shown

After Settings -> "Restart terminal service", every pane that was open kept its
dead process: typing did nothing and agents never resumed until Orca relaunched.

A restart-replaced exit now arms the pane: the visible pane spawns its
replacement at once, any other pane when it is next shown, through the same
fresh spawn and cold-restore agent resume the hibernation wake uses, so old
output stays above the new prompt. The exit keeps the agent status the resume
reads, and clears the dead binding so the replacement binds as a first spawn.
Local spawns wait for an in-flight restart so they reach the new daemon.

Claude-Session: p2-headless

* fix(terminal): resume from the live recovery record; skip a pane another flow rebound

A restart exit is a process death to main's agent-status store, which clears the
pane's row before the window sees the exit, so keeping the window's copy changed
nothing. The resume reads the live recovery record every agent turn already
writes, as a relaunch after a crash does. The replacement also stands down when
the pane's stored binding already names another process.

Claude-Session: p2-headless

* fix(terminal): send every restart exit down one path

A finished-turn agent pane took the hibernation wake, which gives up when its
record changes before the pane is shown. Every restart exit now arms the
replacement directly.

Claude-Session: p2-headless
2026-10-14 20:10:33 -04:00
Jinwoo HongandClaude Opus 5.5 f9ef559781 refactor(tabs): choose new tab ids before creating them at simple create sites (#28888)
Ten create sites now mint the new tab's id (or browser page id), pass it to
the create action, and key every follow-up (select, focus, tab order, group
focus, remote page handle) off that id instead of the returned record.
Creates stay synchronous, so the follow-ups still run immediately.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:05:32 -04:00
Neil a90ff597be Show whole credit balances in usage displays (#28887)
* fix: limit credit balances to two decimal places

* fix: show whole credits in usage displays
2026-10-14 17:04:24 -07:00
Jinwoo HongandClaude Opus 5.5 02d9366198 refactor(tabs): mint a workspace's root group with the loader's id (#28884)
A workspace with no groups got a random root group id from the window. It
now gets the name-based id the shared loader already mints for that
workspace's group, so main can derive the same id without the window.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:57:07 -04:00
Neil 0b1a6007e0 feat(wsl-mover): a moved pane reopens in its last folder on the WSL host (D1b-6b) (#28885)
* feat(wsl-mover): a moved pane reopens in its last folder on the WSL host (D1b-6b)

* fix(wsl-mover): consume a restore hint only when its pane spawns, scope it to its worktree, retry failed installs (D1b-6b)
2026-10-14 16:53:05 -07:00
Jinjing 232f6048e7 Remove obsolete Hermes cron documentation and translations (#28877) 2026-10-14 16:29:28 -07:00
Neil 66451a2c3a feat(orcad-migration): a capability-gated blob channel beside scrollback (D1b-6a) (#28866)
* feat(orcad-migration): a capability-gated blob channel beside scrollback, for the WSL mover's new data (D1b-6a)

* fix(orcad-migration): the commit names its blob set, prune clears leaked blobs, reserved names refused (D1b-6a)
2026-10-14 16:28:23 -07:00
Jinwoo Hong 818e7829f6 feat(relay): split step-5 shadow compare and dry-run logs per cell (#28693)
* feat(relay): split step-5 shadow compare and dry-run logs per cell

The 24 h per-cell shadow gate needs per-cell sticky-compare counts, per-cell
unexplained counts, and dry-run `full` refusals while the map showed room.
Both summary lines gain additive per-cell fields; existing fields are unchanged.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): count a cell-mismatch on both cells and bound per-cell maps by the fleet

- countByCell (was cells) counts a compare on every cell it involves: the
  database's cell, every seat's cell when there is no row, and both cells
  for a cell-mismatch. Drop the 64-cell cap and cellsOverflow.
- Add ReservePlacer.free and use it for the dry-run free share.
- Rename the room bound to RESERVE_DRY_RUN_ROOM_BOUND.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): count a duplicate-seat compare on every seat cell

An unexplained duplicate seat counted only on the database's cell, so a cell
whose stale feed kept a second seat could pass its own step-5 check. Count it
once on every seat cell, the same as a cell-mismatch.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): count explained cell-mismatch and duplicate-seat on the database cell only

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): key per-cell counts on prototype-free records

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): explain a duplicate seat left by a just-superseded half-open control

When the database names the newest seat exactly, every other seat has a
strictly lower epoch, and the newest seat joined within the cell's silence
close (timeout + one ping interval + the map lag bound), the older seat is
a half-open control awaiting its 75 s pong-silence close, not a fault.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): bound the superseded duplicate-seat rule on the move's age

A same-epoch rejoin resets joinedAt, so a stale older seat could stay
explained forever; the director-clock sighting of the epoch moves only
when the epoch rises.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): leave a superseded duplicate seat unexplained without an epoch sighting

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): explain a superseded duplicate seat only from a prior sighting

compare() recorded the epoch before classifying, so a first-ever sighting
(restart or eviction) read as a fresh move. Classify first and take the
move's age from the record that existed before this compare.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): also bound a superseded duplicate seat on its join age

A long-ago lower-epoch sighting dates the move to this compare, so the
current seat's join age bounds it too.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): count an ageless superseded duplicate seat as its own class

A superseded-shaped duplicate-seat with no prior sighting of the host (the
first compare since a restart) has no move age, so it is now
`superseded-unknown-age` (explained: false) rather than a plain duplicate.
The count line also carries `uptimeMs`, so the gate can discount that
class in an instance's first 100 s.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): limit superseded-unknown-age to an instance's first 100 s

Flushes are lazy, so a window has no upper bound and an uptime field on the
count line cannot date it. The compare now records when it started and
emits superseded-unknown-age only within the superseded close bound of
that; later, a missing sighting means another instance served the host or
the sighting was evicted, so it is a plain unexplained duplicate-seat.
Drops the uptimeMs field.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): say where a missing sighting comes from after the first 100 s

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f
2026-10-14 19:15:04 -04:00
Neil 6607c54984 perf(ci): cut redundant tests and reuse expensive setup (#28669)
* perf(ci): reduce test overhead and reuse expensive build work

* test(ci): keep worker startup outside failure-case deadlines

* test: isolate push fixture advisory locks

* test(ci): preserve fixed drain retry allowance

* Align CI contracts with current triggers and guide content

* Stabilize consolidated reveal scenarios and retain behavioral CI contracts

* Fix upstream lint blockers while retaining behavior and assertions

* Classify global Jira preferences in the WSL migration inventory

* Align Jira test fixtures with startup and build safety checks

* Include package-test routing dependency in CI checkouts and fixtures

* Keep native stream classes intact in transcript dependency tests
2026-10-14 16:14:27 -07:00
Kelvin Amoaba bd6f1ca32a fix(automations): sleep a finished run's terminal instead of closing it (#28838)
* fix(terminal): keep a hibernating pane's resume record through its stop

The agent dies before its shell, so the shell's command-finished mark read
as the agent exiting and deleted the record the hibernation had just written.

* fix(automations): sleep a finished run's terminal instead of closing it

Closing it left the run's own workspace empty, so opening it later showed a
blank chat or shell instead of the run's conversation.

Fixes #28645
2026-10-14 23:13:22 +00:00
Brennan Benson 850e71e672 Resume in New CLI for OpenCode and OMP chats (#28075)
* feat(chat): Resume in New CLI for OpenCode and OMP chats

OpenCode's and OMP's CLIs have no copy flag, but their chat protocol copies a
conversation (ACP session/fork). For a fork of one of their chats, the chat's
host now has a short-lived agent, launched exactly as the chat's own, copy the
conversation; the terminal then opens the copy with a plain resume. The chat
keeps its own conversation, so the CLI is never a second writer on it.

The copy travels on the existing aiVault.prepareSessionResume fork request as
an optional forkedSessionId; a host that predates it answers without one, and
the client asks for an Orca update instead of resuming the original.

* fix(chat): open an OpenCode 2.x chat's CLI copy with opencode, not opencode2

The copy kept the row's agent, so Resume in New CLI ran `opencode2 --standalone --session <copy>`,
which OpenCode 2.x doesn't install. The chat's host now answers the copy with the agent whose CLI
opens it: the chat's own.

* fix(chat): type the copied conversation's agent as a history agent

* fix: preserve independent chat copies through CLI launch

* test: keep only the tests that pin user-visible behavior

* fix(chat): validate OpenCode copies on their execution host

* test(chat): expect copied OMP forks after main integration
2026-10-14 16:06:50 -07:00
Neil 7244bf3e6f Reclaim idle worktree space automatically on dev startup (#28864)
* Run safe worktree cleanup on dev and add interactive pnpm reclaim

* Rename storage cleanup command to reclaim-space

* Keep real stream constructors in transcript dependency tests

* Detach cleanup output and align historical cache previews
2026-10-14 15:47:02 -07:00
Jinwoo Hong e18e7fcf10 chore(relay): pin staging c1, c5, c6 and c7 to image G for E-pre (#28872)
Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f
2026-10-14 18:28:05 -04:00
Jinwoo HongandClaude Opus 5.5 e53b20095d fix(terminal): reject a requested tab id that is already taken (#28841)
* fix(terminal): reject a requested tab id that is already taken

createTab silently minted a fresh id when the caller's requested id was
already in use. The caller's PTY routes agent status by the id it asked
for, so the new tab never received its agent status and the status went
to the other tab instead. Now the create throws and changes nothing.

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

* refactor(agent-launch): drop the unreachable tab id mismatch guard

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:54:46 -04:00
Jinwoo Hong 27c0275e3a feat(mobile): log OTA shell lifecycle to the connection diagnostics (#28840)
* feat(mobile): log OTA shell lifecycle to the connection diagnostics

Exported connection logs showed nothing about the web shell, so a phone
stuck on "Opening workspace" could not say whether it stalled activating
the download, loading the page, or waiting for its first paint. Record
sparse shell milestones, page signals, one-shot 15s stall warnings and
bridge diagnostics into the per-host connection log. Diagnostics only.

Refs STA-10545, #28626

* fix(mobile): cut shell log volume and pinpoint each waiting screen

Every page route mounts its own shell, so four lines per healthy open
evicted connection history from the 200-entry log. A healthy open now
writes one line, on paint, carrying source, build and elapsed.

The 15s stall line now also covers "Checking host" (gate verdict and
whether the cache read answered) and a download with no progress. The
"page has not drawn" line counts the page's ready asks and whether the
document loaded, telling a page that keeps refusing init from one that
took it and died. Frames dropped with no bridge host, an unreadable
host profile, post-failed error names and route-refused issues are now
logged; path masking catches quoted, bracketed and Windows paths. The
exported report header names the shell build and bridge protocol.

Diagnostics only: no behaviour, UI or wire change.

Refs STA-10545

* fix(mobile): dedupe flapping shell lines and mask spaced paths

A host with no cache flapping between unreachable and dialling wrote
"checking host" and "host unreachable" every cycle per mounted shell;
each is now written once per host until a different shell line lands.
Path masking keeps going across a space while the next word still holds
a separator, and accepts a comma before the path. The paint line's
elapsed time is renamed activatedMs, which is what it measures. A
route-only session rebuild re-arms the stall timer.

Refs STA-10545

* refactor(mobile): split the shell log module by responsibility

shell-lifecycle-log.ts passed the 300-line lint cap. The line type,
recorder and flapping dedupe move to shell-lifecycle-line-recorder.ts,
bridge diagnostic lines to shell-bridge-log-lines.ts, and user-path
masking to shell-log-path-masking.ts, with tests split to match. No
behaviour change.

Refs STA-10545
2026-10-14 17:42:22 -04:00
Jinjing ebd4f00453 Skip reindexing unchanged transcripts on cold session list reads (#28798)
* Skip reindexing unchanged transcripts on cold session list reads

A cold session-list read re-parsed every self-contained transcript and rewrote its search rows, even when nothing had changed. That wasted work and churned the index on every list.

- When a transcript's stat (mtime, size, inode, device) matches a complete index entry, the list read now skips the rewrite. Agents with sidecar metadata, unknown sizes, or partial or stale entries still reread.
- Test fixtures that replay a "changed" transcript now pass a new mtime so they still exercise the replace path.
- New tests cover unchanged skips, changed size/mtime/file identity, sidecar refresh, due/failed repair, and append.
- Dependent-transcript and Codex title tests check that cold rereads keep search and title data current.

* Make the self-contained transcript check exhaustive over every agent

- Replace the hand-maintained `Set` of self-contained agents with a
  `Record<AiVaultAgent, boolean>`, so adding an agent type fails typecheck
  until someone decides whether its transcript is self-contained.
- Classification is unchanged: the agents that were in the set are `true`,
  and the rest are `false` with a short note on the sidecar or metadata
  source they depend on.
- Make the replace-generation tests derive the second mtime from the first
  candidate instead of a hard-coded timestamp, so they stay valid if the
  default fixture mtime changes.

* Guard file-stat reuse with a self-contained transcript test

Indexing skips reparsing unchanged transcripts for agents marked self-contained. That is only safe if their parsers read nothing but the transcript itself, and nothing enforced it. A parser that started reading a sidecar or dependency file could serve stale index entries.

- Export SELF_CONTAINED_TRANSCRIPT_AGENTS so the test checks the real list
- Add a test that parses each self-contained agent's fixture, both direct and cached, and fails if any file other than the transcript is read
- Register the test in the SQLite runtime-file include list

* Require stored file identity before skipping a transcript rewrite

- Rows with a NULL dev or ino were treated as covered, so a file replaced with the same size and mtime could be skipped and never reindexed.
- Such rows now count as uncovered, so the rewrite runs and records the identity.
- The identity is read through the shared `fileIdentity` helper instead of the raw file fields.

* Add regression test for session rows indexed without file identity

A session row indexed before file identity (dev/ino) was recorded was never reread once the stat began supplying one, so it could keep stale search content. This test covers that case: the first read with identity replaces the row, and a second read with the same identity leaves it alone.
2026-10-14 14:37:57 -07:00
Jinwoo HongandClaude Opus 5.5 f8cbfef4fd test(session): compare what the window shows before and after the shared session loader (#28842)
* test(session): compare what the window shows before and after the shared session loader

Boots the window's real startup hydration on each saved-profile fixture as
stored and as the shared loader rewrites it (load then save), and compares
tabs, groups, active tabs, pane trees, terminal bindings, editor and browser
tabs. Today's eight differences are listed with the loader rule behind each;
the list may only shrink, so any new difference fails.

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

* test(session): let the parity test own its store so the harness reads no host focus

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

* test(session): restore the parity boot's UUID spy even when the boot throws

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:31:04 -04:00
Jinwoo Hong 47b63b4ea1 chore(relay): pin staging c4 to image G for E-pre (#28854)
G is main 2de73812 (relay sha256:07b5bf67…), which adds the cell's t_preauth latency metric.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f
2026-10-14 17:30:53 -04:00
Jinwoo Hong cc7d03d19c fix(daemon): keep every pane in main's layout through a terminal service restart (#28852)
Restarting the terminal service ends each session with a synthetic -1 exit. Main
read those as ordinary deaths and retired every terminal surface from the saved
layout, while the window kept drawing them. Phones and the CLI lost the tabs, and
the next launch restored a split as a single pane.

The restart now labels those exits as replaced, and main records them in its
existing intentional-stop register, the one terminal restarts already use. The
runtime keeps the surface and the window receives the same replaced label.

Claude-Session: p2-headless
2026-10-14 17:25:20 -04:00
Jinwoo HongandClaude Opus 5.5 c8f080764b refactor(terminal): publish new tabs together with their startup command (#28848)
Seven call sites created a terminal tab and then queued its startup command
in a second store write. Pass the command through createTab's existing
pendingStartup option instead, so the tab and its command land in one write.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:15:46 -04:00
Sojiro.OandBrennan Benson a93a236577 fix(i18n): use クリーンアップ in the Japanese VM cleanup stop dialog (#28533)
The dialog title and cancel button said 掃除 (household cleaning), while its own description and confirm button already say クリーンアップ. Use クリーンアップ throughout.

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-14 14:14:09 -07:00