mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 16:02:11 +00:00
efbf651c7bb2eec778daf1844f8228e70809ec9f
12374
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
efbf651c7b |
Reduce CI setup costs and fixture failures (#24537)
* Let scheduled CI warmers wait and measure WebRTC startup * Measure a smaller daemon shutdown fixture image * Counterbalance WebRTC startup and verify retained fixture files * Record CI fixture measurements and remove temporary pilots * Clarify fixture build dependency cleanup evidence * Make coalesced snapshot fixture delivery deterministic * test: type the PTY write delay observer |
||
|
|
d9fbb4eecf | Keep SSH typing replies inside narrow split terminal panes (#24682) | ||
|
|
66799f7e8f |
Keep paired browser terminal insertion in the host's requested position (#24676)
* test: align source-control fixtures with current store contracts * Bound E2E package setup and retain cancelled-job traces * Remove empty passing sentinels from opt-in socket tests * Make SSH typing pressure fixture readiness and replies observable * Advertise browser support for anchored terminal placement |
||
|
|
b49abdb1f4 |
fix: recover renderer launch failures in the running app (#24250)
* fix(recovery): back off a launch-failed renderer instead of tripping the crash breaker
A renderer that the OS refused to spawn (macOS exit 1003 = LAUNCH_RESULT_FAILURE; field
cause: per-user process limit, posix_spawn EAGAIN) burned the 3-reload crash-loop budget
in ~750ms and raised a "graphics driver" prompt, while the condition lasted minutes.
- launch-failed retries in place on a 250ms..60s backoff (~2 min), outside the breaker;
a loaded document resets it. Other crash reasons keep the breaker.
- Each launch failure records renderer_launch_failed_probe {spawnError} from a cheap
spawn probe, so bundles name EAGAIN/EACCES/ENOENT directly.
- The exhausted prompt says the process limit was hit (probe EAGAIN), drops the
graphics-driver wording, keeps Try Again as default, and offers no Restart:
app.relaunch also needs a free process slot and silently fails without one.
* fix(recovery): skip the launch probe on Windows and probe the prompt once
- Re-check quitting after the prompt's probe; don't re-probe on Copy Commands.
- recordRendererLaunchFailureProbe never rejects (breadcrumb write guarded).
- Windows: no spawn probe; a child per failed launch is the per-operation burst EDR scores.
* test: cover quitting and duplicate renderer launch failures
* test: use typed access in PTY delay regression fixture
* fix: scope extended launch retries to POSIX hosts
* test: cover launch probe behavior on native Windows
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: m4air <m4air@Mac.localdomain>
|
||
|
|
6153fbcfe4 |
Reduce redundant headless server CI work (#24527)
* ci: avoid unrelated headless server qualification * ci: skip headless detection for ineligible draft PRs * ci: preserve cross-host qualification and skip supplied prerequisites * ci: include Windows server cache validation in change detection |
||
|
|
53930a161b |
Keep SSH typing replies visible during background pressure (#24629)
* test: align source-control fixtures with current store contracts * Bound E2E package setup and retain cancelled-job traces * Remove empty passing sentinels from opt-in socket tests * Make SSH typing pressure fixture readiness and replies observable |
||
|
|
f20836c296 |
fix(recovery): prompt instead of reloading into a repeat Windows OOM when commit is exhausted (#23886)
* fix(recovery): ask instead of reloading into a repeat Windows OOM with exhausted commit When another program exhausts Windows commit (RAM + page file), the renderer OOMs, Orca auto-reloads 250 ms later, and the new renderer OOMs again within seconds (launch 13084: 3.5 s after the reload; launch 22912: 34 s). The crash-loop breaker (3 in 60 s) never opens for this cadence, so the user is never told the machine is out of memory. Keep the first automatic reload, but when a win32 reason=oom death follows another OOM within 5 minutes and the pre-gone host sample shows under 512 MB of available commit, escalate to the existing recovery prompt with a new 'low-commit' cause that names the MB left and suggests closing apps or growing the page file. Records renderer_recovery_low_commit_prompt. No-op on macOS/Linux and when commit is healthy. * fix(recovery): gate low-commit prompt on post-OOM readings and recovered deaths only - Reject pre-gone samples taken at or before the previous OOM; they miss the commit that corpse released. - Record an OOM for the repeat window only once recovery actually runs, so skipped teardown OOMs cannot suppress the next first reload. - Skip the install-ACL diagnosis on the low-commit prompt, whose text would not explain Copy Commands. * fix(recovery): read commit at gone time when no sampler tick followed the previous OOM The 10 s pre-gone sampler lands between ~3.5 s repeat OOMs only ~35% of the time, so the gate usually fell back to a silent reload. A gone-time read can only over-report free commit (the corpse already released its pages), so it can miss a prompt but never raise a false one. * fix: reject invalid low-commit readings and clarify recovery advice --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
76c79f473a |
fix(linux): move Chromium shared memory off a tiny /dev/shm (#23751)
* fix(linux): move Chromium shared memory off a tiny /dev/shm Containers such as GitHub Codespaces mount a 64 MB /dev/shm by default. When it fills, Chromium aborts the renderer (IMMEDIATE_CRASH, SIGILL/SIGTRAP) on every reload, trapping Orca in a renderer crash loop. On Linux, stat /dev/shm before app ready and append --disable-dev-shm-usage when it is under 512 MB or unreadable (ORCA_DEV_SHM=off|force overrides). Records a dev_shm_policy crash breadcrumb so future container crashes are diagnosable. * docs(linux): correct dev-shm hasSwitch rationale --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
e3621295e6 |
Remove empty passing sentinels from opt-in socket tests (#24621)
* test: align source-control fixtures with current store contracts * Bound E2E package setup and retain cancelled-job traces * Remove empty passing sentinels from opt-in socket tests |
||
|
|
54be527fd1 |
fix(markdown): cap rendered size of large Markdown previews to stop renderer freezes (#23634)
* fix(markdown): cap rendered markdown size in preview tab and rich override The Open Preview tab rendered any file synchronously through react-markdown with no size check, and 'Open anyway' removed the rich-editor cap entirely. A 2 MB file blocked the renderer 7.8 s at 2.3 GB (5 MB: 38 s, 4 GB), matching scan29 crash reports where users killed a frozen Orca after opening a large .md. - Preview tab: over 600 KB shows a 'Render anyway' gate instead of rendering. - Both overrides stop at a 1 MiB hard cap; above it no render is offered. * fix(markdown): gate diff-tab markdown preview by size and localize gate copy The single-file diff Preview toggle sent the whole modified side to MarkdownPreview with only the 6 MB large-diff limit, so a multi-MB .md diff still froze the renderer. Wrap it in MarkdownPreviewSizeGate keyed by the diff tab id, and add the gate's strings to all locale catalogs. --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
a050afbe86 |
fix(terminal): stop parking pass queueing no-op renders that trip React #185 during worktree removal (#23636)
* fix(terminal): stop parking pass queueing no-op renders during pane-close bursts Removing an active worktree with many terminal panes closed each pane in its own commit. Every commit re-ran the parking pass, whose functional setters queued a render even when the sets were unchanged, so each commit left work pending and React's nested-update counter climbed past 50 (#185), taking down the terminal workbench boundary. Dispatch only when a set actually changes. * fix: initialize parking state mirror lazily and verify transitions --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
5a56636f66 |
Bound E2E package setup and retain cancellation traces (#24617)
* test: align source-control fixtures with current store contracts * Bound E2E package setup and retain cancelled-job traces |
||
|
|
5f308bfa9c |
revert: take the 26 Phase 3 (#16741 port) PRs back out of main (#24559)
* Revert "feat(orcad): source-side dormant export of a relay-hosted SSH target (#16741 T6-8) (#24519)" This reverts commit |
||
|
|
306b4578aa |
Enable Option shortcuts for ABC keyboards in Auto mode (#24528)
* Clarify Option shortcut settings and cover punctuation input * Enable Auto Option shortcuts on ABC keyboards safely |
||
|
|
3f37fcc423 | test: align source-control fixtures with current store contracts (#24571) | ||
|
|
ba9af21d75 | test: classify terminal driver input with the current PTY contract (#24560) | ||
|
|
e2c5414f76 |
fix(native-chat): an older Orca keeps a chat with a newer row kind read-only instead of deleting the rest of its history (#24477)
* fix(native-chat): an older Orca skips and keeps a journal row of a kind it does not know
* test(native-chat): a newer build's journal row kind survives reads, writes, rewinds and reopens
* fix(native-chat): an older Orca keeps an unknown journal row kind read-only unless its writer declared it skippable
A row of a kind this build does not know, in a well-formed envelope, now latches the chat
read-only with every row kept, the same way a newer row version does. It is read past only
when its writer declared `ifUnknown` on the row: `skip` (a rewind drops it) or `carry` (a
rewind carries it after the rebuilt history, epoch, seq and fence restamped). Every existing
kind changes queue or turn state, so skipping by default would let an older build write from
a wrong fold.
- journal-row-kind-compatibility.ts: each kind states how older builds read it, typed over
every row kind, so a new kind cannot be added without a declaration.
- Rewind restates the Resume and Stop as before, then carries `carry` rows in source order;
the restatement goes back to { lifted, liveStop }.
- Replay treats a row whose body names another sequence than its stored key as malformed at
the key, so the next write never collides with it; catch-up reads stop there too.
* refactor(native-chat): drop the writer opt-in; an unknown journal row kind only latches read-only
An older Orca now treats a row of a kind it does not know exactly like a row from a newer
schema version: every row stays on disk and the chat opens read-only until an update. The
writer-declared skip/carry opt-in, its in-memory placeholder, the carry through rewinds and
the per-kind registry are removed: no current or planned kind could use them, and they can
come with the first kind that may safely be read past.
Kept: an unknown kind needs the envelope every row keeps (epoch, sequence, fence, timestamp),
else it is damage as before; a row whose body names another sequence than its stored key is
malformed at the key; the epoch row's validator names its kind. The schema header states the
rule for adding a kind: keep the envelope, and either ship the reader first or bump `v`.
* refactor(native-chat): derive the journal's known row kinds from the row union
Each kind's own-field check now lives in one table keyed by every kind JournalRow holds, and
the set of kinds this build knows is derived from that table. A kind added to the union without
a check fails to compile, rather than latching this build's own chats read-only as a newer
build's kind. A test reads one valid row of every kind.
|
||
|
|
c9a9b8d109 | test: restore delayed PTY writes in large-paste coverage (#24556) | ||
|
|
b666d07117 | test: update worktree setup and enforce cleanup results (#24552) | ||
|
|
34ae0933e4 |
fix(worktrees): keep creation fast in large repositories (#24346)
* fix(worktrees): remove repeated scans and keep prepared checkouts fresh * fix(worktrees): reclaim unlocked fallback preparations safely * refactor(worktrees): simplify creation ownership and idle maintenance * fix(git): keep ref maintenance armed after an index-only pass An idle attempt that found the pack index due but refs still cooling down returned without rescheduling, so loose refs from the arming fetch waited for the next write instead of the ref cooldown. |
||
|
|
da51e5a148 |
fix(ssh): name missing build tools when the relay has no node-pty (#22670)
On an SSH host without a C/C++ compiler, the relay installs without node-pty, and opening a terminal used to say "could not establish why … reconnect to retry", which never helped. The relay now treats a missing node-pty folder ("not found" only) as not installed, runs its existing build-tools check, and names the missing tools with the install command for the host's package manager. It diagnoses the node-pty install the relay's import actually resolved, and keeps any other error as "can't tell".
Part of #20386. Removing the need for a compiler on the host is #1693.
|
||
|
|
7ad76f801b | test: hold the usage snapshot burst clock fixed (#24551) | ||
|
|
1fbfb13e0f | test: isolate seeded Git repositories per Playwright worker (#24550) | ||
|
|
0b7b9a9af5 | test: isolate session fixtures and wait for completed indexing (#24544) | ||
|
|
783101b304 |
feat(orcad): source-side dormant export of a relay-hosted SSH target (#16741 T6-8) (#24519)
Read-only export of a direct-SSH target's catalog and dormant state into the signed T6-7 manifest: repositories, folder workspaces and their project groups, worktree metadata and lineage, sparse presets, retired worktree names, the workspace session with bounded scrollback snapshots, automations, and client routing. Reads go through the profile-state Store via a read-only OrcadSourceExportPersistence domain; nothing retires the source. Adds the export-aware migration preflight on top of T6-5's dependents census, a resumable snapshot transfer driver with injected destination operations, and destination-side chunk staging keyed to a caller-supplied staged manifest. Lands the P7/P9 holds: session-owner projection hooks, syncDirectoryDurablySync and the durable-write mode, scrollback path and stored-bytes exports, retained refs, and dormant-tab buffer preservation. Inert until T6-10. Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
026b8378a4 | test(wire): make release compatibility probes deterministic (#24538) | ||
|
|
11b1c8f353 | test(e2e): dismiss the browser tour before starting screenshot markup (#24534) | ||
|
|
cdfdadf9ea |
fix(runtime): settle tui-idle on hook state for agents whose hooks cover the whole turn (#24388)
* fix(codex): install Codex's Interrupt hook so an Esc-cancelled turn settles
Codex 0.150+ fires an Interrupt hook when the user presses Esc on an
approval prompt or mid-tool, and nothing else. Orca did not install it, so
the pane stayed blocked/working until the next prompt.
- Add Interrupt to the managed Codex events and label maps, written with
Codex's 3s cap (a larger value triggers a startup clamp warning).
- Hash the timeout Codex hashes (Interrupt is clamped to [1,3], default 1)
so self-computed trust matches Codex; pinned against a real 0.159.3 hash.
- Map a root Interrupt to the existing cancelled-turn record
(markCodexLeadTurnInterrupted), keeping child work in the fold; a
child-scoped Interrupt is ignored. Relayed rows take the same path.
* test(runtime): add a readiness census pinning every tui-idle verdict
Replays every recorded agent PTY transcript frame by frame through a real
runtime pane (agent-known and agent-unknown, clocked and clockless) and a
synthetic evidence matrix for all 43 TuiAgents, and compares each verdict
and tui-idle wait outcome to committed run-length-encoded baselines.
Refs STA-9098
* test(runtime): pin the census quiet probes to literal windows
A census that read TUI_IDLE_QUIESCENCE_MS would move with it; fixed 2999/3000 ms
reads and a fixed 2000 ms poll step make a changed window show as changed verdicts.
Refs STA-9098
* test(runtime): say which census probe writes runtime state
Refs STA-9098
* refactor(codex): let the hook builder own Codex's per-event timeout
The managed hook's timeout is now Codex's own normalization of the shared
budget, and every installer derives its trust entry from the hook it wrote,
so no installer repeats the Interrupt special case.
Claude-Session: codex-interrupt-hook review
* refactor(codex): route Interrupt through the Stop lead update with an outcome
Interrupt now writes the lead record through the same setCodexMainAgentTurnState
call as Stop, so markCodexLeadTurnInterrupted keeps its original signature.
Drops the child-scoped Interrupt guard: Codex never runs Interrupt hooks for
subagents and its input schema has no agent_id.
Claude-Session: codex-interrupt-hook review
* test(runtime): observe the census through settled panes and caller-visible waits
- Read each verdict through the runtime's own settle seam (evaluateTuiIdleForLeaf) instead
of re-wiring evaluateTuiIdle/leafTuiIdleEvidence/buildTerminalWaitText, so the census is
coupled to one runtime method, not to the module STA-9098 rewrites.
- Let the runtime finish each chunk (one macrotask turn) before reading. The old read raced
work chained on the paint, so 14 frames pinned a microtask-ordering artefact.
- Record when a wait settles (@start vs @poll), not just its outcome.
- Exit each pane's PTY after reading it so its emulator is freed.
- Replace the hand-grouped families, literal fixture list and per-pane split flag with a
directory-scanned catalog, one baseline per replayed pane, and size-balanced shards.
- Run the synthetic matrix in one file; it takes about 2 s.
* test(runtime): cross dialog-versus-ready-screen order with every title in the census matrix
Blocked detection is position-ordered (design doc 11.5): the later of a blocker and a ready
anchor wins. The matrix now paints a workspace-trust dialog after, and before, each agent's
ready screen under every title, so a rule engine that loses that ordering fails per agent.
* test(runtime): read the census baseline field without Reflect.get
The anti-slop lint rejects Reflect.get on parsed input.
* refactor(runtime): read Antigravity, Cline, Prime Agent and Cursor readiness from rule files
Adds agent-state-rules/: a zod-validated JSON file per agent, one priority list of
screen rules per agent (idle with strength and requiresQuiet, or hold), and text
anchors that feed the shared, position-ordered blocked layer every pane reads first.
The three screen-ruled agents and Cursor's approval menu and prompt move to data;
the Antigravity text scan stays code as a named anchor. Their old code paths are
deleted. Every other agent still runs through the existing lanes, unchanged.
The readiness census baselines are untouched and pass.
Refs STA-9098
* test(runtime): cover the agent state rule engine's schema, priority, rows, anchors and lanes
Refs STA-9098
* fix(runtime): refuse rule patterns that repeat an optional or alternating group
The load-time regex check only flagged a repeated group whose body held * + or {,
so (a?)* and (a|aa)+ passed though both backtrack exponentially. A repeated
group's body must now be fixed: no quantifier of any kind and no alternation.
The comment states the remaining polynomial gap instead of claiming linearity.
* refactor(runtime): give agent state rules and text anchors one when/answer shape
Every rule and text anchor is now when (a region and what it must show) plus
answer, each a discriminated union, so part (b) adds title, text and status
regions and working or blocked answers as new variants instead of new fields.
- Cursor's prompt is two anchors answering working and idle; the one-off
workingIfAfter and followedBy fields become a general after test.
- Anchor literals and the probe banner must be lowercase, since they are
matched against the lowercased tail.
- screenProbeBanner moves under profile, the place for non-detection facts.
- why is required on every rule and anchor.
- A blocked anchor must name a lastOf literal, which the prefilter keys on.
* docs: point the readiness evidence docs at the agent state rule files
* refactor(runtime): read Codex, Claude, OpenCode, Pi, OMP and Gemini readiness from rule files
The rule engine gains the regions and answers these agents need, as closed-list entries:
- rule regions `title` (the classified title status) and `text` (one of the file's idle text
anchors, settled), and a `predicate` form of the screen region for named engine scans;
- `withoutClock: skip` for strong quiet rules a clockless pane must not believe;
- anchors (renamed from textAnchors) gain a `title` region, and `live` and `hold` answers;
- `profile.screenSource` (trusted grid or live screen), and an `unknown-pane` file for panes
with no known agent.
Codex's header, composer and provisional-startup checks become named predicates referenced
from codex.json; its ready header, header and startup hold become shared text anchors. Native
idle title markers become shared title anchors; name-only title handling becomes each agent's
idle-title rule. The agent-specific branches in terminal-wait-detection.ts and
tui-idle-evidence.ts are deleted, and the "later live prompt cancels a blocker" rule now reads
only rule-file anchors (plus Muse, which moves in part b2).
No behaviour change: the readiness census baselines are untouched and pass.
Refs STA-9098
* test(runtime): cover the rule engine's title, text and predicate regions and the bundled anchors
Refs STA-9098
* fix(runtime): reject a rule file that repeats an anchor or rule id
A text rule names its anchor by id, so a repeated id let a file pass validation and then throw
while compiling. Also states that engineVersion bumps once a version ships; version 1 is still
being defined.
* refactor(runtime): fold the working anchor answer into live
The engine treated an anchor's working and live answers identically: both mark a live prompt
that cancels an earlier blocker and settles nothing. Cursor's busy prompt now answers live, so
anchors have one non-settling prompt answer.
Refs STA-9098
* refactor(runtime): read the shared π title anchor from pi.json alone
Pi and OMP paint the same `π - <session>` rest title, and title anchors apply to every pane,
so one copy covers both.
Refs STA-9098
* refactor(runtime): key every rule file and read the trusted screen from screenSource alone
readsTrustedScreen no longer also asks for a screen rule (every trusted file has one, and the
schema requires screenSource where it matters), so rule-less files need no filter. A rule's
match is a plain boolean, and compileTitleAnchors is module-private.
Refs STA-9098
* test(runtime): pin that a clocked Codex pane takes no other agent's ready text
No test failed when holdsReadyTextToQuiet was removed; this one does.
Refs STA-9098
* fix(agent-hooks): keep an OMP approval wait until omp resolves it
omp posts tool_execution_start a few milliseconds after
tool_approval_requested, while its Approve/Deny select still holds the
human. Both mapped onto the pane row, so the working event overwrote the
blocked one and the pane read as busy for the whole prompt.
A working event now leaves an OMP approval wait in place; only
tool_approval_resolved or a new turn ends it. An ask row is unchanged:
its own tool_execution_end ends it. The test replays the order a live
omp 17 run posted for a denied bash call.
Refs STA-9100
* refactor(runtime): select the fresh hook row on any of a terminal's handles or pane keys
selectFreshExplicitAgentStatus matched one handle and one pane key and
returned only the mapped status. The row selection now takes sets of
handles and pane keys, an optional received-at floor, and returns the
row itself, so a reader can see the main agent's own state. The old
function keeps its signature and result on top of it.
Refs STA-9100
* feat(runtime): let tui-idle read hook state for agents whose hooks cover the whole turn
tui-idle read no hook state. Hook state reached readiness only through
the `<Agent> ready` titles the window writes, so a headless `orca serve`
never saw it (#16095), and Codex settled only once its screen had been
quiet for three seconds.
Rule files gain `profile.hooks: "authoritative" | "identity-only"`,
defaulting to identity-only. Codex (with its Interrupt hook), OpenCode,
OpenCode 2, Pi and OMP are authoritative. For them a fresh hook-store
row decides ahead of every other lane:
- the main agent's turn decides (`mainAgent.state` when published), so a
subagent's Stop does not end the lead turn: done settles strong,
working holds, a permission wait never settles;
- the tail's blocked text goes through the existing permission arbiter
with the turn as its explicit status, so a denied prompt's dialog left
in the tail no longer blocks a turn the hook says ended;
- the row joins on every pane key and terminal handle the PTY owns.
No row, a stale, restored or other agent's row, a session-start done,
and a row from before a PTY respawn all fall back to today's lanes. That
keeps startup on the screen and text rules: Codex posts SessionStart
only with the first prompt. Claude, Cursor, Gemini and the rest stay
identity-only.
The readiness census has no hook server, so its frames are unchanged.
Refs STA-9100
* docs(agent-status): record readiness as a reader of the hook store
Refs STA-9100
* fix(runtime): ignore a hook done older than the latest input Orca wrote
A finished turn leaves a fresh `done` row. A caller that sends the next
prompt and waits at once could settle on it before the new turn's first
hook arrives, so the wait returned while the agent was starting work.
Orca's own input writes (terminal send, agent prompts, mailbox pointers)
now stamp a per-PTY input clock, and the hook lane reads no `done`
received before it; the pane falls back to the screen and text rules
until the agent reports again. A `working` row is unaffected.
Refs STA-9100
* docs(agent-status): note the input floor on the hook lane's done
Refs STA-9100
* fix(runtime): take the hook lane's input floor from the PTY run's input record
The hook lane ignored a done older than Orca's latest write to the pane, kept in a
new per-PTY map stamped by a wrapper threaded through four write sites. The PTY
run register already sits on both write funnels, so it now records the last
input (launch writes included, terminal replies not) and the lane reads it.
Keys the user types now count too, which closes the restart-in-the-same-shell
gap: typing `codex` to relaunch no longer lets the previous process's done read
ready while the new one boots.
The respawn floor moves from the shared row join into the lane, beside the
input floor; the freshest row predates a floor exactly when every row does.
* test(runtime): drop runtime hook-lane cases the unit suite already proves
Working over a ready title, a permission wait, and an identity-only agent are
decided inside evaluateTuiIdle and covered there; the runtime suite keeps the
wiring: the join, both floors, Pi's own OSC 133 markers and the arbiter.
* fix(runtime): record a PTY's last input even when main adopted it without a spawn commit
A materialized pane re-adopted by the renderer returns before the spawn-commit
site, so it had no run record and its input never moved the hook lane's floor.
The last input now lives beside the run records: any PTY's input counts, and a
new process's commit still clears it.
* fix(runtime): keep a running process's input time when main reattaches or adopts it
A reattach or adoption commit without an incarnation id cleared the PTY's
last-input time, so a prompt sent just before an SSH adoption was forgotten
and the hook lane could accept the previous turn's done as ready. Only a new
process (or a reattach naming a different incarnation) now starts clean; the
first-input fact follows the same rule.
* docs(runtime): say why a lead turn that ended reads ready while a subagent runs
* fix(runtime): refuse uppercase contains terms in text anchors, which read the lowercased tail
A text anchor's after and lines tests run on the lowercased tail, so an
uppercase contains term loaded and then never matched. Build the text test
schema from the literal it accepts and give anchors the lowercase one. Also
drop a probe-banner early return that no bundled catalog reaches.
* refactor(runtime): state Codex's provisional startup and title anchors as plain rules
The provisional-startup hold becomes a lastOf anchor with an all/none test, so
its TypeScript scan goes. Title anchors drop their status field (every caller
already gates on an idle title), and withoutClock keeps only the value a rule
can set.
* fix(runtime): leave Codex readiness to its title and screen rules
Codex before its Interrupt hook posts nothing for an Esc mid-turn, so its hook
row stays working and a hook-authoritative tui-idle wait hangs until the row
goes stale. Current Codex already settles fast through its ready title.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
24b97e8909 |
refactor(runtime): read Codex, Claude, OpenCode, Pi, OMP and Gemini readiness from rule files (#24375)
* test(runtime): add a readiness census pinning every tui-idle verdict
Replays every recorded agent PTY transcript frame by frame through a real
runtime pane (agent-known and agent-unknown, clocked and clockless) and a
synthetic evidence matrix for all 43 TuiAgents, and compares each verdict
and tui-idle wait outcome to committed run-length-encoded baselines.
Refs STA-9098
* test(runtime): pin the census quiet probes to literal windows
A census that read TUI_IDLE_QUIESCENCE_MS would move with it; fixed 2999/3000 ms
reads and a fixed 2000 ms poll step make a changed window show as changed verdicts.
Refs STA-9098
* test(runtime): say which census probe writes runtime state
Refs STA-9098
* test(runtime): observe the census through settled panes and caller-visible waits
- Read each verdict through the runtime's own settle seam (evaluateTuiIdleForLeaf) instead
of re-wiring evaluateTuiIdle/leafTuiIdleEvidence/buildTerminalWaitText, so the census is
coupled to one runtime method, not to the module STA-9098 rewrites.
- Let the runtime finish each chunk (one macrotask turn) before reading. The old read raced
work chained on the paint, so 14 frames pinned a microtask-ordering artefact.
- Record when a wait settles (@start vs @poll), not just its outcome.
- Exit each pane's PTY after reading it so its emulator is freed.
- Replace the hand-grouped families, literal fixture list and per-pane split flag with a
directory-scanned catalog, one baseline per replayed pane, and size-balanced shards.
- Run the synthetic matrix in one file; it takes about 2 s.
* test(runtime): cross dialog-versus-ready-screen order with every title in the census matrix
Blocked detection is position-ordered (design doc 11.5): the later of a blocker and a ready
anchor wins. The matrix now paints a workspace-trust dialog after, and before, each agent's
ready screen under every title, so a rule engine that loses that ordering fails per agent.
* test(runtime): read the census baseline field without Reflect.get
The anti-slop lint rejects Reflect.get on parsed input.
* refactor(runtime): read Antigravity, Cline, Prime Agent and Cursor readiness from rule files
Adds agent-state-rules/: a zod-validated JSON file per agent, one priority list of
screen rules per agent (idle with strength and requiresQuiet, or hold), and text
anchors that feed the shared, position-ordered blocked layer every pane reads first.
The three screen-ruled agents and Cursor's approval menu and prompt move to data;
the Antigravity text scan stays code as a named anchor. Their old code paths are
deleted. Every other agent still runs through the existing lanes, unchanged.
The readiness census baselines are untouched and pass.
Refs STA-9098
* test(runtime): cover the agent state rule engine's schema, priority, rows, anchors and lanes
Refs STA-9098
* fix(runtime): refuse rule patterns that repeat an optional or alternating group
The load-time regex check only flagged a repeated group whose body held * + or {,
so (a?)* and (a|aa)+ passed though both backtrack exponentially. A repeated
group's body must now be fixed: no quantifier of any kind and no alternation.
The comment states the remaining polynomial gap instead of claiming linearity.
* refactor(runtime): give agent state rules and text anchors one when/answer shape
Every rule and text anchor is now when (a region and what it must show) plus
answer, each a discriminated union, so part (b) adds title, text and status
regions and working or blocked answers as new variants instead of new fields.
- Cursor's prompt is two anchors answering working and idle; the one-off
workingIfAfter and followedBy fields become a general after test.
- Anchor literals and the probe banner must be lowercase, since they are
matched against the lowercased tail.
- screenProbeBanner moves under profile, the place for non-detection facts.
- why is required on every rule and anchor.
- A blocked anchor must name a lastOf literal, which the prefilter keys on.
* docs: point the readiness evidence docs at the agent state rule files
* refactor(runtime): read Codex, Claude, OpenCode, Pi, OMP and Gemini readiness from rule files
The rule engine gains the regions and answers these agents need, as closed-list entries:
- rule regions `title` (the classified title status) and `text` (one of the file's idle text
anchors, settled), and a `predicate` form of the screen region for named engine scans;
- `withoutClock: skip` for strong quiet rules a clockless pane must not believe;
- anchors (renamed from textAnchors) gain a `title` region, and `live` and `hold` answers;
- `profile.screenSource` (trusted grid or live screen), and an `unknown-pane` file for panes
with no known agent.
Codex's header, composer and provisional-startup checks become named predicates referenced
from codex.json; its ready header, header and startup hold become shared text anchors. Native
idle title markers become shared title anchors; name-only title handling becomes each agent's
idle-title rule. The agent-specific branches in terminal-wait-detection.ts and
tui-idle-evidence.ts are deleted, and the "later live prompt cancels a blocker" rule now reads
only rule-file anchors (plus Muse, which moves in part b2).
No behaviour change: the readiness census baselines are untouched and pass.
Refs STA-9098
* test(runtime): cover the rule engine's title, text and predicate regions and the bundled anchors
Refs STA-9098
* fix(runtime): reject a rule file that repeats an anchor or rule id
A text rule names its anchor by id, so a repeated id let a file pass validation and then throw
while compiling. Also states that engineVersion bumps once a version ships; version 1 is still
being defined.
* refactor(runtime): fold the working anchor answer into live
The engine treated an anchor's working and live answers identically: both mark a live prompt
that cancels an earlier blocker and settles nothing. Cursor's busy prompt now answers live, so
anchors have one non-settling prompt answer.
Refs STA-9098
* refactor(runtime): read the shared π title anchor from pi.json alone
Pi and OMP paint the same `π - <session>` rest title, and title anchors apply to every pane,
so one copy covers both.
Refs STA-9098
* refactor(runtime): key every rule file and read the trusted screen from screenSource alone
readsTrustedScreen no longer also asks for a screen rule (every trusted file has one, and the
schema requires screenSource where it matters), so rule-less files need no filter. A rule's
match is a plain boolean, and compileTitleAnchors is module-private.
Refs STA-9098
* test(runtime): pin that a clocked Codex pane takes no other agent's ready text
No test failed when holdsReadyTextToQuiet was removed; this one does.
Refs STA-9098
* fix(runtime): refuse uppercase contains terms in text anchors, which read the lowercased tail
A text anchor's after and lines tests run on the lowercased tail, so an
uppercase contains term loaded and then never matched. Build the text test
schema from the literal it accepts and give anchors the lowercase one. Also
drop a probe-banner early return that no bundled catalog reaches.
* refactor(runtime): state Codex's provisional startup and title anchors as plain rules
The provisional-startup hold becomes a lastOf anchor with an all/none test, so
its TypeScript scan goes. Title anchors drop their status field (every caller
already gates on an idle title), and withoutClock keeps only the value a rule
can set.
|
||
|
|
8ff6296bc7 |
Speed up serializer checks and keep native caches stable (#24476)
* Reuse serializer oracle cells and isolate native cache policy * Preserve native cache post-save paths and record hosted oracle gain * Record native cache reuse and separate cancel-test startup budget |
||
|
|
444f1952c7 |
ci: run every cross-version wire test, picked up by folder so new ones can't be skipped (#24499)
* ci(cross-version-wire): run the whole directory so no compatibility test is left out Three cross-version tests ran in no CI job because the job named its files by hand. Run the directory instead, ratchet that every file kept out of the unit shards runs in some PR job, and re-run the job when the modules the newly running tests guard change. * test(cross-version): give the orchestration downgrade test its siblings' 120 s budget * ci(unit-exclusion): count only merge-gating jobs, and require each excluded file's job to fire on it The coverage check counted any pr.yml job, including e2e, terminal IME and Windows WSL, which are left out of verify.needs and so cannot block a merge. It now reads verify.needs and the reusable workflows those jobs call. It also only proved that some step names each excluded file, not that the job runs when the file changes. The structured-session zsh login-shell test runs only in shell_contracts, whose path trigger matched neither it, its harness nor its subject, so a PR touching only those ran it nowhere. The check now asserts a change to each excluded file fires a gating job that names it, and the shell trigger gains those three paths. * ci(cross-version-wire): trigger on the turn-outcome vocabulary and the schema version-skew resolver A change confined to src/shared/agent-turn-outcome (the arms a newer host publishes) or to orchestration-schema-version-skew (how current code reopens a downgraded database) skipped the job whose tests guard exactly those contracts. Also corrects the publish/read direction in the turn-end comment. * test(cross-version): state why the orchestration downgrade test needs 120 s * test(ci): glob the unit tree once for the unit-exclusion coverage checks |
||
|
|
9dea72f0ae |
fix(claude): an informational note is a warning row in its own words, never a raw frame row (#24471)
* fix(claude): an informational note is a warning row in its own words, never a raw frame row Claude Code shows info, notice and suggestion notes as transcript chrome; only a warning earns a row. The frame used to fall through to the provider fallback and print its opcode. * fix(claude): name the real source of an informational warning in comments and tests |
||
|
|
9e34bd06e7 |
Revert "fix(markdown): return focus to editor from find bar (#23175)" (#24500)
This reverts commit
|
||
|
|
9f395208c8 |
fix(native-chat): move the chat-tab surface out of the session host so main passes lint (#24496)
The host file reached 302 lines after #24203 and #24311, over the 300-line limit, so the static-analysis job failed on every commit to main. The five chat-tab members now come from createStructuredAgentSessionTabSurface in the existing structured-agent-session-host-tabs module; behaviour is unchanged. |
||
|
|
453408746d |
fix(build): import the Electron remote capability list from its own module (#24494)
#24203 moved ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES out of protocol-version.ts into electron-remote-runtime-client-capabilities.ts. Three files that landed on main while it was in review (SSH access links and managed orcad server work) still import it from protocol-version.ts, and a test #24203 added predates main making the structured host's logger option required, so main's node typecheck fails. Point the three imports at the new module and pass the logger. |
||
|
|
e3cb32791e |
refactor(runtime): read four agents' readiness from JSON rule files through one engine (#24348)
* test(runtime): add a readiness census pinning every tui-idle verdict
Replays every recorded agent PTY transcript frame by frame through a real
runtime pane (agent-known and agent-unknown, clocked and clockless) and a
synthetic evidence matrix for all 43 TuiAgents, and compares each verdict
and tui-idle wait outcome to committed run-length-encoded baselines.
Refs STA-9098
* test(runtime): pin the census quiet probes to literal windows
A census that read TUI_IDLE_QUIESCENCE_MS would move with it; fixed 2999/3000 ms
reads and a fixed 2000 ms poll step make a changed window show as changed verdicts.
Refs STA-9098
* test(runtime): say which census probe writes runtime state
Refs STA-9098
* test(runtime): observe the census through settled panes and caller-visible waits
- Read each verdict through the runtime's own settle seam (evaluateTuiIdleForLeaf) instead
of re-wiring evaluateTuiIdle/leafTuiIdleEvidence/buildTerminalWaitText, so the census is
coupled to one runtime method, not to the module STA-9098 rewrites.
- Let the runtime finish each chunk (one macrotask turn) before reading. The old read raced
work chained on the paint, so 14 frames pinned a microtask-ordering artefact.
- Record when a wait settles (@start vs @poll), not just its outcome.
- Exit each pane's PTY after reading it so its emulator is freed.
- Replace the hand-grouped families, literal fixture list and per-pane split flag with a
directory-scanned catalog, one baseline per replayed pane, and size-balanced shards.
- Run the synthetic matrix in one file; it takes about 2 s.
* test(runtime): cross dialog-versus-ready-screen order with every title in the census matrix
Blocked detection is position-ordered (design doc 11.5): the later of a blocker and a ready
anchor wins. The matrix now paints a workspace-trust dialog after, and before, each agent's
ready screen under every title, so a rule engine that loses that ordering fails per agent.
* test(runtime): read the census baseline field without Reflect.get
The anti-slop lint rejects Reflect.get on parsed input.
* refactor(runtime): read Antigravity, Cline, Prime Agent and Cursor readiness from rule files
Adds agent-state-rules/: a zod-validated JSON file per agent, one priority list of
screen rules per agent (idle with strength and requiresQuiet, or hold), and text
anchors that feed the shared, position-ordered blocked layer every pane reads first.
The three screen-ruled agents and Cursor's approval menu and prompt move to data;
the Antigravity text scan stays code as a named anchor. Their old code paths are
deleted. Every other agent still runs through the existing lanes, unchanged.
The readiness census baselines are untouched and pass.
Refs STA-9098
* test(runtime): cover the agent state rule engine's schema, priority, rows, anchors and lanes
Refs STA-9098
* fix(runtime): refuse rule patterns that repeat an optional or alternating group
The load-time regex check only flagged a repeated group whose body held * + or {,
so (a?)* and (a|aa)+ passed though both backtrack exponentially. A repeated
group's body must now be fixed: no quantifier of any kind and no alternation.
The comment states the remaining polynomial gap instead of claiming linearity.
* refactor(runtime): give agent state rules and text anchors one when/answer shape
Every rule and text anchor is now when (a region and what it must show) plus
answer, each a discriminated union, so part (b) adds title, text and status
regions and working or blocked answers as new variants instead of new fields.
- Cursor's prompt is two anchors answering working and idle; the one-off
workingIfAfter and followedBy fields become a general after test.
- Anchor literals and the probe banner must be lowercase, since they are
matched against the lowercased tail.
- screenProbeBanner moves under profile, the place for non-detection facts.
- why is required on every rule and anchor.
- A blocked anchor must name a lastOf literal, which the prefilter keys on.
* docs: point the readiness evidence docs at the agent state rule files
* fix(runtime): refuse uppercase contains terms in text anchors, which read the lowercased tail
A text anchor's after and lines tests run on the lowercased tail, so an
uppercase contains term loaded and then never matched. Build the text test
schema from the literal it accepts and give anchors the lowercase one. Also
drop a probe-banner early return that no bundled catalog reaches.
|
||
|
|
757736628f |
fix(native-chat): a paired server admits structured chat by client capability, not its own chat setting (#24203)
* fix(native-chat): a host admits structured sessions by client capability, not its own chat setting A host's experimentalStructuredNativeChat decided whether any paired client could reach agentSession.* at all, and whether session.tabs.* showed it structured tabs. That setting is the host user's own launch preference: whether a new agent opens as a chat or a terminal is decided by whoever launches it. Using it as admission control meant a client whose own preference was "structured chat" was refused on a host whose preference was "terminal", and chats opened while the setting was on were withheld from mobile once it was turned off. The gate now asks one thing: did the client advertise agent-session.structured.v1 (in-process callers negotiate nothing and are always admitted). Tab projection and restore follow the same rule. With the setting no longer gating anything, the separate cleanup gate (close, cancel, unsubscribe, release), which existed only so those kept working after the setting was switched off, is identical to the main gate and is folded into it. The settings listener that republished tabs when the setting changed is removed, since projection no longer depends on it. The host setting still picks the default for launches that start on the host itself (agent.launch from mobile, orchestration worker-start). * fix(native-chat): negotiate client-chosen launch mode so released phones and old servers keep terminals Hosts advertise agent-session.structured.client-launch-mode.v1: they admit structured sessions by client capability alone. A remote client that does not advertise it (phones released before agent.launch) asks createSupport to pick the launch mode, so the host keeps answering that with its own setting, exactly as before. Cleanup methods keep their own named gate so a future admission condition cannot make close or cancel refusable. * chore(native-chat): justify the two type assertions this change's lines touch * fix(native-chat): chats that already exist keep showing whatever the chat setting says The structured chat setting decides only what new agents open as. With it off, this machine's structured chats used to be hidden while the host, which no longer reads the setting, still reported them to the workspace activation gate, so a workspace holding only a chat opened empty. The local chat mirror and its startup restore now run whatever the setting says, the continue-after-restart offer follows the chats that exist, and the setting's copy says it applies to new agents. * test(native-chat): pin that a host advertises the client-chosen launch mode * fix(native-chat): mirror this machine's chats only where it holds them Round 1 ran the local chat mirror for everyone so existing chats show whatever the setting says. That gave every desktop a permanent session-tabs listener, which turns on the runtime's phone replication paths, plus two full session-tab censuses at startup, and made the browser client mirror its remote host a second time. The runtime now says whether it holds structured chats: its structured host is built only when saved chats were restored at startup or a client created one here, and it announces the moment one is built. The mirror, the startup restore and the continue-after-restart offer run only when the setting launches chats or the host holds some, and never in the browser client. A chat a paired client creates here with the setting off still appears at once. The chat behaviour settings show wherever chats exist, and the setting's copy says it picks what new agents open as. The toggle-off teardown this made dead is removed. * test(native-chat): record install listeners without a cast * fix(native-chat): mirror this machine's chats only once it holds one, not once its host is built Session history, resume preparation, terminal resume commands and replay-safe phone launches all build the structured host for users who never had a chat, which turned on the chat mirror and the structured-only settings rows until the next restart. The signal is now derived from the host's records (or a records file still owed its import) and pushed when the first chat is restored or created. A throwing listener no longer fails the install that fired it. * feat(native-chat): createSupport reports the saved selection a new chat on this host starts with A chat on a paired server starts with the server's saved model and options, which the desktop could not read, so its picker showed a guess. createSupport's answer, which the desktop already waits for before a paired launch, now also carries that seed as a new optional field (older clients ignore it). Create and createSupport read it through one resolver so they cannot drift. * refactor(protocol): move the Electron remote client capability list into its own module Merging main left protocol-version.ts one line over the max-lines limit on this branch. The list of capabilities the desktop advertises to a paired host moves, unchanged, into electron-remote-runtime-client-capabilities.ts, the module the next PR in the stack already uses for it; importers point there. * test(cross-version): stub the launch seed resolver createSupport now reads * fix(native-chat): the desktop tells its own host it picks each launch mode, so retrying an existing chat works with the setting off * docs(native-chat): name the real exit for the released-phone createSupport rule * test(cross-version): a released client still gets the host-setting createSupport answer; a launch-mode client gets supported plus the seed |
||
|
|
0d2300ca8e |
test(e2e): retry the crash probe's main-process reads through the transient-evaluate helper (#24479)
expect.poll does not retry a thrown read, so one spurious 'Resulting promise was garbage collected' from Electron's main evaluate failed the crash-recovery test. |
||
|
|
4e919f3b5a | test: send Codex Ctrl+C to a live raw terminal fixture (#24480) | ||
|
|
02790c53e8 |
Fix Codex status after Ctrl+C copy and side-chat navigation (#24339)
* fix: preserve Codex status on ambiguous Ctrl+C input * fix: confirm Codex turn cancellations from host rollout records * fix: keep ephemeral side hooks separate from the Codex main turn * perf: watch active Codex rollouts and skip unrelated records * fix: retain confirmed Codex cancellation across late relay events |
||
|
|
6e7e964705 |
feat(orchestration): tell each agent its own orchestration address (#22636)
* feat(orchestration): report the caller's host-resolved orchestration address in orca status orca status --json gains a caller block: the calling agent's address as the host resolved it from the identity its environment carries. A structured session is session:<id>; a terminal agent is its handle, with whether the host still knows it. A session the host refuses reports that refusal instead. The host answers through a new read-only orchestration.callerShow, so the session claim runs through the same dispatch-entry resolver every verb uses. An older host leaves caller unresolved. The help footer and the run/check specs stop describing identity only in terminal terms. * docs(orchestration): tell agents their address and give chat coordinators a non-waiting loop The orchestration guide now states that a chat session's address is session:<id> (never the provider's id), that orca status --json reports it, and that no caller flag should name another agent. A consuming check no longer tells every caller to name itself with --terminal. A chat coordinator starts its wave, ends the turn, and on each turn Orca starts for new mail runs a non-waiting check and ack; it never blocks in check --wait. The guide also names ORCA_CLI_COMMAND as the executable in chat sessions. * feat(native-chat): add Copy Orchestration Address to a structured chat's context menu Copies session:<id>, the Orca-minted address other agents message the chat by. The existing Copy Session ID still copies the provider's id and is left as is; the new action is labelled so the two cannot be confused. Strings are added to every locale catalog. * feat(orchestration): tell every dispatched worker its own orchestration address The worker preamble names the coordinator's address rather than a terminal handle, and states the worker's own address. A structured worker is told it is session:<id>, that its coordinator reaches it there or at its dispatch mailbox, and that mail arriving while it is idle starts a new turn. Its commands invoke the CLI through ORCA_CLI_COMMAND in its own shell's form, the same rendering the pointer turn uses, because a bare orca in a login shell can reach a different Orca. * docs(orchestration): give the ORCA_CLI_COMMAND form for POSIX shells and PowerShell A chat session's shell reads the variable as "$ORCA_CLI_COMMAND" in a POSIX shell (Git Bash included) and as & $env:ORCA_CLI_COMMAND in PowerShell, the same two forms the pointer turn and worker preamble render. The chat coordinator loop now runs the check its pointer turn names. * docs(orchestration): say that /clear gives a chat a new address and Orca moves its Runs * fix(orchestration): keep CLI resolution in the shared skill stub and the orchestration kernel in budget The guide-contract tests own two rules this PR broke: only the shared skill stub may describe how to resolve the CLI, and the always-loaded orchestration kernel stays within 202 lines. The ORCA_CLI_COMMAND text moves to the stub's resolver block, which now covers chat sessions and login shells beside WSL and gives the POSIX and PowerShell forms; every skill projection and the bundle manifest are regenerated. The kernel keeps one line each for the caller's address, the environment-resolved check caller and the chat coordinator's non-waiting loop; the loop steps and the address details move to the coordinator-loop and messaging references. The two kernel pins now assert the new check contract and refuse the old --terminal <your_handle> shape. * fix(orchestration): refuse a blocking check --wait from a native chat session A chat runs turn by turn through a shell tool with its own timeout, so a blocking wait is killed mid-wait and retried. The host now refuses it with wait_requires_terminal and the turn-loop recovery, keyed on the session's lease: a session a terminal view holds still runs in a PTY and may block. * fix(orchestration): resolve orca status's caller with the verbs' ladder, host-side callerShow now answers a terminal caller the way the coordinator verbs act: the carried handle while it is live, else the handle its pane was reminted as. The CLI always asks, so the host decides that a process has no identity from the same envelope every verb sends; a pane key alone now resolves. * fix(orchestration): show a structured worker as session:<id> wherever agents read mail A structured worker was session:<id> in orca status and its preamble, but structworker_<uuid> in check rows, banners, reply hints, its own check label and a sub-worker's coordinator line. The minted handle is now only the mailbox key: mailbox reads, the check label and preamble coordinator lines spell the worker session:<id>, which the host binds back to that mailbox. Send receipts still echo the stored row, whose sender key worker_done settlement matches. * fix(orchestration): teach a chat worker the turn loop and pin preamble parity at the contract The worker preamble was byte-identical across modes except its address, so a chat worker was taught a 600s blocking ask its shell tool kills before the message ID for --resume prints, heartbeat exemptions for check --wait, and to keep a shell open. Parity now pins the contract (sections, verbs, flags, lifecycle ids); interaction discipline follows the mode: a chat asks with a 5s wait and ends its turn, owns sub-workers through the turn loop, and names itself session:<id> in every command. The guide says a chat's address survives /clear and that Orca refuses a chat's check --wait. * test(orchestration): pass the db to preamble delivery and fence a terminal-view waiter The coordinator line maps a structured coordinator's handle through the orchestration db, so delivery takes it from its caller. The consumer-fencing waiter test now waits as a terminal-view session, the only session kind that may still block in check --wait. * test(orchestration): read the Run id with the fixture's checked accessor * feat(orchestration): copy a chat's conversation address, which /clear keeps Copy Orchestration Address copied session:<live id>. A chat's address is its conversation's, derived by the host from the session records, so the menu now asks the host for it at copy time through orchestration.sessionAddress, the same derivation a verb acting as that session binds to. A host that predates the method has no /clear lineage, so there the live id is the address. The guide's /clear text says the address survives and nothing moves. * test(orchestration): pin that a cleared chat's successor copies its conversation's root address * test(orchestration): give the mode-opacity fixture's record store the listing a lineage lookup reads A structured worker's agent-visible address now resolves through its conversation's lineage, which lists the session records; the fixture's partial store lacked that listing, so the sub-worker start failed at dispatch input. * refactor(orchestration): format a chat's copied and reported address from its root Orca session id Carries the Orca session id rename into the self-address surfaces. orchestration.sessionAddress, the copy action's fallback, callerShow and the address a structured worker is shown now format `session:<id>` from the conversation's bare root Orca session id with formatOrcaSessionAddress, and ids arriving as strings are checked with isOrcaSessionId first. The CLI status line, the check caller label and the dispatch preamble spell the prefix from the one exported constant. * refactor(orchestration): resolve a session's reported address through the party resolver, and refuse every session's check --wait - orchestration.sessionAddress, and the agent-visible spelling of a structured worker, resolve through the party resolver, so they format the lineage root the one id hook derives; sessionAddress.sessionId is classified as a target. - With the terminal handoff gone every structured session runs turn by turn, so check --wait is refused for any session caller, a worker included, and the session caller no longer carries its lease's runtime kind. - The coordinator loop no longer mentions a terminal view, and the messaging reference says a chat takes messages but is refused as a Dispatch assignee. * fix(orchestration): cap a session caller's blocking wait below its shell tool instead of refusing it A chat or structured worker runs each command under its provider's shell-tool timeout, so check --wait was refused for every session caller and chats were taught a separate loop. The host now caps check --wait and ask for a session caller below that timeout (Codex 10s one-shot exec default, Claude Code Bash 120s) and answers the normal timed-out result, so the terminal coordinator loop runs unchanged in a chat. A terminal caller's wait is untouched. * refactor(orchestration): teach a chat worker the terminal worker's preamble, byte for byte but the address One preamble for both modes: the chat variant (short ask, end your turn, this chat stays available, ORCA_CLI_COMMAND invocation) is deleted. A structured worker's only difference is its address, session:<id>; the byte-parity test between modes is restored with just that substituted. * docs(orchestration): drop every chat-specific instruction; name the address once, generically The guide, its references, the shared CLI-resolution stub and the help return to main's text, with one kernel line saying `orca status --json` shows your address (the kernel stays at main's length). The status caller block reports only the opaque address, the same shape for a chat and a terminal agent. Guides regenerated. * test(orchestration): pin that a chat and a terminal agent see the same preamble, pointer and guide * test(orchestration): key the wait-cap fixture's records by plain session id strings * chore(i18n): add the copy-address strings at the head of native-chat, clear of main's catalog edits * test(orchestration): fail the capped-wait test on the settle, not on the test timeout * test(orchestration): keep main's takeover assertions on a session coordinator's waiting check With the session wait capped rather than refused, the test main extended runs as it is: the restack re-added the shorter pre-main version over it. * fix(orchestration): show a /clear-ed chat its lineage root's address everywhere it reads its own check labelled a session caller with session:<live id>, while orca status and the preamble show the conversation's root. The CLI cannot read the lineage, so the label now comes from the same host answer orca status prints (orchestration.callerShow), asked only when there are messages to render, and falling back to the live id only when the host cannot say. The host also spells a session address it shows an agent with the lineage root: a dispatch preview filled in from the chat's own address, and the provider-id refusal that names a session's address. * test(orchestration): the parity test's gate facts resolve like the host's * test(orchestration): the parity test's gate facts carry the submissions main's pointer lane reads * fix(orchestration): wait a chat's check --wait and ask exactly as long as a terminal's The host capped a session caller's blocking wait (Codex 6s, Claude 100s) so the provider's shell tool would not kill it. Neither provider kills a long shell call: default Codex's exec tool yields and keeps the command running, and Claude Code moves a timed-out Bash call to the background. Terminal agents run the same tools uncapped, so the cap only made a chat coordinator re-poll every few seconds. A session caller's check --wait and ask now wait the budget asked for. * fix(orchestration): show every agent one address, the mailbox address its mail is keyed by A structured worker was told `session:<id>` in orca status and its preamble, but its own send receipts, inbox, worker-list, dispatch previews and task rows still showed the `structworker_` handle its mail is stored under; only some reads were re-spelled. Instead of re-spelling reads, callerShow, sessionAddress and the preamble now report the caller's stored mailbox address (mailboxAddressOf): a terminal's handle, a structured worker's handle, and a chat's `session:<lineage root>`. The read-side re-spelling layer (withAgentVisibleAddresses and its check/banner/preamble call sites) is gone. Dispatch previews spell the coordinator by its party's mailbox address, so a `/clear`ed chat's dispatch-show still names its root. * refactor(orchestration): label check output from what the CLI already knows check asked the host for orchestration.callerShow after every non-empty check by a session, only to fill a label used when a legacy row lacks to_handle, which host rows never do. The label is again the caller's handle or its injected mailbox address, with no second round trip after mail is consumed. * refactor(native-chat): offer Copy Orchestration Address on chat tabs only No mount passes both terminal-pane actions and an orchestration address: a chat shown inside a terminal pane is that terminal's agent, copied by its terminal ID. Drop the unreachable terminal-pane placement and its tests. * fix(orchestration): have orca status report the handle the agent's own check reads After a window reload a terminal agent keeps ORCA_TERMINAL_HANDLE=term_old while its pane is reminted as term_new. callerShow reminted and advertised term_new, but check, send and ask act as the carried handle and never remint, so mail sent to the advertised address was never read by that agent. callerShow now answers the carried handle with its liveness, and null for a pane key alone, from which the mailbox verbs have no identity. Resolving terminal callers once on the host for every verb is a separate follow-up. * fix(orchestration): read the renamed coordinator line in the long-prompt repro, and trim round-one leftovers The reliability repro's fake worker parsed "Your coordinator's terminal handle is:", which the preamble now spells "Your coordinator's address is:", so it silently skipped worker_done; it accepts both. Dispatch and its dry-run go back to main's coordinator line (their `from` is already bound at the entry); only dispatch-show, whose `from` is unbound, resolves it. Also drops a stale status-caller comment, trims the wait test to its one uncapped-wait case, and reverts comment-only churn in the worker opacity test. * docs(orchestration): keep worker obligation 1 as main words it The guide grows by the one caller.address line; the parity test bounds the kernel at main's length plus that line instead of forcing a reword. * test(native-chat): prove a structured chat tab offers Copy Orchestration Address Renders the pane-commands hook as a structured chat tab and selects the item: it asks orchestration.sessionAddress with the tab's target and session id. Also corrects the menu item's comment to what it copies. * test(orchestration): D5's tests expect the orca_session_id prefix and 'Orca session ID' wording * fix(orchestration): name a session by its Orca session ID, and leave terminal agents as main has them Terminal agents keep main's exact wording: a terminal worker's preamble is byte-identical to main's, and orca status prints nothing new for them. A session is named by its Orca session ID (orca_session_id:<id>, its /clear root's): a structured worker's preamble says "Your Orca session ID is: …" and its commands use that ID, and a session coordinator is "Your coordinator's Orca session ID is: …". orca status shows a session caller's `caller.orcaSessionId`; callerShow answers null for anyone else. The chat tab menu item becomes "Copy Orca Session ID" with a tooltip saying what the ID is, and its toasts match. No agent-read text calls this ID an address. A structured worker's mail is still keyed by its minted handle. * test(orchestration): check CLI help and status for "address" wording from a CLI test The node project cannot compile src/cli, so the guard over CLI help, specs and status text moves to src/cli; both halves share one pattern. Also brings two comments and the long-prompt repro's coordinator-line regex to the Orca session ID wording. * fix(native-chat): keep the Orca session ID tooltip within the tooltip primitive's typography Drops a restyle the design-system gate refuses on TooltipContent, keeps "Agent" untranslated in the Japanese tooltip as that catalog does, and types the test's tooltip mock without an assertion. * fix(native-chat): the Orca session ID tooltip names the agent CLI's own session ID in the singular |
||
|
|
8c1670f2c4 |
test(e2e): fake Codex answers the --no-daemon --help probe without a spawn (#24440)
* test(e2e): fake Codex answers the --no-daemon --help probe without a spawn Since #23933 Orca runs `codex --help` before a path-named Codex launch. The fakes logged it as an agent spawn and held the probe for its 5 s timeout. Move the app-server refusal and --help answer into one shared FAKE_CODEX_LAUNCH_PROBES_SOURCE used by every fake Codex. * test(e2e): stop asserting the dispatch capability column a current worker no longer has #23994 stopped minting the per-dispatch capability, so capability_hash is null. The --help spawn failure used to stop this test before it got here. |
||
|
|
ccb63afc06 |
fix(native-chat): a Stop still reads as yours after Orca restarts, because the turn's end reads the Stop's event (#24311)
* test(native-chat): a Stop over a card sent now into the running turn keeps it paused
Red on main: Codex's turn end withdraws the steered hand-off and the queue
sends the card again as a new host turn, with no pause recorded.
* fix(native-chat): a Stop's queue pause holds a card whose hand-off is still unanswered
A card sent now into the running turn was still pending when Stop judged the
pause, so nothing was recorded; the interrupt then withdrew the hand-off, the
card went back to waiting unpaused, and the queue sent it again as a new turn.
The pause now counts a hand-off that may still return to waiting, judged with
the appended row applied, so a withdrawal lands under the pause and an
acceptance retires it in that same write. Codex and Claude both hit it.
* test(native-chat): the Claude re-send case fails on its diff, inside the test's budget
* test(native-chat): a pause held by an unanswered hand-off ends on every path that ends it
The provider's answer, the provider dying, the chat closing, a restart, a
withdrawal still owed at open, and a /clear (refused until the hand-off ends,
then carrying every waiting card paused 'cleared'); each ends with the queue
sending again.
* fix(native-chat): narrow the pause's settled hand-off, and assert the queued receipt's card
* refactor(native-chat): derive the queue's pause from Stop and Resume journal rows
Stop now appends one journal row where it takes effect, before the interrupt,
whatever the queue holds; Resume appends its own. The pause is a pure function
of the fold: the latest Stop with no later Resume and no later accepted turn a
person asked for. A /clear's carried cards name their source, which is the
replacement's 'cleared' pause. Host-origin turns never lift either.
One predicate decides which cards a pause holds; by default every waiting card
without a hold of its own, including one queued after the Stop. The drain's
consume re-judges it inside its own transaction.
The rows are tombstones of an id no item takes, carrying the mark: a released
host reads an unknown row kind as corruption and truncates the journal there.
Deletes the stored pause (recordPause, the retire hook on every appended row,
the settle-before-record step, mayReturnToWaiting and its row overlay) and the
tests that only proved it retires. The queued_message_pauses table stays in the
schema, unread and unwritten, for downgrade safety.
* fix(native-chat): a card queued after a Stop sends normally, never ahead of held ones
A Stop's pause now holds only the cards queued before its row, plus a steer it
withdrew, which returns to its own place. Each card records the journal
position it was queued at, and the one hold rule compares that with the Stop
row. A card queued after the Stop is a new instruction: it sends as usual, but
the drain still stops at the first held card, so it never overtakes them.
/clear's pause holds the cards it carried. Holding every card again is a
one-line switch in that rule.
* fix(native-chat): the queue's own send re-checks the no-overtake rule in its transaction
The drain's pick and its consume now read one function, nextSendableQueuedCard,
so a Stop row that lands between them holds a newer card behind an older held
one exactly as the pick would. Notes why Stop and Resume ride a tombstone row.
* fix(native-chat): stop creating the unused queue pause table
The queue's pause is derived from journal rows, so nothing reads or writes
queued_message_pauses. It was still created on every open "for downgrade
safety", but an older build creates it itself when it opens the database, so
the table only sat empty in every new database. The tests now pin that no
pause table exists.
* fix(native-chat): a Stop's pause never hides the restart pause
A Stop holds only the cards queued before it. The pause derivation still
returned the Stop alone whenever it was in force, so the restart pause was
never considered: a card queued after the Stop, written by a host process
that has since exited, sent by itself after Orca restarted, with no pause
header and no Resume. A /clear pause that held nothing could hide it the
same way.
Every pause in force is now derived. A card is held if any of them holds
it, and it names the first that does. The drain's pick, the consume
transaction's re-check and the published header all read that one rule;
the header names the pause holding the first card Resume would send.
* test(native-chat): pin the Stop's no-resend, lift and held-card rules
- The Claude and Codex Stop-withdraws-a-steer tests checked "not sent
again" at one instant, before a queue ignoring the pause re-sends. They
now wait for the stopped turn to end and re-check after a quiet window.
- The deleted-card test read a card queued after the Stop, which sends
whether or not a person's turn lifts it; it now reads the Stop's pause
before and after that turn.
- Unit cases pin that a Stop holds a card with no recorded position and one
queued before a rewind.
* refactor(native-chat): a Stop writes one Stop event with its reason, turn and caller
The Stop row that paused the queue becomes the general Stop event
{ reason, turnId?, at, caller? }, whose reason is the host's existing stop
cause. It still rides a tombstone of a host-only id (a released host deletes
the journal from the first unknown row kind), and Resume keeps its own marker
on its own id. Only a person's Stop (reason user-stop) pauses the queue.
* test(native-chat): a rewind keeps a lifted /clear pause lifted and restates the same Stop event
* test(native-chat): pin that Stop and Resume rows never reach apps or count as history
* test(native-chat): only a person's Stop event pauses the queue
* test(native-chat): pin that a Stop's event precedes the interrupt and the at-start stop
Through the real host: the event names the turn and who asked and is in the
journal when the interrupt reaches the agent; at an agent still starting it is
there before the start is ended and holds a card queued before it; an idle Stop
writes one only when it withdrew a send; and the queue's claim re-judges a
pause that landed after its pick.
* test(native-chat): a card held at a starting agent is checked before the Stop's timing
Also says precisely what the claim's in-transaction pause check defends
against: the Stop and the drain share one serialized lane.
* test(native-chat): a released build keeps and folds a journal holding Stop events
Replays this build's rows from the released build's own journal database: every
row is kept, the history after the Stop still folds, and an older client is sent
only removed ids no item uses.
* style(native-chat): format the Stop event changes
* test(native-chat): type the released build's exports through one checked helper
* fix(native-chat): the Stop/Resume row guard narrows to those tombstones only
* test(native-chat): run the Stop-event downgrade test in CI, and cover a writable downgrade
The Stop-event downgrade test ran in no CI lane: unit shards exclude the
cross-version folder, and the cross-version lane runs a fixed file list that
did not name it. It is now on that list.
Its only case replayed the rows into a release's own fresh database, because
that release cannot open the current host database. A second case opens the
journal this build wrote with a main build that shares the database: it opens
writable, keeps every row, appends, and this build then reopens it with the
person's Stop still pausing the queue.
* fix(native-chat): a Stop that stops nothing new writes no Stop event
A Stop reaching a running agent wrote a Stop event on every press. Two
presses before the first interrupt landed wrote two events, so a card
queued between them counted as before the latest Stop and was held,
though a card queued after a Stop should send normally. A Stop naming a
turn that had already ended, as a phone sends late, also wrote an event
for a turn it never stopped.
It now writes one only when it withdrew a queued send, or stops something
no event records yet: not a turn the journal no longer runs, and not the
live turn a Stop still in force already names, unless a card was handed
over into it since, which this Stop's interrupt sends back and must hold.
The interrupt and the "already finished" note are unchanged. A Stop at a
starting agent still always writes.
* test(native-chat): pin that a later host, eviction or close Stop never lifts a person's Stop
* chore(native-chat): put each Stop-row doc on its own declaration, and say only user-stop is journaled
* fix(native-chat): any later Stop event ends a person's Stop pause
A person's Stop paused the queue until their next accepted turn or Resume,
and a later Stop of another reason (the host stopping the agent, an
eviction, a close) was ignored. Now the pause is the latest Stop event's:
a later Stop of any reason ends a person's pause, and only a person's Stop
pauses. The fold keeps the latest Stop event whatever its reason.
An eviction of a resting chat writes no Stop event (a Stop that stops
nothing writes nothing), so it cannot release held cards; a test pins that
no event means no lift.
* fix(native-chat): a second Stop press is a repeat even when the first came before the turn showed
A Stop pressed before the agent's turn shows in the journal (before
Claude's echo, or before Codex opens the turn) records no turn. A second
press once the turn showed compared that missing turn with the live one,
wrote a second Stop event, and held a card queued between the presses.
A repeat is now judged by what was sent since the Stop in force: with
nothing sent after it (a refused send aside), a Stop that named no turn,
or named the live one, is repeated and writes nothing. Anything sent since
and not refused, including a send whose fate is unknown, makes the new
press write, since its interrupt may send that card back to waiting.
Tests: the two-press case across the turn showing; a steer between the
presses settled unknown; and a Stop naming a turn that ended while the next
card is sent but shows no turn yet, which writes and holds that card. The
fold test that claimed an eviction path is renamed.
* fix(native-chat): the queue's pause ignores a Stop or Resume row holding a value no build writes
A Stop or Resume row's value is read from disk with no shape check, and
the pause fold stored whatever it found. A stored `stopEvent: null` would
then throw on every pause check for that chat: the queue's pick, its
send, and every queue update to clients. No build writes such a row, so
this is hardening.
The fold now reads a Stop only when it is an object with a string reason
and a finite time, and a Resume only when it is `true`. Anything else is
ignored: it pauses nothing and ends nothing. The row is still not treated
as malformed, which could cut the history short.
* fix(native-chat): a Stop still reads as yours after Orca restarts before the turn ends
Every stop that ends work now writes the Stop's event before it ends the child: a
person's close of the chat, an eviction (worktree teardown, orchestration stop, tab
cleanup) and the idle sweep's stop of a start that never landed. A stop that ends
nothing writes nothing, and quit writes none: its resume marker records why.
The turn-end write reads the latest Stop event where every turn row is built, so the
adapter's settle, the host's fallback and the relaunch's settle all agree: a turn a
person's Stop or close named, ending with no verdict of its own after that Stop, ends
as their cancellation. A relaunch's probe-bounded end is no earlier than a Stop that
found the turn running. When the provider refuses the interrupt and the turn runs on,
a refusal row answers the Stop, so a later crash still reads Failed; pressing Stop
again after a refusal is a new Stop.
* refactor(native-chat): a stop no longer carries its cause; the turn's end reads the Stop event
The cause of a stop was threaded in memory from each entry through the host's stop
step, the adapter router and each adapter's close onto the `ended` it settled with,
and Claude kept a per-turn copy of a Stop it sent. All of that is gone: adapters
settle a turn they cut as interrupted with no verdict, the host's fallback does the
same, and the one rule where a turn row is built (`turnEndAfterStop`) reads the
journal's latest Stop event to say whether it was a person's.
- `closeSession` / `disposeSession` take no cause; `ended` has no `stopCause`.
- Claude reads an error result after a person's Stop as their cancellation from the
journal's Stop event (through the event sink), not from a per-turn slot, and a
refused interrupt is the host's refusal row, not `withdrawTurnStop`.
- An owed wind-down keeps no cause: its retry's fallback reads the Stop event.
- The mutation context's Stop passes no cause: its step already wrote the event, and
the delivery loop's child-end reason is read back from it.
- A Stop pressed before its turn showed applies to the turn that opens under it,
unless a send a person made since was accepted.
* test(native-chat): a turn a later send opened is no Stop's that named no turn
* test(native-chat): the restart test's death proof carries its detail
* refactor(native-chat): a refused Stop leaves no record; a Stop only ever ends the turn it names
The stop-refused mark is gone: its tombstone kind, its fold, the clock-keyed match that tied it to
a Stop, and the exception that let a second press after a refusal write a new Stop. A Stop that
stops nothing writes nothing. A Codex refusal names a turn that is no longer its active one, and
the Stop names that turn, so the turn running instead never reads as the person's by its id alone.
* fix(native-chat): a Stop pressed before any turn showed stops only the turn opened next
A Stop that named no turn read as the person's cancellation for every later turn that opened
after it, until a send a person made was accepted. The queue's drain, orchestration mail and a
restart continuation send as the host, so a turn they opened long after, cut by a crash, read
"Interrupted" as if the person had stopped it. The Stop now applies only to the first turn
opened after it.
* fix(native-chat): an older Claude's error end after a Stop pressed before its echo reads Interrupted
Claude CLIs before 2.1.91 end an interrupted turn with an error result that names no reason. The
translator judged whether a person's Stop explained it by its own copy of the Stop rule, which
ignored a Stop that named no turn, so a Stop pressed before Claude echoed the send read "Failed".
The translator now writes such an end as interrupted with no verdict and no error row whenever a
person's Stop may name the turn, and the journal's one rule decides as it writes the end.
* fix(native-chat): a person's Stop and /clear each name why they end the agent
The host's mutation path ended the agent with one "recorded" ending for every caller, which read
back the reason of whatever Stop event the journal held last, however old. /clear writes no Stop
event, so its end took an unrelated earlier reason. Each caller now names its own: the chat's Stop
`user-stop`, whose event its own step wrote, and /clear `user-close`, the user replacing this chat.
* fix(native-chat): a host stop judges whether it ends work after the provider's rows land
A close, eviction or host stop decided whether it ended a running turn from the journal as it
stood, while the provider's own rows (the turn its echo opened) could still be in the session's
event sink. A close landing in that gap wrote no Stop event, so the turn it cut read as news. It
now reads after the sink drains, as a person's Stop does, through the same check; a drain that
fails or takes over a second reads working.
* fix(native-chat): a Claude Stop naming a turn that just ended still marks the follow-up it cuts
A phone names the turn it last saw. When that turn had ended and a follow-up was still unechoed,
Claude's Stop interrupted the follow-up and ended the child, but the Stop's event named the ended
turn, so the follow-up's turn the child's end cut read "Failed" under "Cancellation requested.".
A Stop that ends the provider's session ends whatever is in flight, so its event now names the
live turn or none, and a Stop that names none binds the turn opened next. Codex keeps naming only
the turn the Stop names.
The Claude Stop turn-end tests move to their own file, since the session-ending Stop suite is at
its line budget.
* fix(native-chat): the idle sweep reads working by the same rule as a stop's event
The sweep judged a chat resting while a send whose reply was lost was still unanswered, but the
stop's event writer counts that send as work. So the sweep evicted it and wrote an evict event,
which ends a person's Stop pause and let the cards behind it drain on their own. The sweep's owed
work now reads the main agent working the way every session list and the event writer do.
* test(native-chat): an aborted eviction's injected drain failure lands on the eviction's own drain
A host stop now drains the session's sink once to judge whether it ends work, so the tests that
fail the eviction's drain-published step skip that first drain.
* fix(native-chat): the idle sweep's rest writes no Stop event; it evicts a send that never echoes
The previous commit made the sweep count an unanswered send as owed work, which pins a chat whose
admitted send Codex never echoes forever, and the sweep exists to retire exactly that. That rule
returns. The sweep stops only an agent it judged resting, so its eviction now writes no Stop
event, whatever send it retires: a person's Stop pause holds through it.
* fix(native-chat): stopping a start that carries no send writes no Stop event
A host stop, eviction or close of a starting child wrote a Stop event whatever the start carried.
A start with a send already reads working, so the clause only mattered for a start with none,
which ends no turn and no send: its event only lifted a person's Stop pause and bumped the idle
clock, which is why the idle sweep had been changed to close the conversation in the same pass.
The clause goes and the sweep is #24072's again. The child's end still reads host-stop, as before.
* test(native-chat): a Stop's pause across a restart is tested with a restart that writes no event
The rig's restart closes the chat with an eviction, which now writes a Stop event when work runs
and so ends a person's Stop pause. "A Stop never hides a restart's pause" then passed with no Stop
pause left to hide anything. Those tests, and the pause-lift test whose dropped assertion returns,
restart as a process that dies with no close, which like a quit writes no Stop event, and assert
that both the Stop's and the restart's pauses are in force first.
* fix(native-chat): a host stop of a turn a person's Stop is still ending keeps that Stop's reason
An eviction or host stop that landed while a person's Stop or close was already ending the same
turn wrote a newer Stop event, and the turn's end reads only the latest, so the person's Stop of
that turn read as news. A host reason now writes nothing while a person's Stop still decides what
runs: the live turn it names or bound, or, with none, the turn a send opens next. The person's
own close still writes. The E2 tests now open and end the stopped send's own turn, as Codex does,
so the mail turn after it is not the turnless Stop's.
* fix(native-chat): an older Claude's error on a later turn keeps its error text after a Stop
The translator left an error result that names no reason to the journal's Stop rule whenever a
person's Stop named the turn or none, but the rule binds a Stop naming no turn only to the turn
opened next. So a real error on a later turn read "Failed" with its error text dropped. The
translator now asks the journal's rule itself (`personStopDecidesTurn`, the one core
`turnEndAfterStop` and a host stop's in-force check share), so the two cannot disagree.
* fix(native-chat): a Stop of a start that never landed binds no later turn, whatever sent it
A person's Stop pressed while the agent starts names no turn, and the send it stopped is
cancelled before it opens one. The Stop then bound the next turn anything opened (orchestration
mail, a restart continuation, the queue's drain, all of which send as the host), so a host
eviction of that turn wrote nothing and its crash or close read as the person's cancellation. A
Stop that named no turn now binds only a turn no send journaled after it opened: any send since,
of any origin and not refused, opens its own. The E2 test's mail send is accepted as Codex
accepts it, instead of opening the stopped send's own turn first.
* test(native-chat): a rewind's restated turnless Stop binds no turn opened after the rewind
A Codex rewind restates a person's Stop still in force after the turns it keeps, at a new
sequence, so by sequence alone it would bind the next turn opened after the rewind. A send
journaled after the restated row voids that binding (the previous commit), which this pins.
* fix(native-chat): a relaunch settles a person's stopped turn with no "stopped while in progress" row
After a restart, a turn a person's Stop ended reads "Interrupted after N" with the muted mark, but
the relaunch still added the error row saying the provider stopped mid-response, which a live Stop
never writes. The settle now skips that row when every turn it interrupts is the person's Stop's
by the journal's one rule; a crash nobody stopped keeps it.
* test(native-chat): the unexpected-exit settle's journal fake answers whether a person's Stop decides a turn
* fix(native-chat): a host stop whose sink drain fails reads the journal as it stands
A host stop drains the session's sink before judging whether it ends work, and a failed or slow
drain read as working. So an eviction of an agent at rest wrote a Stop event that ended nothing,
which lifts a person's Stop pause, and a close wrote a person's event naming no turn. The drain is
now best effort: the stop goes ahead either way and only its record is at stake, so a failed or
slow drain leaves the journal's read as it stands. A person's Stop keeps its own rule.
* fix(native-chat): a Stop that named no turn applies only to a turn a send it stopped opened
A person's Stop pressed before any turn showed names no turn. It bound the first turn opened
after it, then (
|
||
|
|
1553bc3b80 |
fix(terminal): reattach a background terminal to its own tab instead of opening a duplicate (#24458)
* fix(terminal): reattach a background terminal to its own tab instead of opening a duplicate When a workspace with already-running terminals is opened and the renderer has lost a tab's link to its terminal, the activation gate asks the host who owns each unlinked terminal. The host's graph only carries mounted or provably live panes, so an unmounted tab whose link was lost reads as "no surface", and the gate opened a brand-new tab on the running terminal: the same terminal then showed in two tabs once the original tab mounted and reattached. The host now names the pane it last recorded for an orphaned terminal (`recordedPaneKey`, optional). The renderer, which owns its tabs, rebinds the terminal there when it still holds that pane free, and opens a tab only when the pane is gone or holds another terminal. * test(terminal): pin that an unowned PTY never rebinds to a vanished leaf or another worktree's tab The rebind to the host-recorded pane relies on two existing guards in the exact-surface binder: the recorded leaf must still be in the tab's layout, and the tab must belong to this worktree. Neither was pinned on the unowned path. Both new cases mint a fresh tab and leave the recorded tab untouched. |
||
|
|
f69052e113 | Reuse qualified Windows server builds and dependency verification records (#24448) | ||
|
|
38c2d1dcb9 |
feat(ssh): update, roll back, recover and stop a managed orcad server (#16741 T6-5 follow-up) (#24463)
* feat(ssh): update, roll back, recover and stop a managed orcad server (#16741 T6-5 follow-up) Builds managed-server maintenance on T6-2's deploy, rollback and recovery and T6-4's journaled decommission. Each step reads a terminal census through the server's tunnel; orcad answers it through a new capability-gated orcad.terminalCensus RPC, and an older host or lost answer is unverifiable. Update defers over live or uncounted terminals and status reports the last deferral. A stop unlinks the server (deployment record, tunnel, SSH claim) only after a proven exit. Inert until the T6-6 settings UI. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(rpc): load the orcad terminal census lazily so the dispatcher does not pull in the xterm window polyfill Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
eefc49f7e3 |
feat(terminal): warn when a typed Codex joins Codex's shared server (STA-9051) (#24217)
* feat(terminal): warn when a typed Codex joins Codex's shared server Orca adds --no-daemon to the Codex it launches and to a codex typed in shells whose wrapper it controls, but a codex typed another way (fish, cmd.exe, a path-named binary) still joins Codex's shared server, which mixes up agent status across tabs. When a local pane's Codex is on that server, show a banner at the top of the pane with the command that turns auto-start off, a Copy button, "Don't show again" (a new setting next to the Codex server setting) and a per-pane dismiss. The banner takes layout space; the terminal refits below it. Main answers pty:isCodexOnSharedServer from the pane's outermost Codex command line (flags and subcommands that keep Codex embedded rule it out), the CODEX_HOME the pane launched with, and whether that home's server is live: a socket connect on macOS/Linux, the server's pid record plus creation time on Windows. The renderer asks only while the pane already shows Codex, on a short bounded ladder. Refs STA-9051 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: restore the Claude WSL trust-file fix (#23973) dropped by the banner commit Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(terminal): redesign the Codex shared-server banner and fix dialog Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(terminal): let the Codex shared-server fix run its commands The fix dialog now runs each step with the shared server's own Codex on the pane's CODEX_HOME, verifies the result (feature read back, server probed), and falls back to a copyable command on failure. Stopping asks first. Also: an apostrophe in a prompt no longer hides an opt-out flag, restored panes fall back to the saved pty id, and the IPC guards have a table test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(terminal): give each fix step its own card and label the command it runs Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(terminal): simplify the Codex shared-server banner after review - Read a subcommand only from Codex's first positional, so prompt words like "a", "update" or "review" no longer hide the banner. - Probe the server fresh on every ask; drop the probe cache. - Make the pty preload methods required and stub them on the web client, replacing the optional-method and paired-client checks. - Render the banner from the existing Codex pane portal loop. - Treat a non-zero or timed-out Codex command as failed; skip the read-back when the disable write failed. - Reserve the banner's space with a CSS :has() selector instead of a data attribute. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(terminal): make the Codex shared-server fix persist on Orca's mirror home - Step 1 now writes daemon_auto_start = false to the user's own Codex home when the pane runs on Orca's shared mirror home (as Windows panes do), then to the mirror home too; the mirror is rebuilt from the user's home on every launch, so a mirror-only write was lost. - The server probe is three-state (live / absent / unknown); stop reports success only once the server is proven gone. - The banner retires the one-time "runs Codex without its shared server" toast it contradicts. - A command line with no Codex program never counts as joining the server. - The fallback local PTY provider reports each pane's root pid. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(codex): keep Turn off in Orca's Codex home when ~/.codex has no config A pane on Orca's mirror home wrote the setting to ~/.codex first. With no ~/.codex the spawn failed on its cwd and Codex rejects a missing CODEX_HOME; and creating a config holding only this setting would make the next mirror replace every setting made in Orca's Codex. The mirror skips a missing or blank ~/.codex/config.toml, so write only the mirror home then. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(codex): promote [features].daemon_auto_start from Orca's Codex home Promotion now carries one [features] key alongside the [tui] keys, so a shared-server Turn off written in Orca's mirror home reaches ~/.codex/config.toml instead of being reverted by the next mirror. Table keys share one <table>.<key> scan for read, removal and upsert. Orca's own daemon socket override is never read as a user value, and a blank source config is seeded from the runtime like a missing one. * refactor(codex): run Turn off once, in the pane's own Codex home Settings promotion now carries the setting to ~/.codex, so the separate settings-home resolution and the two-home loop are gone. * fix(terminal): offer Stop server only after sharing is turned off Stopping while sharing is still on closes every sharing session, and the next Codex starts a new shared server. * fix(terminal): skip legacy mirror panes off Windows and quoted dotted keys A retained shared-home pane on macOS/Linux points at a mirror that is no longer promoted, so Turn off there would be reverted; name no home for it. A quoted top-level key such as "tui.theme" is one key, not [tui].theme. * fix(terminal): drop the Turn off note that promised the setting reaches Codex outside Orca Orca's tabs are what this fix is for; carrying the setting to ~/.codex is best-effort. * fix(terminal): keep the Turn off note that the setting also applies outside Orca It holds for nearly everyone; the rare Windows upgrade gaps don't justify hiding it. * fix(codex): promote Turn off to ~/.codex under an older Orca's baseline A pane on Orca's Windows mirror home writes daemon_auto_start = false into the mirror. A promotion baseline from an Orca that predates this key has no entry for it, so the next mirror pass kept the write as a conflict and then recorded it, and ~/.codex never got the setting. Turn off on a mirror-home pane now runs the same mirror pass a terminal launch runs before and after the write: the first records the key in the baseline, the second promotes the write to ~/.codex. The passes are synchronous, so they cannot interleave with a launch's pass. A failed pass is logged and does not fail Turn off, since the write still fixes Orca's tabs. Real-home panes are unchanged. --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
8b76683b40 |
feat(ssh): deploy and pair an empty managed orcad server over SSH (#16741 T6-5) (#24453)
* feat(ssh): deploy and pair an empty managed orcad server over SSH (#16741 T6-5) Adds deploy + pair + status for a managed orcad environment on an empty SSH host, the loopback tunnel it is reached through (rebuilt on reconnect and after host resume), SSH provisioning of a new host, and SSH access for an already paired server. The deployment link lives in the environment sidecar so a downgraded build cannot strip it. Inert until the T6-6 settings UI. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(ssh): refuse a managed claim while saved state still references the host Until the T6-8 census exists, a target is claimable only when no workspace session, automation, worktree metadata or saved PTY lease (any status) points at it. An unreadable store refuses as unverifiable. Refusals name what blocked them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(ssh): keep electron out of the resume path; report a decommission journal in status The caller now passes the profile path for managed-tunnel recovery after host resume, so ssh-host-sleep-reconnect no longer reads electron's app. Status maps T6-4's decommission transaction. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
b804c13934 |
test(runtime): add a readiness census that pins every tui-idle verdict (#24336)
* test(runtime): add a readiness census pinning every tui-idle verdict Replays every recorded agent PTY transcript frame by frame through a real runtime pane (agent-known and agent-unknown, clocked and clockless) and a synthetic evidence matrix for all 43 TuiAgents, and compares each verdict and tui-idle wait outcome to committed run-length-encoded baselines. Refs STA-9098 * test(runtime): pin the census quiet probes to literal windows A census that read TUI_IDLE_QUIESCENCE_MS would move with it; fixed 2999/3000 ms reads and a fixed 2000 ms poll step make a changed window show as changed verdicts. Refs STA-9098 * test(runtime): say which census probe writes runtime state Refs STA-9098 * test(runtime): observe the census through settled panes and caller-visible waits - Read each verdict through the runtime's own settle seam (evaluateTuiIdleForLeaf) instead of re-wiring evaluateTuiIdle/leafTuiIdleEvidence/buildTerminalWaitText, so the census is coupled to one runtime method, not to the module STA-9098 rewrites. - Let the runtime finish each chunk (one macrotask turn) before reading. The old read raced work chained on the paint, so 14 frames pinned a microtask-ordering artefact. - Record when a wait settles (@start vs @poll), not just its outcome. - Exit each pane's PTY after reading it so its emulator is freed. - Replace the hand-grouped families, literal fixture list and per-pane split flag with a directory-scanned catalog, one baseline per replayed pane, and size-balanced shards. - Run the synthetic matrix in one file; it takes about 2 s. * test(runtime): cross dialog-versus-ready-screen order with every title in the census matrix Blocked detection is position-ordered (design doc 11.5): the later of a blocker and a ready anchor wins. The matrix now paints a workspace-trust dialog after, and before, each agent's ready screen under every title, so a rule engine that loses that ordering fails per agent. * test(runtime): read the census baseline field without Reflect.get The anti-slop lint rejects Reflect.get on parsed input. |
||
|
|
d3f8c5063b |
fix(ssh): orcad GC honors the activation journal; readiness requires proven daemon coverage (#16741 T6 follow-up) (#24451)
GC pins every slot an in-flight activation journal names and skips the pass entirely when a journal is unreadable or a fence is held without one. orcad's self-test now reports the coverage the daemon says its probe achieved, and remote readiness probes accept a slot only on pty-spawn coverage, or handshake on win32; builds without the field keep the identity gate. Co-authored-by: m4air <m4air@m4airs-Air.localdomain> |