Repository navigation
Maple Agent on the Pi crates: the runtime, then every feature, then no Goose - #1109
Conversation
5e736e8 to
82e42dc
Compare
Local live validation of the Pi runtimeTested The core workflows exercised successfully with real inference: streaming, file tools, image handling, cancellation, stdio/HTTP MCP, manual compaction, standalone proxy mode, and basic Goose-profile migration. Desktop queueing, steering, plan/question cards, task management, and settings were also exercised. The edge cases below record what happened and under which conditions. They are not severity rankings, requested changes, or merge criteria. Whether each usage pattern is worth supporting, and what complexity is appropriate, is left to the owner and implementation agent. There is no claim that these observations all originated in the Pi rewrite. Environment and scope
Observed edge cases1. Separate desktop and ACP processes can produce different history branches for one taskConditions: two processes use the same isolated account/data root and the same existing task. This is specifically a cross-process observation; it does not establish what happens between two surfaces inside one runtime. Reproduction:
Observed once with confirmed overlap: both prompts were accepted. The desktop visibly completed its turn during ACP's sleep. After relaunch, the desktop prompt/reply were absent from the displayed conversation and the ACP turn was present. Both remain in the JSONL file on sibling parent chains; the desktop records were not irreversibly deleted. Before relaunch, the desktop still displayed its own completed turn. Sanitized protocol and persisted-record evidence (UTC log timestamps): The chain selected after relaunch excludes 2. Returning from Settings during a completed turn can show stale chat stateReproduction: start a long reply, open Settings while it streams, wait for the answer to finish persisting, then use Back. Observed twice: the returned chat showed a partial answer and running indicator even though the complete answer was saved. Selecting another task and reselecting the original loaded the full answer. Subsequent prompts worked. One saved reply contained 1520 words; no corresponding runtime error appeared. This was an observed display-refresh condition, with the answer retained. No Goose comparison was run. 3. ACP
|
|
Following up on the live validation report. Fixed: observation 2 (back from Settings shows a stale chat), in b994f08. The app passed the runtime's events only to the chat on screen, so a reply that finished while Settings was open never got its end. Master's app does the same. The fix still needs a CI run. Postponed: the ACP observations (1, 3, 4 and 5). ACP is not the main path. For this PR the bar is that ACP keeps working on the new runtime as it did before. Rather than patch these one by one here, the ACP layer (Maple's own server code) will be reviewed as a whole later, including its process model: each ACP client starts its own copy of the runtime on the same account data as the desktop app.
Generated by Claude Code |
c584589 to
4639ec5
Compare
6931e8e to
961f4b4
Compare
4639ec5 to
17fb5d3
Compare
b81ca91 to
d95c54c
Compare
17fb5d3 to
338f45e
Compare
d95c54c to
ec74687
Compare
338f45e to
a40c870
Compare
d433cdb to
98220ba
Compare
|
DeepSeek reasoning smoke rerun: all five levels passed on At 2026-10-11 00:46:03–07 UTC (October 10, 5:46 p.m. Pacific), the existing smoke harness exercised Maple's provider adapter through the Rust SDK and local OpenSecret → Tinfoil, using the isolated Pro test account. Off ( The earlier DeepSeek batch ran at 2026-10-10 23:37:31–32 UTC, with 502/503 results; local logs confirm Tinfoil returned the initial 502s. That timing falls inside the Tinfoil outage window reported by the owner. The successful rerun strongly supports the outage as the explanation, though it does not independently prove the incident's root cause. This closes the previously unverified DeepSeek server-acceptance check: all 29 tested model/level choices now have a successful live result across the original run and this targeted rerun. This was a short acceptance/stream-completion smoke, not a reasoning-quality comparison. |
98220ba to
b4b41a5
Compare
2a33432 to
76309fc
Compare
b4b41a5 to
b838666
Compare
|
This failure is not caused by the PR. The stack pins the same GPUI revision as master ( Generated by Claude Code |
Master restack and live validation — 2026-10-11Restacked the three phases onto master
Conflicts/fixes: none. Range-diff reports all 1 / 77 / 31 commits unchanged. Commit messages and trailers are unchanged. The Pi crate trees are identical to their corresponding old heads. The new top tree is the old top tree plus master's changes, with no extra application changes. Native stack #1125 still has #1124 → #1104 → #1109. Draft states were preserved; nothing was merged. macOS checks before pushingmacOS 27.0.1, arm64. Full
Every #1109 commit separately passed Per-commit macOS checksEvery row passed formatting, workspace/all-target Clippy with warnings denied, and warning-denied workspace tests with
Hosted Agent CIAll nine Agent jobs passed on the exact new heads: Linux, macOS and Windows for each PR. Runs: #1124, #1104, #1109. Live retest of
|
| Area | Result |
|---|---|
| Streaming and file tools | Passed: read, bash, edit and write on all three model categories; expected files and replies. |
| ACP basics | Passed: handshake, new/prompt, model locking, replay, usage windows, provisional-session close, mode refusal and independent sessions. |
| Cancellation | Passed: bash and descendants ended; immediate cancellation, follow-up recovery and client-disconnect cleanup. |
| Images | Passed: local, HTTPS and attached images, read/read_image, vision and text-only paths. MCP images for a text-only model remain the documented note. |
| MCP | Passed: HTTP tools including an image result, account stdio tools and process cleanup. Startup-notice observation below remains. |
| Trust | Passed: untrusted project instructions/commands excluded, trusted instructions effective and trusted slash commands execute. Initial command-list observation below remains. |
| Ignored skills | Passed: the skill excluded by its folder's .gitignore was absent from / and from the model's reported skill list; the unignored skill remained. No discovery tool calls were used. |
| Nested linked worktree | Passed: the worktree's AGENTS.md marker was present and the main checkout's marker absent, without discovery tool calls. |
| Compaction | Passed: short-history response, long manual compaction, and automatic compaction with notices at 56,856 tokens using an explicit 64,000-token window. This is a bounded trigger/recovery check, not a production-window stress test. |
| Bash background/output | Passed: ordinary background child survived its command; the model received the last 2,000 of 3,000 output lines, and the full output file retained all 3,000. |
| Desktop | Passed: live Low and High thinking turns, changing the existing task's level, queue delivery after a running bash call, and steering a queued instruction into the current run. |
| Brief backend interruption | Passed: stopped/restarted the local backend mid-reply; the reply completed. This confirms this recovery scenario, not every retry condition. |
| Retry exhaustion | Passed over ACP after 181.09 seconds: Network error: could not connect to Maple, not Request timed out.. The desktop run also settled to one row with that same message after retries ended. Both this build and 98220ba6 displayed multiple error rows while retries were in progress, then one row when the run settled. |
Observations reproduced on the previous 98220ba6 build
Reran the affected cases on that exact old binary, with separate state and the same local backend:
- A config change waits for the active ACP prompt to settle/cancel. An overlapping prompt is refused.
session/deleteleaves an archived entry visible insession/list.- A failed MCP-server startup is not announced to the ACP client.
- The first available-command notification after newly trusting a project omits its newly enabled skill, although invoking the skill/template succeeds.
These observations also occur on 98220ba6; no fixes or decisions about their intended behavior were made here.
One initial long-output harness flag was a measurement error: it inspected the separately capped ACP display, not the tool result delivered to the model. Reading each build's persisted Pi session and saved output confirmed the correct 2,000-line tail and 3,000-line full file on both builds.
Not run in this retest
- Hosted OAuth return-to-app: only Local credentials were available; no authorized Dev account was used.
- System One: Maple has no live caller yet.
- Buzz, as requested.
- No fresh live external-agent, built-in CUA, voice, notification, proxy, exhausted-credit/revoked-login or Goose-profile migration run; earlier results are not presented as newly retested.
- Live Linux/Windows execution; coverage there is hosted Agent CI.
The local backend was restored after the interruption tests. No application code was changed during validation.
b838666 to
32c08e7
Compare
now_ms returns a later time on every call, a millisecond ahead of the clock if need be, so a message's timestamp tells it apart from every other message the process made. Maple keys its timeline rows by it. Pi has no equivalent: its messages take the plain clock. This is a Maple-driven adaptation of the Pi crates, so it stays with the Maple integration until the owner reviews it; Maple could key its rows another way instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
The Goose runtime moves to crates/maple-agent-goose, as the package maple-agent-goose, so the runtime on the Pi crates can take crates/maple-agent. The code is unchanged and stays compiled for side-by-side checks until it is deleted; the app keeps using it under its old name until it switches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
The runtime crate is now built on pi-ai, pi-agent-core and pi-coding-agent. The Goose runtime moves to crates/maple-agent-goose, unchanged and still compiled, for side-by-side checks until it is deleted. What runs on Pi: - Maple's provider: Pi's Chat Completions provider over OpenSecret's encrypted transport, with Maple's catalog as Pi models. Every failure becomes one fixed Maple message per category, worded so Pi's retry and context-overflow checks classify it. The transport times out a response that does not start or stalls, so Pi's own request timeout is off. - Tasks: one JSONL session file per task in the account's owner-only folder, and a SQLite index of titles, state, web switch and settings that lists tasks while the runtime is stopped. - Runs: one per task, through one session factory. Pi's events become Maple's timeline rows, with ids that stay the same from the live rows to the reloaded ones. Stop keeps the reply so far and adds "Stopped by user". Messages sent during a run wait as chips and follow as one turn; steering joins the running turn. - Maple's read, edit and write tools as Pi tools. The account's MCP server list is checked and saved as before: names, commands, endpoints, environment variables and headers, and no new server under the computer use integration's name. Reads and saves of the account's settings take one lock, as they took Goose's runtime lock, so a save cannot overwrite a change made since its read. Features that have not moved yet answer from placeholders: running MCP servers, integrations, external agents, skills and slash commands, side models, image attachments and ACP. The app imports the new crate. Its Goose wording for compaction notices goes, since the Pi runtime never sends those strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Tasks now get Pi's read, bash, edit and write from the Pi session, or powershell in place of bash on Windows without Git Bash. Maple's own read, edit and write go; the timeline shows bash and powershell calls as Terminal rows. Maple sets up how their commands start, through bash's spawn hook: - the login shell's PATH, so an app opened from the Dock or a desktop launcher finds the tools the user's terminal finds. It is read once per process, from when the runtime starts, behind a marker so profile banners cannot be mistaken for it, bounded in time and output, and everything the probe starts is stopped; - the task's id as AGENT_SESSION_ID; - the task's tool context, so the ACP bridge's variables stay out. MAPLE_SHELL names the bash to run and replaces GOOSE_SHELL; the README says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Settings and the composer's integrations chip list Maple's curated integrations again, as Goose's runtime did: built-in computer use (Cua), and the Codex and Claude Code command lines. - A card joins what Maple finds on the device with the choice saved for new tasks in the account's device-local integrations.json, version 2. A file from a newer build is reported and left alone, and a choice of the retired standalone CuaDriver is dropped. - Codex and Claude Code are looked for on the login shell's PATH once it has been read, else on the app's own. Each version is read with a probe of three seconds and bounded output; a Codex older than 0.143.0 needs an update. Sign-in is reported but does not gate the switch. A probe runs in its own process session, which is ended afterwards, and on the PATH it was found on, so a script finds its interpreter there (Goose ran the version probe on the app's PATH). - An external agent can be switched on only while its CLI can run, and off at any time. Built-in CUA reads as not available until embedded CUA returns: it cannot be switched on, and its setup finishes nothing. Switching it on is refused while a custom MCP server has its name. - The choices are read and saved under the settings lock; detection runs before it is taken. What a choice does in a task moves with external agents and embedded CUA, and so do the skills Goose's runtime installed for external agents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Desktop tasks get Maple's todo_write and request_user_input again, with Goose's schemas, descriptions and results: - todo_write records the plan the user watches. The interface draws the list from the call, and the model gets it back, confirmed. - request_user_input asks one to three questions on a card and waits for the answers. A stopped run takes the card back, and an empty answer reads "(no answer provided)". As in Goose, a near miss does not dead-end the model: `questions` as one object, one question at the top level, missing or extra fields. Blank questions and options are dropped, ids made unique, and a card holds at most three questions of five options. The questions go through the broker of the service that started the runtime, which the interface answers, instead of the process-wide one Goose's tool clients had to reach for: a service built later, or a test running alongside, no longer takes another runtime's questions. ACP tasks get neither, as before. Errors read as Pi's tools' do, without Goose's "Error: " prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Tasks get Maple's web_search and open_url again, with Goose's schemas, descriptions, limits and checks, through Maple's privacy-preserving web provider: - web_search returns links, titles and short snippets as JSON marked as untrusted evidence, dropping results from the end until it fits 64,000 characters, so it stays valid JSON. - open_url fetches one public HTTPS page as sanitized text, bounded to 32,000 characters without cutting a code delimiter, which would turn inert image code back into an image. The URL must use HTTPS, carry no credentials and name a public host: loopback, private, link-local, documentation and internal names and addresses are refused, embedded IPv4 included. - Errors are bounded to the same limits, and read as Pi's tools' do, without Goose's "Error: " prefix. As in Goose, a task's web switch decides whether the tools are offered, and a change takes effect from the task's next run. Desktop and ACP tasks both have one. New dependency: pulldown-cmark, already in the lockfile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
The small requests Goose's runtime made beside a task move to the Pi runtime, with Goose's models, prompts, limits and timeouts: - A new task's title: the first prompt still names the task at once, and llama3-3-70b is asked for a three-to-five-word title, which replaces it unless the title changed meanwhile, by the user or anything else. The answer is cleaned (reasoning blocks, quotes, control characters) and bounded like any title. - One-line summaries of a finished tool call or thinking block for the activity feed, from the same model, at most 100 characters. - /btw side questions: the task's model gets the task's context, then the side thread's earlier turns and the framed new question. The task's tools stay in the request, so the provider can reuse its cached prefix, but tool_choice is none. The answer streams as SideQuestion events and is not saved to the task. Titles and side questions end when the runtime stops; a request that times out is cancelled and drained, as Goose's were. The requests go through the runtime's model registry, so they take the same transport as the task's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Every task gets Maple's read_image again, with Goose's schema, limits and messages: - An image from a Maple attachment of the task, a local file (relative to the task's folder, or a file URL), or an http(s) URL. A URL must name a public host, carry no credentials, and answer within 30 seconds without a redirect, which could send the fetch to a loopback or cloud-metadata address. Files must be regular files. - At most 20MB, PNG, JPEG, GIF or WebP. An optional crop comes back as a PNG of the cropped rectangle. The result is a one-line summary and the image, which Pi then fits for the model as it fits every tool's images. - A task whose model cannot see images gets the image described instead: the tool takes a required context, and gemma4-31b describes the image for it, with thinking turned off in the request, as Goose sent it. A failed description says what was loaded and why it failed. Goose staged the image in a temporary file for its own image tool; the port decodes the bytes it read directly. The description's token usage is not recorded yet: that moves with the usage ledger. New dependency: image, already in the lockfile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Tasks get Pi's resources: instruction files, skills, prompt templates, and SYSTEM.md and APPEND_SYSTEM.md, as Pi loads them, from the account's own folder and a project's .maple folder where Pi has ~/.pi/agent and .pi. - Instruction files: AGENTS.md (or CLAUDE.md) from the project folder and every folder above it, the account's, and ~/.agents/AGENTS.md, which Maple's Goose runtime read too. They load in every project, as in Pi. Goose's .goosehints is not read. - Skills: the account's, ~/.agents/skills, and in a trusted project .maple/skills and .agents/skills up to the repository root, as Pi reads them. The folders Goose's runtime read besides, ~/.claude/skills and ~/.config/agents/skills, and in a trusted project .claude/skills and .goose/skills, are Pi's extra skill paths, so they come after the user's own. The model sees each skill's name, description and file in the system prompt and reads the file with the read tool, as in Pi; Goose's load_skill tool is gone. - Prompt templates: the account's prompts folder and a trusted project's .maple/prompts. - Resources are read again at each run and a loaded session takes them through set_resources, so a skill or instruction file changed since shows from the next run, as Goose's skills did. Only the prompt sections that changed go out again. The composer's / list holds the skills, then the prompt templates whose names no skill has, with argument hints from a skill's frontmatter (top level or metadata, as Maple's bundled skills give them). A command expands into the skill's instructions as Pi's /skill:name does, with the arguments after them, or into the filled template; names match regardless of case. Resolving a command now takes the account, so the app passes it. Project trust covers everything a project adds besides its instruction files. The trust prompt asks when trusting would add a skill or template that does not load now, or a .maple/SYSTEM.md or APPEND_SYSTEM.md, and names which (two new protected features). Before, it looked only for .agents/skills/**/SKILL.md, and the / list offered and ran an untrusted project's skills, which the model's load_skill refused; now an untrusted project's skills are neither listed nor run. The README describes where instructions, skills and templates come from, and its file table drops the Goose rows: task sessions and their index are in the account's sessions folder, and the account's skills folder replaces the Goose one, where the delegation skills will go when external agents return. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Maple always appends its opening instructions, and the host's appended text takes the place of APPEND_SYSTEM.md, as Pi's own flag does. So a project's APPEND_SYSTEM.md never reaches a task, and trusting the project for it would add nothing: only its SYSTEM.md now counts for the trust prompt. The README no longer lists APPEND_SYSTEM.md among the account's files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Images attached in the composer reach the model again. They are stored with the task as before: owner-only, PNG, JPEG or WebP, at most ten per message and 10MB each. - The model gets what the user typed, then each image's name and maple-attachment source, as Goose's runtime wrote them. A model without vision is told to look with read_image, which reads the task's attachments. A model with vision gets the images beside the text; unlike Goose's runtime, its text names their sources too, so read_image can crop one. - A message sent during a run waits as a chip with its images, steering carries them, and a stopped run puts its unsent messages back with theirs. A message Pi had already taken for steering comes back with its images by source only. - The timeline shows what the user typed with the images, live and after a reload, read back from the message itself, so no other record is kept. The interface draws them from the task's attachments. - A task is named from its first message with text; images alone do not name it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
A task's MCP servers run on rmcp's client, by the conventions of Pi's
MCP support. Maple starts a stdio server itself, so it can stop it as
Pi does; a Streamable HTTP server is reached through reqwest.
Which servers a task has is saved with the task, by name: a new task
gets those switched on in Settings, or exactly the ones the composer
names, and the composer's menu switches them per task. A task runs the
servers' current settings, so an edit applies from its next run, and
no secret is copied into the task. A chosen server that is no longer
configured stays listed, switched on and unavailable, and can only be
switched off.
The servers start with the task's run, in the background, and the run
waits up to ten seconds for them before its first prompt, as Pi's first
prompt does; one that connects later joins from the next prompt. Their
tools reach the model as mcp__<server>__<tool>, with the server's
descriptions and schemas, and their results as text and images, long
text cut in the middle with the whole saved to a file. Tool list changes
are read again, and a dropped connection opens again on the next call.
As in Pi, a tool is never taken from the session: one the server
withdrew, or of a server switched off or stopped, stays, and calling it
fails with Pi's words until the server offers it again. The connected
servers' instructions join the system prompt. A server that fails to
connect is named in the run's notice with Maple's wording, once, and is
tried again at later runs without holding them up. Switching a server on
for a loaded task connects it at once and keeps it off with the reason
when it fails.
A stdio server runs in the task's folder with the login shell's PATH
and its environment variables, in its own process group, and stops by
its input closing, then SIGTERM, then SIGKILL to the group, which also
ends what it started; a failed start shows the end of its error
output. An HTTP server's URL and header values take $NAME and ${NAME}
from its environment variables, and redirects are not followed. Calls
time out by the server's timeout, which progress resets, and Stop
tells the server the call was cancelled.
The servers live with the task's loaded session and stop when it shuts
down; the four idle tasks used last keep theirs. MCP tool rows read as
"server: tool".
A fake Streamable HTTP server in the tests drives rmcp's client for
real: the handshake, calls, progress, cancellation, timeouts and tool
list changes, and three runs end to end, among them a withdrawn tool
and a server switched off, whose tools stay and fail.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
The composer's context ring read the latest row of Goose's usage ledger in its sessions.db, which nothing writes now that Goose is gone, so the ring stayed empty. It now asks the runtime, which counts the task's context as Pi does: the last reply's reported usage, plus an estimate of what came after it, which also covers a compaction. A loaded task counts its live session; any other task, its session file, which a test checks gives the same count. A task with nothing in it yet has no count, as before. The limit still comes from the model catalog or MAPLE_CONTEXT_LIMIT. The app drops its read-only handle on Goose's sessions database. The ledger had no other reader: side-model and image-description usage was recorded there only for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
A desktop task can hand work to Codex or Claude Code again, with the Goose runtime's agent_start, agent_send, agent_status, agent_cancel and list_agent_providers tools. The Codex app-server client, the Claude Code stream-json client, and the host that runs them (activity, approvals, questions, Stop, process groups) are the Goose runtime's, with its Goose types replaced: - The tools are Pi tools every desktop task has. A run declares them only while the task may use a provider: one switched on in Settings and for the task. The task's choice is saved with the task, as `externalAgents` in its settings, and its MCP menu lists the agents switched on in Settings, as before. - A turn's progress repaints the row of the tool call that started it. The tool finds that row from the reply Pi stored before running it. - A background turn's result reaches the model as a custom message the user does not see. Maple keeps it out of sight in the task's queue behind a running turn, sends it as a turn of its own, or starts a run for it. A run stopped before it went leaves it in the task's history for the next message, as Goose wrote it to history on Stop. - The turn's end is a session entry the transcript projects back onto the call's row, so a reopened task shows what the agent did, followed by a notice. A task reopened while a turn runs shows the row as it stands, and the row above the composer and its Stop button come back. - Deleting a task, stopping the runtime or signing out ends its agents without a report. - The /handoff, /committee and /advisor skills go into the account's skills folder, which Pi reads, while an agent is on in Settings. One change from Goose: an agent whose command could not start no longer keeps one of a task's four places. The driver tests run the Goose runtime's fake Codex and Claude Code CLIs. New end-to-end tests run them under the runtime with a scripted model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Computer use runs in desktop tasks again, with the Cua Driver SDK in Maple's process as before: the SDK dependency at the same pin returns (Linux with portal input, and zbus for the GNOME helper check), with its Nix hash and the vendored GNOME Shell helper, which is byte-identical at the pin, so the helper's recorded revision is now the pin. What the Goose runtime did, on Pi: - Each run binds a task that has CUA on to its own account-and-task scoped CUA session anew, which renews its authorization, and registers the catalog's tools on the task's Pi session as cua-driver__<tool>, with the instructions in a prompt section. The lifecycle tools and every session argument stay hidden. The binding happens beside the MCP servers' start, and a failure is named in the same notice, as "Computer use (CUA)". The binding closes with the task's session, or when CUA is switched off for it; the tools stay, as in Pi a tool is never taken from a session, and calling one fails until the task is bound again. - Every model gets the observation's structured grounding as bounded, image-free text; a model without vision, read from the run's model, gets each screenshot described by the side model in its place. - A task's choice is saved with it, as computerUse in its settings. A new desktop task records one once CUA is set up on the device: on when the composer names it, or else the default for new tasks. The MCP menu ends with CUA's row, and switching it on binds a loaded task at once. - The Integrations card reads the device's permissions, the GNOME helper's state and the SDK version; setup asks for the permissions on the click's thread and installs the helper where it is missing. Pi fits a screenshot like every tool image (2000 pixels, 4.5MB), with its note on mapping coordinates back; Goose sent screenshots as captured. Helper usage is not recorded. The unit tests are Goose's, on the new types; the SDK's own runtime runs the lifecycle test. An end-to-end test covers the switch, the row and new tasks' choice, and, where CUA can run, a real binding's tools, which fail once CUA is switched off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
`maple-agent acp` serves the Agent Client Protocol again, now on the Pi runtime: the agent-client-protocol dependency returns at the same pin, and the server's transport, handler, conversions and session state are ported from the Goose runtime's, with its protocol surface unchanged (protocol 2, model selector only, no modes, list, load, close, delete, /compact and the skills' commands, images, usage, Buzz's systemPrompt and session title). The runtime gains what a calling surface needs, in agent::surface: - A lease holds each task a connection drives. While it lasts, the task's session is built with the lease's tool context (the bridge's allowlisted variables), the desktop's own tools stay out of it (the plan, questions, external agents and computer use), and the client's HTTP MCP servers run beside the task's, in place of a saved one of the same name. The desktop cannot send to a held task. Releasing revokes the context, stops the task's run and unloads its session. - `send_surface_message` runs a prompt as a run of its own, after any run the surface stopped has ended, with events for the caller alone; the prompt's cancellation stops the run from its setup on. - A task created over ACP is provisional until its first prompt: lists leave it out, and closing it untouched deletes it. A runtime start removes provisional tasks a crash left behind for more than ten minutes. - The caller's system prompt follows Maple's own for the tasks it creates, for as long as the runtime runs. - Trust asked over ACP is saved for the session's project, and the task's session is built again with it. How a turn reaches the client on Pi: text and thinking stream as they come, and the row that settles a message sends only what did not stream; a tool call is announced with its kind, which result updates keep. A failed reply is told when the run ends failed, since Pi may still retry it. Runs now publish `Compacting` and `Compacted`, which the client sees as the reference adapters' notices; the desktop ignores them and reloads on `HistoryReplaced` as before. `usage_update` counts the task's context as Pi does rather than the turn's tokens. `/compact` on a history too short to summarize says so instead of failing. The dispatcher no longer waits on slow requests: prompts are admitted in order, so a `session/cancel` right behind one finds it, and everything else about them, close, load and config changes run in the background. The ported unit tests run unchanged where the shapes did; stdio tests on the scripted provider cover a full turn and its replay, cancel, a failed turn, /compact, images through read_image, the trust prompt, and Buzz's fields; runtime tests cover leases, provisional tasks, the bridge context reaching a command, a client's MCP server, and the startup sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Cargo resolved the packages that built-in CUA and ACP brought back at the latest compatible versions, so twelve of them came back newer than the lockfile Maple shipped with: async-compat, camino, crossbeam-channel, ctutils, hybrid-array, lru, textwrap, wl-clipboard-rs and zstd-safe for the Cua Driver SDK, and jiff-tzdb and serde_with (with its macros and darling) for agent-client-protocol. They are pinned back, so every package those two commits added is one the shipped lockfile had, at its version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
With every feature on the Pi runtime, the crate's temporary `allow(dead_code)` goes, with what the Goose runtime left unused: the transient MCP server types ACP's servers no longer need, a usage delta, a permissions helper for Goose's config file, three tool context methods (the launch fence's test now covers revocation), and the tool context's ephemeral flag, which nothing reads. Renaming a task refuses a signed-out session again, as it did before the port. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Three rows named Goose-era tests that are gone: the queued follow-up now names the Pi runtime's test, and the old computer-use row checks settings an older build saved, as its tests do. The row for an old task's `delegate` history checks instead that the app starts on an account with Goose-era tasks, which the Pi runtime does not read. The tools row says `bash`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Pi's `read` opens images, and gives a model that cannot see them Pi's note that the image is left out. Goose's runtime answered such a read with the way to a description: the file is an image, to inspect with `read_image`. A Pi extension of each task's session answers so again, through Pi's `tool_result` rewrite, while the session's model cannot see images; a model with vision still gets the image, as in Pi. The prompt of a model without vision carries Goose's line again: "Use read_image when you need to inspect an image." `read_image` was set up for the model its session was built with, and a task loaded for a side question or `/compact` is built without vision, so a run on a model with vision could get descriptions in place of images, and the reverse images it cannot see. The same extension sets it up again on Pi's `model_select` when the model's vision changes. It now resolves a local path as `read` does, macOS screenshot names included, so the path `read` answers with opens. Images in MCP servers' results still reach a model without vision as the provider's note that it does not accept images. Both run end to end on the scripted model: `read` on an image for a model without and with vision, and `read_image`'s schema for runs that can and cannot see after a load without vision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Windows builds the runtime without built-in CUA, so the screenshot descriptions only CUA's driver asks for, and the fake agents' search path only the Unix end-to-end tests set, went unused there once the crate's `allow(dead_code)` went. They build where their callers do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
A run's model is built from what its host sends: the model's vision and context window. Neither host sent the window, so every run compacted at the 128,000-token default, whatever the catalog gives the model. ACP also looked up vision only for a prompt with images, as it did under Goose, so a vision model ran as one without vision on every other turn: `read` sent it to `read_image`, which described images it could see. The runtime now answers one catalog lookup with both, `model_catalog_entry`. ACP looks it up on a session's first turn and keeps it until the model changes, and sends both with every turn. Its `usage_update` reports the same window. The desktop keeps each model's entry where it kept vision and sends the window with each message. The context ring already showed the catalog's window; compaction now agrees with it. A model the catalog does not describe, or a catalog that cannot be read, still runs without vision and on the default window. ACP asks again on the next turn. The ACP server runs a turn without images on a catalog that marks the model with vision and a 64,000-token window. The run's model sees images and has that window, `read_image` needs no context, and `usage_update` reports 64,000. The runtime's lookup gives a listed model's entry, an empty one for a model the catalog does not list, and none while the catalog cannot be read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
The app passed the runtime's events to the chat only while the chat was on screen. Settings parks the chat, so a reply that went on streaming, or ended, while Settings was open lost its last rows and its end: back from Settings, the chat showed the partial reply with its run still going until another task was selected and this one again. Master's app does the same. The parked chat now gets the events too, as it already gets the update banner. It only updates its own state from them. Found in live validation. The window code has no test harness; Maple Agent CI builds it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
The scenario checklist records the live run of 2026-10-09 on 680ca68: what passed, what was not run, and what it found, with the ACP findings deferred to a later review of the ACP layer. The README says what MAPLE_CONTEXT_LIMIT does: it replaces the catalog's window for compaction and the context ring, rather than filling in a missing one. Its ACP section asks for a task to be used in one place at a time, since the desktop app and `maple-agent acp` are separate processes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Check that ordered steering survives session setup and an early stop. Keep the first prompt and accepted steers in their original order. Co-Authored-By: Codex <noreply@openai.com> Codex-Session: 01a1192f-4f37-7821-af8a-76570a6f5f1f
Map catalog reasoning controls to Pi and clamp its default per model. Persist task picks through Pi and save the choice for new tasks. Keep side requests independent of the composer selection. Co-Authored-By: Codex <noreply@openai.com> Codex-Session: 01a1192f-4f37-7821-af8a-76570a6f5f1f
Maple's model registry now serves Pi's System One classifier as well as its chat models: the classifier API over the account's encrypted transport, and System One's model (glm-5-3-flash, the only one Maple's API serves), which judges text and images. Any Maple code or extension with the registry can classify with it. As Pi's classifier requests do, a request the server fails or asks to wait is sent up to two more times. The transport sends Pi's System One requests to /v1/systemone, where it sent only chat completions before, and asks for a JSON reply there. A failed reply keeps Maple's fixed message, as for chat, so System One's error carries no part of the server's body. Responses now carry their headers, names in lowercase, as Pi's transports give them: Pi's response hook sees them, and a classifier retry follows the server's retry-after or x-should-retry. Chat requests still send nothing again themselves, so headers change no retry there. Nothing in Maple classifies yet. A result carries its own usage, for the feature that asks to report: the server counts what Maple bills, and a run's usage counts its replies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Buzz is out of scope, so the ACP server keeps what any editor uses and loses what only Buzz's bridge needed: - `_maple/bridge/hello`, and the bridge's variables (Buzz's credentials) read from the process or from a `buzz-dev-mcp` server definition and handed to a session's commands. A client's stdio MCP server is now always refused. - The tool context those variables travelled in, with its scrubbing of `BUZZ_*` from every task's commands and external agents, its revocation and its launch fence. A lease still ends its hold at once when it is released, and an ACP task's commands keep the PATH the client started Maple with, as they did through the context. - `session/new`'s `systemPrompt` and `_meta.sessionTitle`, and the runtime's per-task system prompts. ACP tasks are named from their first prompt, as desktop tasks are. - Protocol version 2, which told Buzz that `session/new` takes its `systemPrompt`: `initialize` answers with the latest stable version, 1. - The count of connections that carried Buzz's credentials. The README's ACP section, the external agents doc and the scenario checklist drop Buzz, and two errors no longer name a tool context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
Maple Agent's ACP has no users yet, so its configuration needs no migration: a missing file means the defaults, and Maple no longer looks for one in the proof of concept's account folder to copy over. Copying it was all that wrote the file, so saving goes too: Maple only reads it. The note and test for fields that older builds saved go, and a test of loading the file replaces the one of saving it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d
e774410 to
52a8002
Compare
Stacked on #1104 (base
claude/kind-turing-gzv8nl). Do not merge: nothing in the stack merges until the end result is accepted.What this is
Phase 2 of replacing Maple Agent's embedded Goose runtime: Maple's runtime rebuilt on the Pi crates from #1104 (
pi-ai,pi-agent-core,pi-coding-agent). The app switches to it, every feature moves over, and Goose is deleted. Where Pi has a built-in, Maple uses it; Maple keeps its own code only for what Pi does not have.Every feature has moved: the vertical slice (a desktop task runs end to end on Pi, and the app no longer calls the Goose runtime), Pi's built-in tools, Maple's own tools (
read_image, plan, questions, web), the side models (generated titles, summaries,/btw), instruction files, skills and slash commands on Pi's resources, image attachments, custom MCP servers per task (on rmcp, by Pi's conventions), the integration cards, the context ring, external agents (Codex and Claude Code), built-in CUA, and ACP agent mode (maple-agent acp). Live checks ran on 2026-10-09 and again against the restacked implementation on 2026-10-10 (see Live validation). It stays a draft until the stack's merge is decided.Goose is gone: nothing called its runtime once the app switched, so commit 5 deleted it, and the later features were ported from its code in history.
Commits
crates/maple-agent-goose, as the packagemaple-agent-goose. Code unchanged.crates/maple-agent-goose, the Goose and Cua git dependencies (Cua returns with embedded CUA), their Nix hashes, and 241 packages fromCargo.lock. No package was added or changed version; a few lose features only Goose turned on. The default log filter no longer namesgoose, and the README describes the Pi runtime.todo_write,request_user_input).web_search,open_url)./btw.read_image.APPEND_SYSTEM.mddoes not ask for trust, since Maple's opening instructions always take its place.rmcp3.4.1 (client only: its async read/write transport for stdio, and the Streamable HTTP client overreqwest).agent-client-protocolreturns at 2.2.0. Runs publishCompactingandCompacted, which the app ignores (it reloads onHistoryReplaced, as before).allow(dead_code), with what the Goose runtime left unused, the tool context's ephemeral flag among them. Renaming a task refuses a signed-out session again.bash.tool_result,model_selectandbefore_agent_start) gives such a model Goose's answer fromreadfor an image and keepsread_imagedescribing exactly while the task's model cannot see.usage_updatereports that window. The desktop sends the window with each message, as it sent vision.MAPLE_CONTEXT_LIMITdoes now, and using a task in one place at a time over ACP.BUZZ_*scrubbing;session/new'ssystemPromptand_meta.sessionTitle; and protocol version 2, which only told Buzz aboutsystemPrompt. An ACP task's commands keep the PATH the client started Maple with.Changes to the Pi crates
The Pi features this phase first needed moved down to #1104 on 2026-10-10, since they are Pi's: a transport's cancellation and a host-chosen session id, resources replaced between prompts, images in steering and follow-ups,
truncate_middleandwrite_output_file, tools registered after the session starts, and a custom message that starts a turn. The stack's result is unchanged.One change that Pi does not have stays here, a Maple-driven adaptation the owner reviewed and kept:
The owner dropped the other two on 2026-10-11, so the stack now does as Pi does:
unregister_toolis gone. As in Pi, a tool is never taken from a session (see MCP servers and Built-in CUA).containis gone, and commit 30 removes the ACP bridge whose credentials it contained (see ACP).The runtime on Pi (
crates/maple-agent)Provider (
agent/provider.rs)MAPLE_CONTEXT_LIMIToverrides)./btwcontinue to send no thinking control./v1/systemoneand ask for a JSON reply, with the one model Maple's API serves for it,glm-5-3-flash. As Pi's classifier requests do, one the server fails or asks to wait is sent up to two more times, after the server'sretry-afterorx-should-retry. A failed reply keeps Maple's fixed message, so the error carries no part of the server's body. Nothing in Maple classifies yet: a result carries its own usage for the feature that asks, and the server counts what Maple bills.Tasks (
agent/store.rs,agent/tasks.rs)Runs (
agent/runtime.rs,agent/runs.rs)maple-attachment://source, as Goose's runtime wrote them: a model without vision is told to look withread_image, and one with vision gets the images beside the text (now with their sources, soread_imagecan crop one). Chips and steering keep their images; a message Pi had already taken for steering when the run stopped comes back to the queue with its images by source only. The timeline reads the typed text and the images back from the message itself, live and after a reload. A task is named from its first message with text.Settings (
agent/mcp.rs,agent/integrations.rs,agent.rs)PATH,LD_PRELOAD,NODE_OPTIONSand the rest of Goose's list). A new server cannot take the computer use integration's name; one an older release saved stays saveable. Listing checks the saved list the same way.integrations.json(version 2; a newer build's file is reported and left alone, a retired standalone CuaDriver choice is dropped). Codex and Claude Code are looked for on the login shell's PATH once it has been read, else the app's; their version is read by a three-second probe with bounded output, in its own process session that is ended afterwards (on Windows, its process tree is ended if it overruns), and on the PATH the CLI was found on (Goose's version probe used the app's). A Codex older than 0.143.0 needs an update; sign-in is reported but does not gate the switch. An external agent switches on only while its CLI can run, and off at any time. Built-in CUA's card reads the device's permissions, the GNOME helper's state and the SDK version (see below).MCP servers (
agent/mcp/)Some MCP servers could not connect: …, at most three), once; later runs try it again without waiting for it. Goose's runtime reported it the same way at a run, but then switched it off for the task, and said nothing when it failed as the task was created.mcp__<server>__<tool>(everything outside[A-Za-z0-9_]as_, at most 64 characters, a hash for a name too long or taken), the server's description (or title) and input schema, all declared to the model directly. Results become text and images: audio and resource links as a line, embedded text as text, blobs as images, text, or a file the model can open; structured content when there is nothing else; an error result stays an error. Text beyond 20KB is cut in the middle in Codex's format and saved whole to a file. A tool list change is read again: new tools join and changed ones are replaced. As in Pi, a tool is never taken from the session, and it keeps its name for the session's life: one the server withdrew, or one of a server switched off or stopped, stays declared, and calling it fails with Pi's message (MCP server "…" is disabled.,… is still starting., orMCP tool "…/…" is no longer available.) until the server offers it again.mcp_serverssection, as Goose's did.~expanded, in its own process group. It stops by its input closing, then SIGTERM after 500ms, then SIGKILL after 2s, to the group, which ends what it started (Goose's runtime killed only the direct child, sonpxcould leavenodebehind). A failed start shows the end of its error output.$NAMEand${NAME}from its environment variables, as Goose's did; redirects are not followed, so headers do not travel to another host.server: tool.External agents (
agent/external_agents/)accept, Claudeallow), questions on Maple's card (async Codex questions answered in a turn of Maple's own), activity bounded as before, at most four agents per task, a process group per agent that Stop and shutdown kill, and the login shell's PATH.agent_start,agent_send,agent_status,agent_cancel,list_agent_providers) keep Goose's schemas, descriptions, result text and errors (without Goose'sError:prefix). Every desktop task's Pi session has them; a run declares them only while the task may use a provider, one switched on in Settings and for the task, and a call must name such a provider.externalAgentsin its settings; the composer's menu lists the agents switched on in Settings after the MCP servers and switches them per task, with Goose's rules and messages. Each task starts with none, as before./handoff,/committeeand/advisorskills are written to the account's skills folder, which Pi reads, while any agent is on in Settings, and removed when none is; files without Maple's marker are left alone. Goose kept them in its own config folder.codex resumeorclaude --resumestill reach the threads), and a Windows job object (the process tree is ended withtaskkill /Tinstead).Built-in CUA (
agent/cua/)cua-driver__<tool>, with the instructions in a prompt section. The lifecycle tools and everysessionargument stay hidden. The binding happens beside the MCP servers' start, and a failure is named in the same notice, as "Computer use (CUA)". The binding closes with the task's session, or when CUA is switched off for the task; its tools stay, as in Pi a tool is never taken from a session, and calling one fails until the task is bound again.computerUsein its settings. A new desktop task records one once CUA is set up on the device: on when the composer names it, else the default for new tasks. The MCP menu ends with CUA's row, and switching it on binds a loaded task at once.ACP (
acp/,agent/surface.rs)maple-agent acpserves the Agent Client Protocol again, with the Goose runtime's transport (10 MiB frames, credit-tracked backpressure), handler, conversions and session state, and the same protocol surface, less what only Buzz used (commit 30): protocol 1; load, list, close and delete; image prompts and HTTP MCP servers; the model selector, locked after a task's first message, and no modes (session/set_modeis refused);/compactand the skills' commands; the turn's usage on each response and ausage_update.session/cancelstops it from its admission on.session/cancelright behind it finds it, and the rest of it, close, load and config changes run in the background. In Goose's, a second prompt or a config change during a turn held the dispatcher, cancels included, until the turn ended.usage_updatecounts the task's context as Pi does, where Goose sent the turn's tokens;/compacton a history too short to summarize says so instead of failing; and what only Buzz used is gone (commit 30): the bridge's hello, its variables and itsbuzz-dev-mcpdefinition,systemPromptand_meta.sessionTitle, and protocol 2.Side models (
agent/side_models.rs)/btw: the task's model gets the task's context, the side thread so far and the framed question; the task's tools stay in the request for its cached prefix, withtool_choice: none. The answer streams as events and is not saved;Instructions, skills and slash commands (
agent/resources.rs).maplefolder where Pi has~/.pi/agentand.pi:AGENTS.md(orCLAUDE.md) from the project folder and every folder above it, the account's, and~/.agents/AGENTS.md, which Goose's runtime read too. They load in every project, as in Pi. Goose's.goosehintsis not read;~/.agents/skills, and in a trusted project.maple/skillsand.agents/skillsup to the repository root. The other folders Goose's runtime read (~/.claude/skills,~/.config/agents/skills, and a trusted project's.claude/skillsand.goose/skills) are Pi's extra skill paths, so they come after the user's own;promptsfolder and a trusted project's.maple/prompts, andSYSTEM.mdandAPPEND_SYSTEM.mdwhere Maple gives no text of its own (Maple always appends its opening instructions, soAPPEND_SYSTEM.mdis ignored).read, as in Pi; Goose'sload_skilltool is gone./list holds the skills, then templates whose names no skill has, with argument hints from a skill's frontmatter. A command expands into the skill's instructions as Pi's/skill:namedoes, with the arguments after them, or into the filled template; names match regardless of case. Resolving a command takes the account, so the app passes it..maple/SYSTEM.md(two new protected features). Before, it looked only for.agents/skills/**/SKILL.md, and the/list offered and ran an untrusted project's skills.Timeline (
agent/timeline.rs)Tools (
agent/tools/,agent/login_path.rs)read,bash,editandwrite, orpowershellin place ofbashon Windows without Git Bash. As in Pi,grep,findandlsare there but off. Maple's ownread,editandwriteare gone.bash's spawn hook:AGENT_SESSION_ID.MAPLE_SHELLnames the bash to run and replacesGOOSE_SHELL.bash(wasshell), and its timeout argument istimeout(wastimeout_secs);cmd);readalso opens images for a model with vision. A model without vision gets Goose's answer instead, that the file is an image to inspect withread_image, rather than Pi's note that the image is left out.agent/tools/read_image.rs,agent/tools/desktop.rs,agent/tools/web.rs), with Goose's schemas, descriptions and limits:read_image: an image from a Maple attachment of the task, a local file or file URL, or an http(s) URL (public host, no credentials, 30 seconds, no redirects), at most 20MB, PNG, JPEG, GIF or WebP, optionally cropped to a PNG. Pi then fits the image as it fits every tool's images. For a model without vision the tool takes a required context and returns the side model's description instead, and the prompt carries Goose's line "Use read_image when you need to inspect an image." The tool follows the task's model: a task loaded before its run (for a side question or/compact) switches when the run's model can or cannot see images. Local paths resolve asread's do, macOS screenshot names included;todo_write, the plan the user watches, echoed back to the model, andrequest_user_input, one to three questions on a card. A stopped run takes the card back. As in Goose, near misses are accepted (questionsas one object, one question at the top level, missing or extra fields), blank questions and options are dropped and ids made unique. The questions go through the broker of the service that started the runtime rather than the process-wide one Goose's tool clients had to use, so a later service or a parallel test cannot take another runtime's questions;web_searchandopen_urlwhile its web switch is on, from its next run, as in Goose. Search results come back as JSON marked as untrusted evidence, trimmed from the end to 64,000 characters; a page comes back as sanitized text, at most 32,000 characters, cut without reactivating inert image code.open_urltakes public HTTPS URLs only: no credentials, and no loopback, private, link-local, documentation or internal host, embedded IPv4 included;Error:prefix.Not implementing, yet
!and!!commands in the composer (the runtime has them from Agent core in Rust after Pi: pi-ai, pi-agent-core, pi-coding-agent #1104; the composer does not offer them).MCP compared with Pi's
Maple's MCP keeps Pi's conventions where they touch the model and the server processes, on rmcp rather than Pi's own client, and keeps Maple's settings and per-task switches. For later comparison:
initializehandshake (Goose's runtime probedserver/discoverfirst)mcp.jsonin the agent folder and a trusted project; values may name environment variables or a!command$NAMEfrom the server's own environment variables, as Goose's didcodemodeby default (tools reached from scripts), ordeferred(tool_search),direct,hidden, per server or tooldirectdescribeNamespace()returns themmcp_serversprompt section, as Goose's runtime did/mcpto fixNot implementing, yet: sign-in (OAuth; Goose's runtime had a browser flow but cleared its tokens at every start), resources and prompts (Pi's
list_mcp_resources,list_mcp_resource_templatesandread_mcp_resource; Goose's runtime offered none), codemode andtool_search, elicitation and sampling (neither Pi nor Goose's runtime answered), Goose's OSV malware check ofnpxanduvxpackages (it let a start through when OSV could not be reached), and Goose's_metatask ids on each request andAGENT_SESSION_IDin a stdio server's environment (bashstill gets it).The app
CompactingandCompacted, which it ignores. The new crate keeps the API the desktop UI calls, such asAgentIntegration::is_external_agentfor the Codex and Claude Code cards, andsession_subagentsandcancel_external_agentfor the agents' rows.set_thinking_level, persists in the session, and becomes the account default for new tasks. Reopening a task keeps its own level. Stale menu reads are discarded and Send waits for a pending choice to save.Testing
e774410b: commit 32, the owner's, changes only the desktop sidebar and its test, which this container does not build. Hosted CI is green on this head: Agent on Linux, macOS and Windows (run), and the macOS and Linux app builds and packaging (run).a2cae39c, in a Linux container without the GUI app: commit 31 changes only the ACP configuration and its tests.cargo fmt --all -- --checkandcargo clippy --workspace --all-targets -- -D warningspassed, and so did the 38 ACP tests, with a test of loading the configuration in place of the two of saving it and of older builds' fields. Hosted CI was green: Agent on Linux, macOS and Windows (run), and the macOS and Linux app builds and packaging (run).4f76e26b, in a Linux container without the GUI app: commits 1 to 29 are unchanged from32c08e71. At commit 30,cargo fmt --all -- --check,cargo clippy --workspace --all-targets -- -D warningsandcargo check -p maple-agent --no-default-features --all-targetspassed, and 677 workspace tests passed, with 3 ignored. Commit 30 also removes three lines of the GUI app's code (it no longer passes a tool context or a system prompt) and changes two comments there, which this container does not build. Hosted CI was green: Agent on Linux, macOS and Windows (run), and the macOS and Linux app builds and packaging, which build those lines (run).32c08e71: every commit compiled with no warnings, the changed tests passed at their commits, and 683 workspace tests passed with 3 ignored. Hosted CI was green: Agent on Linux, macOS and Windows (run), and the macOS and Linux app builds and packaging (run).b838666b, on macOS arm64 with the GUI app: all 31 commits independently passedcargo fmt --all -- --check,cargo clippy --workspace --all-targets --locked -- -D warnings, andRUSTFLAGS='-D warnings' cargo test --workspace --locked(their per-commit table is in this description's edit history). At that head, 1,121 workspace tests passed, with 3 ignored, plus 51 headless app tests. Fulljust cialso passed, including warning-denied GUI builds and the headless, ACP-only and proxy-only feature configurations.just cipasses at Maple Agent phase 0: simplify the existing runtime #11245782e611(873 workspace tests, 5 ignored; 51 headless) and Agent core in Rust after Pi: pi-ai, pi-agent-core, pi-coding-agent #110476309fcb(1,278 workspace tests, 5 ignored; 51 headless). Hosted Agent CI was green on Linux, macOS and Windows for the three heads of the master restack (Maple Agent on the Pi crates: the runtime, then every feature, then no Goose #1109's beingb838666b): #1124, #1104, #1109.readtool, saves, lists with its prompt title, and after a restart lists and reads back the same rows while stopped; a follow-up carries the earlier context; its requests leave timeouts to the transport;bashruns in the task's folder with the task's id, and a failed command reaches the model and the timeline with its exit code;read_imagesource and a model with vision beside the text, a chip keeps its image, and the user's rows show the typed text and the images the same live and after a reload; unit tests cover the prompt's round trip, and Pi's tests a steered image;fake: echo; switched off, its tools stay declared and a call fails with Pi's message without reaching the server, while its instructions leave the prompt; a tool the server withdrew stays declared and fails as no longer available; switched on, it connects at once; removed from Settings, it stays chosen but unavailable and cannot be switched on. A server whose command does not exist is named once in the run's notice, not at the next run, and switching it on again fails with the reason. Of six tasks with servers, the idle one used longest stops its servers, which start again when it runs;read_imagereads local files, file URLs and the task's attachments, crops, refuses other formats, oversized files, private hosts and credentials in URLs, and for a model without vision requires a context and returns the description (unit tests against a scripted vision model). End to end,readsends a model without vision toread_imagefor an image, and that model's prompt says to look at images there, while a model with vision gets the image fromread; a task loaded without vision getsread_image's image mode for a run that sees, and its description mode back for one that does not;initializeand the model selector without modes;session/set_moderefused; a turn streamed once (thinking, text, a tool call whose kind updates keep, the locked selector, the title) and replayed by a latersession/load;session/cancelstopping a turn and the next prompt running; a failed turn told once and ended;/compacton a short and a long history; an image prompt throughread_image; a turn without images on a vision model's catalog entry, with its window in the run and inusage_update; the trust chooser asked once and its answer kept; a task closed before its first prompt removed. Goose's unit tests for the conversions, the trust prompts, the transport's credits and the configuration run unchanged where the shapes did;/v1/systemoneasking for JSON, a 429 withretry-after-msis sent again, and the answer's usage counts its input, cached and output tokens; a failed request's error leaves out the server's body; a chat response carries its headers to Pi.Cargo.lockwas resolved by cargo over the full workspace, GUI app included, and a locked resolution accepts it unchanged. The rename commit only renames the package; the runtime commits change the runtime crate's dependency list; the deletion only removes packages. The MCP commit addsrmcp3.4.1 withsse-stream0.2.6,tokio-stream0.1.19 andbase640.23.1 (rmcp's own); no other package was added or moved, and Maple Agent's dependency check passes. Before that no external crate was added or moved here: the web tools andread_imageadd edges topulldown-cmarkandimage, both already in the lockfile, and Agent core in Rust after Pi: pi-ai, pi-agent-core, pi-coding-agent #1104 addssimilar. The CUA commit brings back the Cua Driver SDK (141 packages) and the ACP commitagent-client-protocol2.2.0 (16), and commit 19 pins the twelve cargo had resolved newer, so every package they add is one the lockfile Maple shipped with had, at its version; a locked resolution accepts the result, and the dependency check passes.Live validation (2026-10-09)
Another agent ran the live checks on
680ca688on macOS, against a local backend with real inference (report). The scenario checklist's results are indocs/scenario-checklist.md.usage_updatereported each model's catalog window.maple-agent acpprocess splits its history, so use a task in one place at a time;session/deleteleaves the session listed, a model change waits for the running turn, and an MCP server that fails to start is not reported to the ACP client, all as on master.readandread_imageare, or stays a note. A model the catalog gives no window for compacts at the 128,000-token default, while the context ring assumes 200,000.Restack and thinking validation (2026-10-10–11)
Restacked onto #1104 at
df7bfbd1, with the three Pi adaptations it had then (two since dropped; see the end). Compared the oldd433cdb9GUI release build with the new GUI debug build against the same local backend using isolated test-account state and password login. These are behavior checks, not performance comparisons. The live-tested tree is identical to final head98220ba6; the final head was rebuilt and checked after the history rewrite.reasoning_effortfield (or its absence) and now have successful HTTP 200 results across the original run and a targeted DeepSeek rerun. The original run passed 24 choices; at 2026-10-11 00:46 UTC, DeepSeek Off (none), Low, High, Xhigh and Max all returned HTTP 200 and completed normally through local OpenSecret → Tinfoil. The earlier DeepSeek 502/503 failures at 2026-10-10 23:37 UTC coincided with the owner-reported Tinfoil outage window and were likely outage-related (rerun details).Restack onto #1104's parity ports (2026-10-11)
Restacked onto #1104 at
76309fcb(headb838666b), which ports the rest of what the parity review left to decide: request options and retries afterretry-after, classifiers and System One, the one-call setup and session runtime, the boundary drafts, and smaller session items.AGENTS.mdreplaces the main checkout's, both as in Pi.httpIdleTimeoutMs0, as Pi turns it off), so its transport's timeouts and messages stay as they were, and a test checks it.b838666b; see the dated validation below. The earlier results above remain historical results for98220ba6.Master restack and live validation (2026-10-11)
Restacked #1124 → #1104 → #1109 onto master
9e49de8738612537b24d17d877d689da331c49fb. The heads were then5782e611,76309fcbandb838666b; #1109 has since moved (see the next section). There were no conflicts or build fixes: range-diff keeps all 1 / 77 / 31 commits unchanged, messages and trailers unchanged, Pi crate trees identical to the old respective heads, and the top tree is the old top plus master's changes. Pushed in order with explicit leases after the local checks above; draft states are unchanged.b838666b, macOS 27.0.1 arm64, isolated Local test-account state and password login. Real inference used GLM 5.3, Gemma 4 31B, Llama 3.3 70B and GLM 5.3 Flash.Network error: could not connect to Maple, notRequest timed out.. The desktop run also settled to one row with that same message after retries ended. Both this build and98220ba6displayed multiple error rows while retries were in progress, then one row when the run settled. The backend was restored.98220ba6: config changes waiting for an active ACP prompt, archived deleted sessions remaining listed, missing failed-MCP-start notices over ACP, and the initial command-list update omitting newly trusted skills were reproduced on both builds. Invoking trusted commands succeeds. The long-output harness initially inspected the capped ACP display; persisted sessions and saved output confirmed the correct 2,000-line tail and complete 3,000-line file on both builds. These observations did not result in code changes.The Pi adaptations decided (2026-10-11)
The owner reviewed the three Pi adaptations: the timestamps stay, and
unregister_toolandcontaingo, so the stack does what Pi does. Restacked as 29 commits on #1104 at76309fcb; head32c08e71.containtests are gone.Buzz out of ACP (2026-10-12)
The owner: what is Buzz-specific in ACP goes, and ACP that any client might use stays. Commit 30, head
4f76e26b:_maple/bridge/hello; the bridge's variables (Buzz's credentials), read from the process or from abuzz-dev-mcpserver definition; the tool context that carried them into commands, with itsBUZZ_*scrubbing in every task's commands and external agents, its revocation and its launch fence;session/new'ssystemPromptand_meta.sessionTitle, with the runtime's per-task system prompts; protocol version 2, which only told Buzz aboutsystemPrompt(Maple now answers with the stable version, 1); and the count of connections that carried credentials. The commit removes 860 lines and adds 153./compact, usage updates, and a failed turn that ends without a JSON-RPC error, so a client never resends work Pi already did.a2cae39c). The owner: the code is greenfield, so the ACP configuration's copy from the proof of concept's account folder goes, and with it the only code that wrote the file; Maple only reads it now.🤖 Generated with Claude Code
https://claude.ai/code/session_01STEaSNTNUiiNNLXcKidE2d