Commit graph

207 commits

Author SHA1 Message Date
Petr Houška
73f8de80bc fix(ai): recover from Copilot request-size overflow
Copilot rejects request bodies over its byte-size limit with HTTP 413 and a generic
'failed to parse request' body, so the existing token-count heuristics never matched.
Treat any HTTP 413 as request-size overflow regardless of provider or body text, so
the agent compacts and retries instead of surfacing a raw API error.

Validated against the live Copilot API (40MB image payload): main classifies the 413
as a plain error, this branch detects it as overflow.
2026-10-19 22:46:26 +02:00
Petr Houška
61060ddf8c fix(coding-agent): resolve Windows shells from installation directories
Use installation environment variables for PowerShell discovery and legacy WSL launcher detection.
2026-10-19 21:55:12 +02:00
David Brailovsky
6fcb48670f
fix(coding-agent): remove fixed output limits from summary requests (#11036)
Compaction summaries were limited to 0.8 * compaction.reserveTokens (13107 by default), turn-prefix summaries to 0.5 * reserveTokens, and branch summaries to a hardcoded 4096. On providers where reasoning shares the output limit (OpenAI Responses, adaptive Anthropic), high thinking levels used most of that limit and compaction failed with "generation hit the token cap", even though the model allows far more output.

Tying the limit to reserveTokens assumed the summary request is as large as the full context, so its output had to fit in the reserved space. It is usually much smaller: recent messages kept by keepRecentTokens are not sent, a split turn goes to a separate request, tool results are truncated, and tool definitions and the normal system prompt are not included. Manual compaction can also run long before the context is full. The provider layer already sizes output from the actual request, using the model's output limit reduced to fit the remaining context, as for normal turns.

Summary requests now set no maxTokens and rely on that. compaction.reserveTokens only controls when compaction starts.

generateSummary() and generateSummaryWithUsage() now ignore the reserveTokens parameter.

closes #9512, closes #9075, closes #10908
2026-10-16 13:53:16 +02:00
David Brailovsky
6f2f164ab7
fix(coding-agent): restore active tools on session resume (#10887)
The SDK always passed initial tools, so the constructor never restored the
loadout recorded in the transcript, and resumed sessions dropped tools activated
during the session. Sessions that use the default tools now restore the recorded
loadout, with --tools +name/-name entries applied to it.

fixes #10785
2026-10-13 15:55:12 +02:00
Armin Ronacher
b6d505cd1f fix(codemode): reject // @options: lines that are not the first line
A misplaced options line was ignored, so its timeout_ms never applied and
the script ran without a deadline. parseCodemodeSource now throws
CodemodeSourceError before the script runs.

fixes #10631
2026-10-12 17:55:33 +02:00
Armin Ronacher
f11e50f72e fix(coding-agent): make codemode output joining linear
joinAdjacentText checked endsWith on the growing joined string, which
makes V8 flatten the concatenation on every item. Collect each text run
in an array, check the separator against the last non-empty part, and
join once. Same fix in the durable code mode copy.

closes #10678
2026-10-12 10:03:56 +02:00
David Brailovsky
a447ba11f3
fix(coding-agent): emit before_agent_start for runs started by custom messages (#10739)
Runs started by pi.sendMessage(..., { triggerTurn: true }) skipped before_agent_start. After their first tool call, the next-turn refresh fell back to the base prompt options and patched away the sections handlers had added, changing the system prompt mid-run and breaking the provider prompt cache.

These runs now go through the same setup as prompt(): compaction check, before_agent_start, tool loadout, nextTurn messages, handler messages, and the system patch. BeforeAgentStartEvent is a union on messageType ("user" or "custom"); custom events carry the starting message.

The session counts as busy from the start of setup, so messages sent while handlers run go into the starting run instead of starting a second one, and an abort requested meanwhile aborts the run once it starts.

closes #10267, closes #5581, closes #10639, closes #10289
partly fixes #10606
2026-10-12 09:53:24 +02:00
Armin Ronacher
24382bedd0 refactor(codemode,coding-agent): decouple MCP from codemode
Tools that reach other tools declare it with reachesTools(input); MCP uses it to wait for the servers a call needs and to decide which codemode or tool_search tool to activate, instead of parsing codemode scripts and comparing parameter schemas. Namespaces get aliases, replacing codemode's MCP-specific __ suffix rule.

pi-codemode no longer knows MCP: MCP_TYPESCRIPT_PREAMBLE and mcpStructuredContentSchema() are removed, and the MCP result schema describes itself, with the server's local $refs relocated so they resolve as before.
2026-10-12 09:19:30 +02:00
Armin Ronacher
21d1dcd4d4 fix(coding-agent): strip fake cursor markers in OAuth prompt regression test 2026-10-08 09:54:07 +02:00
Mario Zechner
503c605528 feat(tui,coding-agent): report program status via OSC 7501
Report idle, working, blocked, done, and error to terminals that support
the Program Status Protocol. ProcessTerminal sends the support query with
the keyboard query and reports only after the terminal answers before DA1;
PI_PROGRAM_STATUS=1|0 overrides detection. Interactive mode reports runs,
compaction, extension dialogs, and logins. agent_settled now carries
aborted so cancelled runs report idle.

closes #10607
2026-10-07 19:05:41 +00:00
Armin Ronacher
f10993bc7f fix(coding-agent): cancel MCP OAuth sign-ins and stop refreshing on close
The OAuth flow in pi-mcp takes a signal for every request, and an aborted
refresh no longer falls back to a new authorization. The streamable HTTP
transport closes its session with the last request's token instead of
asking the auth provider, which could refresh first.

The MCP extension owns one signal per session: it aborts sign-ins on Esc
and on session shutdown, replaces the generation counter, and shutdown
waits for aborted sign-ins to clean up. Requests to the authorization
server time out after 15s, and pi mcp login --timeout limits the whole
sign-in.

closes #10565
2026-10-07 16:18:46 +02:00
David Brailovsky
27c7b6ff48 fix(coding-agent): strip ANSI sequences split across user bash output chunks
closes #10504
2026-10-07 15:02:59 +02:00
Armin Ronacher
ce8972a0e9 feat(ai,coding-agent): add OpenAI Decisions classifier and classifier images
Adds the openai-decisions classifier API with gpt-6-luna on the openai provider. bool questions map to predicates with the criteria in the instructions, refusals fail the result, and gateway timeouts (504) are not retried. The model needs an API key and is hidden from availability while openai uses Sign in with ChatGPT.

Adds optional images to ClassifierContext. Models without image input and APIs that cannot send images return an error result. Codemode's models.classify() accepts images.

Moves the shared classifier HTTP code out of system-one-shared.ts into classifier-shared.ts.
2026-10-07 08:27:56 +02:00
Armin Ronacher
eb326d265a fix(codemode,coding-agent): separate codemode output items
Text items get ==> text N/M <== headers when there are several, and
console output is grouped into one <console_output> block. Console text
items are marked with console: true.
2026-10-06 23:51:09 +02:00
Armin Ronacher
c30840c2eb fix(coding-agent): keep hidden tools out of prompt rules and skills hint
Rules and the tool list follow the declared tools (active minus
hiddenDeclarations). The skills hint names no tool when the reader is
hidden. Codemode shows each tool's prompt guidelines with its
declaration, via the new ToolLoadout.getPromptGuidelines().

closes #10343
2026-10-05 18:50:17 +02:00
Armin Ronacher
04b97ef000 fix(coding-agent): keep MCP tools with --tools, add tool patterns and --no-mcp
--tools no longer removes MCP tools unless an entry starts with mcp__, so
pi --tools codemode can reach MCP servers. Unnamed MCP tools are never
declared directly; only tool_search can load them. --no-tools still
removes everything.

--tools and --exclude-tools accept * patterns, for example
--tools read,codemode,'mcp__radius__*'. --no-mcp disables the built-in
MCP support for one run.
2026-10-05 17:21:55 +02:00
Armin Ronacher
147b502816 fix(mcp): send application_type in OAuth dynamic client registration
OpenID Connect authorization servers treat a registration without application_type as a web client and reject http loopback redirect URIs. Derive it from redirect_uris when unset (MCP SEP-837): native for loopback hosts and custom schemes, otherwise web.

closes #10493
2026-10-05 17:11:00 +02:00
Armin Ronacher
021eae60a7 fix(coding-agent): resolve codemode read calls on images to image blocks
read declares an output schema: text for text files, and an image block
with its note for images, which codemode's image() shows.

closes #10251
2026-10-05 16:58:05 +02:00
Armin Ronacher
b2b5c42f61
fix(coding-agent): expect saved image label in codemode MCP test (#10463)
The codemode image() output now names the saved temp file before each
image, so the MCP integration test must expect the extra text item.
2026-10-05 00:58:33 +02:00
Armin Ronacher
4c6b724ea6 fix(coding-agent): do not report dead terminal stdin errors as crashes
When the terminal goes away, stdin reads and setRawMode fail with EIO (pi left in an orphaned background process group) or ENOTTY (revoked tty on macOS). These were uncaught, recorded in crashes.json, and prompted users to run /bug. Handle them on stdin like stdout/stderr and skip crash recording for dead terminal errors.
2026-10-05 00:41:56 +02:00
Armin Ronacher
d677d0ee71 feat(coding-agent): save codemode image() output to temp files
image() output is also written to a temp file and the result names the
path before each image, so later turns can reference generated images.
Output files are now created through one helper (still tmpdir()) and
are readable only by the user.

closes #10310
2026-10-04 23:32:43 +02:00
Armin Ronacher
11449730c8 fix(coding-agent): render MCP tool calls before their server connects
Add pi.registerToolRenderer((toolName, next) => renderers), which chooses the
renderers for calls to any tool, including tools that are not registered.
Resolvers run in extension load order; next() returns what the remaining
resolvers, then the registered tool, would use. Interactive mode and HTML
export both resolve through it.

The MCP extension uses it to render every mcp__<server>__<tool> call with the
MCP renderers, so calls in a resumed session no longer render fully expanded
before their server connects, or for good if it never does.

closes #10285
2026-10-03 13:54:18 +02:00
Armin Ronacher
1499466d85 feat(coding-agent): opt-in MCP OAuth client ID metadata documents
Adds oauth.clientRegistration: "cimd", which identifies pi with its Client ID Metadata Document on pi.dev instead of dynamic client registration, matching Codex: a shared document when the authorization server sends iss, otherwise a document and callback path specific to the MCP server. Authorization responses are only accepted on the exact redirect URI. Dynamic client registration stays the default.

closes #10302
2026-10-02 16:02:43 +02:00
Armin Ronacher
1c1e9c0efc feat(coding-agent): remove daxnuts easter egg 2026-10-02 09:50:04 +02:00
Armin Ronacher
6f1072cc08 feat(coding-agent): shrink codemode prompt and guide scripts back from errors
Move the codemode script reference, including the models API, into
docs/codemode.md and keep only one line per global in the tool
description. Declared tools get a one-line note on how scripts call them,
and the codemode system prompt guidance and MCP server section are shorter.
With the default tools a GPT-5.6 request drops from about 5,300 to 3,300
tokens.

Errors now point the way back: unknown tools and models members name close
matches, models.classify() and models.generateImages() validate their
arguments, unknown or mistyped models point to getAvailableOfType(),
store() overflow explains the store, and unshown generated images get a note.
2026-10-01 18:27:23 +02:00
Armin Ronacher
aab34df655 feat(coding-agent): add models.generateImages() to codemode
Scripts run image models with the session's credentials, like
models.classify(). Results are base64 image blocks that image() attaches
to the codemode result; usage counts toward the session cost.
2026-10-01 16:50:29 +02:00
Armin Ronacher
f29ea3deb2 feat(coding-agent): add header-only quietStartup mode
quietStartup: "header" keeps the startup header with version and key hints
but hides the model scope line and loaded-resource listing.
2026-10-01 14:42:02 +02:00
Armin Ronacher
c662ec7e37 fix(coding-agent): restore MCP tools loaded by tool_search on resume and reload
The session restored its tool loadout before MCP servers reconnected, so deferred tools that tool_search had loaded were dropped. Restored tools that are not registered yet now stay pending and are activated when they register, until a setActiveTools() call deactivates a tool or the next prompt starts.
2026-10-01 13:35:33 +02:00
Armin Ronacher
d850edee9c feat(coding-agent): add MCP oauth.authServerMetadataUrl and check RFC 9207 iss
The configured metadata document replaces discovery and is trusted as
configured. Authorization codes are only exchanged when the response's
iss parameter names the flow's authorization server.

closes #10172
2026-09-30 23:56:10 +02:00
Armin Ronacher
b29db895c5 fix(coding-agent): align MCP tool names with codemode identifiers
MCP tool and namespace names now replace - with _ like Codex, so the
pi tool name is also the codemode identifier. Tools of a server whose
names collide all get a hash suffix, and server names that differ only
in - and _ are rejected.

closes #10239
2026-09-30 18:40:06 +02:00
Armin Ronacher
028c0ec56e fix(coding-agent): do not list codemode-hidden tools in the system prompt
In codemode.mode "only", tools whose declarations codemode hides were still
listed in the <tools> section. Drop their snippets so the list matches the
declared tools.

closes #10192
2026-09-30 17:46:43 +02:00
Armin Ronacher
e029c3ed0a feat(coding-agent): stop waiting for MCP servers on the first prompt
The first prompt waits only for servers with direct tools. Other servers
connect in the background; codemode scripts, tool_search, and the
resource tools wait for the servers they need. codemode and tool_search
are activated from the config.

Tool declarations no longer depend on connected servers: the codemode
description leaves out deferred tools, and the tool_search description
no longer lists sources. Servers are listed in an mcp_servers system
prompt section instead, set in before_agent_start from the config and
connected state, so changes are appended to the conversation.

describeNamespace() and searchTools() accept mcp__dev-radius,
mcp__dev_radius, dev-radius, and dev_radius.

closes #10212
2026-09-30 17:21:05 +02:00
Armin Ronacher
7c9fe66452 feat(coding-agent): allow a custom OAuth client name for MCP servers
Adds oauth.clientName (and pi mcp add --oauth-client-name) to set the
client_name sent during dynamic client registration. Defaults to pi.

closes #10226
2026-09-30 13:59:24 +02:00
Armin Ronacher
1c7e7df765 feat(coding-agent): list MCP servers instead of their tools in codemode
MCP codemode exposure no longer lists tools, tool counts, or server
instructions in the codemode description. Servers are listed by name and
an optional mcp.json description; scripts find tools with searchTools()
and read instructions with describeNamespace(). codemode-deferred is an
alias for codemode.

refs #10212
2026-09-30 13:54:02 +02:00
Armin Ronacher
d2931ad3d5 fix(coding-agent): validate codemode image() base64 and detect image type
image() now rejects malformed base64 and data without a PNG, JPEG, GIF,
or WebP signature, and derives the MIME type from the signature. Invalid
image blocks were persisted and made every later provider request fail.

closes #10215
2026-09-30 12:10:30 +02:00
Armin Ronacher
9a100c7cc4 feat(coding-agent): report codemode classifier and nested tool usage in session cost
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.
2026-09-29 16:21:39 +02:00
Armin Ronacher
8562bcf66a feat(coding-agent): codemode and MCP
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
2026-09-29 09:12:50 +02:00
Armin Ronacher
540e174c72
feat(coding-agent): Virtual models (#10035)
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.
2026-09-28 23:30:45 +02:00
David Brailovsky
ff72faba28 fix(coding-agent): save new session file at the first user message
A new session was only written to disk once the first assistant message
existed. If pi exited during the first turn (Ctrl+C twice while streaming,
or the process was killed), the user's prompt was lost and the session
never appeared in /resume.

The file is now created as soon as the session has a user or assistant
message. A session with only setup entries (model, thinking level) still
stays in memory, so starting pi and quitting without chatting leaves no
file. Forking uses the same rule through a shared helper, so the two paths
cannot drift apart and write the header twice (#1672).

closes #10000
2026-09-25 16:27:51 +02:00
David Brailovsky
e473b5cd8b feat(coding-agent): report prompt disposition in RPC responses
Successful RPC prompt, steer, and follow_up responses include
data.disposition ("handled", "queued", or "started" for prompt), so clients
know whether to wait for agent_settled.

fixes #9098
2026-09-25 14:53:11 +02:00
David Brailovsky
d192bd6dca
fix(coding-agent): avoid Fable split-turn summary refusals (#9908)
Separate conversation content from instructions and replace prefix/suffix wording with continuation-oriented summarization guidance.

Hopefully this avoids the refusals.

Fixes #9652
2026-09-22 16:52:07 +02:00
Armin Ronacher
aef5fc429b
fix(coding-agent): keep prompt and tool state across context handlers (#9846)
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
2026-09-21 17:14:22 +02:00
Mario Zechner
466db0fecd feat: add canonical session context boundaries
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.
2026-09-21 16:59:20 +02:00
Armin Ronacher
f5c946480c feat(ai,coding-agent): add image input limits (closes #9631) 2026-09-20 22:59:16 +02:00
Armin Ronacher
40c256cccb feat(coding-agent): defer extension loader dependencies
Load ordinary jiti lazily on Node while retaining jiti/static for Bun and SEA binaries. This closes #9540.
2026-09-19 23:25:47 +02:00
Armin Ronacher
de2de549bc fix(coding-agent): close compaction cancellation races
Closes #9340 and closes #9777.
2026-09-19 19:18:18 +02:00
Vegard Stikbakke
8bdcd4498a fix(coding-agent): compact oversized trailing tool results
Fall back to the latest safe assistant boundary when trailing tool results exceed the retained-token budget, allowing older history to compact before the next provider request.

Closes #9740
2026-09-18 13:38:30 +02:00
Mario Zechner
cf8d5fac30 feat(agent): add Pico storage foundation 2026-09-18 12:32:00 +02:00
Christian Klotz
509ee2bd0b
fix(coding-agent): fail closed on user bash hook errors (#9662)
* 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
2026-09-16 17:21:17 +01:00
Armin Ronacher
9e05370b29
Mid conversation system messages (#9548)
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.
2026-09-16 15:54:07 +02:00