Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
5782e61
maple-agent: consolidate phase 0 runtime simplification
AnthonyRonning Oct 10, 2026
2a36d1a
pi-ai: model messages, streaming events and an OpenAI-compatible prov…
claude Oct 8, 2026
f259cf3
pi-agent-core: the agent loop, tools, hooks and message queues
claude Oct 8, 2026
3a26e00
pi-coding-agent: sessions, compaction, extensions and the agent session
claude Oct 8, 2026
8636cf9
pi-ai: harden the OpenAI stream, overflow checks and argument coercion
claude Oct 8, 2026
7c06215
pi-agent-core: keep queues and cancellation right around runs
claude Oct 8, 2026
302496c
pi-coding-agent: fix sessions, compaction and the agent session
claude Oct 8, 2026
38ed800
pi-coding-agent: refresh each turn's settings and let stop cancel sum…
claude Oct 8, 2026
259e901
pi-ai: send a model its earlier reasoning, as Pi does
claude Oct 8, 2026
e273b91
pi-coding-agent: Pi's built-in tools, read, bash, edit, write and pow…
claude Oct 9, 2026
d37e73c
pi-coding-agent: Pi's grep, find and ls tools
claude Oct 9, 2026
a40072f
pi-ai: never retry an exhausted quota, as Pi does not
claude Oct 9, 2026
0481241
pi-coding-agent: project trust, SYSTEM.md and APPEND_SYSTEM.md, and .…
claude Oct 9, 2026
0634ac5
pi-ai: strict tool schemas, after Pi's constrained sampling
claude Oct 9, 2026
6555570
pi-coding-agent: the user's own shell commands, as Pi's ! and !!
claude Oct 9, 2026
3b72e15
pi-coding-agent: make images that tools return fit, as Pi does
claude Oct 9, 2026
782adb3
pi-coding-agent: session stats, as Pi's
claude Oct 9, 2026
2ac6eb1
pi-coding-agent: Stop ends a command whose background job keeps printing
claude Oct 10, 2026
724d912
pi crates: transports get the cancellation; a host names a session
claude Oct 10, 2026
80ed705
pi-coding-agent: replace a session's resources between prompts
claude Oct 10, 2026
772ced7
pi-coding-agent: steering and follow-ups carry images, as Pi's do
claude Oct 10, 2026
b6852bb
pi-coding-agent: Pi's truncate_middle
claude Oct 10, 2026
e508251
pi-coding-agent: register tools after a session starts
claude Oct 10, 2026
50a2786
pi-coding-agent: a host starts a turn with a custom message
claude Oct 10, 2026
1e21bec
pi-ai: clamp thinking levels up first, and send off's value
claude Oct 10, 2026
edb7b1d
pi-ai: a reply without a known finish reason fails, as Pi's does
claude Oct 10, 2026
b0f7608
pi-ai: error messages keep the whole error, as Pi's do
claude Oct 10, 2026
5054191
pi-ai: send the transcript as Pi's Chat Completions provider does
claude Oct 10, 2026
ac0739e
pi-ai: classify overflow and transient errors as Pi does
claude Oct 10, 2026
db4c588
pi crates: fit the output cap to the window with Pi's estimate
claude Oct 10, 2026
de78cbb
pi-ai: key OpenAI's prompt cache and route by the session id
claude Oct 10, 2026
bdfec62
pi-ai: read each server's cache usage, as Pi does
claude Oct 10, 2026
a06a1c1
pi-ai: repair broken strings in tool-call arguments, as Pi does
claude Oct 10, 2026
0d37b71
pi-ai: coerce tool arguments through unions, as Pi does
claude Oct 10, 2026
038eed5
pi-ai: apply_event follows a tool call's arguments as they stream
claude Oct 10, 2026
b9656d8
pi-ai: read server-sent events as Pi's OpenAI client does
claude Oct 10, 2026
4a28ea7
pi-coding-agent: a broken settings file no longer stops the load
claude Oct 10, 2026
64eadd0
pi-coding-agent: a prompt template path can name one file
claude Oct 10, 2026
7478f71
pi-coding-agent: hold back a colour code split between output chunks
claude Oct 10, 2026
08f24de
pi-coding-agent: compaction summaries think at the session's level
claude Oct 10, 2026
52a816a
pi-coding-agent: write branch summaries as Pi writes them
claude Oct 10, 2026
a189d58
pi-coding-agent: estimate the context after edits and compactions
claude Oct 10, 2026
4ecbbe9
pi-agent-core: queue modes are spelled as Pi spells them
claude Oct 10, 2026
87692a7
pi-coding-agent: a compaction update asks in Pi's update format
claude Oct 10, 2026
1aefee9
pi-coding-agent: an extension's compaction passes on no file lists
claude Oct 10, 2026
0a37ff7
pi-coding-agent: an empty argument hint is no hint
claude Oct 10, 2026
90f21a1
pi-coding-agent: search prompt template folders as Pi does
claude Oct 10, 2026
439338b
pi-coding-agent: thinking levels as Pi sets them
claude Oct 10, 2026
8b1c42c
pi-coding-agent: before_agent_start changes last for one run
claude Oct 10, 2026
11d03e0
pi-coding-agent: Pi's two context events
claude Oct 10, 2026
e17d11e
pi-coding-agent: manual compaction stops a run first
claude Oct 10, 2026
46d7df0
pi-coding-agent: settings, tools and commands as Pi gives them
claude Oct 10, 2026
4654dfd
pi-coding-agent: steering goes the way of a prompt, as in Pi
claude Oct 10, 2026
112958a
pi-coding-agent: session events as Pi reports them
claude Oct 10, 2026
4e54d47
pi-coding-agent: forks as Pi makes them
claude Oct 10, 2026
f256366
pi-coding-agent: what extensions can do, as in Pi
claude Oct 10, 2026
11430aa
pi-coding-agent: images attached to a prompt are made to fit
claude Oct 10, 2026
bd6ffdd
pi-coding-agent: moves in the tree as Pi makes them
claude Oct 10, 2026
567b7cd
pi-coding-agent: compaction checks and reports as Pi's do
claude Oct 10, 2026
dfe4e8c
pi-coding-agent: context usage is unknown after a compaction
claude Oct 10, 2026
eb527f7
pi-coding-agent: summary requests report their retries
claude Oct 10, 2026
7add7eb
pi-coding-agent: settings resource paths resolve and rank as in Pi
claude Oct 10, 2026
5d194c6
pi-coding-agent: thinking levels for particular models, as Pi's
claude Oct 10, 2026
a5216bb
pi-agent-core: a failed run still ends, and prompts take images
claude Oct 11, 2026
1552e2d
pi-ai: request options and retries, as Pi's requests take them
claude Oct 11, 2026
dac51dd
pi-coding-agent: session requests carry Pi's provider settings
claude Oct 11, 2026
142201a
pi-ai: classifiers and System One, after Pi's classify()
claude Oct 11, 2026
fa411fd
pi-coding-agent: skill folders follow their ignore files, as in Pi
claude Oct 11, 2026
a8090bb
pi-coding-agent: a nested worktree's context file shadows the main's
claude Oct 11, 2026
f8f2ec0
pi-coding-agent: compaction per model and blocked images, as Pi's
claude Oct 11, 2026
87fcd9d
pi-coding-agent: the resources_discover and user_bash events
claude Oct 11, 2026
497f3a5
pi-coding-agent: Pi's small session helpers
claude Oct 11, 2026
aea5510
pi-coding-agent: the rest of Pi's extension interface a host can show
claude Oct 11, 2026
4e81abb
pi-coding-agent: settings changed in a session are saved, as in Pi
claude Oct 11, 2026
080d06b
pi-coding-agent: a fork names its parent file and keeps label times
claude Oct 11, 2026
e4b841b
pi-coding-agent: a session in one call, as Pi's createAgentSession
claude Oct 11, 2026
c1f3149
pi-coding-agent: the session runtime, as Pi's AgentSessionRuntime
claude Oct 11, 2026
76309fc
pi-coding-agent: boundary drafts at turn_end and agent_before_settle
claude Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/develop-maple-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ request. Keep fixture launch and environment setup shared with the Codex tests.

