The #5800 drain guard suppressed the abort-finally stranded-message
drain while the session was disconnected from the agent event stream.
newSession/switchSession drop the agent queues on transition, so nothing
is lost there. compact() preserves the queues and only reconnected in
its finally — it never re-drained — so a steer/follow-up arriving during
compaction (async IRC, an xd:// mount notice, an SDK steer) stayed
stranded until the next explicit prompt.
Re-drain in compact()'s finally after #reconnectToAgent (and after the
compaction AbortController is cleared, so isCompacting is false and the
scheduled agent.continue() actually runs). Added a regression test that
queues a follow-up mid-compaction and asserts it resumes.
Fixes#5800
- Updated schema and runtime paths to use `dev.autoqaConsent` and `todo.remindersMax`, including auto-QA consent reads/persistence and todo reminder limit checks.
- Adjusted settings expectations so obsolete BM25-discovery keys were dropped on load and `tools.xdev` now kept its default unless explicitly set.
- Added/updated tests for the setting key migration and refreshed issue-consent flows, plus a new `refreshMCPTools` test for steered `xdev-mount-notice` updates without prompt rebuilds.
- Renamed the `find` and `search` tools to `glob` and `grep` respectively across the codebase to improve command clarity.
- Implemented full-stack support for the renamed tools, including CLI arguments, system prompts, SDK exports, and tool registration.
- Added automated migration logic in `settings` to transform legacy `find` and `search` configuration keys to their new equivalents.
- Updated the `collab-web` renderer registry to ensure backwards compatibility with legacy tool outputs.
The preserveCompaction abort path skipped abortCompaction() entirely,
so a manual /compact starting while auto-compaction was in flight no
longer cancelled it. Both passes could then appendCompaction/
replaceMessages, double-rewriting history (reachable via the RPC/
extension compact paths, whose only guard checks #compactionAbortController).
Preserve the just-installed manual controller but still abort the
auto-compaction controller. Adds regression coverage.
Install the manual compaction abort controller before abort teardown so input routing observes session.isCompacting during the starting window.
Fixes#3485
Active-goal threshold compaction can pre-empt the normal post-turn tail
and return once it schedules a deferred handoff or auto-continue. When
that turn is the successful response from an auto-retry, returning there
skips the later retry-gate cleanup and leaves isRetrying stuck.
Resolve the completed retry gate before the compaction-continuation
return, and cover the retry-success-over-threshold path so future
changes cannot strand prompt()/waitForIdle() behind a stale retry state.
Refs #3174
Codex review on #3175: the active-goal compaction pre-empt I added in
8ab754f636 short-circuited #handleEmptyAssistantStop. That handler is
the only path that strips an orphan toolUse assistant (stopReason
"toolUse" with no toolCall block) from both active context and the
session branch via #removeEmptyStopFromActiveContext. With the pre-empt
ordering, an over-threshold goal turn that returned an empty toolUse
left the orphan as the session leaf, and the compaction auto-continue
prompt fed it back into the next Anthropic turn as a tool_use with no
matching tool_result — the exact history-corruption pattern the
existing cleanup comment defends against.
Move #handleEmptyAssistantStop back ahead of the active-goal
compaction probe. Empty stops still self-retry and never reach the
threshold pre-empt; non-empty stops (the reporter's failure in #3174)
still hit threshold maintenance before the unexpected-stop classifier.
Regression test seeds a goal-mode empty toolUse stop billed at 91k
against thresholdTokens 76384 and asserts the threshold compaction
never starts and the orphan is no longer in the session branch.
Fixes#3174
Active goal turns that stopped with text could hit the empty/unexpected-stop
continuation guards before threshold maintenance. When those guards scheduled
another goal turn, #checkCompaction never ran, so no auto_compaction_start was
emitted even while visible context stayed above thresholdTokens.
Run threshold maintenance once before those active-goal self-continuations and
log the threshold decision inputs: billed context, stored estimate, resolved
trigger tokens, post-maintenance tokens, strategy, threshold, promotion state,
and shouldCompact.
Also pass post-prune maintenance tokens into the shake recovery-band check so
supersede/drop-useless savings are preserved when deciding whether shake still
needs to fall back to context-full compaction.
Fixes#3174
Pruning frees bytes for the NEXT prompt — it does not change the size of
the prompt the LLM just billed for. Subtracting the per-turn
`#pruneStaleToolResults` / `#pruneToolOutputs` savings from the
threshold input let a long-running `/goal` session sit above
`compaction.thresholdTokens` indefinitely: the visible context
(anchored to the same provider billing) showed >threshold, but
`shouldCompact` no-op'd because the subtraction dropped the input below
the trigger. The `compactionContextTokens` floor against the post-prune
local estimate is still applied, so a payload-compression hook still
can't deflate the trigger.
Regression test seeds one large `useless` tool result whose suffix sits
inside the 8k cache-warm window so `#pruneStaleToolResults` actually
returns ≥20k savings, then asserts compaction fires when the final turn
bills 91k tokens against the reporter's `thresholdTokens: 76384`.
Fixes#3174
Run threshold compaction maintenance when an active goal turn ends through a successful yield, while preserving the final-yield skip for non-goal completions.
Fixes#3146
Fix all Windows-specific test failures caused by path handling problems
and EBUSY errors from unclosed SQLite database handles.
Root causes fixed:
1. POSIX path assumptions: replaced hard-coded file:///tmp, /repo, etc.
with pathToFileURL/path.resolve/path.join computed expectations
2. shortenPath() now normalizes backslashes to forward slashes after ~
and respects home directory boundaries
3. HistoryStorage.resetInstance() leaked its Database — added #close()
that finalizes all prepared statements and closes the DB
4. AgentStorage gained the same resetInstance()/#close() pattern
5. SqliteAuthCredentialStore.close() leaked one-off prepared statements
from inline this.#db.prepare() calls — wrapped each in try/finally
6. model-cache.ts used a process-global DB even for custom dbPath —
now opens/closes per-call via withModelCacheDb
7. createAgentSession leaked AuthStorage on construction failure —
added ownsAuthStorage cleanup in catch block
8. MnemopiBackend.removeDbFiles() now truly best-effort (catches errors)
9. TempDir retry window expanded from 4x10ms to 40x25ms
10. TempDir prefix convention: non-@ prefixes created dirs relative to
cwd instead of os.tmpdir() — all test temp dirs now use @ prefix
11. Shell-escaped interpolated paths in bash tool tests
12. git core.autocrlf false in autoresearch test repo init
All 522 previously-failing Windows tests now pass.
Two defects dropped the first steering/follow-up message typed as
auto-compaction began:
- The compaction AbortController (which backs isCompacting) was installed
AFTER auto_compaction_start was emitted. The emit awaits extension
delivery and yields to the event loop, so a message typed as the loader
appeared was read while isCompacting was false and mis-routed into the
core agent queue (which the handoff reset then wiped). Install the
controller before the emit, and move the emit to the first statement
inside the existing try so the catch/finally cleanup still runs, in both
#runAutoCompaction and #runAutoShake. The handoff branch now passes the
run's local abort signal instead of the mutable controller field, so a
superseded run bails at the handoff entry check rather than resetting the
session.
- handoff() calls agent.reset(), which clears the core steering/follow-up
queues. Capture both queues immediately before reset and restore them
immediately after (synchronous, no await gap), so queued steers and
follow-ups -- including in-flight RPC/SDK steer()/followUp() and a hidden
user companion such as an ultrathink notice -- survive the new-session
reset instead of being silently dropped.
Adds regression tests for both defects (controller-before-emit for the
context-full and shake paths, queue preservation across the reset for both
pre-enqueued and in-flight messages).
The continue() mock used mockResolvedValue(), which never consumed the
queued follow-up. After threshold compaction, #endInFlight ->
#drainStrandedQueuedMessages -> #scheduleQueuedMessageDrain reschedules a
zero-delay continue whenever messages remain queued, so the no-op mock spun
the drain into an unbounded microtask loop that allocated until the test
worker OOM'd (~101GB) and segfaulted, taking sibling tests down with it.
Mirror real continue() semantics (it polls and consumes the queue) by
clearing the queues in the mock, matching the sibling steer-idle-drain
tests. The drain now settles after one resume.
Move bundled models, model cache/manager, thinking metadata, effort helpers,
provider descriptors/discovery, wire constants, and model identity utilities
into the new @oh-my-pi/pi-catalog package.
Update pi-ai to keep provider runtime/auth concerns, move catalog provider
metadata into CATALOG_PROVIDERS, and migrate coding-agent, agent, stats, docs,
and tests to import catalog values from pi-catalog.
Split coding-agent model registry helpers into discovery, roles, and models
config modules while preserving registry orchestration.
BREAKING CHANGE: @oh-my-pi/pi-ai no longer exports catalog subpaths such as
/models, /model-cache, /model-manager, /model-thinking, /effort,
/provider-models*, discovery helpers, and provider wire constants; use the
matching @oh-my-pi/pi-catalog subpaths instead.
- Empty `content: []` triggered the empty-stop guard, short-circuiting the agent_end handler before compaction checks ran.
- Replaced with a minimal text turn so auto-compaction queue resume tests complete under fake timers.
- Converted systemPrompt APIs and state types to ordered `string[]` across agent, AI, and coding-agent surfaces.
- Added `normalizeSystemPrompts` and applied it to context normalization before building provider request payloads.
- Updated AI providers to emit separate normalized prompt blocks/messages instead of a single merged system prompt.
- Removed dedicated `projectPrompt` state and remapped that context into system-context buckets in session, dump, and token accounting.
- Aligned tests and changelogs to pass and assert `systemPrompt` as arrays with ordered prompt semantics.
- Removed `id` fields from todo models/fixtures and switched session clones to content-based task identity.
- Replaced `/todo_write` `replace` with `init`, updated setup schemas to `list`/`phase`, and append content-only items.
- Updated `/todo` command flows to match phases and tasks by names/content (exact/prefix/substr, case-insensitive), with no ID targeting.
- Updated rendering/output labels to `# Todos`, `formatPhaseDisplayName`, and Roman-numeral phase headings across todo views.
- Aligned prompts, changelog, and todo tests/fixtures with the new init and content-based todo-write contract.
- Added `attribution` option to `PromptOptions` for controlling billing and initiator attribution.
- Updated subagent prompts to explicitly set `attribution: "agent"` for accurate billing attribution.
- Refactored message attribution logic to use configurable `promptAttribution` with fallback defaults.
- Added test coverage for `attribution` option in subagent reminder prompts.
Fixes#439.
feat(coding-agent): added attribution option and explicit session directory control
- Added `attribution` option to `PromptOptions` for explicit billing and initiator attribution control.
- Updated `SessionManager.create()` to require both `cwd` and `sessionDir` parameters for explicit session directory control.
- Changed session directory naming for temporary working directories from `--` format to `-tmp-` prefix.
- Made `cwd` and `sessionDir` fields mutable in SessionManager to support session relocation.
- Fixed automatic migration of legacy session directories to new `-tmp-` prefixed naming scheme.
- Updated all test fixtures to pass both `cwd` and `sessionDir` parameters to `SessionManager.create()`.
- Added `close()` method to SessionManager and AuthStorage for proper resource cleanup and finalization of prepared statements.
- Added `initiatorOverride` option support in OpenAI and Anthropic providers for message attribution control.
- Fixed resource leaks in RpcClient timeout handling by centralizing timeout creation with unref() and adding explicit clearTimeout() calls.
- Fixed AgentSession disposal to call SessionManager's `close()` method for guaranteed resource cleanup instead of fallback flush.
- Updated all test suites to properly dispose AuthStorage instances in cleanup hooks to prevent resource leaks between tests.
- Removed Kagi Universal Summarizer integration from fetch tool and YouTube scraper.
- Removed `fetch.useKagiSummarizer` configuration setting from settings schema.
- Simplified renderHtmlToText() and renderUrl() functions by removing Kagi summarization fallback logic.
- Fixed indentation inconsistencies in test files from tabs to spaces.
- Added public `waitForIdle()` and `getLastAssistantMessage()` APIs to AgentSession for deterministic session state access.
- Refactored deferred continuation scheduling from raw `setTimeout()` to centralized post-prompt task tracking system for concurrent recovery operations.
- Fixed race conditions between deferred TTSR/context-promotion continuations and `prompt()` completion via shared recovery orchestrator.
- Replaced `#waitForRetry()` with `#waitForPostPromptRecovery()` to unify retry and TTSR resume gate handling.
- Consolidated @oh-my-pi/pi-utils subpath imports into single package root import across 100+ files.
- Moved tryParseJson utility from local web scrapers module to @oh-my-pi/pi-utils package for centralized JSON parsing.
- Renamed loadSkillsFromDir to scanSkillsFromDir and refactored skill discovery to use fs.promises.readdir instead of glob-based approach.
- Replaced custom parseJSON with tryParseJson across discovery modules for consistent error handling.
- Removed emitCustomToolSessionEvent method and cleanupSshResources function, consolidating shutdown logic into dispose method.
- Updated glob pattern construction to use GlobBuilder with literal_separator(true) for improved path handling.
- Added getTodoPhases() and setTodoPhases() methods to ToolSession API for in-memory todo phase management.
- Added getLatestTodoPhasesFromEntries() export to retrieve todo phases from session history entries.
- Changed todo state management from file-based (todos.json) to in-memory session cache with automatic persistence.
- Changed todo phases to sync from session branch history during branching and rewriting operations.
- Removed file-based todo loading logic and replaced with session-based todo phase retrieval throughout codebase.
- Added ModelManager API with createModelManager() factory for managing bundled and dynamically discovered models with configurable refresh strategies.
- Exported discovery utilities for fetching models from Antigravity, Codex, Cursor, Gemini, and OpenAI-compatible endpoints with provider-specific model manager configuration helpers.
- Renamed public API functions for clarity: getModel() -> getBundledModel(), getModels() -> getBundledModels(), getProviders() -> getBundledProviders().
- Added on-disk model caching with TTL-based invalidation and resolveProviderModels() function for runtime model resolution with source precedence.
- Refactored model discovery script to dynamically fetch models from Codex, Cursor, and Antigravity using OAuth credentials instead of hardcoded lists.
- Extracted directory path utilities from multiple packages into a centralized '@oh-my-pi/pi-utils/dirs' module.
- Moved 30+ path helper functions (getAgentDir, getConfigRootDir, getPluginsDir, getMCPConfigPath, etc.) from scattered locations into a single shared utility module.
- Consolidated APP_NAME, CONFIG_DIR_NAME, and VERSION constants into the centralized dirs module for reuse across packages.
- Updated 70+ import statements across packages/ai, packages/coding-agent, packages/stats, and packages/tui to use the new centralized module.
- Removed local path construction logic and replaced with utility function calls for improved maintainability and consistency.
- Deleted packages/coding-agent/src/extensibility/plugins/paths.ts as its functions were moved to the centralized dirs module.