Introduce `CompactionCancelledError` and `CompactionOutcome` ("ok" |
"cancelled" | "failed") so callers can discriminate user-driven aborts
from generic failures via `instanceof`, instead of inspecting error
messages or `AbortError`-name strings.
`AgentSession.compact()`'s two abort-rejection sites now throw the
typed sentinel; the model-call wrapper normalizes AbortError-shaped
rejections to the sentinel only when the compaction's abort signal
is actually set, preserving every other exception unchanged so real
compaction bugs are not silently relabeled as cancellations.
`CommandController.executeCompaction` and `handleCompactCommand`
return `Promise<CompactionOutcome>`; the catch classifies via
`instanceof CompactionCancelledError`. Existing callers (`/compact`,
loop runner, auto-compact) ignore the return value — non-breaking.
Op: extend
- Replaced Python execution with a local `python -u runner.py` subprocess and NDJSON stdin/stdout framing.
- Removed shared-gateway architecture, including coordinator lifecycle APIs, `useSharedGateway` wiring, and `jupyter` CLI/actions.
- Simplified setup checks to a plain Python 3 availability probe and removed automatic dependency-install fallbacks.
- Updated kernel cancellation and display processing to use status frames, SIGINT/SIGTERM escalation, and normalized output coercion.
- Added `python-runner` integration and display tests while deleting legacy websocket and kernel lifecycle test suites.
- Fixed query tokenization to split on non-alphanumeric runs, aligning with FTS5 unicode61 indexer so punctuation-delimited terms like `git-commit` match correctly.
- Added LIKE-based substring fallback so infix queries FTS5 prefix matching cannot reach (e.g. `mit` matching `commit`) still return results.
- Merged FTS and substring results with deduplication, prioritizing FTS matches before substring-only matches up to the requested limit.
- Added ownerId metadata to async jobs and to task/bash progress items from the session agent id.
- Extended async job registration and query methods with optional owner filters, and updated cancelAll to target matching owners.
- Updated session handoff and disposal so subagents inherit the parent manager, top-level sessions own it, and teardown cancels own jobs only.
- Added owner-aware async-job tests using hold/AbortSignal and scoped cancelAll assertions for running versus cancelled jobs.
- Added parent-to-subagent artifact manager adoption so subagents reuse the parent `ArtifactManager` and write artifacts into a shared directory with shared IDs.
- Passed the shared artifact manager through tool/session context into subagent executor startup and exposed it via `SessionManager` and `ToolSession` for lookup.
- Updated kernel environment and artifact-resolution paths to prefer `PI_ARTIFACTS_DIR`, falling back to existing session-file-based behavior when absent.
- Added process-wide singleton instances for InternalUrlRouter, AsyncJobManager, and MCPManager.
- Changed internal URL protocols to resolve through registered sessions and scan all active roots/datasets for matches.
- Refactored agent, artifact, memory, rule, skill, jobs, and mcp handlers to use shared manager and rule/skill state.
- Removed per-session protocol/tool wiring and switched tests to initialize and reset global singleton state.
- Stored a failure counter during single-path edit execution and set isError on aggregate results when any entry edit failed.
- Set streaming-edit handling to always evaluate auto-generated-file checks, but only primed the file cache when edit.streamingAbort was enabled.
The 50ms retry loop in #scheduleBackgroundExchangeFlush had no guard
against session teardown. If dispose() was called while streaming,
the setTimeout callbacks kept firing and attempted emitExternalEvent
on a disconnected agent.
- Add #isDisposed flag; set it at the top of dispose() before any
async teardown so poll ticks that fire mid-teardown bail out cleanly
- Clear #pendingBackgroundExchanges in dispose() to drop queued
messages that will never be rendered
- Widen the attempt() bail condition to include #isDisposed so the
loop terminates even if new messages arrived between the dispose()
clear and the next tick
- Added macOS power assertion settings for idle, system, user, and display with schema defaults.
- Added idle, system, and user options to MacOSPowerAssertionOptions in TS typings and Rust, preserving display.
- Changed agent-session power flow to use begin/end/reset in-flight helpers and avoid manual counter updates.
- Updated power assertions to combine multiple kinds per native handle and support safe repeated-stop behavior.
- Updated unreleased changelog notes to document the breaking behavior and canceled-prompt unblock correction.
- Model resolver: provider-prefixed `<provider>/<id>` selectors are now
strict. If the provider is known and the exact pair does not resolve,
return undefined instead of silently crossing provider boundaries
(e.g. routing `anthropic/claude-3-7-sonnet` to amazon-bedrock when
the user only has Anthropic auth). Unqualified resolution is unchanged.
- Compaction: when the current model's provider has no credentials,
manual compaction now retries across compaction model candidates and
falls back to an authenticated role; if no usable fallback exists, it
throws a clear provider-specific pre-stream error instead of bubbling
a 503 `auth_unavailable` from the provider stream.
Fixes#986Fixes#980
Adds an opt-in onSseEvent callback across HTTP-streaming providers (Anthropic, OpenAI Responses/Completions, Azure OpenAI Responses, OpenAI Codex SSE, Google Gemini CLI, GitLab Duo, Kimi, Synthetic) so callers can inspect raw SSE frames without altering parsed output. Provider fetch wrapping only tees response bodies when an observer is wired; standalone packages/ai consumers without onSseEvent are not penalized.
Adds streamIdleTimeoutMs (env: PI_STREAM_IDLE_TIMEOUT_MS, with PI_OPENAI_STREAM_IDLE_TIMEOUT_MS as a backward-compatible alias). Anthropic now enforces a steady-state idle watchdog (default 120s) in addition to the first-event watchdog. OpenAI Responses, Azure Responses, and Codex (SSE + WebSocket) gain a semantic-progress predicate so response.in_progress-style keepalives no longer keep stalled tool calls alive forever.
Adds a coding-agent debug-panel raw SSE viewer backed by a per-session bounded buffer (1000 records / 512KB) that AgentSession populates unconditionally so users can post-hoc inspect a stuck stream from the TUI.
Real CC's getAPIMetadata includes device_id alongside session_id and
account_uuid. Rather than reading OS machine UUIDs (hardware fingerprinting)
or storing a random persistent ID, derive it as:
sha256("omp-device-id-v1:" + account_uuid).hex()
Properties:
- Indistinguishable from a randomly generated device ID on the wire
- Deterministic per account — survives reinstalls, no persistent storage
- Auditable: derived solely from the OAuth UUID already shared with Anthropic
- Zero hardware access, zero extra I/O
- Omitted for API-key callers (no account_uuid → no hash)
Anthropic counts sessions by metadata.user_id. Without this fix, OMP
generated fresh random entropy on every API request, inflating the
session count and preventing backend attribution to the authenticated
account.
Changes:
packages/ai:
- resolveAnthropicMetadataUserId() now accepts JSON-format user_id
matching real Claude Code's getAPIMetadata shape
({ session_id, account_uuid, ... }). Previously only the legacy
cloaking format was accepted on OAuth, causing stable caller-supplied
values to be silently discarded.
- AnthropicOAuthFlow.exchangeToken() and refreshAnthropicToken() now
populate OAuthCredentials.{accountId, email} from the token response
account block, removing the need for a separate /api/oauth/profile
round-trip.
- AuthStorage.getOAuthAccountId(provider, sessionId) returns the OAuth
accountId for the session-sticky credential, used to build
account_uuid in metadata.user_id. Guards against misattribution for
API-key, runtime-override, env-key, and fallback-resolver paths that
do not record a session credential.
packages/agent:
- Agent.metadataForProvider(provider) resolves request metadata for
the given provider via the installed resolver, or returns the static
metadata value. The plain metadata getter now returns only the static
value; provider-aware resolution is explicit.
- Agent.setMetadataResolver(fn) installs a (provider: string) resolver
evaluated per LLM request in agent-loop, after getApiKey records the
session-sticky credential, so account_uuid reflects the credential
actually used.
- AgentLoopConfig.metadataResolver is called with config.model.provider
after getApiKey, overriding the static metadata field.
packages/coding-agent:
- AgentSession.#syncAgentSessionId installs a metadata resolver that
builds { user_id: JSON.stringify({ session_id, account_uuid? }) },
matching the Anthropic session attribution format. account_uuid is
only included for provider="anthropic" to avoid leaking the OAuth
identity to third-party Anthropic-format-compatible providers.
- sessionId getter prefers providerSessionId when supplied via
AgentSessionConfig so all API paths (getApiKey, direct calls,
metadata resolver) share the same provider-facing session ID.
- prepareSimpleStreamOptions stamps session metadata on direct calls
(runEphemeralTurn, compaction, branch summary, title generation) so
they share the same session bucket as Agent.prompt requests.
- generateBranchSummary and generateSessionTitle accept a
(provider: string) metadata resolver evaluated after their own
getApiKey call for correct credential attribution.
- Added hideThinkingSummary options across stream, agent, and session payload paths.
- Routed Coding-Agent hideThinkingBlock toggles to agent hideThinkingSummary during session updates.
- Updated OpenAI, Azure OpenAI, and Codex requests to omit reasoning.summary when hide/ summary is null.
- Reworked system-prompt preparation with per-step timeouts, fallback defaults, and step-level warnings.
- Added optional `loadMode` and `summary` fields to `AgentTool` and related type declarations.
- Added `loadMode` and `summary` metadata to built-in tool classes for discoverable/essential behavior.
- Replaced `BUILTIN_TOOL_METADATA` with per-tool fields in discovery code paths.
- Updated `search_tool_bm25` and discovery indexing to use each tool's `summary` text.
- Updated discovery tests to validate tool `loadMode` and summary completeness.
Restore BUILTIN_TOOLS to Record<string, ToolFactory> so external SDK callers can
still invoke BUILTIN_TOOLS.read(session) directly, and move per-tool discovery
metadata (loadMode, summary) into a dedicated BUILTIN_TOOL_METADATA map. All
internal callers (computeEssentialBuiltinNames, getBuiltinDiscoverableEntries,
createTools, sdk.ts initial-tool filter, agent-session built-in collection) now
read metadata through the new map.
Restore the legacy MCP discovery API on AgentSession: getDiscoverableMCPTools()
returns DiscoverableMCPTool[] with description, and getDiscoverableMCPSearchIndex()
returns the legacy DiscoverableMCPSearchIndex whose documents expose
tool.description while remaining usable by searchDiscoverableTools (summary is
populated from description so the BM25 corpus still scores correctly). Generic
discovery via getDiscoverableTools / getDiscoverableToolSearchIndex is unchanged.
Centralize discovery cache invalidation in #invalidateDiscoveryCaches and call it
from #applyActiveToolsByName, refreshMCPTools, and refreshRpcHostTools so the
generic search index can no longer return tools that have already been activated
or registry entries that have been replaced.
Restrict #collectDiscoverableBuiltinTools to entries whose
BUILTIN_TOOL_METADATA[name].loadMode === "discoverable", which keeps hidden
tools (resolve, yield, exit_plan_mode, report_finding, report_tool_issue) and
unknown extension/custom registry entries out of the discovery corpus.
Tests: add coverage for callable BUILTIN_TOOLS factories, legacy MCP description
shape on getDiscoverableMCPTools / getDiscoverableMCPSearchIndex, stale-index
invalidation on setActiveToolsByName, and hidden-tool exclusion from
getDiscoverableTools({ source: "builtin" }).
The internal class is now SearchToolsTool but the wire name stays
"search_tool_bm25" so persisted MCP selections in user session files
continue to resolve correctly.
The BM25 corpus is now drawn from the generic DiscoverableTool list
(built-ins, MCP, extension, custom) rather than only MCP tools.
createIf fires when either tools.discoveryMode !== "off" or the
legacy mcp.discoveryMode is true.
AgentSession gains generic discovery methods (isToolDiscoveryEnabled,
getDiscoverableTools, getDiscoverableToolSearchIndex,
getSelectedDiscoveredToolNames, activateDiscoveredTools). The existing
MCP-named methods are kept as thin shims that filter by
source === "mcp" so existing callers continue to work.
Updated the tool's prompt copy to advertise discovery across all
sources rather than MCP only. Tests extended for the new shape.
- 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.
- Updated read schema, path utilities, and dispatch to parse selectors from :raw/:L suffixes on path, removing standalone sel usage.
- Changed truncation/error notices to continue with :<nextOffset> and :1 guidance for read and sqlite pagination.
- Added summarizeCode support with tree-sitter summaries, read.summarize settings, and N-API Summary types/exports.
- Added tests and docs updates for path-embedded selectors, summary behavior, and explicit raw/offset SQL/read cases.
- Added session-draft persistence methods in SessionManager to write unsent editor text to an artifacts-sidecar draft file and delete it after single-shot consumption.
- Persisted editor text during interactive shutdown and restored that draft on resume when the editor was empty, enabling Ctrl+D draft recovery.
- Updated Ctrl+D handling in the editor/controller path and added tests covering draft round-trip, artifact cleanup, stale-draft eviction, and in-memory no-op behavior.
- Added `HindsightSessionState` to `AgentSession` and bound hindsight lifecycle hooks to session state.
- Removed global hindsight state/queue handling and replaced it with per-session `HindsightRetainQueue` batching and scoped flushing.
- Reworked recall, reflect, and retain tools to use `session.getHindsightSessionState()` instead of sessionId-based lookup.
- Updated SDK/task/backend/controller flows to pass `session`/`parentHindsightSessionState`, scope `/memory` behavior, and document it in changelog.
- Added per-session retain queues with size/time auto-flush, recursive drain, and lifecycle flushes on end/clear/enqueue.
- Changed `hindsight-retain` to validate session state, enqueue writes, and return `Memory queued.` immediately.
- Added `notice` event support and handlers that route error/warning/status messages with source-aware formatting.
- Updated client and tests with shared request mapping, `RequestOptions`, `buildMemoryItem`, and expanded batch/list/doc APIs.
- Added inline hashline parse and apply support for `<` prepend and `+` append operations with prefix+suffix edits.
- Added fail-fast behavior to reject inline modify ops combined with delete or replace on same line.
- Renamed HASHLINE_* and mode symbols to HL_* in prompt tooling, read/search checks, and prompt templates.
- Standardized separators to `PI_HL_SEP`/`HL_EDIT_SEP` and fixed `HL_BODY_SEP='|'`, updating parser formatting behavior.
- Updated benchmark subtype constants and python cleanup test setup to use HL_* values and AgentRegistry mock failure injection.
- Added a new optional `beforeAgentStartPrompt` hook to `MemoryBackend` and implemented it in the Hindsight backend to recall long-term context for the first turn.
- Updated `AgentSession` startup flow to inject the recalled context into the turn-specific system prompt before the first response is generated.
- Preserved `<hindsight_memories>` tags in Hindsight developer instructions and added tests for first-turn injection and state caching.
- Added `memory.backend` and `hindsight.*` settings schema with migration from `memories.enabled` legacy mode.
- Added Hindsight memory backend runtime modules for resolved config, client creation, bank ID derivation, and state lifecycle.
- Added off/local/hindsight backends and resolver wiring across SDK, commands, and compaction context.
- Added `hindsight_recall`, `hindsight_reflect`, and `hindsight_retain` tools with schema validation and backend gating.
- Added Memory tab metadata and symbols to expose backend selection in the settings UI.
- Added package export barrels and tests for bank ID, content formatting, and hindsight config env precedence.
- Removed title-source aware branching from session terminal-title and accent helpers, and updated callers to use session name plus cwd only.
- Dropped UUID-based recent-session naming by preferring explicit header titles or first user prompts and generating an "Untitled · <time>" fallback.
- Adjusted welcome session-row rendering for width-aware name truncation and disabled reasoning in title generation requests to keep terminal titles concise.
Manual handoff starts a fresh session and seeds it with a displayed custom handoff message, not an assistant message. Session persistence normally waits for an assistant message before creating the session file, which made the new handoff session exist only in memory until later activity.
Persist the seeded handoff session after injecting the handoff context, and record the previous session file as its parent so lineage remains discoverable.
- Added a unified eval framework with parser grammar, backend interfaces, and JS/Python execution result types.
- Added eval tool docs and updated prompts for fenced cells, `eval.py`/`eval.js`, and fallback behavior.
- Replaced the built-in `python` tool with `eval` across registry, rendering, interactive modes, and tool settings.
- Migrated Python execution runtime from `src/ipy` to `src/eval/py`, renamed state fields, and removed legacy introspection.
- Refactored browser tooling from in-process VM helpers to worker-managed tab supervisors and protocol transport.
- Added eval parser fallback and JS tool-bridge tests, updated imports, and removed obsolete python-mode suites.
buildSystemPrompt injects today's date into the prompt body. The
applied-tool signature skips rebuilds when tools are byte-identical,
but did not cover the date — so a session spanning midnight with only
tool-stable MCP reconnects would keep yesterday's date indefinitely.
Append the current YYYY-MM-DD date as a suffix to the signature so any
reconnect after midnight triggers exactly one rebuild, then resumes
skipping normally for the rest of the new day.
Built-in tools whose prompt-rendered metadata depends on settings
(`TaskTool`, `SearchToolBm25Tool`, `EditTool`) expose `description`/
`label` via getters that re-evaluate on every access. The skip
optimization in `#applyActiveToolsByName` is correctness-safe for these
because `#computeAppliedToolSignature` reads `tool.description` live
each call, so a settings flip mutates the rendered string and differs
the signature on the next refresh.
This contract was implicit; a future refactor that caches per-tool
description strings would silently break it. Defending it explicitly:
- Added a regression test that wires a getter onto a CustomTool's
`description`, verifies `refreshMCPTools` skips while the underlying
state is unchanged, then mutates the state (without changing tool
object identity) and verifies the rebuild fires.
- Expanded the `#computeAppliedToolSignature` docstring to document
the getter-based coverage path and the SDK-init-time closure
constants in `sdk.ts` that genuinely cannot change at runtime
(`repeatToolDescriptions`, `eagerTasks`, `intentField`,
`mcpDiscoveryEnabled`, `secretsEnabled`).
Triggered by a review question on whether the skip breaks settings-
based prompt changes. It does not, but the property is non-obvious.
A tool's wire-visible name (`customWireName`) is rendered into the
system prompt body via `toolPromptNames`, but the applied-tool signature
only hashed name+label+description. A future tool whose wire name varied
without touching the other fields would silently produce a stale system
prompt that advertises the wrong callable name to the model — desyncing
prompt guidance from actual tool routing.
Today the only mutation path (edit-mode toggle) is also covered by a
description change and an explicit `refreshBaseSystemPrompt` from
`#syncEditToolModeAfterModelChange`, so this is a defensive fix rather
than a live bug. Including `customWireName` makes the signature a
self-consistent model of the prompt inputs.
- Extended `describeTool` in `#computeAppliedToolSignature` to include
`tool.customWireName ?? ""`. Applies to both the active-tool segment
and the (mcpDiscoveryEnabled) registry segment via the shared helper.
- Updated the docstring to call out wire-name coverage.
- Added a regression test that mutates `customWireName` between
identical-metadata refreshes and asserts the rebuild fires.
Per Codex review on #890.
Two cache-stability fixes for Anthropic prompt caching during MCP server
reconnects, which happen routinely (~5 min per server) in long sessions
due to SSE transport keepalive timeouts.
1) MCPManager: deterministic tool ordering
`#tools` is now sorted by name after every mutation. The previous
filter-out + push-to-end pattern in `#replaceServerTools` moved the
reconnecting server's tools to the end of the array, producing a new
byte order whenever the reconnect sequence differed from the initial
discovery sequence. With multiple healthy servers, each reconnect of
the non-last server flipped the order and invalidated the tools
cache breakpoint sent to Anthropic.
Sort applies in `discoverAndConnect` (initial population) and
`#replaceServerTools` (used by `reconnectServer` and
`refreshServerTools`). The comparator is character-code based,
locale-independent and deterministic. `sortMCPToolsByName` is
exported as a small generic helper and unit-tested.
2) AgentSession: skip system-prompt rebuild when inputs are unchanged
`#applyActiveToolsByName` (called from `refreshMCPTools` after every
reconnect) used to unconditionally call `rebuildSystemPrompt` and
`setSystemPrompt` even when the resulting prompt was byte-identical.
This wasted CPU on every flap and risked silent cache invalidation
if the rebuild path ever became non-deterministic.
Now `#applyActiveToolsByName` computes a stable signature of the
inputs `rebuildSystemPrompt` reads and skips the rebuild when the
signature matches the last successful one. The signature covers:
- active tool names in render order
- active tool labels and descriptions (rendered as `{{label}}:
\`{{name}}\`` in the prompt body)
- when MCP discovery is on, every registry tool's name + label +
description (the prompt summarizes discoverable-but-inactive
MCP tools)
- per-server MCP `instructions` text (embedded under "## MCP
Server Instructions" in the appended prompt; can change on
server upgrade while tool list stays identical)
Server instructions are read via a new optional
`getMcpServerInstructions` callback on `AgentSessionConfig`, wired
from the SDK as `() => mcpManager.getServerInstructions()`.
`refreshBaseSystemPrompt()` continues to rebuild unconditionally and
refreshes the cached signature, so explicit refreshes still pick up
ambient changes (edit-mode toggles, memory writes, etc.) that the
signature does not cover.
Signature inputs deliberately NOT covered: tool input schemas, memory
instructions read from disk, and other ambient state. Callers that
mutate those must call `refreshBaseSystemPrompt()` explicitly; existing
hooks (`#syncEditToolModeAfterModelChange`, memory hooks, `/clear`)
already do.
- Added `Process` class with pidfd (Linux), libproc (macOS), and handle (Windows) ownership for race-free signaling.
- Replaced `killTree`/`listDescendants` free functions with `Process.fromPid`, `fromPath`, `terminate`, and `waitForExit`.
- Added `TerminationTargets` for batching pgid+pid sets across pty and shell job teardown.
- Migrated `procmgr` and `ptree` to use the new native API, removing the `setNativeKillTree` injection pattern.
- 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.
- Integrated `isUnexpectedSocketCloseMessage` into transient error detection so Bun socket-closure failures are treated as retryable.
- Added a retry fallback test that simulates a Bun socket close error and verifies the request is retried successfully with matching retry start/end events and recovered output.
- Fixed bash interceptor to check both raw and cwd-normalized commands, catching commands hidden behind leading `cd ... &&` wrappers.
- Fixed LSP client shutdown to await graceful shutdown with a 5s timeout before killing the process, and parallelized `shutdownAll` via `Promise.allSettled`.
- Fixed concurrent bash command tracking by replacing a single abort controller with a Set, preventing premature cancellation of parallel commands.
- Removed `./hooks` and `./hooks/*` export entries from the coding-agent package exports map.
- Updated pinned Rust nightly toolchain from `nightly-2026-03-27` to `nightly-2026-04-29` in `rust-toolchain.toml` and CI workflow.
- Replaced custom already-published detection in `ci-release-publish.ts` with `bun publish --tolerate-republish` flag.
Mirror anthropic.ts:disableThinkingIfToolChoiceForced for backends that 400
on combined reasoning + forced tool_choice. Kimi explicitly rejects this
combination ('tool_choice specified is incompatible with thinking enabled')
on its native API, OpenCode-Go, OpenRouter, etc. Anthropic itself enforces
the same constraint, so Claude reached through OpenAI-compat proxies
(LiteLLM, Vertex chat-completions, OpenRouter) needs the same handling.
Adds disableReasoningOnForcedToolChoice compat flag, defaulted on for any
Kimi (moonshotai/kimi*, kimi-* ids) or Anthropic (provider/baseUrl/claude*
ids) model. When tool_choice resolves to anything that forces a tool call
(required, named function), reasoning_effort and the OpenRouter-shaped
nested reasoning object are dropped for that turn. The forced tool_choice
itself stays so the agent still gets the tool call.
Replaces the previous (incorrect) approach of unconditionally dropping
tool_choice for kimi reasoning models, which broke explicit tool routing.
Fixes#827
buildSessionContext walked the entry path and unconditionally overwrote
models.default from every assistant message's reported model. Temporary
fallbacks (retry fallback, context promotion) and codex-side model
downgrades both produce assistant messages tagged with a different model
id, which clobbered the user's explicit /model pick on resume and made
the session silently revert to the older model.
Treat assistant-message inference as a legacy fallback that only fills
in models.default when no explicit `model_change` with role="default"
has been seen on the path.
Fixes#849
Add optional defaultLevel to ThinkingConfig schema/type so models.yml can
declare a preferred starting thinking level per model. On model switch
the agent session adopts model.thinking.defaultLevel when present (with
explicit caller-supplied level still winning); otherwise current behavior
is preserved. SDK initial selection prefers the model's defaultLevel
before falling back to the global defaultThinkingLevel setting.
Fixes#775
The autoContinue post-compaction prompt echoed the summary's '## Next
Steps' heading, but that section is generated only from the compacted
tail; the kept ~20k recent tokens are not fed to the summarizer. When
the user pivoted within the kept window, 'Continue if you have next
steps.' anchored the model on the now-outdated plan instead of the
latest intent.
Move the prompt to prompts/system/auto-continue.md (per AGENTS.md
no-inline-prompts rule) and rewrite it to direct the model to re-read
the kept recent messages and follow the user's most recent request,
explicitly allowing it to stop when nothing remains.
Fixes#840
- Added a shared session path resolver that maps local:// URLs through local-protocol options, skips other internal schemes, and returns an absolute filesystem path for real files.
- Updated streaming-edit pre-cache and post-edit cache invalidation to use the shared resolver, preventing internal-scheme assertions while keeping filesystem-based flow for local plan files.
- Extended streaming-edit tests to confirm local:// plan edits complete without panicking and that auto-generated checks receive resolved absolute paths.