Apply `$review-maple-security`'s trust-boundary and evidence methodology to the
actual GPUI source; its Tauri-specific file list is for Research. Validate
account isolation, tool approval, MCP/ACP inputs, persistence, and process
account isolation, tool execution, MCP/ACP inputs, persistence, and process
ownership at the layer implementing the effect. Never treat a passing source
import or a native login screen as authenticated chat or containment proof.

Expand Down
5 changes: 3 additions & 2 deletions apps/maple-agent/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ Read the repository-root `AGENTS.md` and `$develop-maple-agent` as well.
Commands below run from this component directory through its pinned Nix shell.

GPUI desktop app for Maple. Workspace crates: `app` (package `maple-agent-app`, binary `maple-agent`),
`crates/maple-agent`, `crates/maple-billing`. See `README.md` for the
layout, prerequisites, and command line modes.
`crates/maple-agent`, `crates/maple-billing`, and the agent core `crates/pi-ai`,
`crates/pi-agent-core`, `crates/pi-coding-agent`, which must not depend on Maple
crates. See `README.md` for the layout, prerequisites, and command line modes.

Packaged Dev and Prod OAuth use their compiled Auth origins and a one-attempt
loopback listener in `app/src/hosted_oauth.rs`; unpackaged builds retain the
Expand Down
61 changes: 61 additions & 0 deletions apps/maple-agent/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/maple-agent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["crates/maple-agent", "crates/maple-billing", "app"]
members = ["crates/maple-agent", "crates/maple-billing", "crates/pi-ai", "crates/pi-agent-core", "crates/pi-coding-agent", "app"]

