Codemode sums the usage of models.classify() calls into its tool result and shows each call's cost. Usage of tools called through ctx.executeTool() is added to the calling tool's result usage instead of being dropped.
This adds codemode and MCP support to pi. Codemode (packages/codemode) runs
model-written JavaScript in a QuickJS wasm VM inside a worker. From there the
script calls pi's tools as async functions, uses a per-session store, and can
read the model catalog and run classifiers. The codemode tool in coding-agent
is compatible with Codex's exec tool, and codemode.mode works like Codex's
tool modes (on or only). Large results are written to a JSON temp file. MCP
(packages/mcp) is a standalone client for stdio and streamable HTTP servers,
with OAuth.
The core only gets general mechanisms; codemode, tool_search and MCP are
built-in extensions that use them. Other extensions can replace these
built-ins. Tools declare an exposure: direct, model-only, codemode, deferred
or hidden. prepareLoadout() lets an orchestrating tool change which tool
declarations the model sees. ctx.executeTool() runs nested calls through
validation and the tool_call/tool_result hooks. Those calls emit events with
parentToolCallId and are recorded as bounded nestedCalls on the parent
result, which compaction and HTML export use. Tools can also return
structuredContent with an outputSchema, and can report errors with isError
instead of throwing. MCP servers come from mcp.json (global, or per project
after the project is trusted) or from pi.registerMcpServer(). They are
managed with /mcp and pi mcp list|login|logout, and their runtimes load on
first use.
Closes#10040
This adds experimental support for virtual models. A virtual model is a catalog entry that an extension registers with pi.registerVirtualModel(). It does not talk to a provider itself; for every request it picks a physical model and thinking level. The motivation is to make routing policies pluggable: plan on a strong model and implement on a cheap one, pick a model with a classifier, or move to a larger window when the context grows. Today this needs model switching by hand or hacks in prepareRequest.
The selection stays virtual: model_change records it, resume restores it, and each response records the physical model and thinking level that produced it. Router state can be stored on the session branch, so it follows /tree and forks and survives compaction. Context limits, compaction and image sizing use the physical model a request was routed to. examples/extensions/jev-router.ts shows a full router that uses the Jev classifier to pick Sol or Terra for planning and hands off to Luna after the first edit. docs/virtual-models.md describes the API.
Since 0.86 the prompt and tool declarations live in transcript system
messages, so a context handler that filters or slices messages could drop
them. After extension-driven compaction this sent requests without built-in
tools and made Codex emit raw tool-call text.
context handlers now see the conversation only. An unchanged list keeps the
transcript as is; a changed list gets the replayed prompt sections and tool
declarations as one leading system message. Handler-added system messages
are kept after it.
Add context_with_system, which runs after every context handler on the full
transcript and sends its result verbatim, for extensions that need to edit
system messages per request. Dropping the leading system message is reported
as an extension error.
closes#9789, closes#9822
Make SessionManager projections authoritative for provider requests, add append-only context edits and actionable turn boundaries, and preserve existing queue scheduling during continuation and recovery.
Remove SystemMessage.replace. A prompt forced from before_agent_start is a rendering
of the current prompt for the run, not conversation state: persisting it as a
replacing system message wrote the full prompt and tool list on every change,
lost the structured sections while forced, and disabled mid-conversation system
messages for the rest of the session once a replacement existed.
The transcript now always records section patches. AgentSession wraps
transformContext and, when a forced prompt is active, collapses the system
messages into one head carrying the forced text and the current tools. Providers
receive the same request as before; nothing is persisted and the projection ends
with the run.
The evals harness validated the without_docs variant from the transcript, which
relied on the persisted replacement. Its transform extension now records the
prompt it forced and validates that instead.
Add SystemMessage.replace: replaying a system message with the flag discards the
accumulated prompt content, sections, and tools before applying it, and providers
collapse the transcript into one leading system message whenever a later message
replaces it.
A forced prompt from before_agent_start is opaque, so it is persisted as a replace
message holding the text in content with no sections (the agent loop fills in the
full tool set). Leaving force mode persists another replace message with the
structured sections. Previously the forced prompt became a preamble section patch,
so models with native mid-conversation system messages kept the original prompt
as their leading system prompt and received the forced one as a later update.
* fix(coding-agent): fail closed on user bash hook errors
Prevent failed execution-routing hooks from falling back to the local shell.
Fixes#9068
* fix(coding-agent): validate user bash hook results
* docs(coding-agent): clarify user bash result semantics
* docs(coding-agent): streamline user bash semantics
* fix(coding-agent): strengthen user bash regressions
* docs(coding-agent): clarify user bash breaking change
This change makes system prompt text and tool changes part of the transcript rather than silently rewriting its starting conditions. This lets Pi record when instructions changed or tools became available, restore that state after resuming or navigating branches, and preserve cached prompt prefixes where the upstream supports it.
Follow-up to e687434a6.
Recheck compaction state after navigation dialogs and response cancellation before replacing status indicators or escape handlers. Document navigation conflicts and add regression coverage.
Extensions have no way to read the session's scoped model set (the models
resolved from --models / enabledModels against the available catalogue --
the same list /scoped-models shows). The only model surfaces on
ExtensionContext are ctx.model (active) and ctx.modelRegistry.getAvailable()
(the whole catalogue, unfiltered), so an extension that wants a scoped
model picker must either enumerate everything or re-implement pi's scope
resolution (minimatch on provider/modelId / bare modelId + enabledModels +
--models) from disk -- duplicating core logic.
Add ctx.scopedModels: readonly ScopedModel[] to ExtensionContext, mirroring
how ctx.model is exposed (field -> bindCore -> lazy getter in createContext()).
Empty when no scoping is configured (all available models usable). Each entry
is { model, thinkingLevel? }.
- types.ts: ExtensionContext.scopedModels + ExtensionContextActions.getScopedModels
- runner.ts: field, bindCore wiring, lazy scopedModels getter
- agent-session.ts: provide getScopedModels: () => this._scopedModels
- test: assert ctx.scopedModels reflects the action (default [] + live ref)
- docs/extensions.md: document the new field
Co-authored-by: ngSoftware <alessandro@pungitore.ch>
This adds cache-friendly dynamic tool loading anchored to tool results. Purely additive active-tool changes are recorded with `addedToolNames`, allowing supported Anthropic and OpenAI Responses models to load tool definitions at the point they become available instead of placing them in the cached prompt prefix.
It retains safe fallback behavior for unsupported models and non-additive changes but it will wipe caches.
* feat(coding-agent): add before_provider_headers extension hook
Extensions can already rewrite the request payload through before_provider_request, but there is no way to adjust the outgoing HTTP
headers of a provider call. This hook fills that gap for cases like request tracing, session correlation, or tenant routing.
Handlers mutate the headers map in place - a null value deletes a header - and the return value is ignored, so a handler cannot accidentally drop auth or attribution headers by
forgetting to spread.
* docs(coding-agent): document before_provider_headers extension hook
Add the before_provider_headers section and lifecycle-diagram entry to
extensions.md; drop the now-superseded proposal file.
session_before_compact and session_compact now carry
reason ("manual" | "threshold" | "overflow") and willRetry, matching
what the RPC protocol already exposes via auto_compaction_start/end.
Extensions can now distinguish manual /compact from threshold
auto-compaction and overflow recovery through the public API instead
of monkeypatching AgentSession internals.