mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 08:02:35 +00:00
3fb72d135de28d61c0bbbda7bb3a40be363a7cae
12927
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3fb72d135d | Run Node event-loop measurement after ordinary test suites (#26015) | ||
|
|
c0273b1ff7 | test(e2e): move the Source Control reveal golden into its own spec so older release tags skip it (#26005) | ||
|
|
2d08b3a1da |
Speed up expensive test fixtures (23–82% less time) (#26000)
* Advance Codex fixture deadlines with simulated clocks * Build large file-listing fixtures without promise batches * Compare large binary test results with native byte equality * Speed up Claude stop-note deadline fixtures |
||
|
|
37ff3873a0 | Run combined localization catalog verification on Bun (#25999) | ||
|
|
00e3027762 |
feat(agent-launch): show an agent's tab at once, where the caller asked (#25430)
* feat(agent-launch): host-assigned caller identity and a launch record written when the surface exists Step 1 of the agent-launch unification, on main. - The dispatcher stamps every request's caller from what its connection proved (runtime socket: the local CLI; the desktop's IPC: the desktop; a paired socket: its device). Params never set it. - The launch record is written twice: once when the tab exists (what creation settled: on the launch command, a draft, or a submit still `unconfirmed`), and again once the prompt's fate is known. A restart in between finds the running agent instead of answering "unknown". - A replay re-derives its terminal handle from the pane key in the running host, and shows `unconfirmed` only to callers that advertise agent.launch.prompt-unconfirmed.v1. - The record store opens in its own slot, without building the chat host; the chat host is built on that same store. Rebuilt from this PR's own commits ( |
||
|
|
66c775fbf7 |
refactor(terminal): project main's terminal layout after each save (no reader yet) (#25682)
* refactor(terminal): publish main's terminal topology after each session write Adds a by-value projection of each worktree's persisted terminal topology and a publisher hooked on the two persistence funnels (scheduleSave and durable mutations). Changed slices are pushed to the local window on session:terminal-topology-changed with a monotonic publishSeq; a startup pull (session:get-terminal-topology-slices) and publishSeq on pty:spawn and session:close-terminal-surface replies are in place. No renderer consumer yet, so behavior and saved state are unchanged. Observer failures are counted and logged once and never reach the save. * refactor(terminal): keep the topology publisher dormant until a reader pulls Writes now cost nothing extra until the first session:get-terminal-topology-slices pull (or subscribe()) takes the baseline; markDirty before that is a no-op. * refactor(terminal): narrow topology publishing to an unwired publisher and save hook Drop the window sink, pull handler and publishSeq replies; no listener attaches in production. Filter sleeping records by worktree id only, and guard observer failures once inside the publisher. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
f0520851ab | Keep mobile restore SQLite fixture in the Node test runtime | ||
|
|
1040f673b1 | Keep orchestration SQLite fixtures in the Node test runtime | ||
|
|
14bae2e172 | Route mirrored editor closes through their captured runtime owner | ||
|
|
0c32e80cc8 | test(codex): reuse unproven close fixture sequence | ||
|
|
983098dd00 | test(codex): synchronize fake clock with forced process close | ||
|
|
31d85749b4 | fix(editor): retain placeholders for drafts during bulk close | ||
|
|
8bd567a015 | fix(editor): protect document backing files and complete close cleanup | ||
|
|
91bb636530 | test(editor): align model and cursor custody fixtures with owner-close policy | ||
|
|
c9f4cd7e27 |
fix(editor): close stale duplicate documents safely
Adapt the document-sibling cleanup from Pr1p's #23347 with exact owner and captured provenance checks. Preserve divergent drafts and the backing bytes needed by retained views, and keep one-pane close behavior intact. Co-authored-by: Chen <zwq19980411@gmail.com> |
||
|
|
9c6702bb07 |
test(codex): guard that turning hooks off wins over an in-flight turn-on (#26001)
Claude-Session: codex-hook-e |
||
|
|
3308ff8b26 |
Bound YAML merge conversion and close SQLite routing review gaps (#25998)
* Close test runtime and YAML merge review gaps * Bound YAML conversion inside explicitly tagged pairs * Make completion notification fixture cadence deterministic |
||
|
|
323f312819 |
refactor(terminal): mark layout updates from user gestures (#25680)
* refactor(terminal): mark layout updates from user gestures
Divider drag end, divider double-click reset, pane reorder drop, equalize,
and pane rename/title clear now report themselves as gestures. Their
remote pane-layout push carries an optional intent: 'gesture'; every
other persist is unmarked and byte-identical to before. Saved layouts
are unchanged, and no host reads the field yet: the params schema
accepts it and degrades any other value to unmarked, and the handler
does not forward it.
* refactor(terminal): name the layout gesture marker once, as the wire intent
Gesture sites pass onLayoutChanged('gesture') and persistLayoutSnapshot('gesture')
directly, dropping the per-gesture names and the options bag. The params schema
reads intent as z.literal('gesture') with .catch(undefined) so unknown values
still degrade to unmarked.
|
||
|
|
61bcca9fee |
fix: stop Codex and OpenCode helper servers when Orca quits or crashes (STA-9254, 2 of 2) (#25753)
* fix(supervisor): add a one-shot lifetime that runs past stdin end, and relay a provider's last output A one-shot CLI (claude -p, codex exec -) reads its request until stdin ends, so the supervisor's session rule (stdin end means the owner is closing) would stop it about 1 s into its answer. The one-shot lifetime passes stdin end through and leaves only the owner-death watch and explicit signals to stop it. The supervisor also exited as soon as its provider did, dropping output still in the pipes when the owner reads slowly. It now waits, bounded, for the provider's output to be relayed before exiting. * fix(text-generation): run agent one-shots under the provider supervisor on POSIX The Claude model-list probe, model discovery for every agent, and commit message, pull request and branch name generation all spawned the agent CLI as a plain child of Orca. If Orca quit, crashed or was killed while one was running, nothing stopped it, and a CLI that hung kept running after Orca was gone. They now run under the provider supervisor that native chat already uses, in its one-shot lifetime, so Orca's exit stops the agent's whole process group however Orca exits. A timeout or cancel asks the supervisor to stop (SIGTERM) and only tears the tree down once it has had its full stop time; killing the supervisor first would orphan the agent's group. A missing binary now reaches Orca as the supervisor's exit 127, which is mapped back to the existing not-found message. Windows and WSL keep spawning the agent directly. * fix(supervisor): carry the provider argv on the supervisor's argv, map every spawn error back, and share one stop ladder - The supervisor read the provider's command and arguments from one base64 JSON env string. Linux caps a single env string at 128 KiB, so an argv prompt of about 90-120 KiB, which passes the 120 KiB per-argument guard, failed execve with E2BIG. The provider argv now follows the supervisor script's '--' as real arguments; the env keeps only small fields. - A supervisor that cannot start its provider reports Node's spawn error line and exits 127. That line now becomes the same error a direct spawn emits, so ENOENT still reads as 'not found on PATH' and EACCES or any other spawn error reads as 'failed to start'. - stopSupervisedProvider is the one ask, wait and force ladder: it gives a supervisor its full stop time before forcing. Agent one-shots, the Codex app-server close and the Claude child exit proof now share it, with the same requests, bounds and forced steps as before. * fix(supervisor): report every provider spawn failure on one marked stderr line Node throws most spawn failures (ENOEXEC, ENOTDIR, ELOOP, EPERM, ...) instead of emitting them, and the supervisor had no catch, so it died with exit 1 and a stack trace that the user saw as the agent's failure. A thrown or emitted spawn failure now exits 127 with one marked line carrying whether it was thrown, its code and its message. Orca reads only the last stderr line, so a runtime warning printed earlier cannot hide it, and maps it to the message a direct spawn gave: thrown is 'could not be started', ENOENT is 'not found on PATH', and any other emitted error is 'failed to start'. * fix(supervisor): keep the user's Node options away from the supervisor and hand them to the provider The supervisor runs Electron in Node mode, which honours NODE_OPTIONS and NODE_REPL_EXTERNAL_MODULE. A user value such as a --require of a missing file stopped the supervisor from starting, breaking even native CLIs like Codex that never load it. The launch now takes both out of the supervisor's environment, carries them in its spec, and restores them for the provider only, so a Node-based CLI still gets them. * fix(text-generation): file a forced agent one-shot teardown under its own breadcrumb site The forced tree teardown recorded every self-initiated kill as the Codex app-server's, so a forced commit-message or model-discovery stop read as a Codex teardown in crash breadcrumbs. The teardown now takes the caller's site; source-control stops pass the site their Windows tree kill already uses. * test(text-generation): cover the supervised stop under timeout and output limit; name the direct-child suites The commit-message suites that drive fake children spawn them directly, the unsupervised shape Windows and WSL use, so they now say so. The supervised POSIX stop gets its own compositions: a timed-out Codex generation settles at once but holds the Codex home until its supervisor has stopped (faithful fake, fake timers), and an agent that floods past the output limit is stopped through its real supervisor with no process left behind. * fix(codex): run short-lived app-server sessions under the provider supervisor on POSIX The Codex model-list probe, the hook trust grant and the session index heal each start a short-lived `codex app-server` as a plain child of Orca, and counted on it exiting when its input closes. A wedged Codex (a cold model/list waiting on the network, say) kept running after Orca quit, crashed or was killed. These sessions now run under the provider supervisor that native chat's Codex connection already uses, in its session lifetime, so Orca's exit stops the server's whole process group. The session's end and its deadline share the one stop ladder: end its input (and SIGTERM a session past its deadline), give the supervisor its full stop time, and only then tear the tree down. A missing binary reaches Orca as the supervisor's exit 127 and is mapped back to the spawn error, so the trust-grant telemetry still reads it as a missing binary. Windows and WSL keep spawning the server directly, with the same timings as before. The supervisor now takes only the command line it starts, so the CLI build, which also runs these sessions, no longer pulls in the native chat connection's types. * refactor(supervisor): share the stop of a supervised child process Agent one-shots stop their supervisor with SIGTERM through the shared stop ladder, watching the child's own exit. That adapter moves into one helper so the Codex backfill recovery can use it with its own stop request, instead of a copy. * fix(codex): supervise the app-server that keeps a Codex index backfill alive While Codex rebuilds its session index, Orca keeps a read-only `codex app-server` running for up to an hour so Codex can finish. It was a plain child of Orca with its input held open, so a quit, crash or kill left it running. On POSIX it now runs under the provider supervisor in its session lifetime, so Orca's exit stops its whole process group. Stopping it (done, aborted, or given up) ends its input, as a Codex connection close does; the supervisor then SIGTERMs the group and SIGKILLs it after the grace, and the tree is torn down only if the supervisor outlives its full stop time. Windows and WSL keep the direct spawn and the drain-first probe termination. The spawn and stop of that process move into their own module. * fix(opencode): stop the launch model preflight server with Orca on POSIX Before an OpenCode launch, Orca starts `opencode serve` to read the configured agent and models, then stops it. The server was detached into its own process group and never exits when its input ends, so if Orca quit, crashed or was killed during that preflight (up to 10 s), nothing ever stopped it. On POSIX the server now runs under the provider supervisor in its one-shot lifetime: the preflight's closed input does not stop it, and Orca's exit does. The preflight's teardown asks the supervisor to stop (SIGTERM) and forces the tree only after the supervisor's full stop time; signalling or SIGKILLing the supervisor's own group would orphan the server's. A descendant that ignores SIGTERM is now killed with the group rather than left running once the pipes close. Windows keeps the direct spawn and its existing teardown. * test(codex): pin when supervised session and backfill stops escalate A session past its deadline is SIGTERMed through its supervisor rather than waiting out the stdin-end grace, and its tree is torn down only after the supervisor's full stop time; Windows keeps its deadline kill and 1.5 s close wait. A supervised backfill app-server is stopped by ending its input, with the same full stop time before any teardown. * test(text-generation): run the direct-child suites on the Windows path and cover supervised discovery The commit-message suites that drive fake children mocked the supervisor away on POSIX, so they asserted a direct root SIGKILL that production no longer takes there. They now pin the platform to Windows (with an empty PATH, so host installs cannot answer a bare agent name) and assert the Windows kill, taskkill included. The three tests that check the host's own discovery spawn shape run on the host and read the agent argv past the supervisor's '--'. Model discovery gets its supervised composition: a timed-out Codex discovery settles at once but holds the Codex home until its supervisor has stopped. * fix(supervisor): show a supervised spawn failure in native chat as the spawn error it was Native chat's exit errors carry the provider's stderr tail into Details. Under the supervisor a missing CLI left the supervisor's internal spawn-failure report there instead of Node's own 'spawn <cmd> ENOENT'. The report, its parser and a display formatter now live in one module; the Codex app-server and Claude stream-json exit errors pass the tail through the formatter, which turns a report back into the spawn error and leaves any other stderr unchanged. * fix(supervisor): report a spawn that failed without a pid instead of crashing on its missing pipes When the provider spawn fails outright (EMFILE, ENFILE), Node emits 'error' later and leaves the child with no pid and no stdio. Piping stdin into the missing pipe threw first, so the supervisor died with exit 1 and a stack trace and never wrote its spawn-failure report. The pipes are now wired only for a provider that started. * refactor(supervisor): share the stop of a supervised child process Agent one-shots stop their supervisor with SIGTERM through the shared stop ladder, watching the child's own exit. That adapter moves into one helper beside the ladder, so other supervised children can use it with their own stop request instead of a copy. The caller's breadcrumb site still reaches the forced teardown. Same request, wait and force as before. * fix(codex,opencode): file forced backfill and preflight teardowns under their own breadcrumb sites A forced teardown of the Codex backfill app-server is filed under 'codex-state-db-backfill-recovery', and one of the OpenCode launch model preflight under 'opencode-launch-model-preflight', instead of the generic 'codex-app-server-teardown'. * test(codex): write the stand-in pid report atomically A loaded host let the test read the pid file between its creation and its write (Unexpected end of JSON input); the stand-in now renames it into place. * fix(supervisor): give a session provider its stdin end and grace when its owner dies The owner-death watch went straight to the group SIGTERM and cancelled any stdin-end grace, so when Orca quit or crashed a session provider such as the Codex app-server never saw the EOF that lets it finish writing its state (auth.json, the state database). A session whose owner is gone now closes as an owner's stdin end does: the provider's stdin is ended, it gets the stdin-end grace, and only then the SIGTERM and SIGKILL ladder. A one-shot already had its EOF at the end of its request, so its owner's death still stops it at once. * fix(opencode): stop the preflight server through the shared supervised stop, and trust only a proven stop The preflight's own stop wrapper waited on the supervisor's pipes and counted a forced teardown as proof, though the teardown reports success even when it found no descendants to check, and a supervisor that failed to reap its group exits 1 with its pipes closed. It now uses stopSupervisedChildProcess with its breadcrumb site, and counts the server stopped only when the supervisor ended on its stop signal or relayed the server's own exit. A forced stop, or an exit of 1, returns no context, as an unverified stop did before. * chore(codex): state the supervised stop time in the probe and trust grant deadline comments * test(codex): assert the signals a supervised stop sends itself, not a SIGKILL the mocked teardown never could * fix(supervisor): kill the rest of the provider group once the provider exits on a stop A requested stop waited out the whole SIGTERM grace for the provider's group even after the provider itself had exited, so a SIGTERM-ignoring helper it left behind held every stop for up to 3 s. Under a stop, the rest of the group is now SIGKILLed as soon as the provider has exited, the same rule its own exit already follows. * test(text-generation): cover a supervised Codex discovery past its output limit Model discovery's supervised stop was covered only under timeout. A Codex discovery that floods past the output limit now runs through a real supervisor: it settles with the too-much-data error, its agent is stopped through the supervisor, and the next discovery on the same Codex home starts only after that agent is gone. The direct-child suites' headers now list exactly the supervised cases that are covered. * fix(supervisor): close a provider whose owner is gone the way its owner closes it Owner death gave every session provider the stdin-end grace, so after an Orca crash a Claude session, whose close is a stdin end plus SIGTERM, could keep working on its turn for a second with nobody watching. The spawn spec now names the provider's close request: 'stdin-end' (the Codex app-server drains and exits on EOF, then gets its grace) or 'stdin-end-and-sigterm' (Claude; the default). A gone owner gets that same request. One constant per provider feeds both its spawn spec and its owner-side close, through one requestProviderClose, so the two cannot drift. One-shots still stop at once. * fix(codex): close short-lived sessions and the backfill app-server the way a Codex connection closes Codex finishes its writes and exits on its stdin end, so the Codex connection's supervisor closes it by ending stdin, and an owner that is gone now gets that same close. The short-lived sessions and the backfill app-server now name the same close request, through one shared constant, in their spawn spec and in their own close: Orca quitting or crashing gives them the stdin end and its grace before SIGTERM, instead of an immediate SIGTERM. A session past its deadline still adds a SIGTERM, since it is wedged. * test(codex,opencode): an owner's death drains Codex before SIGTERM, and the preflight stop no longer waits out the grace The stand-in now records when its stdin ended and when SIGTERM arrived. A SIGKILLed owner leaves a Codex session or backfill app-server its stdin-end grace before SIGTERM, and the OpenCode preflight ends within 1 s of its server's SIGTERM even with a descendant that ignores SIGTERM. * test(codex): give the trust-grant deadline tests room for a supervised start on a loaded host At 500 ms a loaded full run hit the deadline before the supervisor had started the stub, which never wrote the pid the test reads. * fix(supervisor): keep the SIGTERM grace for a session's group after its provider exits Killing the rest of the group the moment the provider exited under a stop also reached native chat's closes, so an MCP server, a tool's child or a dev server still in Claude's or Codex's group was SIGKILLed mid-cleanup instead of getting the rest of the SIGTERM grace. The early group kill now applies only to one-shots, where the saved wait was the point; a session's stop is back to waiting out the grace for its group. * test(claude): pin that Claude's spawn passes its close request explicitly The spawn-spec assertion matched the default close request, so dropping Claude's explicit request still passed. The test now checks that the spec is built with the exit-proof ladder's own constant. * refactor(codex): give the Codex app-server close request its own module Other Codex app-server spawns will name the same close request as the connection does. Holding it in its own small module lets them import it without the connection itself. * build(cli): list the Codex close request and the provider supervisor in the CLI project The command-line build runs the short-lived Codex app-server session, which will name the same close request as the Codex connection. Listing the close request, the provider supervisor it takes its type from, and the spawn-failure report the supervisor uses lets the CLI project typecheck that import without pulling the connection in. * test(codex): give a stand-in 20 s to report its pids on a loaded host At a load average near 50 both owner-death tests timed out waiting for the bundled owner's stand-in at 10 s; they pass alone. * test(codex): start the deadline tests' clocks past the stand-in's start, and cover a server that ignores SIGTERM The session and trust-grant deadline tests ran a 2-4 s deadline from spawn, so a loaded host could stop the stand-in before it wrote its pid. The session test's deadline now outlasts its pid-read budget, and the trust-grant deadlines are 8 s. The trust-grant comment said a wedged server may ignore everything but SIGKILL, but that stub dies on its stdin end; a new POSIX case pins that a server ignoring both its stdin end and SIGTERM is SIGKILLed after the SIGTERM grace. * test(text-generation): check the ENOEXEC start failure only where Node reports one On Linux, glibc's execvp hands an executable that is not a program to /bin/sh, so both a direct and a supervised spawn run it and it exits 127; only macOS throws ENOEXEC. The not-a-program case now runs on macOS only; the path-through-a-file case (ENOTDIR) still covers a thrown start failure everywhere. * test(wsl): follow the backfill's wsl.exe spawn into its new process module The WSL invocation boundary lists files that spawn wsl.exe directly. The backfill recovery's spawn moved into codex-state-db-backfill-recovery-process.ts, so the entry moves with it; the count is unchanged. * test(codex): keep factory child_process mocks loadable now that Codex stops reach the process-table reader The backfill recovery and Codex sessions now stop through the shared supervised teardown, whose process-table reader binds execFile when it loads. Five rate-limit fetcher tests mocked node:child_process with only spawn and failed at load: they now mock the backfill recovery, as their sibling fetcher tests already do. The account add-login tests' child_process mocks gain an execFile stub. * fix(opencode): count no forced preflight stop as proof the server is gone A forced teardown walks and group-kills the supervisor's tree, but the server leads its own detached group, so a teardown verdict of 'exited' does not cover members left in the server's group. Only a supervisor that reaped the group itself, by its own stop or relaying the server's exit, now counts as a proven stop. The Windows session-stop test now says why it sees no direct kill. |
||
|
|
86d03ff908 |
fix(native-chat): sending brings the latest into view and follows the reply (#24514)
* fix(native-chat): sending brings the latest into view, with visible jumps to latest and top Sending while scrolled up left the reader parked in old history, and the way back was a faint button. A send now resumes following, opening a row to read it stops following until it is closed, and Jump to latest and Jump to top sit above the composer. Refs #23797. * fix(native-chat): a late or unsent answer no longer moves a reader who scrolled away An answer's reveal is now held from the click and dropped if the reader acts before the host accepts. In the terminal lane, an answer with no terminal to write to reveals nothing. * fix(native-chat): an empty answer no longer moves the reader The terminal lane's reveal now uses the same check the send does, and the send-site guard test ignores comments. * fix(native-chat): preserve upstream retry and question cancellation * refactor(native-chat): leave Jump to top out of this change Jump to top moves to its own PR so this one lands the send reveal and follow behaviour on their own. * refactor(native-chat): keep Jump to latest's existing look in this change The solid, fading Jump to latest button moves to the UI PR (#25702) so this one carries only the scroll behaviour. * Preserve native chat reader intent across submissions and disclosure layout Replace open-row lifetime tracking with bounded position preservation, and tie delayed reveals to the originating reader and session. Keep queued drafts and picker actions from navigating the transcript. Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> * fix(native-chat): retain reader takeover until its frame ends Pending reader takeover could keep an earlier end target once a duplicate 250 ms gate expired. Let the existing pending-frame check keep the reader's actual offset until that frame ends. Move unchanged interactive reveal and approval projection into their existing modules so both view files meet the line limit. Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> * fix(native-chat): only a scroll gesture stops following; sends reveal at the press Opening or closing a row used to stop the transcript from following the newest output. A reader at the live end who expanded a tool run then lost the stream. Sends that wait on the host (answers, commands, goals, option changes) only brought the latest into view after the host replied, so the reader watched nothing happen at the press. Now following stops only on a reader gesture: an upward wheel or scroll key when there is content above, a scrollbar press, a touch drag once it has carried the view off the end, or a press on content while already away from the end. Opening, closing, layout changes and the app's own scrolls never stop it, and returning to the end resumes it. Every local send reveals the latest at the press. A message that waits as a queued card, a message from another device, and Stop leave a reader who scrolled up where they are. Queue Resume still reveals only after the host lifts the pause, and only in the pane that pressed it while that pane is shown. Removes the disclosure position hold, the reader-opens wiring, the held reveal and its reader generation tracking. The queued-card decision moves into the outbox send so the session hook stays under the line limit. Also restores the transcript label import and the approval card's verified send that the merge with main dropped. * chore: leave an unrelated fixture as main has it * refactor(native-chat): share the terminal send paths' common options in the composer * test(native-chat): drop a field the main merge declared twice * test(native-chat): the pane's steer still steers the queue, and also reveals --------- Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
faed899cd3 |
fix(ci): run three new SQLite-backed tests in the Node runtime project (#26010)
#25888 and #25766 added tests that import the orchestration database or the structured session runtime without registering them in the Node runtime list, so vitest-sqlite-runtime-boundary fails on main and every PR. |
||
|
|
e717ea6b4c |
feat(native-chat): copy chat images from the right-click menu (#24161)
* feat(native-chat): copy chat images from the right-click menu
Fixes #23904
* test(native-chat): cover Copy image and the open preview in Electron
* fix(native-chat): keep the image preview open by click target, confirm copies, pass PNGs through
The image preview stayed open through a chat-menu click by reading which
element had focus, which depends on the menu's exit-animation timing.
It now ignores an outside interaction whose target is the chat menu, the
same target check other Orca surfaces use for portaled menus. Both
previews drop their color and padding overrides on the dialog surface,
which the design system reserves to the dialog primitive.
A successful Copy image now shows "Image copied"; before, nothing told
the user the copy had finished.
An image that is already a PNG is copied as-is after the size checks,
instead of being decoded and re-encoded, which cost time and could grow
a screenshot past the clipboard size limit.
* fix(native-chat): pass an image through as PNG only when its bytes are PNG
Copy image skipped re-encoding any image whose type said PNG, but a chat
image's type comes from its file extension. A WebP or GIF saved under a
.png name was then sent unconverted, the main process could not decode
it, and the user saw "Image copied" with nothing on the clipboard. The
pass-through now checks the PNG file signature instead.
* test(native-chat): format combined image-copy and session-ID mocks
* Release chat image menu when the retained pane hides
* Validate inherited style directives in their owning scan
* Copy displayed chat images and refuse hidden menu capture
Use full-size sent-image sources, read HTTP images when copying is chosen,
and rasterize browser-decodable formats through the existing converter.
Preserve the current preview surface and prevent a hidden preview from
retaining another copy action.
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* Revert "Validate inherited style directives in their owning scan"
This reverts commit
|
||
|
|
f6448a1e26 |
fix(native-chat): say which retry a Claude retry is on and what it last failed with (#25818)
* fix(native-chat): say which attempt a Claude retry is on and what it last failed with * fix(native-chat): count Claude's retries as retries, not attempts Claude Code sends its first api_retry frame only after the first request failed, so frame "attempt N of max_retries" is the Nth retry. Say "Retry N of M." and store the bound as maxRetries. |
||
|
|
35e173a5b7 |
fix(native-chat): alert when a structured chat asks for approval or input mid-turn (#25766)
* fix(native-chat): alert when a structured chat asks for approval or input mid-turn A structured Claude or Codex chat that stopped mid-turn to ask for approval or a question raised no OS notification, no phone push and no unread dot: the host's attention feed only fired when a turn settled. The host now announces each newly pending prompt once, from the same feed, and owns the phone push for its own structured sessions; desktops keep presentation only. - host feed: a `prompt` edge per pending approval/question, opt-in on the existing stream; a clean settle that only restates an announced prompt is not re-announced to prompt-aware clients; failures always are - host push: an in-process subscriber pushes the host's paired phones with labels from in-memory metadata, and withdraws a prompt's alert once answered - one attention identity for desktop banner, phone push, dedupe and retirement; delivery dedupes on it instead of the per-workspace burst window - agentSession.acknowledgeAttention routes reading a chat to its owning host, which retires what it pushed for that session via its dismissal record * fix(native-chat): keep prompt alerts under their retirement identity * Fix structured attention read and delivery lifecycle * Allow later reads to retry failed attention retirement * Prevent delayed prompt alerts after accepted reads * Keep attention integration tests across process boundaries * Check structured capability before cursorless attention reads * Keep host retirement fixture in the Node typecheck boundary * Keep attention test seams in the mock boundary * Keep host notification formatting usable without Electron * Declare agents in restored-host attention fixture * Group session attention capability declarations * Retire structured alerts on every read and when a remote prompt ends - Mark read and Mark all read on a chat that was never opened, or is hidden, now cover what lit its row: the read boundary is the later of the transcript cursor and the newest edge the tab was handed, captured at the click. The one boundary closes the banners, retires relayed phone alerts and drives the host acknowledgement; an edge after the click stays live. - An alert from a host older than journal cursors has no position, so a pane read retires it again, banner and relayed phone push, as before this change. - A desktop-relayed prompt alert for a remote chat is withdrawn once that host's live status stops reporting attention (answered elsewhere, cancelled, host restart); cached status after loss of contact never settles it. - The attention acknowledgement is agent-session.attention-ack.v1 with a required journal cursor and strict params; the cursorless no-op is gone. - The dismissal store keeps a record whose origin a newer build wrote, dropping only that origin. - Host reconciliation runs on restore and when a prompt leaves the pending set, not on every journal commit. - Remove unused session-wide retirement (retire, retireMatching, retireMobileNotificationsMatching) and test-only store lookups. * Settle relayed prompts only from host-dated status; reads default to view - A remote chat's status and its prompt edges arrive on separate sockets, so a status row queued before the prompt could land after it and withdraw a relayed alert that was still pending. Settlement now needs a live, non-attention row whose host updatedAt (the journal's latest row time, never decreasing) is later than the prompt's host raisedAt. The owed prompt is cleared only once the settle call succeeds, so a failed call retries on the next qualifying row. - An acknowledgement without captured reads now reads as a view. Mark read, Mark all read, the hover-card jump and both dashboard acks pass 'explicit'; Activity's automatic per-turn read stays a view. * Re-judge relayed prompt settlement after each call; dashboard watching is a view - Settlement is one check of the current mirror, run on each status row, after each settle call returns, and when a prompt edge arrives. A row that lands while a call is out is no longer dropped, and an edge that arrives after its own resolution settles at once. A rejected call is retried only once the mirror holds a different row, so a failing call cannot loop. - The Agent Dashboard's card click acknowledges as explicit; watching the open dialog acknowledges as a view, through both the in-window drawer and the pop-out relay, and no longer re-acks the state the click just read. * Keep a reused read owner findable after its holders release it A chat pane memoizes its read owner. When every holder let go in one commit and the same owner was picked up again (React StrictMode's effect re-run in development, or a pane remounted in one commit, as on a tab move), the release dropped it from the registry and nothing put it back. The attention bridge then found no owner, so a view read carried no cursor: the unread dot cleared but the banner and phone alerts stayed. An owner now names its key again whenever it gains an activation or a subscriber and the key is free, never over a different live owner. * Type the ack test's execution host ids * Keep the turn-completion types in main's turn-completion wire module Main moved these types into agent-session-turn-completion-wire.ts while this branch moved them into agent-session-attention-wire.ts. They now live at main's path with this branch's additions (journal cursor, prompt attention, prompt arm); the unused completion key is dropped. |
||
|
|
53b9ccba6c |
refactor(terminal): add in-place pane layout geometry apply (unused) (#25671)
* refactor(terminal): add in-place pane layout geometry apply (unused) PaneManager.applyLayoutGeometry applies a layout's split orientation and ratios to the mounted pane tree without remounting panes, and never fires onLayoutChanged. Nothing calls it yet; the live-layout reconciler adopts it in a later change. * fix(lint): merge duplicate removal import in delete-worktree failure toast Main (#25668) introduced a duplicate import that fails the focused code-quality lint. * refactor(terminal): share split ratio read; refuse zoomed or dividerless trees in geometry apply |
||
|
|
31f103535f |
fix(orchestration): serve a /clear'd native-chat worker through the session running it (#25875)
* fix(orchestration): serve a /clear'd structured worker through the session running it A structured (native chat) worker the user /clear'd keeps working in a successor session, but orchestration kept acting on the session it was started with, which the clear had closed. Terminal read and @idle status lost the handle, worker-read and the release archive served the pre-clear transcript, worker-show reported the worker exited, worker-stop closed the already-closed session and left the successor running, mail and Dispatch nudges were refused, and the idle sweep could put the successor to rest while its Dispatch was open. One forward walk over the durable session records now answers which session runs a worker's conversation. Worker authority and custody are judged on that session, so every caller holding a worker handle gets it by default; reads, archive, observation, status, group addressing and the incarnation liveness probe use it too. Stop closes the running session and re-resolves, so a clear that commits while the close waits is followed to its successor. The reverse direction (a successor's child env, user takeover, the idle sweep) maps a session to its worker through the lineage root. A successor's idle edge re-derives the worker's Dispatch mailbox, and settlement forgets parked mail on every session of the lineage, derived rather than stored. When the running session cannot be found (host not installed, a successor with no record, a looping chain) observation answers unverifiable, never exited; readers refuse with session_caller_not_live and stop closes nothing. worker-read and the archive warn that earlier conversation from before a /clear is not included. * fix(orchestration): close the review gaps in serving a /clear'd structured worker Review of the first change found places where a /clear'd worker still went wrong, plus a type gap that let the original bug shape compile: - worker-stop refused a structured worker whose old session read exited mid-clear (stopped, but the successor not yet committed); it now goes to the clear-aware stop once identity and ownership are proven. The terminal (PTY) gate is unchanged. - A worker stopped while its /clear successor had never run could never be released: no close writes death evidence for an agent that never started, so the probe read unverifiable forever. A released session that never started anything and whose chat is gone now reads exited; it stays unverifiable while the chat is listed. - worker-read, terminal read and the release archive now read the worker's whole lineage, oldest session first, under the same page limit and byte bound, instead of only the running session. This replaces the "earlier conversation is not included" warning. Cursors key later sessions' items by session, so a cursor taken before a clear stays valid and continues into the successor. - Pointer delivery re-derives a mailbox's target when an attempt ends; if a /clear moved it meanwhile, it delivers once to the new session. - Custody and status reads take the resolved running session, and one typed hold (held, not held, unverifiable) replaces the rebuilt authority in terminal read, worker-show and group addressing. - Smaller fixes: stop keeps an earlier close on its receipt; abandoning a side task no longer forgets the worker's parked mail; a release whose lineage cannot be verified ends release_unknown instead of staying requested forever; mail reach reports an unverifiable lineage as unverifiable, not ended; party resolution scans records once. A /clear typed into a worker's chat is reported as a user takeover, the same as any other message the user types there; a test pins it. * fix(orchestration): tighten the never-ran rule and the follow-ups to /clear handling Round-2 review of the /clear'd structured worker fixes found that the new rule for "a session no agent ever ran is exited" was too loose, and two of the new follow-ups missed a case: - The rule now requires the founding fence, which every reservation moves. A reservation that a restart released without proof, or a failed first start whose exit was never proven, stays unverifiable instead of reading exited. It no longer asks whether the conversation is open (any history read opens it, which stranded a stop, read, release sequence), and it treats only an authoritative tab index without the chat as retired. - Pointer delivery follows a mailbox a /clear moved on a thrown attempt too, keeping the attempt's own failure, and tries each session once. - Releasing a retired worker whose newest session cannot be read keeps the readable earlier sessions' output and says the latest one was lost, instead of freezing an empty archive. - worker-show judges status and addressability on one lineage walk. - The worker-stop comment says plainly that any structured worker that reads exited (a crashed agent too) is closed and its chat hidden; tests cover that and a takeover reported by the successor session. * fix(orchestration): keep a retired worker's earliest readable output across several lost sessions Releasing a worker whose chat was retired and whose newest session could not be read kept the earlier sessions' output only when exactly one later session was lost; after two clears with both later journals gone, release still froze an empty archive although the first session held the worker's answer. The archive now walks back past every unreadable later session, under the same page limit, and its warning says how many later sessions could not be preserved. The rule that reads a never-started session as exited also requires that the record carries no fence floor: a copy restored from backup may hide a reservation that the backup lost, so it stays unverifiable. * test(orchestration): build the takeover test's session record from the shared fixture The changed-code quality gate rejected the test's `as unknown as AgentSessionRecord` stub, which this branch had touched. It now builds a real record with agentSessionRecordFixture and a typed lease, so no type assertion is needed. * test(orchestration): give the cleared-worker pointer harness the sender-name dependency main added |
||
|
|
5345ba34bf |
Fix ownership for terminal and chat file drops (STA-6940, PR 1/6) (#25749)
* Fix terminal and chat file drop destination ownership * fix runtime terminal drop ownership and queued chat retries |
||
|
|
deb737478a |
Simplify reveal and test fixture cleanup (#25978)
* refactor: remove redundant reveal and watcher test wrappers * test: keep closed restore fixtures free of deferred WAL writers |
||
|
|
2137295bb6 |
fix(git): use branch-safe author names as username fallback (#25984)
Co-authored-by: Kyou0203 <kyou12138@gmail.com> |
||
|
|
1eeb8f3f72 | fix(test): drop the duplicate resolveLaunchArgs that two merges both added (#25997) | ||
|
|
7bc9ff947c |
fix(grok): add Grok 4.7 to the fallback model catalog (#25976)
grok 1.0.41's `grok models` now reports `Default model: grok-4.7` and its model cache advertises low/medium/high/xhigh effort for grok-4.7. Seed it as the CLI default with an xhigh ceiling, keep grok-4.6 and grok-4.5, and move the probe spec default in step with the seed. Fixes STA-9652 |
||
|
|
add534475d |
fix(browser): detect Comet in its Windows vendor directory (#25983)
Co-authored-by: Masaki Yamamoto <nnasakick@gmail.com> |
||
|
|
fa42e57659 |
fix(gitlab): select SSH workspace host for merge request and issue writes (#25985)
Co-authored-by: makoto-developer <72484465+makoto-developer@users.noreply.github.com> |
||
|
|
d8c871a1f0 |
Speed up unit tests with cross-runtime duration scheduling (#25967)
* Schedule unit tests across runtimes by measured duration * Keep new SQLite fixture suites on Node after updating main * Inject scheduling timings instead of mocking the module * Keep agent-session database lifecycle contracts on Node |
||
|
|
6352ff3ff9 |
Run nested workspace deletion in the background (#25975)
* Run confirmed nested worktree deletion in the background * Reuse the translated workspace deletion failure title * Supply launch arguments in the Codex session test fixture |
||
|
|
72b84118e0 |
test(e2e): terminal layout parity check against main (#25681)
* test(e2e): add terminal layout parity check for topology refactor PRs Runs fixed terminal-layout journeys in the real app on two builds, captures the renderer topology and the saved workspace session, normalizes volatile values and fails on any difference not declared for a named bug. Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): record quit exit status and report paths main does not reproduce Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): invoke pnpm correctly under corepack and silence the typeless-module warning Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): accept pnpm's forwarded -- in the layout parity runner Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): close parity panes through the user's chord and treat absent maps as empty Driving PaneManager.closePane directly left main to learn of the close from the PTY exit, which raced quit; the keyboard path commits the close in main first. Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): build parity checkouts before running, forward -g, and add a drag-out scenario Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
c893048aeb |
feat(native-chat): show which agent a chat message is from, and open it (#25888)
* feat(native-chat): show which agent a chat message is from A message another agent sends into a native chat (today the orchestration mail notice) looked exactly like the person's own: a right-hand bubble and a queued card identical to theirs. The sender was recorded only on the queue row, so a message sent to an idle chat, or a card once it was sent, had no sender at all. The sender now lives on the message itself (`from` on the message body): - The send path carries it on both deliveries; an idle chat records it too, and it survives the queue, /clear's carry, restart and history. - No fingerprint covers it: one shared payload projection leaves it out at every fingerprint site, so stored digests are unchanged and the provider's echo still folds into the same bubble. The provider never receives it. - A client cannot set it: its send params stay strict. - One reader normalizes it where it is read (journal load, queued row, client projection); a newer message kind keeps the sender an agent's. - Each sender keeps a name snapshot from names Orca controls: a chat's conversation name or agent label, a terminal's own tab title or agent, a federated sender's task name. Never an agent-set terminal title. - The separate queued-row sender column and its plumbing are removed; it never shipped. Desktop shows such a message as a left-aligned "Message from <name>" row; the name opens the sender: its chat through the existing open-chat flow (after a host lookup that follows /clear and dispatch addresses), or its terminal through the terminal-link path, with the existing feedback when it is gone. A queued card shows a plain "From <name>" line. Mobile shows the same attribution, not yet clickable. * fix(native-chat): name and open an agent's message by what Orca shows for it Review fixes for showing who an agent message is from. - Names: a sender is named by its dispatch's task (local workers too, not only federated ones), else a chat by the label its tab shows, else a terminal by its tab's stored title, else its agent. The conversation name the snapshot read before has no writer, so every chat read "<Agent> Chat". - One sender builder (agentMessageSender) and a public runtime naming object (orchestrationSenderNames) any send site can use; the terminal naming moves out of the unrelated runtime layer into its own module. - Opening a terminal sender always asks the chat's host: a terminal handle is issued per run, so the host finds an earlier run's terminal again through the pane its mail was sent from. The pane key never leaves the host. - Feedback follows what the host found: a gone chat or a gone pane by the kind it lost, "pane no longer available" only when the terminal answer proves it, and the existing "couldn't reach" / "needs a newer Orca" words otherwise. - Another agent's message is not offered rewind (it would put the agent's text in the person's composer), and a Codex rewind keeps the sender on the messages it retains. - The queued card and mobile name every sender, two that share a name included, and count in "+N" only those left out; the transcript row separates names with ", ". - The host no longer normalizes the sender when loading the journal or the queue: nothing on the host reads it, and clients read it through the one reader at their projections. * fix(native-chat): name a worker's final report by its task; clearer open-sender failures - A worker's own worker_done settles its dispatch before the mail lane names who the report is from, so a terminal worker's completion read "Codex". A local worker is now named by the dispatch it holds, else the one it last held (handles are per run, so that is this run's). The coordinator mail rig now attests the worker's process, so its report is accepted and settles as it does in production. - Opening a sender: a version block or an unknown answer says "Update Orca to open it.", since either side may be the older one; "This needs a newer Orca on the computer running this chat" stays only for a host that predates the lookup. "Orca couldn't reach the agent." is followed by "Try again.", each sentence translated on its own. No new copy. * fix(native-chat): name a worker only by work it holds or just reported A worker with no active dispatch was named by the newest dispatch ever assigned to its terminal, at any status and any age: one that failed before it ran, or a task finished long ago that its later, unrelated mail kept showing. Now a worker is named by its active dispatch, else by the dispatch its own worker_done in the announced mail names (only if that dispatch is its own), else by its tab or agent as any terminal is. * fix(native-chat): restore a rewound message's sender through its typed body; mobile card fixtures name their attribution - The rewind's held-sender restore lives with the retained rows and reads both bodies through the journal's message-body check, instead of a shared helper taking any object (anti-slop: no-object-parameters). - Mobile queued-card test fixtures carry the attribution field the card gained. * fix(native-chat): never call a sender gone without proof; the sender builder moves out of the mail lane - A federated sender (dispatch:<id>) runs on another host, which the chat's host cannot open: its name is plain text, and the host's lookup answers 'not found' rather than 'gone' for a dispatch it does not run. Not seeing a worker is no evidence it stopped. - The lookup calls a chat gone only when the identity is proven lost; any other failure reaches the client as 'could not reach'. - A sender open that throws shows 'couldn't reach the agent' instead of an unhandled rejection. - agentMessageSender, the one sender builder, lives in its own module, so the runtime's naming no longer imports from the mail lane. |
||
|
|
cc6006779f | fix(test): pass resolveLaunchArgs in the Codex stopped-send-order test so main typechecks (#25977) | ||
|
|
51062e653e | fix(test): pass resolveLaunchArgs in the record store slot test so main typechecks (#25970) | ||
|
|
6aa12c30ef |
Name native Claude and Codex chats after their first message (#25724)
* feat: generate chat names through configured text agents * Project structured chat names across stored tabs and session rows * Name native chats from their first live message * Restore the journal test provider handle import * Fix first-message naming and live Vault title updates * Preserve Unicode characters in bounded chat naming prompts * Read chat naming settings only when a turn needs them * fix(chat): store only generated conversation names * fix(chat): keep ordinary labels across unnamed chat surfaces * fix(chat): preserve naming after fast first turns * Preserve native chat names across command-first sends and Vault lifecycle * Route native chat SQLite contracts through the existing Node test pool * feat(settings): add chat naming controls to Chat page * Preserve chat naming drafts and configure Custom commands through host settings * Scope synthetic command output to its journal thread in naming test * Keep naming test fixtures within their typed project boundaries * Resolve the real Vault hook directly in its integration test * Publish chat naming save refs after render commits * fix: keep Japanese chat naming copy stable during repair * Update test contracts for the naming integration * Keep journal fixture reads on the host clock * Give Chat names a separate settings section |
||
|
|
e813fa5819 |
feat(native-chat): keep a message a Stop took back on screen, with one stop row after it (#25051)
* 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 (
|
||
|
|
28ed544da8 |
Fix send picker reveal under collapsed hosts and parents (#25950)
* fix: reveal send picker targets through host-owned ancestors * test: type picker close fixture as a nullable target |
||
|
|
e1e9d1c14a |
feat(agent-launch): host-assigned caller identity and a launch record written when the surface exists (#24934)
Step 1 of the agent-launch unification, on main. - The dispatcher stamps every request's caller from what its connection proved (runtime socket: the local CLI; the desktop's IPC: the desktop; a paired socket: its device). Params never set it. - The launch record is written twice: once when the tab exists (what creation settled: on the launch command, a draft, or a submit still `unconfirmed`), and again once the prompt's fate is known. A restart in between finds the running agent instead of answering "unknown". - A replay re-derives its terminal handle from the pane key in the running host, and shows `unconfirmed` only to callers that advertise agent.launch.prompt-unconfirmed.v1. - The record store opens in its own slot, without building the chat host; the chat host is built on that same store. Rebuilt from this PR's own commits ( |
||
|
|
0a26f3ec90 |
fix(native-chat): honor saved agent Command and Arguments (#25721)
* fix(native-chat): honor saved agent launch commands and arguments * fix(test): retain provider import after syncing main * fix(native-chat): preserve launch arguments and explain invalid settings * refactor(native-chat): require the current launch arguments source * fix(native-chat): require saved arguments when installing the runtime * fix(native-chat): show saved argument refusals beside launch retry * fix: ship saved-argument error messages in every locale * test: align model discovery with executable overrides * chore: drop the local pnpm install lockfile block |
||
|
|
ce4c99ae07 |
fix(terminal): keep a pane closed just before quit from coming back on relaunch (#25711)
* fix(terminal): keep a pane closed just before quit from coming back on relaunch The daemon now lists a session whose kill it accepted as state 'exiting' (still live), the inventory carries that through to pty:listSessions, and the worktree activation gate never adopts an exiting PTY into a new tab. Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): use the Linux close-pane chord (Ctrl+W) in the closed-pane relaunch spec --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
f7c542c7a3 |
Keep profile saving alive after a stalled main loop (#25318)
* Keep profile saving alive after a stalled main loop After a long main-loop stall (overnight sleep, dark wakes), the profile writer's overdue 30s timeout could run before an acknowledgement that was already queued, permanently retiring the writer until restart. Terminal creation then failed because pane bindings could not be saved. - Writer deadlines measure lateness on the monotonic clock and grant a bounded fresh window when the callback is overdue or the system reports suspended; resume re-arms without spending grace. Applies to initialization, every command, and the close/exit wait. - The "Saving stopped" alert is parented to a visible main window (never a parentless synchronous macOS alert), deferred until shown, deduplicated, and says whether the latest change is unconfirmed. - Timeouts, grace, writer faults, and alert presentation leave sanitized durable breadcrumbs. * Fix profile writer timeout and shutdown races * Run profile writer stall regression on Linux and Windows * Keep Electron probes out of headless runtime qualification |
||
|
|
40f03199f8 | fix(native-chat): fingerprint the consumed transcript boundary (#25965) | ||
|
|
d6b155daf5 |
Fix Claude's stuck spinner after Escape cancellation (#25846)
* Fix Claude interruption status with native terminal evidence * Fix relay evidence test wiring and current host-turn test inputs * Fence Claude interruption against continued work and replaced PTYs |
||
|
|
7e52bed4e0 |
fix(editor): recover PDF previews after failed refreshes
Recover PDF previews after failed refreshes, clear previous pages on changed contents, and preserve scroll and zoom while releasing superseded resources. Co-authored-by: sotashimozono <shimozono-sota631@g.ecc.u-tokyo.ac.jp> |
||
|
|
53edf8ec4b |
fix: recover deleted Active Server defaults and preserve editor hosts (#25938)
Repair deleted Active Server defaults through validated registry reads and existing settings notifications. Capture editor ownership from backing files so workspace focus cannot redirect file operations. Correct two live-turn test fixture calls to the current API. Refs #21489. Historical session recovery remains separate work. Co-authored-by: Chen <zwq19980411@gmail.com> |