* feat(agents): default to eager delegation in the main session
The delegation prompt section now defaults to "prefer" in each agent's
canonical main session and "suggest" elsewhere; explicit config wins in
both directions. The section is rewritten to be token-leaner and now
covers the hidden-vs-visible spawn distinction (hidden subagents are
invisible and auto-archived; deliverable-bearing work spawns
visible=true and replies with the link) plus the run-end notification
contract. The Messaging spawn-mechanics line is suppressed when the
Delegation section renders, and the stable Tooling visible:true hint is
aligned with the new guidance.
* perf(sessions): coalesce session-state wake bursts
Watched-session change notices woke the watcher's main session with the
generic 250ms heartbeat coalesce, so a burst of changes across several
watched sessions produced one wake per change. Wakes now coalesce for
20s; notices are already queued and deduped, so none are lost.
* test(agents): regenerate prompt snapshots after rebase
* test(sessions,agents): update sibling assertions for coalesced wakes and visible guidance
session-state-events tests advance timers past the new 20s wake
coalesce window; the sessions_spawn schema test tracks the updated
visible description.
* fix(gateway): stop terminal PTYs on session archive
Bind agent terminals to the durable session incarnation, drain exact ownership during archive, and terminate every job-control process group in the PTY session.\n\nCloses #125769
* test(gateway): cover terminal cleanup on archive
* test(gateway): align terminal outcome assertions
* test(gateway): preserve session exports in invoke test
* fix(gateway): await terminal exit before archive
* test(codex): consolidate supervised instruction coverage
Move the duplicated two-attempt regression into the canonical thread lifecycle test so the exact two-worker extension shard stays bounded on low-core CI.\n\nRelated: #125783
The Applied view rendered every applied proposal as an independent recency-sorted row, so one skill's revision history looked like duplicate skills. Applied now shows one row per live skill with a revision-count badge and grouped tab count; the detail pane gains a lazily loaded History section with operations from the ledger's recorded kind. Pending/rejected/quarantined/stale keep one row per proposal.
Fixes#125632
Two landed-PR races left main red (run 32131816085):
- #125692's rewritten markdown-tables dialog test drives every interaction
through direct handler calls except the reopen, which used a raw
expand.click() that only worked via a leaked cross-file document listener
in the shared lane; #125766 moved the file to the isolated lane where no
such listener exists. Reopen now uses the file's own handler idiom.
- Accumulated startup-JS creep from landed features put main 50 B over its
gzip budget (344395 B vs 343289 B baseline + 1056 B tolerance). Baseline
updated to main's CI-measured bytes via the script's --update-baseline
contract.
* fix(tts): normalize streamed speech and report fallbacks
Streamed Discord voice now strips Markdown and raw link destinations like buffered synthesis.
Record the first failed streaming provider and reason code before successful file fallback.
* test(ui): stabilize reconnect retry timing
Drive the retry deterministically by advancing the exact 100 ms fake timer while preserving the ordering and outbox proof. Repairs CI run 32110130614 job 95627654678.
* fix(discord): ignore expected TTS stream skips
Address the ClawSweeper finding by retaining warning metadata only for actual failed attempts. Expected unsupported-for-streaming skips stay silent while real streaming failures still warn after successful file fallback.
* test(ui): settle interaction state before assertions
Repairs CI run 32117859632 by invoking the markdown table handler directly with a targeted event and waiting for durable IndexedDB composer state before navigation or page close.
* test: remove incidental UI and process identities
CI run 32120793258 exposed two owner-specific test assumptions.
Preserve the child-session hover visibility and pointer assertions, then focus and activate its menu with Enter so an unrelated delayed hovercard cannot intercept the pointer.
Assert the exact owned descendant PID is dead when QA cleanup completes instead of checking a globally reusable PGID.
* test(ui): stabilize connected send retry timing
CI run 32125741781 exposed the wall-clock race in the connected retry test.
Skill proposals created in ephemeral session worktrees targeted the throwaway copy — applied autolearn updates were silently lost — and a missing durable draft failed the whole skill_workshop list action. The session producer now records the selected canonical workspace (projectRoot/requestedCwd/agent workspace) when it swaps cwd into a managed worktree, capture and the workshop tool prefer that recorded fact, persisted pre-upgrade session records are backfilled idempotently at the startup migration owner, and missing-draft proposals render a degraded state with actionable inspect/apply errors instead of dead-ending.
Fixes#125653
* feat(gateway): reach a Gateway behind an identity-aware proxy from the CLI
Operator CLI surfaces (tui, attach, call, probe, onboarding, and configure) can present configured gateway.remote.edgeAuth headers to an identity-aware proxy. Headers are origin-bound, WSS-only, never follow redirects, cannot set transport-owned headers, and are redacted. Identity-proxy upgrade rejections are classified and remediated instead of being reported as an unreachable gateway.
* test(gateway): cover config-aware probe calls
Keep status probe expectations aligned with the resolved configuration forwarded for origin-bound edge-auth resolution.
* fix(gateway): preserve edge auth across wizard saves and enforce wss before secret resolution
Preserve gateway.remote.edgeAuth only when the configured Gateway scope is unchanged, and reject non-WSS targets before resolving any edge-auth SecretInput.
The gateway models.list auth resolver passed an empty external-CLI scope, so models pinned to CLI runtimes (claude-cli, gemini-cli, minimax) showed 'Sign-in needed' in the Control UI picker while the same gateway executed turns on them fine. Availability now derives the overlay scope from config via resolveExternalCliAuthScopeFromConfig — the same scoping the execution path uses — so the picker matches what the gateway will run. A configured CLI-runtime model is an operator declaration of that CLI's login; with no ambient CLI login the row still reads signed-out.
* fix(tts): report actual audio output formats
Detect local CLI audio containers before conversion and derive ElevenLabs delivery metadata from the effective output format. This prevents MP3 or PCM bytes from being persisted or delivered under incompatible file and voice metadata.
* fix(tts-local-cli): distinguish Ogg Opus audio
Treat Ogg as a source container unless its first packet is OpusHead, so Vorbis and M4A inputs are transcoded to the requested target instead of being mislabeled as native voice audio.
* fix(tts-local-cli): validate MP3 frame headers
Require valid MPEG version, layer, bitrate, and sample-rate fields before treating sync bytes as MP3, preventing reserved headers from bypassing conversion under MP3 metadata.
* fix(tts-local-cli): validate tagged MP3 output
Require structurally valid ID3v2 metadata followed by a strict MPEG frame, and stop trusting recognizable file extensions when their bytes do not match. Unknown or malformed output now fails closed instead of bypassing conversion.
* fix(tts-local-cli): accept free-format MPEG audio
Treat bitrate index zero as valid free-format MPEG audio and lock the ID3v2.4 footer offset contract with focused regression coverage.
* fix(tts-local-cli): normalize all Ogg output
Treat Ogg only as a source container and always transcode it to the requested target, so malformed or ambiguous Ogg bytes can never bypass conversion as native voice audio.
* fix(media): surface silent attachment failures
Missing structured images now produce a visible model-facing system note, failed playback transcodes warn once per source identity, and bodyless successful media responses are rejected instead of stored as empty files.
* fix(media): persist failed image outcomes
* fix(media): skip note lookup without failures
* refactor(ui): extract chat pane rail state
chat-pane-render.ts sits at 702/700 effective max-lines on main, latently
failing the lint lane for any change that pulls it into scope. Extract the
rail/sidebar-slot model building into chat-pane-rails.ts (behavior-neutral)
to heal it without a suppression.
* test(ui): isolate markdown-tables from shared module graph
The shared UI runner reuses its module graph across jsdom registries; when
another worker file evaluates markdown-tables.ts unmocked first, this file's
clipboard vi.mock can bind to a stale instance and the copy spy records zero
calls (checks-ui failures on PR #125668). The test belongs on the canonical
singleton-sensitive list, which exists for exactly this class.
Accepted-unconfirmed steer cleanup re-resolved the reply operation by
session key after unbounded awaits, so a same-key successor admitted in
the window could be aborted instead of the run that actually accepted
the injection. Capture the exact reply operation and embedded-run abort
authority before queueing and abort only those, per the
delegated-run-authority invariant (correlation ids never carry abort
authority across awaits).
Regression: successor admitted mid-await is never aborted; the captured
operation is.
* fix(ui): align session owner filtering terminology
Owner filters now use owner terminology consistently across the Gateway, Control UI, and generated native protocol clients. The old terms were prerelease-only, so no migration or compatibility aliases are needed.
* fix(gateway): preserve creator provenance filtering
Keep creatorId scoped to immutable createdActor provenance while ownerId queries the current projected assignable owner. This preserves independent creator and responsibility filtering after reassignment.
* test(ui): wait for remembered draft before reload
CI exposed an IndexedDB draft-persistence race: the test reloaded before the write completed and then re-filled the message while restoration arrived. Wait on the canonical durable-draft helper before reload so the reload cannot race the write.
* fix(ui): keep session menus clear of hovercards
Horizontal sidebar previews were positioned from the inner link rect instead of the complete session row, allowing the hovercard to overlap trailing actions.
Anchor preview placement to the full row while preserving the link trigger and ownership flow so the session menu remains clickable.
* perf(ui): keep hovercard fix within startup budget
Use the sidebar direct-row invariant to anchor the hovercard outside trailing actions without a selector lookup. This preserves the fix without increasing the startup budget.
* fix(qa): preserve Matrix CLI timeout precedence
Keep the primary timeout operator-visible when descendant settlement also reports secondary cleanup trouble. Preserve the complete AggregateError as the public wrapper cause so settlement evidence remains inspectable.
* test(ui): isolate Markdown table clipboard boundary
With isolate:false, the shared module registry can retain clipboard imports before this test installs its module mock, leaving the spy disconnected from the production call.
Stub navigator.clipboard.writeText instead so the test exercises the real copyToClipboard boundary regardless of module load order, then restore the original descriptor.
Skill Workshop could autonomously rewrite or delete handwritten skills and discarded its own drop rationale. Workshop mutations are now gated on ledger-derived ownership (applied create proposals; unknown provenance fails closed to user-owned read-only), ownership claims end at drop and return on restore with failure-safe ordering, legacy backups stay restorable, every reconcile outcome persists with drop reasons in an additive table, and a bounded skill_workshop history action exposes them. Adopt/disown opt-in: #125711.
Co-authored with @jalehman (original ownership design).
Fixes#125652
* fix: show sessions waiting for concurrency slots
* test: align queued session integration fixtures
* test: distinguish queued and reactivated followups
* fix: preserve queued state in workboard and android
* fix: project queued status through chat history
* test(ui): keep queued sidebar case under line cap
Autonomous self-learning captured junk (transcript fragments, resolved transients, one-off narratives). The experience-review and history-scan prompts now carry an explicit never-capture exclusion list from one shared constant; user-invoked /learn intentionally keeps its own bar, protected by a negative prompt assertion. Operator-notice follow-up: #125723.
Fixes#125710
Keep orphan reconciliation progressing when a preserved checkout has dangling linked-worktree metadata, while leaving snapshot safety checks unchanged.\n\nRefs #125719
* improve(ui): standardize fenced code collapse and wrapping
Every Markdown code fence now shares one behavior instead of the JSON-only
40-line disclosure: blocks longer than seven lines show a seven-line preview
with the exact hidden-line count, horizontal overflow stays inside the fence,
and a reversible wrap control appears only once a line measurably overflows.
User fences keep the bubble's own typography and carry no code chrome.
The collapsible markup is emitted only for hosts that own the code-block
lifecycle (codeBlockInteraction: interactive), and that lifecycle collapsed to
one click owner plus one initializer so a host cannot wire half of it. The chat
thread, detail panel, and caretaker surface all run it; the skills preview gets
the click owner its copy button had never been given. The scan is deferred and
coalesced because a Lit element ref commits before that render's children, and
detached ResizeObserver targets are released before each pass.
Also adds the interaction mode to the markdown cache key so a static host cannot
serve interactive markup from a sibling render, and drops the stylesheet rules
and selector guards the removed json-collapse markup owned.
* fix(ui): satisfy fenced code checks