Adds an `on-exit` cron schedule kind: a job fires once when a watched command/process
exits, via gateway ProcessSupervisor exit watchers. Covers CLI (`--on-exit`/`--on-exit-cwd`),
tool/protocol schema, RPC list-filter, Control UI + macOS read-only display, SQLite
round-trip, and origin-aware wake routing. Restart-safe one-shot (persists completion
before firing); platform-aware shell; bounded watched-command execution.
Squashed from 22 iterative commits for a clean rebase onto current main.
Fold a contiguous run of in-flight stream/reading-indicator items into one
assistant group (one avatar/footer, segments stacked as bubbles) so a segmented
streaming reply no longer flashes a separate avatar+footer per segment.
Render-layer only: the shared ChatItem types and build-chat-items.ts are
untouched, and a message/group/divider breaks the run so interleaved tool calls
keep their own groups.
Refs #63956
AI-assisted (Claude Code).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Keyed preamble commentary was appended after the whole tool loop, so it relied solely on the final visible-time sort for placement and lost the insertion-order tiebreaker against tool cards. Splice each keyed commentary segment into the items list before the first item with a strictly-later timestamp, so a preamble that arrived before a later tool renders above that tool while the run is live (not only after final materialization). Tools sharing the commentary timestamp that are already visible stay above it. Adds a buildChatItems regression covering a keyed preamble between two tools.
Add a per-viewer 'Keep commentary' toggle (UiSettings.chatPersistCommentary,
default true) that controls whether keyed Codex preamble/commentary blocks
stay after the final answer or clear with it.
- Persist (default): keyed commentary materializes as durable blocks, current
behavior, existing proof unchanged.
- Transient (toggle off): commentary stays live during streaming but is never
materialized, so it disappears as the final message arrives. This is the
transient-only behavior from #92236, now user-selectable instead of a
maintainer-level either/or policy choice.
Single gating point in materializeVisibleStreamState (skip itemId-keyed parts
when persistCommentary is false); threaded from settings through the chat
event handler. Adds desktop + mobile header toggles and an en.ts label
(locale bundles regenerated via ui:i18n:sync, English fallback).
Tests: reconciliation persist/transient coverage, final-event handler honors
the setting, settings round-trip + header button assertions updated.