Repository navigation
feat(agent): catch the Pi crates up to Pi 1.4.0 - #1389
Conversation
The crates now follow Pi v1.4.0 (4a080252f). The next review starts there. The review log records this full review of v1.0.4..v1.4.0, its scope and validation, and why bd3c9b823 was skipped. Maple #1389 holds the full adopt/skip table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d290c42 to
4fbb64e
Compare
The crates now follow Pi v1.4.0 (4a080252f). The next review starts there. The review log records this full review of v1.0.4..v1.4.0, its scope and validation, and why bd3c9b823 was skipped. Maple #1389 holds the full adopt/skip table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts Pi 911309429 (earendil-works/pi#11000), an upstream fix in Pi 1.3.0. Our port matched Pi 1.0.4, which sent thinking from an earlier reply of another provider, API or model as plain text. The new model then read that reasoning as part of its own replies and could start writing its reasoning into the visible answer. `convert_messages` now drops another model's thinking and keeps the reply's text and tool calls. A reply that was only thinking leaves nothing to send, as before for empty replies. The same model still gets its own reasoning back in the field it came in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts two upstream fixes in Pi 1.3.0's JSON parsing: - 4b994d5e5 (#10868 upstream): string repair treated any `\u` as a valid escape. Arguments with text like `\usepackage` failed to parse, and the truncation repair dropped that field and every field after it. A `\u` without four hex digits is now kept as literal text, as other invalid escapes are. - 87f9a3641 (earendil-works/pi#10944): when a finished call's argument text was not valid JSON, the streaming parser kept the fields before the error and the tool ran with them. `parse_tool_call_arguments` parses a finished call strictly, with the string repair. On failure `arguments` is empty, and the new `ToolCall::arguments_error` holds the reason and the text the model sent. `validate_tool_arguments` fails with it, so the model sees what it sent. The error is serde_json's, where Pi's is `JSON.parse`'s. A call closes when the next block starts (recorded in #1104), so the strict parse runs when the response finishes, as Pi's does at the end of its stream. A call's earlier end event still shows its arguments as they stream, since fragments can follow it. Struct literals in maple-agent tests and store-bench gain the field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts Pi 4dd2b149d, an upstream fix in Pi 1.4.0 (#8720 upstream). Output that is only whitespace, such as a bare line break from bash, was sent as is, and some servers reject it with a 400. It now gets the empty-output placeholder, or the image placeholder when images came with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts the retry and overflow texts Pi added from 1.0.4 to 1.4.0. These are upstream fixes; the classifier lists follow Pi's. - 6e491cfa9: status codes count only as standalone numbers, not digits inside a request id or a byte count. Ours used `\b`; this is Pi's boundary, spelled without lookaround. It closes the difference #1104 recorded. - 550f1f3ec, 7bd01fb92, 7efebff18, 698a2d23b, 0fbdbd3f8, 29057ece3: retry "service is temporarily unavailable", "the upstream provider returned an error", gateway upstream failures, node-fetch "request to ... failed, reason:" and "stream disconnected before completion". - cedefc532: a ChatGPT Business workspace usage limit is never retried. - 73f8de80b: any HTTP 413 is context overflow, whatever the body says, so the session compacts and retries. A bodyless 400 is still overflow only from Cerebras. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts Pi 15e7bdc14, an upstream fix in Pi 1.2.0. `serialize_conversation` wrote every thinking block as `[Assistant thinking]`. Replayed thinking is often not in the live context (another model's is now dropped), so a summary request could grow larger than the context it summarizes. Text, tool calls and tool results record what happened. This applies to compaction, split-turn and branch summaries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts two upstream fixes in Pi 1.4.0: - 6fcb48670 (earendil-works/pi#11036): compaction summaries were capped at 0.8 x the reserve, the split-turn summary at half of it, and branch summaries at 4096 tokens. Reasoning could use up that cap, so compaction failed with "generation hit the token cap". Summary requests now set no `max_tokens`, so the provider fits the model's own limit to the request, as for turns. `reserve_tokens` only decides when to compact. - adfc14461: `/compact` instructions also reach the split-turn summary. When the split turn was the only new content, they were ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts Pi 02726086d, an upstream fix in Pi 1.4.0 (#11227 upstream). This also fixes drift: Pi 1.0.4 already omitted the results at the end of a run, and ours did not. Recovering from a reply cut off at the output limit omitted the reply but kept the tool results that answered its calls. Our provider's transcript repair drops such orphans before sending, but they stayed in the context and its estimates. The omission now also writes a context edit for each result of the omitted reply's calls, as Pi's `_omitRecoveryAttempt` does. This holds before a prompt and at the end of a run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts Pi a447ba11f (earendil-works/pi#10739), an upstream fix in Pi 1.2.0. #1104 had kept the old behaviour on purpose because Pi shared it. A run that a custom message started (`send_custom_message`, or an extension's triggered `send_message`) skipped `before_agent_start`. Its prompt fell back to the base options, so sections the handlers had added were patched away mid-run and the prompt cache broke (Pi #10267). Prompts and custom messages now share `start_run`/`prepare_run`. Both compact first when needed, ask `before_agent_start`, apply the run's prompt options and tools, and send the starting message with the next-turn and handler messages. `BeforeAgentStart` carries a `RunTrigger` (`User { prompt, images }` or `Custom(CustomMessage)`) in place of `prompt` and `images`, after Pi's union on `messageType`. The session stays busy from the start of setup. An abort requested meanwhile now stops the run at `agent_start`; before, it was cleared. Messages added meanwhile go before the run's messages, as when idle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….3.0 Adopts two upstream resource fixes: - 9fe3f3716 (Pi 1.3.0, #10867 upstream): `""` and `''` were dropped from a template's arguments. That shifted the later positional arguments and kept positional defaults from applying. They now keep their place. - f1b2e77f5 (Pi 1.2.0, #10681 upstream): in a worktree nested inside its main checkout, a context file that links to the main checkout's file resolved to the shadowed path. So it was skipped together with the main checkout's file, and no project context loaded. The check now compares the folder's real path joined with the file's name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopts two upstream fixes in Pi 1.4.0's shell lookup: - 61060ddf8: PowerShell is also found in its installation folders (`%ProgramFiles%\PowerShell\7`, then `%SystemRoot%\System32\WindowsPowerShell\v1.0`), and the legacy WSL launcher is recognized in `%SystemRoot%` wherever Windows is installed. `taskkill` ignores an empty `SystemRoot`. - 28bea006b: a Microsoft Store shell alias, which cannot be opened through, counts as an existing shell. Unix lookup is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The crates now follow Pi v1.4.0 (4a080252f). The next review starts there. The review log records this full review of v1.0.4..v1.4.0, its scope and validation, and why bd3c9b823 was skipped. Maple #1389 holds the full adopt/skip table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4fbb64e to
1125298
Compare
|
Review note, recorded here and deliberately not fixed: P3: a broken shell link can be mistaken for a Microsoft Store alias on Windows. We're ignoring it on purpose. Pi 1.4.0 behaves the same way: it checks a shell with |
The first full "catch up to upstream Pi" review of the
pi-*crates, done ascrates/UPSTREAM.mddescribes. It adopts 22 upstream commits, including the three that mid-task model switching needs. It does not switch models or touch Maple's per-task model lock; that is the next PR.Range
v1.0.4(7c10bd433, 2026-10-05), the reference PR #1104 ported, to thev1.4.0release (4a080252f, 2026-10-22). Agent core in Rust after Pi: pi-ai, pi-agent-core, pi-coding-agent #1104's parity review had also checked 1.1.0 (6fb2e78); its notes kept items it had already matched from being redone.packages/ai/src,packages/agent/src,packages/coding-agent/src/core(78 non-merge commits) andpackages/coding-agent/src/utils(6 more). Every one of the 84 is listed below.mainpastv1.4.0was ported.The three must-adopt changes
911309429). Inpi-ai/src/openai.rsconvert_messages, a reply from another provider, API or model keeps its text and tool calls. Its reasoning is no longer sent as visible text. The same model still gets its own reasoning back in the field it came in.15e7bdc14).serialize_conversationno longer writes[Assistant thinking]. This applies to compaction, split-turn and branch summaries.6fcb48670). Before, compaction was capped at 0.8 × reserve, the turn prefix at 0.5 × reserve and branch summaries at 4096 tokens. Summary requests now sendmax_tokens: None, so the provider fits the model's own limit to the request, as it does for turns.reserve_tokensonly decides when to compact.Commits
Each commit names its upstream commits and says whether it adopts an upstream fix or a feature. Each one builds and passes the pi crates' tests on its own.
pi-ai: drop another model's thinking (#11000)pi-ai: repair\uwithout hex digits (4b994d5e5), and report malformed final tool-call arguments (#10944). This addsToolCall::arguments_errorandparse_tool_call_arguments; struct literals inmaple-agenttests andstore-benchgain the field.pi-ai: whitespace-only tool output counts as none (4dd2b149d)pi-ai: retry and overflow texts of 1.4.0, and any HTTP 413 as overflow (9 commits)pi-coding-agent: summaries leave thinking out (15e7bdc14)pi-coding-agent: no fixed summary output limit (#11036), and/compactinstructions reach the split-turn summary (adfc14461)pi-coding-agent: recovery omits a cut-off call's tool results with it (02726086d)pi-coding-agent:before_agent_startfor custom-message runs, and an abort while a run starts (#10739)pi-coding-agent: keep empty quoted template arguments (9fe3f3716), and load a worktree's symlinked context file (f1b2e77f5)pi-coding-agent: Windows shell lookup of 1.4.0 (61060ddf8,28bea006b)UPSTREAM.mdreference revision and review logBehavioural changes to review
These are the riskiest, roughly in order:
send_custom_message, an extension's triggeredsend_message) now start as prompts do:before_agent_start, the run's prompt options, the next-turn messages and the handlers' messages;read_imagehandlers now run for them, so their prompt sections no longer drop out mid-run (Pi #10267: a prompt change and a cache break);BeforeAgentStartnow carriestrigger: RunTrigger::{User { prompt, images }, Custom(CustomMessage)}in place ofprompt/images. Maple's handlers read onlyoptions;agent_start. Before,run_promptcleared the flag and the run went ahead;Invalid arguments for tool "…": <error>\n\nReceived arguments:\n<text>, and the model sees what it sent.New differences from Pi
No new adaptation or Maple policy is added to the crates.
ToolCallEndsent earlier, for a call that can still receive fragments, carries the arguments as they stream.arguments_errorcarries serde_json's message, where Pi's carriesJSON.parse's.regexhas no lookaround, so the status-code pattern spells Pi's boundary as(?:^|[^a-z0-9])…(?:[^a-z0-9]|$). Agent core in Rust after Pi: pi-ai, pi-agent-core, pi-coding-agent #1104 had recorded that ours matched whole numbers and Pi did not; that difference is now gone.win32.normalizerule.before_agent_start" because Pi did the same. Pi changed it, and so does this PR.Storage adaptations revisited
Upstream's session manager and tool output did not change between these tags, so all three adaptations stay.
bd3c9b823is the one change they block. It activates extension tools registered after a branch recorded its tools, and needs every tool the branch ever named; a partially loadedSessionBasecarries only the current snapshot. Maple sets its tools itself and never moves in the tree, so it is skipped and noted inUPSTREAM.md.Validation
All runs were in
apps/maple-agent's pinned Nix shell, with a private build directory. The build log showsCompiling pi-ai … (/Users/admin/workspaces/pi14/…).just ci— passes.fmt, all four clippy runs (-D warnings) and the dependency checks pass.ci-extrapasses: the-D warningsbuild of every target, and 352 headless app tests.ci-test: 2,464 tests in 27 test binaries pass, 8 ignored. (A macOS-only failure in a fix(agent): bound shell execution and show quiet command progress #1378 shell test, from/tmpbeing a symlink, is fixed on master by PR #1398; this branch no longer carries its own fix.)just ci-lintandjust ci-testpassed on every commit.pi-ai,pi-agent-coreandpi-coding-agentpass, andcargo check --all-targets -p maple-agent -p store-benchis clean with-D warnings.\urepair, strict final arguments, a cut-off final call, and validation through the agent loop;before_agent_start, and an abort while the run starts (Pi #10739);%SystemRoot%.Real app, local stack
Maple Work was built from this branch (
just build) and run againstmde smoke pi14, signed in as the local Pro fixture. The task used Kimi K3, with Web off, in a scratch project under/tmp.A reply with thinking, expanded:
A turn with a tool call, which read
notes.txtand answered with its first line:Manual
/compact. At the default 20,000-token keep window a two-turn task reports "Nothing to compact yet", so the task first read two ~50 KB files. Compaction then succeeded on the real model. The app shows a notice, not the summary text:After compaction, a tool-free question about the summarized turns gets a correct answer, so the summary reached the model:
Upgrade check: tasks from master reopened on this branch
The same store was used by a master build and then by this branch's build. Seven tasks were created on master: thinking (Kimi K3), read/shell/write/edit tools,
/compactwith a follow-up, a stopped reply, an image on a vision model, an image on GLM 5.3 throughread_image, and eight turns. All seven reopened on this branch with their transcripts intact, and a follow-up in each that depended on earlier context answered correctly. A new task on this branch also passed, and so did a Claude Code background result delivered back into its task (the custom-message run path this PR changes). Neither build logged errors or warnings.Follow-ups (not in this PR)
check_compaction's overflow and cut-off-reply checks still apply only to the current model's own last reply, as in Pi. After a switch, the threshold check is what compacts before the next prompt.mainafter 1.4.0, for the next review:ContextWindowExceededErrorcounts as overflow.f715a15c1: streamed tool-call arguments are parsed incrementally.6f2f164ab/bd3c9b823(restoring tools on resume and tree moves) would needdefaultToolsand the full branch's tool history inSessionBase.Adopt/skip table
All 84 upstream commits in the reviewed paths,
v1.0.4..v1.4.0.Adopted (22)
911309429(#11000)4b994d5e5\uwithout four hex digits (\usepackage) stays literal instead of dropping the field and everything after it.87f9a3641(#10944)argumentsempty witharguments_error, which validation reports to the model.4dd2b149d(no tool output).6e491cfa9\b, now Pi's boundary.7bd01fb927efebff18550f1f3ec0fbdbd3f8request to … failed, reason:.698a2d23b29057ece3cedefc53273f8de80b15e7bdc146fcb48670(#11036)reserve_tokensonly decides when to compact.adfc14461/compactinstructions also reach the split-turn prefix summary.02726086da447ba11f(#10739)before_agent_start(now with aRunTrigger), next-turn messages and run prompt options; an abort while a run starts stops it.9fe3f3716f1b2e77f5AGENTS.mdthat links to the main checkout's file loads.61060ddf8%SystemRoot%.28bea006bAlready matched (7)
8c4e8738963035734f503c60552agent_settled.abortedported in #1104; OSC 7501 is terminal UI.27c7b6ff4ce8972a0e27075fe078b5708dbbserver_busytexts already retried (#1104).Skipped (55)
4a080252f7f225f25dbd3c9b823SessionBasedoes not carry; its resume half followsdefaultTools(not ported). Maple sets tools itself and does not move in the tree.04a93bad8adc5fa9f8e5fe9777b22dc6c26f26293e74f(#11168)530015d93caa6967b6(#11142)2ec55d8088efd314fa(#11038)95364a54e(#10910)90d863f7c5ab7fe74ca3f265b1c6a1f799b6dc939913fb06c1f2cf910149912(#10938)readlabels.b399797991692bba63d7f7e0dfe(#10913)60ac3bcc3(#10930)195120a916f2f164ab(#10887)defaultTools/--tools, not ported; hosts choose tools at start (#1104).34a8bb38bcea257439/shareand HTML export are not ported.d4d53612be6fb2581dauth.jsonlocking; credentials come from the host.014873d7d6a51a39d4!/+/-resource entries are not ported (#1104).ee409a85f(#10672)24382bedd0f4b1465a6b07b4e577f9e1198f(#9880)f76c1db667fb59f9958d8ae2fc26b5854454fe11328b0f284a2460streamProxy, not ported.3ba22ce1743d37639936a686ee82989eb581(#10142)18336987a(#10557)outputPad.ddaa0a034--tools +name/-nameis CLI anddefaultTools(not ported).0cf65d2bf(#10429)9ad083102(#10433)3e477eab5592fb57b7b30a6dd77imagecrate.f8ab362cc🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.