Commit Graph
12238 Commits
Author SHA1 Message Date
Duncan Ogilvie 4d0c346f87 fix(extensions): abort timed-out tool activations 2026-08-10 00:54:42 +02:00
Duncan Ogilvie c5c686ca11 fix(extensions): drain tool-call registrations 2026-08-10 00:24:11 +02:00
Duncan Ogilvie 550296a16f test(extensions): await credential error callback 2026-08-09 23:57:18 +02:00
Duncan Ogilvie 0abb4a9529 fix(extensions): preserve late tool invariants 2026-08-09 23:51:39 +02:00
Duncan Ogilvie 033c7a725e fix(extensions): preserve restricted tool isolation 2026-08-09 23:39:55 +02:00
Duncan Ogilvie 25dd58ed66 fix(extensions): skip settled registration barriers 2026-08-09 23:34:17 +02:00
Duncan Ogilvie f8c9d26186 fix(extensions): scope lifecycle registrations 2026-08-09 23:28:56 +02:00
Duncan Ogilvie 31333e438a fix(extensions): retain failed registrations for drain 2026-08-09 22:19:42 +02:00
Duncan Ogilvie 2c55e20365 fix(extensions): expose prompt refresh option 2026-08-09 22:13:11 +02:00
Duncan Ogilvie 05f17fa76f fix(extensions): make lifecycle registration atomic 2026-08-09 22:07:46 +02:00
Duncan Ogilvie fe1a615463 fix(extensions): reconcile lifecycle replacements 2026-08-09 21:44:45 +02:00
Duncan Ogilvie f544ddc6f6 fix(extensions): settle failed lifecycle registrations 2026-08-09 21:19:31 +02:00
Duncan Ogilvie 0fd55c3dce fix(extensions): type late MCP deduplication 2026-08-09 16:08:03 +02:00
Duncan Ogilvie 07c7fe0938 fix(extensions): stabilize late MCP collisions 2026-08-09 14:39:32 +02:00
Duncan Ogilvie 1592ca7a1d fix(extensions): preserve late tool activation 2026-08-09 14:13:57 +02:00
Duncan Ogilvie 19b428b757 fix(extensions): register lifecycle tools 2026-08-09 13:44:39 +02:00
can1357 45e12e5bb7 test(mnemopi): awaited detached shared-bank flush in lock-wait test
dispose({ timeoutMs }) legitimately detaches the consolidate pass when
the shutdown budget expires mid-flight (#3641), so asserting the shared
bank flush synchronously after dispose raced the detached pass on slow
CI runners (0 calls observed on run 31287979000). Signal the flush call
through a deferred and await it after releasing the lock; the bounded
<500ms return assertion is unchanged.
2026-08-09 03:39:03 +02:00
can1357 6fb07028fd chore: bump version to 17.2.12 2026-08-08 20:57:55 +02:00
can1357 d85dde52c4 fix(session): fence in-flight disk work behind the terminal seal
- seal() now runs before the final dispose close() and bumps the disk
  epoch: work an event handler enqueues while dispose awaits the closing
  tail is superseded, and an already-running fenced or authoritative
  rewrite fails its commit guard at the rename fence instead of
  publishing over a file a revival reopened.
- The authoritative repair path resets the disk tail itself, escaping the
  close() serialization, and would atomically publish the emptied entry
  list; it now no-ops once sealed and commit guards also check the seal.
- Execution-time gates cover the queued title persist and fenced rewrite
  callbacks; setSessionName/appendCustomEntry attempted by a handler that
  outlives dispose are dropped and covered by the seal regression, and a
  failed atomic batch across the seal can no longer truncate the file.
2026-08-08 20:49:28 +02:00
can1357 22b5ccd978 test(coding-agent): assert exact transcript contents in seal regression
- Replaced count-only assertions with the exact ordered message set:
  revival sees only the seed, the reread holds seed + post-revive, and
  the sealed manager's late persist text appears nowhere in the file.
2026-08-08 20:08:07 +02:00
can1357 63aa8cf6f8 fix(session): seal the manager at terminal release against revival races
- AgentLifecycleManager.park() resolves as soon as dispose() returns, so
  ensureLive() may reopen the same JSONL through a new manager while a
  timed-out event handler still holds the old one; a late append reopened
  a second writer on that file and the deferred finalize then closed it.
- A post-release rewrite was worse: it persisted the emptied entry list,
  truncating the transcript on disk.
- releaseRetainedEntries() now seals the manager: appends, title changes,
  and rewrites become dropped no-ops and the append writer is closed, so
  the deferred dispose pass is in-memory only and can never touch the file.
- File-backed regression: dispose on the drain deadline, revive the JSONL
  immediately, unpark the late handler, and prove the file is byte-stable
  and the revival writer owns it exclusively.
2026-08-08 20:04:09 +02:00
can1357 31d7655477 fix(agent): re-finalize dispose after the drain deadline
- The dispose drain deadline does not cancel in-flight event handlers: one
  parked in a slow extension hook resumed after close/release, reopened the
  append writer for its late persist, and repopulated the released state.
- Track whether the drain settled; on deadline, redo the final close +
  release once the pipeline genuinely settles (hook runtime is bounded by
  the extension runner).
- Expose drainTimeoutMs on AgentSessionDisposeOptions for bounded teardown
  paths and deterministic coverage of the deadline branch.
2026-08-08 19:54:33 +02:00
can1357 b9ddc81f06 test(coding-agent): assert dispose-persisted state from the reopened file
- PR #8004 dispose() now releases the session manager in-memory transcript;
  snapcompact-budget rebuilt a session over the closed manager and the exit
  diagnostics read released entries.
- Snapcompact reopens the persisted file for its replacement session; exit
  diagnostics move to disk-backed managers and assert the exit marker from a
  reopened manager, proving actual durability.
2026-08-08 19:50:59 +02:00
can1357 cab4c4520b test(coding-agent): reopen persisted session after terminal dispose
- PR #8004 made dispose() release the session manager in-memory transcript;
  three handoff tests reused the closed manager for a replacement session.
- Reopen the persisted session file via SessionManager.open, matching
  production revival paths.
2026-08-08 19:47:10 +02:00
can1357 f87a8ecc19 Merge PR #7994: fix(session): surface empty handoff generation as failure not cancel (@roboomp) 2026-08-08 19:38:32 +02:00
can1357 afa54d87f0 Merge PR #8002: fix(web-search): parse double-encoded Z.AI results (@roboomp) 2026-08-08 19:38:32 +02:00
can1357 b2d0ade665 fix(agent): finish session disposal after event drain 2026-08-08 19:38:32 +02:00
can1357 bca8d5281b Merge PR #8004: fix(agent): release parked subagent session memory on dispose (@roboomp) 2026-08-08 19:38:32 +02:00
can1357 ba8e4dabd4 Merge PR #8014: fix(tui): bound WSL idle animation CPU (@roboomp) 2026-08-08 19:38:31 +02:00
can1357 025c1c4df6 Merge PR #8023: fix(task): record subagent model performance (@roboomp) 2026-08-08 19:38:17 +02:00
can1357 7ca140f66e fix(ai): routed policy-rejected accounts through sibling rotation
- Added account-scoped policy error detection to correctly identify Codex cyber-policy rejections.
- Updated credential storage and retry logic to route denied accounts through sibling rotation instead of bypassing it.
- Ensured coding-agent sessions exhaust all sibling accounts before falling back on cyber denials.
- Added comprehensive test coverage for credential rotation and retry behavior on policy errors.
2026-08-08 19:29:49 +02:00
roboomp 506f57fbf2 fix(task): recorded subagent model performance
Shared the parent AgentStorage handle with isolated task settings while keeping setting overrides non-persistent.

Added regression coverage for task samples reaching the shared TPS/TTFT aggregate.

Fixes #8022
2026-08-08 15:59:27 +00:00
can1357 731c051733 feat(pi-shell/minimizer): implemented length threshold and empty preservation
- Add minimum character threshold to bypass minimization for short outputs.
- Add preserve-if-empty configuration and pipeline support for filters.
- Update test fixtures and integration tests to meet length thresholds.
2026-08-08 16:27:03 +02:00
can1357 a30cbbb75d feat(hashline): implemented syntax validation and veto checks for patch application
- Add a tree-sitter syntax probe and parser veto to prevent syntax-unaware boundary repairs.
- Reject span pastes from empty named registers instead of deleting ranges.
- Reject duplicate top-level snapshot row line numbers during parsing.
- Export new syntax module symbols and add associated tests and changelog updates.
2026-08-08 16:18:34 +02:00
roboomp a6aa462a60 fix(tui): bounded WSL idle animation CPU
- Removed the Loader backpressure cap so slow ConPTY paints retain the documented proportional duty cycle.

- Made WSL terminal-title working state static to avoid a second periodic OSC write loop.

Fixes #8012
2026-08-08 13:17:01 +00:00
roboomp 88021b90ea fix(agent): drained in-flight session event handlers on dispose
agent-core dispatches the session's event subscriber fire-and-forget (agent.ts #emit), so a message_end/agent_end handler can still be awaiting extension/subscriber/maintenance work — and its sessionManager/agent.state append — after agent.waitForIdle() resolves. The earlier settle waited only on the core run, so a late handler could append the finished message/entries back into the disposed session and re-pin the transcript.

Track every #handleAgentEvent dispatch in #inFlightEventHandlers and drain it (alongside agent.waitForIdle) inside the bounded settle before reset/clear/release. Added a regression test using a real extension whose message_end hook blocks before persistence, asserting dispose does not release memory until the in-flight handler settles.
2026-08-08 10:26:41 +00:00
roboomp a7a32f35dd fix(agent): settled active turn before clearing session memory
dispose() only *signalled* the agent loop via abort(); it never awaited the run, so a mid-turn dispose (Ctrl-C/timeout/hard-killed subagent) could let the loop unwind after the release ran — its response/SSE interceptors re-recording wire frames into rawSseDebugBuffer and its terminal message re-appending to agent.state.messages, repopulating the disposed session with exactly the retained state the release drops.

Detach the response/SSE interceptors and await a bounded agent.waitForIdle() before the reset/clear so it lands on a quiescent session. Added a deterministic regression test that gates the active turn and asserts dispose blocks on it before clearing.
2026-08-08 10:06:55 +00:00
roboomp 4ca6c376b4 fix(agent): detached append-only context on dispose
Disposed sessions remained reachable through lifecycle reviver closures. Agent.reset() cleared the live message array but left AppendOnlyContextManager attached, retaining its normalized provider transcript and stable prompt/tool prefix.

Detach the append-only manager during terminal disposal and extend the memory-release regression test to cover that second transcript copy.
2026-08-08 09:52:15 +00:00
roboomp ed6300b35e fix(agent): release parked subagent session memory on dispose
Keep-alive subagents are handed to AgentLifecycleManager.adopt, which stores
their reviver closure in the process-global #adopted map. The closure is
defined inside runSubagent's scope, which also captures the live AgentSession
(extension-runner callbacks, buildSubagentSessionOptions), so its lexical
environment pins the whole session graph. park() disposes and detaches the
session but leaves the adoption record indefinitely, and #doDispose never
dropped the in-memory transcript, session-manager entries, or the raw-SSE
debug buffer (whose trimmed records retain slice() views of full wire frames),
so every completed subagent's heavy state leaked for the process lifetime.

dispose() is terminal and every revival path reopens from disk, so #doDispose
now sheds retained conversation memory via agent.reset(),
RawSseDebugBuffer.clear(), and SessionManager.releaseRetainedEntries(). The
adoption record can still reference the session, but only as a husk.

Fixes #8003
2026-08-08 09:42:35 +00:00
roboomp a709a6604f fix(web-search): parsed double-encoded zai results
Decoded the extra JSON string layer returned by Z.AI MCP search and kept structured payloads out of answer text.

Added regression coverage for source extraction and plain prose preservation.

Fixes #8000
2026-08-08 09:04:04 +00:00
roboomp 7914e7c451 fix(session): preserved harness handoff abort reasons
Harness-initiated session aborts previously cancelled compaction before the handoff reason was recorded. The handoff catch then saw only an aborted signal and replaced the harness reason with "Handoff cancelled".

Abort the handoff first with the session reason, forward caller-signal reasons, and reserve "Handoff cancelled" for direct or unreasoned cancellation. Add a regression test for an in-flight handoff aborted through AgentSession.abort.

Fixes #7993
2026-08-08 09:02:28 +00:00
roboomp 92e574cb02 fix(session): surface empty handoff generation as failure not cancel
The #7904 fix stopped masking provider errors as "Handoff cancelled", but
an empty or whitespace-only generation still fell through: whitespace-only
text passed the `!handoffText` guard and produced a bogus handoff, while
empty text returned undefined which the interactive /handoff caller mapped
to "Handoff cancelled" with no detail and no log entry.

Treat empty/whitespace-only output as a real failure: a user-initiated
handoff throws "Handoff generation produced no content" (surfaced as
"Handoff failed: ...") and logs it; auto-handoff keeps returning undefined
so maintenance falls back to context-full compaction. Also log genuine
handoff failures in the command controller so they persist for debugging.

Fixes #7993
2026-08-08 08:21:16 +00:00
can1357 7cebe901b7 refactor(coding-agent): narrowed over-exported internal symbols
- 28 symbols across discovery, mcp header policy, agent-hub projection and
  rendering, the agent registry, shell tokenizing and changelog comparison
  were exported but referenced only inside their own module; they are now
  module-private, shrinking the deep-import surface.
- Kept AGENT_PLUGIN_MANIFEST_SCHEMA, AGENT_PLUGIN_MCP_SCHEMA,
  parseAgentPluginManifest, clearAgentPluginRootCache and mergeMCPHeaders
  exported: each is a seam for tests that defend real parsing or header
  precedence behavior.
- Nothing reachable from an explicit exports entry or public barrel changed.
2026-08-08 06:32:01 +02:00
can1357 cd4e04e8ee refactor(coding-agent): reduced lsp index to a composition barrel
- src/lsp/index.ts is the explicit ./lsp package entry, yet held 2821 lines of
  warmup, config caching, diagnostics, external build-command workspace
  diagnostics, the writethrough batching subsystem and the LspTool class.
- Those are now servers, diagnostics, workspace-diagnostics, writethrough and
  tool modules; index.ts is 22 lines and re-exports the same public surface.
- configCache and writethroughBatches remain single instances and every tuned
  diagnostics timing constant moved verbatim.
2026-08-08 06:32:01 +02:00
can1357 0f3e45f07a refactor(coding-agent): extracted model registry helper modules
- Moved the module-level machinery that sat in front of the ModelRegistry
  class into model-config-values, model-patch, custom-models and
  model-provider-discovery; model-registry.ts drops 646 lines.
- commandValueCache and its negative-cache TTL stay single instances, so the
  execSync storm the cache exists to prevent cannot return.
- The setCodexAttestationProvider import-time side effect stays in
  model-registry.ts. The class itself was left alone: its private state is
  shared across the methods, so splitting it is not a straight move.
2026-08-08 06:32:01 +02:00
can1357 e0ec404de6 refactor(coding-agent): split theme module by responsibility
- theme.ts mixed symbol presets, JSON schema, color math, the Theme class,
  loading, global state, appearance handling and TUI adapters in 3171 lines.
- Symbols, schema, color, theme-class, loader and tui-adapters are now
  siblings; theme.ts keeps global state, the watcher, appearance handling and
  HTML export at 745 lines, with all 44 exports intact.
- Left appearance and export-colors in place: both read private mutable
  auto-theme state, so extracting them would have required new exported
  internals or DI rather than a straight move.
2026-08-08 06:32:01 +02:00
can1357 0697e7f688 refactor(coding-agent): split secret obfuscator into domain modules
- Separated deterministic replacement generation, placeholder derivation,
  placeholder-range scanning and message-tree transforms out of the 2647-line
  module; obfuscator.ts now holds the types and SecretObfuscator.
- ephemeralPlaceholderKey stays a single instance and both global regexes stay
  beside the code that resets their lastIndex, so placeholder stability and
  the security argument in the moved comments are preserved verbatim.
- Repointed every importer at the real modules rather than leaving a re-export
  shim; the public ./secrets barrel exports the same 15 names as before.
2026-08-08 06:32:01 +02:00
can1357 cafe52cd9f refactor(coding-agent): split github tool into domain modules
- gh.ts held wire types, search, Actions run-watch, PR checkout/push/create,
  PR diff parsing and view fetch/format in 3958 lines.
- Split into gh-types, gh-search, gh-run-watch, gh-pr-checkout, gh-pr-diff,
  gh-view and a gh-common module holding the shared primitives and the single
  process-lifetime default-repo memo pair; gh.ts is now 246 lines.
- All 22 exports stay on gh.ts because tools/index.ts star-exports ./gh, so
  the issue:// and pr:// protocol handlers needed no edits.
2026-08-08 06:32:01 +02:00
can1357 7454b6e78f refactor(coding-agent): split read tool into per-source modules
- ReadTool mixed plain-file reading with archive, sqlite, pdf-image, summary,
  selector, formatting and renderer concerns in one 3763-line module.
- Each now owns a sibling module; read.ts drops to 2020 lines and keeps its
  public exports, including the readToolRenderer re-export required because
  tools/index.ts star-exports ./read through the explicit ./tools entry.
- The pdfImageExtractions map and summaryParseCaches WeakMap stay single
  instances; execute() was deliberately left intact.
2026-08-08 06:32:01 +02:00
can1357 8cf3dce6fb refactor(coding-agent): split builtin slash commands by domain
- builtin-registry.ts held a 2341-line array of every command spec with its
  handler inlined, plus the autocomplete builders and the TUI dispatcher.
- Specs moved verbatim into modes, collaboration, session, lifecycle,
  marketplace and control modules; completions moved to builtin-completions.
- builtin-registry.ts is now a 174-line composition/dispatch module and keeps
  all 16 exports. Command order is unchanged, which matters because it drives
  autocomplete ordering and BUILTIN_SLASH_COMMAND_DEFS.
2026-08-08 06:32:01 +02:00