[workspace.package]
edition = "2024"
Expand Down
85 changes: 46 additions & 39 deletions apps/maple-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,19 @@ app/ The maple-agent binary. Owns the window, login, chat,
crates/maple-agent/ Maple's transport-neutral agent runtime, extracted from
the Tauri app with Tauri removed. Owns embedded Goose,
the Maple provider over the Maple Rust SDK, developer
tools, permission policy, account-scoped session
storage, and the ACP server.
tools, account-scoped session storage, and the ACP
server.
crates/maple-billing/ HTTP client for the Maple billing API.
crates/pi-ai/ Model messages, streaming events and an OpenAI-compatible
provider, after Pi's pi-ai package.
crates/pi-agent-core/ The agent loop with tools, hooks and message queues, after
Pi's pi-agent-core package.
crates/pi-coding-agent/
Sessions as an append-only tree, compaction, skills and
prompt templates, the extension API and the agent
session, after Pi's coding-agent core. The pi-* crates
have no Maple dependencies; the runtime does not use
them yet.
docs/ Theme spec measured from the Tauri app.
scripts/ One maintainer helper: screenshot.py takes a desktop
screenshot through the xdg portal on GNOME Wayland.
Expand Down Expand Up @@ -46,9 +56,8 @@ remove Tauri:
- public visibility opened on the service surface the app consumes

Goose is pinned to a fork revision based on upstream v1.53.0 in this
component’s Cargo manifests and lockfile. The fork forwards subagent approvals
to the parent and supports ephemeral native clients for embedded CUA. Research
has an independent dependency graph.
component’s Cargo manifests and lockfile. The fork supports ephemeral native
clients for embedded CUA. Research has an independent dependency graph.

## Features

