- Added `providerRetryWait` and `retryWait` hooks to stream/usage options so tests bypass real scheduler delays.
- Parameterized GitHub Copilot poll intervals and Copilot model retry base delay for fast test execution.
- Replaced `Bun.sleep`/`setTimeout` polling loops with `AbortSignal` event listeners in agent session tests.
- Consolidated auth-gateway E2E helpers into a shared `test/helpers` module, eliminating duplicated `checkGatewayAvailable` implementations.
- Migrated credential-disabled tests from SQLite-backed stores to an in-memory store, removing temp-dir lifecycle overhead.
- Migrated per-object caches (chat/tool starts, model fingerprints, validation contexts, provider indexes, render IDs) from WeakMap to Symbol-keyed properties on the objects themselves.
- Rewrote SSE debug tee as a single-pass inline parser, eliminating the body.tee() + readSseEvents re-parse pipeline.
- Refactored MockModel from a factory function + external WeakMap state into a self-contained class.
- Added FIFO memoization caches for heuristic candidate expansion and namespace suffix lookups.
- Added bucketed emoji dataset with prefix-indexed lookup for O(log n) suggestions.
- Implemented `:name:` inline replace that fires on closing colon without popup.
- Wired emoji suggestions and completions into PromptActionAutocompleteProvider.
- Extended AutocompleteProvider interface with trySyncInlineReplace hook.
- Added `dev.autoqa.consent` setting and single-flight popup handler wired through `InteractiveMode`.
- Added `flushGrievances` to batch-POST unpushed rows to `dev.autoqaPush.endpoint` with cooldown and single-flight deduplication.
- Added `omp grievances push` subcommand with TTY progress bar for manual draining.
- Migrated shared DB logic to `openAutoQaDb` (with `pushed` column migration) exported from `report-tool-issue`.
- Wrapped initial and subsequent print-mode prompts with `logger.time` for timing instrumentation.
- Printed collected timings after session run when `PI_TIMING` env var is set.
- Added `omp auth-gateway serve/token/status` — a forward-proxy injecting broker credentials for OpenAI Chat, Anthropic Messages, and OpenAI Responses wire formats.
- Added `GET /v1/usage` to auth-broker and auth-gateway; usage cache switched to 5-min per-credential TTL with jitter and last-good fallback on failure.
- Added `AuthStorage.setConfigApiKey/removeConfigApiKey/clearConfigApiKeys` so `models.yml` `apiKey` beats OAuth tokens without overriding `--api-key`.
- Added `omp auth-broker migrate --from-local` for idempotent upload of local SQLite/env credentials to the broker.
- Added `AuthBrokerClient`, `RemoteAuthCredentialStore`, `AuthBrokerRefresher`, and `startAuthBroker` server in `packages/ai/src/auth-broker`.
- Renamed `AuthCredentialStore` class to `SqliteAuthCredentialStore`; extracted `AuthCredentialStore` as a persistence interface.
- Added `exportSnapshot`, `forceRefreshCredentialById`, `disableCredentialById`, and `upsertCredential` to `AuthStorage` for broker wire protocol.
- Added `omp auth-broker` CLI subcommand (serve, token, login, logout, import, status) and `discoverAuthStorage` broker-mode path keyed on `OMP_AUTH_BROKER_URL`.
- Extracted `description` from type-array and nullable branches so it lives on the wrapper, not duplicated onto each variant.
- Replaced inline enum-type inference with `inferStrictPrimitiveTypeFromEnumOrConst`, covering both `enum` and `const` in sanitize and enforce paths.
- Mixed-primitive enums and non-primitive consts now fall back to non-strict instead of producing a typeless schema that OpenAI rejects on the wire.
- Non-interrupting tool-source TTSR matches now prepend a system-reminder to the matched tool's `toolResult` content instead of queuing a loop-wide deferred follow-up turn.
- Text/thinking source matches retain the previous deferred-injection behavior.
- Added deduplication so one rule attaches to exactly one sibling tool call per batch.
- Stale per-tool injections are cleared on abort/error before tools produce results.
- Moved sanitizeSchemaForStrictMode, enforceStrictSchema, and tryEnforceStrictSchema into normalize.ts.
- Moved sanitizeSchemaForOpenAIResponses/rewriteOneOfToAnyOf into normalize.ts alongside other normalizers.
- Removed strict-mode.ts and its public re-export; adapt.ts now only exposes NO_STRICT and adaptSchemaForStrict.
- Dropped StringEnum helper from strict-mode.ts (already removed from public API).
- Removed the legacy `parseEvalInput` parser module and `eval.lark`, eliminating `*** Cell` stream parsing.
- Replaced eval tool arguments from single `input` strings to ordered `cells` arrays in tool calls and schema.
- Updated execution to resolve language explicitly, map `py` to `python`, and apply timeout/reset defaults.
- Removed backend sniffing and `ABORT_WARNING` suffix handling, then updated docs and tests to the new JSON cells format.
- Implemented a unified normalization flow by switching Google/CCA handling to normalizeSchemaForGoogle/CCA.
- Added normalize.ts with recursive node normalization, nullable-union checks, and combiner collapsing.
- Removed sanitize-google.ts and normalize-cca.ts, replacing them with normalize exports in schema indexes.
- Added spill-to-description utilities with spill/paren modes and `$defs` exclusion for unsupported fields.
- Updated MCP bridge and schema tests to use normalizeSchemaFor* APIs with expanded compatibility checks.
- Documented normalization behavior changes and breaking rename in constraints and package changelog files.
- Renamed the internal URL protocol handler from `pi` to `omp` and updated the router export/import wiring to use `OmpProtocolHandler` with the `omp://` scheme.
- Updated embedded documentation link rendering and related validation/error messages in the protocol handler to reference `omp://` URLs.
- Adjusted tests and prompt/docs references so `read` examples and harness documentation guidance now use the renamed `omp://` scheme.
- Replaced `finishCleanup` callback with `isPromptTurnInFlight` predicate to unify settled+cleanup gating.
- Extracted `#beginCancelCleanup` (idempotent) and `#runCancelCleanup` to clarify ownership of slot eviction.
- Fork, queue, and close paths now all gate on the combined settled+cleanup window.
`getProjectPathCandidates` walks up from cwd to repoRoot (or home as a
fallback). When cwd is anywhere under $HOME and no closer .git boundary
exists, that walk-up reaches the home directory and enumerates
`~/.agent/<segments>` and `~/.agents/<segments>` as project paths.
`getUserPathCandidates` then enumerates the very same directories as
user paths, so every skill/rule/prompt/command/AGENTS.md found there
loaded twice. The capability deduper marks the second copy as shadowed,
but the Extension Dashboard renders shadowed entries — so users see one
active + one greyed-out duplicate of every home-level skill.
Skip the home directory inside the walk-up while still terminating the
loop on it; ancestors above home are still visited if the cwd happens
to live above (e.g. monorepo `home: tempDir` test fixtures).
Tests:
- Drop the duplicated copy of `getProjectPathCandidates` from the
monorepo-skills test; import the real one so behavior stays in sync.
- Replace the old "walk-up stops at home when no repo root" assertion
(which encoded the buggy behavior) with one that pins the new
contract: home-level `.agent[s]/skills` are NOT enumerated as
project paths.
- Add an explicit regression assertion that project ∩ user candidate
sets are empty when cwd is under home.
Fixes#1116
ConPTY's ClosePseudoConsole can deadlock when it tries to flush output
to a pipe that nobody is reading (microsoft/terminal#1810). This caused
the PTY Promise to never resolve on Windows, making bash commands with
pty:true hang indefinitely.
Root cause: portable-pty's drop(master) calls ClosePseudoConsole
synchronously. If ConPTY's internal render thread is blocked writing to
a full/undrained output pipe, ClosePseudoConsole waits forever.
Fix (three parts):
1. Rust (pty.rs): Reordered teardown to follow Microsoft's recommended
shutdown sequence:
- Drop writer first (close ConPTY input pipe)
- Drain reader thread with 500ms timeout (consume output pipe)
- Drop master in a background thread with recv_timeout(2s):
* Clean case: ClosePseudoConsole completes, thread reclaimed
* Hung case: timeout expires, main thread returns anyway
- Replace child.wait() with try_wait() polling on Windows
(WaitForSingleObject can also hang in ConPTY)
2. TypeScript (bash-pty-selection.ts): Remove the Windows blanket
disable that prevented PTY from ever being used on Windows.
3. Tests: Updated to verify PTY works on Windows with UI context.
Replaces the blanket PTY disable on Windows (PR #1105) with a targeted fix:
- TypeScript: PI_FORCE_PTY env var allows explicit Windows PTY opt-in.
PTY is still disabled by default on Windows to prevent hangs, but power
users who need interactive workflows can override.
- Rust: Adds ct.heartbeat() checks during PTY setup (openpty, spawn, reader
creation) so the existing timeout mechanism works even during setup.
- Rust (Windows): Wraps openpty() in a 5-second startup timeout thread.
If ConPTY hangs during pseudo-console creation, the Promise rejects with
a clear error instead of hanging forever.
Fixes#1103#1106
- Preserved object schemas with explicit `additionalProperties` settings by avoiding strict coercion to false.
- Probed schema strictness before sanitization and set `tool.strict` false for non-strict schemas.
- Set `tool.strict` false for `null`, `true`, and unconstrained `outputSchema` fallbacks.
- Documented the fix in Unreleased changelog entries for both `ai` and `coding-agent` packages.
- Added regression tests covering loose `additionalProperties` and yield strictness behavior across tools.
- SearchTool now tracked the last emitted line and inserted ellipsis markers when noncontiguous match blocks were output.
- Display output gap markers were padded to align with code-frame gutters.
- Added a regression test that verified a no-context search emits an ellipsis between separated matches in the same file.
- Adopted draft-2020-12 tuple validation with `prefixItems`, rejecting array-valued `items`.
- Expanded strict-mode handling to recurse `prefixItems` entries and infer `array` when tuple prefixes exist.
- Normalized Anthropic schemas through `prefixItems`, keeping supported tuple constraints and dropping unsupported fields.
- Updated coding-agent schema metadata and tests to draft-2020-12 `$schema` targets, including MCP/theme fixtures.
- Added `trimTrailingWhitespace()` to strip trailing spaces/tabs and keep the original line when none exist.
- Relocated compaction, branch-summarization, pruning, and utils from coding-agent to packages/agent/src/compaction.
- Moved OpenAI remote compaction helpers from packages/ai to the new compaction module.
- Added handoff.ts with extractHandoffDocument, createHandoffContext, and renderHandoffPrompt helpers.
- Exposed new entries.ts with standalone SessionEntry types so coding-agent no longer owns them.
YieldTool's unresolved-ref fallback now skips literal-value positions such as const, enum, default, and examples when looking for unresolved schema refs. Valid schemas containing data literals like { "": "literal" } no longer degrade to the loose schema and still reject invalid yield payloads.
- disposeAllKernelSessions removes sessions from the registry before awaiting shutdown, preventing queued work from seeing a registered-but-disposing session and spawning a replacement kernel.
- replaceSessionKernel re-checks registry membership after old-kernel shutdown and after starting a replacement, shutting the replacement down if the session was disposed mid-replace.
- owner-scoped disposal now preserves ownerIds when shutdown is unconfirmed so a later disposeKernelSessionsByOwner(ownerId) can retry.
- Detected async wrapper need via AST traversal instead of regex, enabling pre-demote analysis.
- Published demoted var bindings back to `this` (worker global) when inside the async wrapper, preventing function-scope from hiding them across cells.
- Added `collectBindingNames`/`getLexicalBindingNames` to extract names from destructured patterns.
- Type.String({ format, minLength, maxLength, pattern }) no longer drops the length/pattern constraints when a format selects a Zod format-specific schema (z.email, z.url, z.uuid, …). The instanceof z.ZodString guard never matched those subclasses; constraints are now applied via the shared _ZodString base so all format variants honor them.
- Type.Object without explicit additionalProperties now installs .loose() (matching TypeBox's preserve-extras default) instead of stripping unknown keys. additionalProperties: false continues to install .strict(); a schema value installs .catchall(schemaToZod(schema)).
dereferenceJsonSchema leaves $ref strings in place when references are unresolvable (external URLs, missing definitions, certain cycles). The new yield-parameters builder now walks the resolved schema for any remaining $ref strings before installing validation; if any are found, it throws so the existing catch branch swaps in looseRecordSchema and disables strict validation. Previously YieldTool installed a validator that rejected every success payload with an unresolved-reference error.
The status-line path segment was selecting theme.icon.scratchFolder for scratch directories regardless of opts.stripWorkPrefix, while the CHANGELOG promised both behaviors honor the option. Icon selection now uses the same opts.stripWorkPrefix !== false gate as the scratch path stripping, so disabling stripWorkPrefix keeps the regular folder icon even when the project directory is inside a scratch root.
- Added defensive abort re-check after registering the abortSignal listener plus a checkAbort() immediately before await session.prompt(...), so a wall-clock timer that fires during pre-prompt setup is no longer lost between listener registration and the prompt call.
- Late yield events arriving after a wall-clock timeout no longer flip the result to success: a runtimeLimitExceeded flag derived from the internal abortReason forces wasAborted=true and exitCode=1 regardless of hasYield, while yield payloads remain captured in extractedToolData.
- Async task progress now copies contextTokens and contextWindow from the completed SingleResult onto AgentProgress, so backgrounded tasks still surface their context gauge to the UI.
- executeOnSession's runQueued callback now re-checks sessions.get(sessionId) === session at slot entry and before the dead-kernel replace retry; if the session was removed by disposeAllKernelSessions / disposeKernelSessionsByOwner / resetSession during the queue wait, the queued caller now rejects with PythonExecutionCancelledError instead of spawning an untracked replacement kernel on a stale session object.
- Both dispose paths now inspect KernelShutdownResult.confirmed and retain (or re-insert) sessions whose shutdown was unconfirmed or rejected, logging a warn so a later dispose can retry the kernel instead of orphaning the subprocess.
`SessionManager.close()` queues `#closePersistWriterInternal()` on the
persist chain. The task awaits `#persistWriter.close()`, which flips
`#closing = true` synchronously before yielding on its inner writer
`close()`. A concurrent `appendMessage()` landing in that yield window
hit the hot path, got the still-cached (but closing) writer back from
`#ensurePersistWriter()`, and threw `Error("Writer closed")` from
`writeSync`. The throw was stashed into `#persistError`; the next async
caller (`flush()` or a later `appendMessage()`) re-threw it as an
unhandled rejection with the original line-1282 stack.
Expose `NdjsonFileWriter.isOpen()` and treat a mid-close cached writer
as a miss in `#ensurePersistWriter()`. `_persist` now falls back to the
async `#rewriteFile()` cold path so the entry — already in
`#fileEntries` — still lands on disk once the close drains.
- Added a dedicated `@sinclair/typebox` specifier remap path and routed bare legacy imports to the in-repo shim during extension rewriting and module resolution.
- Added resolve hooks for both `file` and legacy-file namespaces so legacy extensions load the shim consistently at runtime.
- Replaced fromTypeBox conversion with a JSON-schema validator flow in ai tool handling and execution paths.
- Added recursive schema validation and expanded TypeBox checks for refs, enums, uniqueItems, and constraint keywords.
- Sanitized Azure/CCA tool schemas by dropping unsupported fields and rewriting oneOf tool branches as anyOf.
- Tightened argument and model-config validation, preserving unknown tool fields and adding apiKey plus compatibility flags.
- Added canonical `pi.zod` schema API exports and removed TypeBox package exports/imports.
- Migrated Tool schema typing from TypeBox to shared `TSchema`/Zod flow with legacy TypeBox compatibility.
- Updated AI provider adapters and MCP/agent builders to convert tool params through `toolWireSchema()`.
- Reworked schema validation from AJV to Zod-safe parsing with `fromTypeBox`, `toolWireSchema`, and meta schema checks.