The host header lays out as a 32px card in 4px top padding (36px), but
estimateRenderRowSize sized the row at 32px. The group tier's 36px pin
offset then fired 4px early, so a project header could pin while its
in-flow copy was still on screen — two headers at one coordinate.
Size the host row 36px so it matches the pinned slot, and resolve each
sticky handoff from the row's real start offset instead of only the
lagging range start, so a header pins exactly when it reaches the slot.
Run Muse Windows hooks through the existing direct launcher on safe profile paths and refresh managed installations. Keep the PowerShell compatibility fallback for paths requiring special quoting. Verified on Windows; CI passed.
Adapted the direct-launcher change from community PR #26734.
Co-authored-by: Naveen Chatlapalli <naveen.aifanatic@gmail.com>
Enable the independent hang watchdog on Windows and preserve the previous run's marker until the startup trace sink is ready. Retain local consent, heartbeat timing, worker cleanup and bounded marker validation. Operation tracking is deferred.
Adapt the startup-order regression coverage from community PR #26468.
Co-authored-by: fanzzzd <fanzzzd@outlook.com>
* Guard desktop open actions with the target workspace owner
* Wait for the folder catalog only in the folder menu exercise
* Resolve the row owner only while its Open in menu is open
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(native-chat): settle a dead agent's leftover work in one write that always fits
A dead generation's cleanup was several separately committed writes, and a
single leftover item larger than one batch row was rejected on every try, so
the chat stayed stuck. Plan every row first and commit them in one
transaction: bounded batch rows, an oversize item as an ordinary row with its
full body, and the settlement's own send verdicts. Readers see it only after
the commit. Settlement rows are rebuilt only from schema-known fields, address
saved items by their stored key, never invent an end time, and new writes
require safe revisions, so cleanup never builds a row it cannot save.
* fix(native-chat): settle Codex exits in one write and keep turn fields a newer build saved
- Codex's own exit cleanup hands the whole settlement to the journal's one-transaction planner instead of committing chunk by chunk.
- One shared planner rejects an item named twice, on both the direct and resolved paths.
- Rewriting a turn keeps fields this build does not know, as the journal schema promises; the legacy carrier's kind still cannot override it.
- Drop the write-only revision admission and the planner skip that worked around it: insert admission is the reader's again, and a saved revision that cannot advance is skipped by the reducer while the rest settles, as before.
- Remove two dead-generation journal members settlement no longer calls.
* fix(native-chat): advance repeated items across a settlement's rows and keep nested turn fields
Repeated Codex question ids in one write fold in order again instead of failing the write,
and a saved turn's context usage keeps fields a newer build stored inside it.
* test(native-chat): run the new Codex journal SQLite tests in the Node runtime project
* feat(runtime): default to bundled Node 24 and require modern hosts
* Update SSH deployment expectations for Node 24 guidance
* Expect Node 24 in Windows runtime installation guidance
* Start worktree.create's agent through the launch executor
The host's worktree.create handler now starts a requested agent (startupAgent,
or a linked startupDraft) through executeAgentLaunch instead of calling the
create directly. Nothing on the wire changes: the request, the reply and
agentTerminalHandle are the same, the agent is still the workspace's startup
terminal whatever the chat default says, and the create still delivers the
text itself (folded into the command at any length, a follow-up for agents
that read it after start, and the draft pasted unsent).
Adds three host-internal executor inputs: terminalOnly, the legacy-host prompt
policy, and a shared entry (createWorktreeWithStartupAgent) that host
producers can call in place of createManagedWorktree.
* Keep a linked-draft create recording only the agent it actually started
Problem: after worktree.create moved onto the launch executor, a create from
a linked issue (phone "create from issue", or a raw worktree.create with
startupDraft) could differ from before in two narrow cases. When the draft's
agent came from the settings default or from detection and its launch command
could not be built (for example unbalanced quotes in the saved agent
arguments), the workspace was recorded as created with that agent even though
it never started, and a background create opened no terminal at all instead
of its plain shell. When no agent was found, detection ran twice (an extra SSH
round trip on remote repos).
User-facing change: none intended; both cases now behave exactly as before
the executor move.
Mechanism:
- The entry hands the draft's chosen agent to the create as a main-internal
startupDraftAgent, which steers only the draft; createdWithAgent stays what
the caller sent. A draft that resolves no agent is created without the
draft, so the create does not detect again.
- AgentLaunchExecution is a discriminated union: every normal launch must
bring its surface factory, and a legacy-host launch must be terminal-only,
bring a workspace factory, and cannot bring surfaces. The runtime guards
the types replace are gone; the target kind is checked before any create.
- Legacy-host receipts follow the delivery module's rule: a blank prompt is
no prompt, a submit folded into the launch command is handed-to-terminal,
a post-start send the create does not await is unconfirmed (host-internal),
a draft is not-delivered.
- The no-agent error lives with the legacy-host policy and is caught only by
the shared entry.
- Tests: a real-runtime parity test (stored agent, terminal spawns,
detection count vs the create alone, including the unbuildable plan),
exact-argument checks over every non-agent startup field, compile-time
checks for the union, and direct tests of the draft agent resolver.
* Say which creates still bypass the shared agent entry
Two comments stated that headless automations and federation already go
through createWorktreeWithStartupAgent; both still call
createManagedWorktree directly. Reword them as the plan. Comment-only.
* feat(native-chat): name an agent's message by its current title, and open it from the queued card
The sender line on another agent's message and on its queued card now shows the
name Orca shows for that agent right now (its sidebar agent row or chat tab
name), falling back to the name recorded when it was sent. The queued card's
names open the sender, as the transcript row's already do.
* fix(orchestration): name a CLI agent sender by its tab's own name, not just its agent
A terminal agent's recorded sender name used only a manual tab rename, else the
agent type, so mail from CLI agents read as "Codex" or "Claude". The host now
names every sender the way Orca shows it: a chat by its rename or saved name, a
terminal agent by its sidebar agent row's name (rename, quick command, generated
or meaningful live title; a split pane only by its own title). The task title and
generic agent label are fallbacks. The renderer shows the live own name, else the
recorded one.
* fix(orchestration): preserve known sender names in message snapshots
* Preserve sender names across chat clears and terminal reuse
* fix: keep sender links on the recipient host across older runtimes
* test: type sender naming fixtures with their declared identities
* Fix sender ownership publication after clearing a chat
* Drop /clear sender lineage now that /clear keeps the same conversation
Main's #26579 keeps a native chat in the same session across /clear, so a
message's sender id still names the live chat. Remove the host's
orchestration-root projection, the optional status field, and the renderer's
lineage lookup and feed capability probe that only served it.
* fix(native-chat): underline a file path only once its workspace confirms it exists
Chat underlined any path-shaped text, so an example path an agent made up
(`src/app.ts`) looked clickable and a click said "File not found". The
terminal already checks a path on its host before underlining it; chat now
goes through that same code: the terminal's path finder, one shared
resolve-and-check step (terminal-file-link-target.ts), and the same
longest-match rule. The terminal's hover and click fallback use the shared
step too.
Fixes STA-9683
* fix(native-chat): recheck every shown file link when a turn ends or the host returns
Chat only forgot "missing" answers at turn end, so a file deleted or renamed
during a turn kept its underline, and a path the host could not answer
stayed plain until the message remounted.
Each answer now records the recheck generation it was asked in. A turn end,
or a reconnect / re-pair of the host answering for the workspace, starts a
new generation and re-asks only the paths shown by mounted messages; old
answers keep showing until the host replies, and only messages whose answer
changed re-render. A reply to an older question cannot overwrite a newer
answer. A host that cannot answer keeps the previous answer and is retried
three times with backoff, ending when no message shows the path or at the
next recheck. While an SSH workspace's connection is unresolved, paths are
treated as unknown instead of being checked on this machine, and the chat
starts over once the connection resolves.
* fix(native-chat): keep main's file-path shape rules and check existence on top
Chat detection goes back to main's rules: a candidate needs a folder plus a
rooted prefix, a line suffix or an extension, and prose spans are split into
tokens as before. Bare names and folders are not underlined, and ordinary
prose no longer sends word prefixes to the host. Existence is required on top,
so the underlined set only shrinks compared with main.
A rooted spaced span ("/Users/me/repo/src/x.ts to use src/app.ts") now also
offers its tokens, so both real paths link; a real spaced rooted path still
wins as the longest one that exists.
Tests: bare names and folders stay plain even when they exist, slash tokens
and versions stay plain when everything exists, `My Notes.md:7` is rejected by
shape, explicit links and fenced code never reach the existence check, plus
the rooted-then-relative and longest-span cases.
* fix(native-chat): catch up file links answered before subscribe; tighter recheck triggers
- A message renders (and asks) before it subscribes; an answer landing in
between was never shown. Each message records the answer its render used,
and on subscribe it re-renders if the answer differs and re-asks outdated
paths. A failure for a path no message shows yet no longer arms a retry.
- A rooted path ending a sentence ("/Users/me/r/a.ts.") inside a longer span
now links, with the punctuation kept outside the link. Unrooted spans are
unchanged, matching main.
- The checker takes the workspace's SSH connection id and decides
"connection not known yet" from it, so the provider memo needs no lint
suppression.
- SSH reconnects recheck only when the connection reaches connected or gets
a new generation, not on every intermediate status.
* fix(native-chat): never check a network share outside the workspace without a click
Chat checks every path in a finished reply on its own. On Windows, a local
check of a path like \\server\share\a.ts or //server/share/notes.md opens an
SMB connection to that server and sends the user's credentials, and agent
text can be steered by prompt injection. Such a path is now never asked
about and stays plain text, unless it is inside the workspace or is a known
workspace root. WSL paths (\\wsl$, \\wsl.localhost) and checks made by an SSH
or remote runtime host are unaffected. Terminal hover and click behaviour is
unchanged.
* chore: restore main's copy of structured-agent-session-queued-mutations.ts
The merge commit's format hook dropped one blank line in this unrelated file.
Committed without the hook so the file matches main byte for byte.
* refactor(native-chat): own the file-link checker in NativeChatView, not each chat view
The provider now sits where NativeChatView picks the chat view, and derives
the file-link context from the tab id. The two chat views keep only a
one-line turn-end hook, since only they know whether a turn is running.
Same lifetime as before: inside the session-keyed owner for structured
chats, and per resolution for terminal-backed ones. No behaviour change;
it keeps NativeChatResolvedView under its line limit after main's find-in-chat.
* fix(native-chat): refuse network shares on SSH hosts too; allow only the workspace's WSL distro
- The "no network share outside the workspace" rule now applies to SSH
targets as well: a Windows SSH host's relay would otherwise stat
//server/share/... and open SMB with no click. Remote runtime paths stay
exempt, since they are always inside the worktree.
- WSL paths are allowed only for the workspace's own distro (from the pane
runtime or the worktree path, via the same helper the path mapping uses),
after `..` is normalized, so `//wsl.localhost/../x` or another distro is
refused.
- Drop the \\?\UNC\ chat test case: the real detector never produces it.
* refactor(terminal): keep file-link host path helpers out of the open flow
terminal-file-link-target imported its pure path helpers from
terminal-file-open-routing, which also opens files and so loads worktree
activation, launch and the agent catalog. Since NativeChatView now hosts the
chat file-link checker, that chain reached tests that render NativeChatView
with a minimal i18n mock (native-chat-view-file-drop.test.tsx failed in CI).
getTerminalFileContext, mapTerminalFilePath, terminalLinkWslDistro and
terminalPathWslDistro move unchanged to terminal-file-path-mapping.ts;
terminal-file-open-routing imports and re-exports them, so its callers and
their mocks are unchanged.
* Show 'Pi isn't signed in' before the first send
A Pi chat whose agent started with no model to use now says so in the
existing availability notice, and the next send replaces that agent so
a sign-in made since takes effect.
* Re-read the sign-in notice when the chat's agent stops; ignore an exited Pi
A Pi's start verdict ends with that Pi: the notice now re-reads when the
host starts or stops the chat's agent, and a Pi whose process already
exited reports nothing.
* Keep one sign-in line after a refused send; re-read on every agent start or stop
The notice steps aside while the newest send was refused for the same
reason, and the chat re-reads its verdict whenever its agent starts or
stops, dropping an answer read before.
* Step the notice aside only while the refused send's own line is on screen
Uses the host's send order and the chat's rendered delivery lines, so a
deleted returned card or a trimmed row brings the sign-in notice back.
* Keep the sign-in notice when the refused message is no longer loaded
Agents often answer a failed session/prompt with a generic JSON-RPC message ("Internal error") and keep the real reason in error.data. Only OMP and Grok had hooks that read it, so OpenCode and future ACP agents showed the bare message.
The prompt error detail now reads, for every ACP agent: the agent's hook, else string data, else data.details, else the message. Other structured data is metadata and is ignored. OMP's details hook and Grok's string-data branch are now covered by that rule and removed.
Folder rows used the bare folder ID for active state, DOM IDs, virtual keys
and activation, so same-ID folders on two hosts collided. Use the existing
host-qualified worktree identity, matching git worktree rows.
Co-authored-by: m4air <m4air@Mac.localdomain>
* Preserve editor file identity during SSH host conversion
* Keep a dirty in-session editor tab on its file record during SSH conversion
A file opened over the relay in the current session carries route provenance,
so the no-host-evidence check repointed its tab to a fresh clean record and
hid the unsaved draft. Keep any retained record with no runtime owner instead.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* Distinguish remote browser service failures from server outages
* Use the shared browser_unavailable code in the stream failure classifier
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Sleeping-session resume, AI vault resume and the plain quick-command
terminal each carried a line-for-line copy of persistAgentLaunchTabOrder.
Call the helper instead. No behavior change.
* fix(native-chat): read a queued card's whole text, and keep Edit from hiding it behind a prompt
A queued card showed one clipped line, so a long message (often another
agent's, never typed by the reader) could not be read before Steer or
Delete. A clipped card now opens in place to its whole text; the phone's
two-line card opens on tap.
Edit moved the card's text into the composer and deleted the card even
while a question or approval card stood in the composer's slot, so the
text vanished from the page. Edit now waits, saying why, until the
prompt is answered; the pane and the queue read one check for whether a
prompt holds the composer's slot.
* fix(native-chat): keep the full-text hover title on a folded queued card
* fix(native-chat): phone card opens only when it clips; test the composer-slot check
The phone card measured nothing and made every card a button; it now lays
the text out unseen at its width and offers Show more only past two lines.
The Edit reason is no longer dimmed twice, and the shared composer-slot
check and the desktop toggle's keyboard reach have their own tests.
* fix(mobile): the unseen measuring copy of a queued card takes no touches
* fix(native-chat): open a queued card's full text below its row, at the card's width
In a narrow chat the opened text sat beside the card's actions and wrapped
a few characters per line. It now opens under the row, indented to the
text, the way the transcript's awaiting-input row opens its question.
* fix(native-chat): the queued card's toggle names the opened text only while it exists
* refactor(native-chat): the queue reads pending prompts itself, keeping the session hook under its line cap
* refactor(native-chat): keep this PR to reading a queued message in full; Edit moves to in-place editing
* test(native-chat): queue list rig passes the pending-prompt flag main's controller takes
* fix(orcad): provide execution host CLI in Unix managed terminals
* fix(orcad): keep the server starting when the CLI launcher cannot be written
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* Native chat: Cmd/Ctrl+F opens find in window
Mod+F inside a focused native chat opens a find bar at the chat's top-right
corner. The bar is a separate sandboxed view stacked over the main window, so
its own input is never a match; main runs the window's find-in-page and relays
the match count back. Enter/Shift+Enter step, Escape closes and clears.
Also stops the terminal search bar from opening over a chat that covers a
terminal pane.
* Native chat: Cmd/Ctrl+F finds inside the chat transcript
Replaces the window-level find panel with a find bar inside the chat.
The panel searched the whole window, pasted into the composer, outlived
its chat, and let Escape interrupt the agent.
- Searches only the transcript text the reader can see and paints matches
with CSS highlights, reusing the markdown preview's search, now shared
as lib/dom-text-search-highlights.ts.
- Re-searches as the transcript streams or scrolls, keeping the current
match; a step leaves "follow latest" like a reader scroll.
- Escape closes the bar first and returns focus; paste and editing keys
stay in the bar's input.
- Terminal guard yields any chat.find chord inside a chat cover; a PDF
no longer takes Mod+F from another surface.
* Native chat find: round-2 fixes
- Escape: the chat root's React capture handler closes the bar before the
composer's own capture handler can interrupt the turn.
- One visibility rule for picking and revealing a match: typing no longer
scrolls to a match that is already on screen, and the bar's real rect,
not a full-width band, decides what it covers.
- Reveal scrolls nested scroll boxes (code lines, tool output, thinking);
text cut off by a box that cannot scroll is not counted.
- PDF find opens from its own tab and the file explorer again, but not
from another group, a chat, or a text field.
- Prompt cards no longer take focus from the find input; clicks on the
bar keep typing in it; buttons keep their own Enter.
- A match whose row scrolled away restarts from the first visible match.
- Streamed text growth re-searches every 120 ms instead of every frame,
and searches reuse their Range objects.
- Hover-only row chrome is marked and skipped instead of read by opacity.
- Chat find stays off in the web client, where the browser's find keeps
Mod+F.
- The text matcher moves to lib/text-match-ranges.ts with neutral names.
* Native chat find: round-3 fixes
- Text that held a match re-searches before the next paint: React
rewrites a growing text node whole, collapsing its ranges, so the
streaming paragraph's highlights no longer blink. Other text growth
keeps the 120 ms re-search.
- A disclosure's height animation ending re-searches, so an output
opened while find is open is counted.
- A prompt card that arrived while the find bar held focus gets it when
find closes.
- A reveal that cannot scroll no longer stops following the latest.
- PDF find follows the panel's chord owner (its tab is the active tab
of the focused group, the rule the browser pane uses), so it no longer
takes Mod+F from a chat, terminal or browser in another split, and it
opens again after clicking its own editor header.
* Native chat find: PDF find answers only its own window surface
The floating workspace panel keeps its own focused group, so a PDF that
owns its panel's chords no longer takes Mod+F from a chat in the other
surface (main window vs floating panel), matching how the browser pane
scopes a floating workspace's chords.
* feat(native-chat): one-time upgrade tip for pre-upgrade Experimental Chat UI users
Show the new native chat upgrade tip through the normal startup feature-tip
flow, only to profiles whose saved Experimental Chat UI setting was on before
this release. Main records that audience once, from the profile as saved by the
previous build, and never re-derives it from the live setting, so later
opt-ins, new installs and the future graduation stay excluded.
* fix(native-chat): keep the upgrade tip readable and dismissed
Focus Got it without scrolling and tighten the ja/es/fr copy so the title is
visible on open. Show the tip exactly like other startup tips (no resume-dialog
wait), keep the illustration's upward menu unclipped, and merge seen tip ids
on save so a stale client cannot bring a dismissed tip back.
* fix(native-chat): say "between chat and CLI" in French and pin the audience basis in tests
* Offer Resume in New CLI / Native Chat from the tab menu
Right-clicking a native chat tab now offers "Resume in New CLI", and
right-clicking a terminal tab running Claude or Codex offers "Resume in
New Native Chat": the same items, gating and handlers the Session History
row menu uses.
The tab finds its Session History row through the existing mappings (the
chat tab's session id against the row's structuredSession, and a terminal
tab's agent-reported provider session, the mapping tab titles use, against
the row's agent and session id), read from the Session History list scoped
to the tab's workspace and host. Both surfaces now share one gate,
resolveAiVaultSessionSurfaceSwitchTargets, one menu-item component, and
call handleResumeInNewCli / handleResumeInNewChat from
useAiVaultSessionLaunchActions.
* Share the Session History list request with the tab menu's chat/CLI move
The tab menu looked its history row up with its own list request, so it
never shared the panel's cached list (and evicted it), forced a full
rescan on every miss outside the panel's rescan budget, and inserted the
item above Pin/Close seconds after the menu opened.
- One list request (scope paths, depth, host, Antigravity flag) and one
renderer-cache read/write, used by the panel and the tab lookup; the
tab builds the panel's own request for its workspace.
- A warm menu answers from the panel's cached list at first paint with no
IPC; a cold one sends the panel's non-forced request and keeps its
result in the panel's cache.
- A forced rescan on a miss now takes the panel's shared forced-rescan
budget; no other force.
- No lookup on hosts where neither move can ever be offered (non-local).
- The item moves to the end of the menu, so a late insert shifts nothing.
- The panel and the tab compose the row's resume inputs through one
function; parity is pinned by unmocked tests.
* Revalidate the tab menu's chat/CLI move and allow it for remote-server chats
- The cached first paint is always replaced by the fresh list answer, so a
conversation a chat has since taken no longer offers Resume in New
Native Chat from a stale cached row.
- Native chat tabs in a remote Orca server workspace look their row up on
that server (it projects chat ownership), so Resume in New CLI shows
there as it does on the row. CLI tabs stay local-only; SSH stays skipped.
- A menu that closed between the two list calls no longer starts the
forced rescan or spends the shared budget; forced rescans stay local.
* Keep the tab menu's history lookup from writing the panel's result cache
A tab lookup wrote its list into the Session History panel's renderer
cache, so a panel opened afterwards could paint that pre-launch list
without asking the host and show a chat-adopted row under its old title.
The tab now only reads that cache for its first paint; the host's cached
list is already shared through the identical request.
* Offer the tab menu's chat/CLI move only from a fresh history answer
The panel's renderer cache never carries current chat ownership (the host
projects it per reply), so painting the menu item from it first flashed
"Resume in New Native Chat" on a terminal tab whose conversation a chat
had taken, and missed the move on that chat's own tab. The item now
appears only once the fresh (non-forced) list answer lands.
* Put the tab menu's chat/CLI move above Pin Tab and hide Switch to chat view
* feat(native-chat): add a chat-owned command receipt store
Adds the agent_session_command_receipts table and an insert-if-absent
receipt write that runs inside a command's own journal transaction. A
receipt records the command's identity, canonical hash, status and a
pointer to the row holding its result; it dies with its chat record.
Nothing uses it yet: the existing operation ledger stays authoritative.
* fix(native-chat): key command receipts like today's ledger ids
Rows are keyed by caller and operation id; a send looks across callers and
other methods look up only their caller's row, matching the existing ledger.
Bad rows and newer table shapes read as unreadable instead of throwing.
* fix(native-chat): order receipt lookups by insertion and key by operation id
Global lookups now take rows in insertion order, so a read and an insert
agree on which receipt answers an id regardless of the host clock. The
primary key leads with the operation id, so its own index serves both
lookups.
* test(terminal): check the runtime's workspace layout against structural rules
A pure rules check over the runtime's session partitions: a terminal in two
panes, a pane in two tabs, a tab outside exactly one group, tab rows and tab
bar disagreeing, two tab orders, and ids changing for the same pane, tab or
group across an update. Wired into the seeded layout model test after every
step, plus late-spawn cases (one known on main, kept as an expected failure).
* test(e2e): layout oracle scenarios with window, headless serve and SSH
Drives layout journeys through window gestures, CLI and paired-client runtime
commands, and after every step checks the runtime's layout against the
structural rules, the window's DOM, the paired-client and CLI views, the
expected end layout, restarts (relaunch, cold daemon, in-app daemon restart)
and per-pane terminal markers. Findings main still has are listed with their
cause; anything else fails. Includes headless orcad/Electron serve scenarios
for the serve issues and a Docker SSH scenario.
* test(terminal): check layout rules on every write and race client commands with the window
The layout model now checks the structural rules on every in-memory
workspace-session write, not only at each step's end, naming the writer
of any breach. Three documented two-writer transients (main mints a tab
row before the window writes its tab bar) are allow-listed by rule, op
and writer; they must still heal by the step's end.
New model ops race a stale window save against a paired client's tab
close, a host-created split, and main's PTY-exit retirement. A new file
runs main's updateMobileSessionPaneLayout (old phone pane trees) against
the real Store; the case with no topology revision is an it.fails: main
drops a live pane and its binding there.
* test(e2e): oracle expected outcomes, transient sampling, remount counts, more scenarios, latency
Every step checks the expected panes, which panes went away and that the rest
kept their terminals; a 100 ms sampler runs the rules between steps; xterm
creations are counted per pane and an unexpected recreation fails. Adds host
navigation, window reload (graph republication), quit right after a change
(quit-time save), an old client's stale pane tree, and a latency measurement
(command to drawn update, idle and under an output flood; no pass/fail).
* docs(e2e): describe the oracle's added checks and latency measurement
* test(e2e): #12723 SSH folder scenario, CI lane for the SSH oracle spec, short socket dir
Adds an SSH folder-workspace (no repo) scenario that checks the layout lives in
one partition across relaunch; registers the SSH oracle spec in the Docker SSH
lane's selection lists; headless hosts put long-TMPDIR profiles under ~/.orca-lo
instead of /tmp.
* ci(e2e): run the headless layout oracle in the job that builds orcad; Linux relaunch findings
The headless oracle spec needs out/orcad, which only the orca serve mode-switch
job builds, so it runs there (and fails rather than skips in CI if orcad is
missing). Records what the first Linux CI run found on main: after a relaunch
main can hold tab rows with no tab bar past the settle wait, and Electron serve
on Linux also lists a renamed terminal's title as null after a restart.
* test(e2e): join partition lists in the SSH folder scenario's log lines
* test(e2e): retry the latency spec's reads through a brief terminal-ownership gap
Linux CI saw terminal.list answer terminal_surface_ownership_unavailable while
the window re-published its terminals; reads retry for up to 10 s, commands do not.
* test(terminal): check the runtime's workspace layout against structural rules
A pure rules check over the runtime's session partitions: a terminal in two
panes, a pane in two tabs, a tab outside exactly one group, tab rows and tab
bar disagreeing, two tab orders, and ids changing for the same pane, tab or
group across an update. Wired into the seeded layout model test after every
step, plus late-spawn cases (one known on main, kept as an expected failure).
* test(terminal): check layout rules on every write and race client commands with the window
The layout model now checks the structural rules on every in-memory
workspace-session write, not only at each step's end, naming the writer
of any breach. Three documented two-writer transients (main mints a tab
row before the window writes its tab bar) are allow-listed by rule, op
and writer; they must still heal by the step's end.
New model ops race a stale window save against a paired client's tab
close, a host-created split, and main's PTY-exit retirement. A new file
runs main's updateMobileSessionPaneLayout (old phone pane trees) against
the real Store; the case with no topology revision is an it.fails: main
drops a live pane and its binding there.
* Use catalog snapshots for native chat workspace reads
* Resolve native chat workspace directories from the catalog snapshot
- Stop falling through to folder-path parsing once the catalog is loaded. The fallthrough let a folder workspace on another host resolve to a local path.
- Look up model discovery settings by the worktree's runtime owner instead of the agent tab's, so a session's model list matches the worktree it runs in.
* commit
* Use worktree owner settings for native chat model discovery
* Resolve native chat files from catalog snapshots
DSH's managed hook posts to the hook server and never emits OSC 9999, so the
dsh-only first-party done lane never fired and tui-idle waits ran to timeout.
Give DSH turn-end hook authority through an agent-state-rules file, mark its
SessionStart as a session boundary, and drop the dead dsh special case.
* ci: share test transforms and remove six redundant checks
* test: remove obsolete project copies and two build string checks
* test: use the injected clock for exact launch readiness budgets
* fix: retain IME confirmation and retire disconnected Windows PTYs
* ci: include relay installer in existing Windows boundaries
* perf: import the canonical SSH manager for filesystem operations
* test: remove 42 source-spelling checks
* test: advance deadlines after real entry and await actual PTY exit
* Preserve native Windows exit acknowledgments and skip daemon-only smoke triggers
* Keep clear in the same chat and reset its provider context
* Derive clear pauses only from journal membership and cover older rewind clients
* Point queued command rewind test at the structured hook
* Group queue consume errors and rewind replay assertions
* feat(native-chat): pace and fade streamed reply text
Snapshots land a clause at a time, 50-700 ms apart. Drawing behind them
at an eased speed is what makes a reply read as flowing.
* feat(native-chat): glide the transcript when following and jumping to latest
The offset is still pinned at once; only what the pin moved is eased, so
nothing races the virtualizer or a reply that keeps growing.
* chore: list remend with the other bundled renderer packages
dependencies is for packages the main process loads at runtime.
* Bound streamed reply reveal and preserve chat interactions
* Smooth overlapping chunks without postponing reveal deadlines
* Fix first-reply following and pin settled layout changes
* Pin concurrent media layout changes without a follow glide
* Keep native chat layout stable when replies complete
* Model delayed browser scroll events in navigation tests
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* Recognize native chat sign-in failures across structured agents
* Fix authentication failures and chat sign-in guidance
* Preserve sign-in diagnostics and command retry context
* fix(native-chat): keep visible sign-in guidance and localize startup rows
* fix(native-chat): separate auth diagnostics from retry guidance
* test(native-chat): align startup guidance expectations
* fix(native-chat): treat OMP's signed-out 'No model selected' as not signed in
* fix(native-chat): start a new agent for the send after it reports not signed in
* fix(native-chat): scope the signed-out check by fence and read without a snapshot
* fix(native-chat): keep a signed-out agent while it owes work, and read only its own rows
* fix(native-chat): keep a signed-out agent that may owe a wake-up turn, and skip the check during a Stop