Expand All @@ -61,31 +70,26 @@ has an independent dependency graph.
the `acp` mode skip sign-in. The window opens while the saved session is
checked, and a check that cannot reach the server keeps the credentials
for the next launch; only a refusal from the server signs the user out.
- Agent chat with streaming Markdown, tool calls, permission prompts,
agent questions, image attachments (picker, paste, or drag and drop),
a per-message Copy button, and a context-window indicator.
- Agent chat with streaming Markdown, tool calls, agent questions, image
attachments (picker, paste, or drag and drop), a per-message Copy button,
and a context-window indicator. Every tool call runs without asking for
approval.
- Slash commands in the composer: `/btw` asks a side question the task
never sees, plus `/compact`, `/new`, `/pin`, `/web`, `/model`, and
`/help`. The account's skills appear in the same list.
- The task's latest todo list stays pinned above the composer.
- Subagents: the task can give a piece of work to a subagent with the
`delegate` tool, which runs it in its own context. The Goose fork makes
the subagent inherit the task's permission mode and forwards its approval
requests to the parent session. Maple also asks before each handoff in
Read only mode. Subagents show above the composer with the tool each one
runs and how long it has worked. A subagent that runs in the background
keeps its row after the turn ends, and Maple tells the task when it
finishes, with a bounded result in the running turn or a new turn Maple
starts automatically. The task can use `load` to retrieve any truncated output.
- External agents: a task can hand work to Codex or Claude Code installed on
this computer with the `agent_start`, `agent_send`, `agent_status`,
`agent_cancel`, and `list_agent_providers` tools, once the provider is enabled
under Settings > Integrations. Each agent runs in the project with its own
account, context, and sandbox settings; whatever it asks approval for
comes to you through Maple's permission card, and Allow all grants it. Its progress streams
into the tool call's row and its row above the composer has a Stop
button. Three skills, `/handoff`, `/committee`, and `/advisor`, teach the
task when and how to delegate. See
account, context, and sandbox settings; Maple accepts its approval
requests, and its questions still come to you through Maple's question
card. Its progress streams into the tool call's row, and its row above
the composer shows how long it has worked, with a Stop button. A
background agent keeps its row after the turn ends, and Maple tells the
task when it finishes, with a bounded result in the running turn or a new
turn Maple starts automatically. Three skills, `/handoff`, `/committee`,
and `/advisor`, teach the task when and how to delegate. See
[`docs/external-agents.md`](docs/external-agents.md).
- Voice: dictate a message with the microphone button, and read any
message aloud. Both use Maple's speech models; the voice and speed
Expand All @@ -111,15 +115,15 @@ has an independent dependency graph.
projects that provide skills ask once for a trust decision before
their guidance loads.
- Sessions grouped by project, with rename, archive, and restore.
- Settings: General (default permission mode, web tools, appearance,
tool call details, desktop notifications, tool call summaries, composer Vim,
application Vim, and the speech voice and speed), Keyboard Shortcuts, System
prompt, Integrations (detected built-ins and custom MCP servers), Usage (plan
meter from the billing API plus totals from the Goose ledger), and About.
- Settings: General (web tools, appearance, tool call details, desktop
notifications, tool call summaries, composer Vim, application Vim, and the
speech voice and speed), Keyboard Shortcuts, System prompt, Integrations
(detected built-ins and custom MCP servers), Usage (the plan meter from
the billing API), and About.
- Dark and light themes; the default follows the system.
- Billing status from the Maple billing API.
- Desktop notifications when a task finishes, asks a question, or needs
permission while the window is not focused.
- Desktop notifications when a task finishes or asks a question while the
window is not focused.
- Release check on launch: a banner links to a newer GitHub release.
Nothing is downloaded or installed by the app.
- Window size and maximized state persist between launches.
Expand Down Expand Up @@ -151,20 +155,19 @@ the embedded transport.
Enabling an integration sets a device-local default for new tasks. Existing
tasks keep their frozen integration choice and expose CUA as an independent
per-task switch in the composer. A task that never chose a backend adopts the
device default only when it can actually run it. A detected standalone
CuaDriver, which Maple looks for on macOS only, remains a legacy-compatible
backend until the user explicitly sets up the built-in one;
Maple does not install or update it, start or stop its daemon, or alter another
client's configuration. Custom STDIO and Streamable HTTP MCP servers remain
account configuration that may roam between devices.
device default only when it can actually run it. Maple no longer looks for a
separately installed CuaDriver application: a task saved with its stdio entry
loses that entry on its next run, and a device setting that selected it reads
as not set up. Custom STDIO and Streamable HTTP MCP servers remain account
configuration that may roam between devices.

The embedded design, migration rules, privacy boundary, and preview limits are
documented in [`docs/embedded-cua.md`](docs/embedded-cua.md).

#### Claude Code

Settings > Integrations lists Claude Code (`claude`) alongside Codex, with the
same per-task selection, streamed activity, permission cards, and Stop control.
same per-task selection, streamed activity, question cards, and Stop control.
Install the Claude Code CLI on the app's PATH and sign in using
`claude auth login`. Maple uses a Rust transport adapted from Goose's Claude
Code provider. The CLI is the only external runtime dependency. The integration
Expand Down Expand Up @@ -443,7 +446,6 @@ ignored. See [build profiles](docs/release-profiles.md).
| `MAPLE_BILLING_API_URL` | Maple billing API. | `https://billing.opensecret.cloud` |
| `MAPLE_CLIENT_ID` | OpenSecret client id (UUID). | Maple's id |
| `MAPLE_MODEL` | Model to select at start. | Runtime default |
| `MAPLE_PERMISSION_MODE` | `smart_approve` or `auto`. Overrides the saved setting. | Saved setting |
| `MAPLE_CONTEXT_LIMIT` | Context window size in tokens, when the model catalog does not report one. | Catalog value |
| `GOOSE_SHELL` | Shell for the agent's shell tool. | `bash` (Windows: `cmd`) |
| `MAPLE_UPDATE_REPO` | GitHub `owner/repo` containing stable `maple-agent-vX.Y.Z` releases. | `MaplePrivacyLabs/Maple` |
Expand All @@ -467,11 +469,10 @@ shows unpackaged builds; packaged Dev and Prod append `maple-agent-dev` and
| --- | --- |
| `<config>/settings.json` | App settings. |
| `<config>/agent/accounts/<scope>/config.json` | Per-account agent configuration (default root, model, custom MCP servers, project trust). May roam between machines. |
| `<config>/agent/accounts/<scope>/goose/config/` | Goose permission file for the account. |
| `<config>/agent/accounts/<scope>/goose/config/skills/` | Skills the account's tasks can load, including the delegation skills Maple installs while any external agent is enabled. |
| `<config>/agent/goose-runtime/` | Goose process configuration. |
| `<local data>/auth.json` | Sign-in credentials (mode 0600). Device-local; never in a roaming profile. |
| `<local data>/agent/accounts/<scope>/integrations.json` | Per-account defaults and validated launch details for integrations detected on this device. |
| `<local data>/agent/accounts/<scope>/integrations.json` | Per-account defaults for the integrations on this device. |
| `<local data>/agent/accounts/<scope>/goose/data/sessions/sessions.db` | Goose session history and usage ledger (SQLite, WAL). |
| `<local data>/agent/accounts/<scope>/tool_summaries.db` | Model-written one-line summaries of tool calls (SQLite, WAL). |
| `<local data>/agent/accounts/<scope>/attachments/` | Image attachments. |
Expand Down Expand Up @@ -504,6 +505,12 @@ a release build. `just ci` is the full local format, lint, build and test gate;
`just release` separately validates the optimized binary. PR jobs have no
signing or publishing credentials. See the root agent guide for shared checks.

Two by-hand checks complement the automated ones: the
[scenario checklist](docs/scenario-checklist.md), one scenario per feature
with the results of each run, and the
[performance check](docs/performance-check.md), a quick release-build sanity
check.

## Update and release boundary

Prod and unpackaged builds accept stable `maple-agent-vX.Y.Z` releases. Dev
Expand Down
57 changes: 1 addition & 56 deletions apps/maple-agent/app/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,6 @@ pub struct PendingQuestion {
pub questions: Vec<maple_agent::agent::AgentQuestion>,
}

#[derive(Debug, Clone)]
pub struct PendingPermission {
pub session_id: String,
pub run_id: String,
pub request_id: String,
pub tool_name: String,
pub prompt: Option<String>,
/// Pretty-printed tool arguments, formatted once when the request
/// arrives instead of on every frame.
pub arguments: Arc<str>,
}

// Re-exported for the settings screens; a headless build has no reader.
#[cfg_attr(not(feature = "desktop"), allow(unused_imports))]
pub use maple_agent::maple_api::{
Expand Down Expand Up @@ -1562,13 +1550,11 @@ impl AgentBackend {
Some(AgentStartRequest {
project_root: None,
model,
mode,
}) => {
let config = handle.load_config().await?;
Some(AgentStartRequest {
project_root: gui_start_root(&config),
model,
mode,
})
}
other => other,
Expand Down Expand Up @@ -2093,24 +2079,6 @@ impl AgentBackend {
.await
}

/// Set the permission policy for a session: "smart_approve" asks for
/// each gated tool, "auto" approves everything (bypass).
pub async fn set_permission_mode(
&self,
user_id: &str,
session_id: &str,
mode: &str,
) -> Result<(), String> {
self.service
.handle_for_user(user_id)
.await?
.set_permission_mode(maple_agent::agent::AgentPermissionModeRequest {
session_id: session_id.to_string(),
mode: mode.to_string(),
})
.await
}

/// Compact a session's history now; reload the session afterwards.
pub async fn compact_session(&self, user_id: &str, session_id: &str) -> Result<(), String> {
self.service
Expand Down Expand Up @@ -2356,35 +2324,12 @@ impl AgentBackend {
.await
}

pub async fn permission_respond(
&self,
user_id: &str,
session_id: &str,
request_id: &str,
allow: bool,
) -> Result<(), String> {
self.service
.handle_for_user(user_id)
.await?
.permission_respond(maple_agent::agent::AgentPermissionResponse {
session_id: session_id.to_string(),
request_id: request_id.to_string(),
decision: if allow {
"allow_once".to_string()
} else {
"deny_once".to_string()
},
})
.await
}

/// Standard start request for this app: the saved project root (see
/// `start_runtime`) with the configured model and the SmartApprove policy.
/// `start_runtime`) with the configured model.
pub fn default_start_request(&self) -> AgentStartRequest {
AgentStartRequest {
project_root: None,
model: std::env::var("MAPLE_MODEL").ok(),
mode: None,
}
}

Expand Down
Loading
Loading