Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 15 additions & 6 deletions docs/design/agent-workflows/documentation/adapters/agenta.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ adapter](pi.md) and produces a Pi-shaped config, so it inherits everything Pi do
tools, the system-prompt layers, tracing). What it adds is a fixed set of Agenta-shipped
extras that the agent author cannot turn off:

- **Forced tools** always unioned into the agent's resolved tools. At minimum `read`
- **Forced tools**: always unioned into the agent's resolved tools. At minimum `read`
(Pi only renders the skills section when `read` is enabled) and `bash` (so skills can run
their helper scripts).
- **Forced skills** Agenta-shipped Pi skills loaded on every run.
- **A base AGENTS.md preamble** the author's `instructions` are appended after it.
- **A base persona** forced onto Pi's `append_system`, with any author-supplied
- **Forced skills**: Agenta-shipped Pi skills loaded on every run.
- **A base AGENTS.md preamble**: the author's `instructions` are appended after it.
- **A base persona**: forced onto Pi's `append_system`, with any author-supplied
`append_system` appended after it.

Read the [architecture](../architecture.md), [ports and adapters](../ports-and-adapters.md),
Expand All @@ -30,8 +30,17 @@ disk because they reference relative scripts and assets, so they cannot ride the
text. The contract between the two halves is the skill **name**: `AGENTA_FORCED_SKILLS` lists
names, and each must match a committed directory under the runner's skills root.

Because the Agenta harness IS Pi, its tools are delivered the Pi-native way (through the
extension on the ACP path, through `buildCustomTools` in process), never over MCP. The forced
`read` and `bash` tools are Pi built-ins, so they ride the wire as built-in names, not resolved
specs.

## How a skill reaches the model

The flow below is for the in-process engine. The deployed path (sandbox-agent over ACP) reaches the
same end state by a different mechanism, described in
[On the sandbox-agent (ACP) path](#on-the-sandbox-agent-acp-path) below.

1. `AgentaHarness._to_harness_config` puts the forced skill names on the `skills` field of
the `/run` request (`AgentaAgentConfig.wire_tools`).
2. The in-process Pi engine (`engines/pi.ts`) resolves each name against its bundled
Expand Down Expand Up @@ -59,7 +68,7 @@ remains available for local/example contrast runs.
## On the sandbox-agent (ACP) path

`SandboxAgentBackend` also lists `HarnessType.AGENTA` as supported, so `agenta` runs over ACP through
the sandbox-agent daemon as well — this is what lets it use the Daytona sandbox. The Agenta harness is
the sandbox-agent daemon as well. This is what lets it use the Daytona sandbox. The Agenta harness is
Pi with an opinion, and the sandbox-agent daemon only knows real agents (`pi`, `claude`, …), so the
runner maps `agenta` onto the `pi` ACP agent (`acpAgent` in `engines/sandbox_agent.ts`) and treats it
as Pi for capabilities, model resolution, and tracing.
Expand All @@ -68,7 +77,7 @@ The forced *skills* cannot ride the `/run` wire as text (a skill is a directory
reference relative scripts and assets), so the wire carries only the skill **names** and the
runner lays the bundled directories into the Pi **agent dir**'s `skills/` (user scope).
`runSandboxAgent` resolves the names against the bundled `skills/` root (`engines/skills.ts`, shared
with the in-process engine). The agent dir is deliberate Pi auto-discovers and enables
with the in-process engine). The agent dir is deliberate. Pi auto-discovers and enables
user-scope skills (`<agentDir>/skills/`) on every run, whereas project skills
(`<cwd>/.pi/skills/`) are trust-gated and would not load in this headless run.

Expand Down
33 changes: 22 additions & 11 deletions docs/design/agent-workflows/documentation/adapters/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,28 @@ Anthropic key" rather than a stack trace.

## Tools over MCP

Claude advertises the `mcpTools` capability, so the runner delivers tools to Claude the
standard ACP way, over MCP. This is the branch that the [capability probe](../ports-and-adapters.md)
chooses: deliver over MCP when the harness reports `mcpTools`, not when the harness name is
something in particular.

The mechanism is a small stdio MCP server (`tools/mcp-server.ts`) that the daemon launches
and attaches to the session. Its tool bodies POST back to Agenta's `/tools/call` with the
same callback-tool envelope the Pi path uses. The resolved specs and the callback endpoint reach the
MCP server through its environment, so nothing tool-specific is written to a file the agent
can read. The safety property is identical to Pi's: the provider key and the connection auth
stay server-side, and the agent only ever asks Agenta to run a named tool.
Claude reports the `mcpTools` capability, so the runner delivers tools to Claude the standard
ACP way, over MCP. This is the branch that `buildSessionMcpServers`
(`engines/sandbox_agent/mcp.ts`) chooses: deliver over MCP when the harness reports `mcpTools`,
not when the harness name is something in particular. In practice the capability comes from the
static per-harness fallback (`engines/sandbox_agent/capabilities.ts`): the daemon rarely fills
a real `info.capabilities`, so the runner uses `mcpTools: true` for any non-Pi harness.

The mechanism is a small stdio MCP server named `agenta-tools` (`tools/mcp-server.ts`, launched
by `tools/mcp-bridge.ts`) that the daemon attaches to the session. This is an Agenta tool
DELIVERY vehicle, not a user-declared MCP server: it carries the same gateway and code specs
the Pi extension would register, just exposed over MCP because Claude cannot take a native
tool. Its env carries only public metadata (names, descriptions, schemas) and a relay
directory; the `call_ref`, the code, the scoped secrets, and the callback auth never reach it.
When the model calls a tool, the server relays the request back to the runner over the file
relay (`tools/relay.ts`), and the runner runs the private spec from memory and POSTs to
`/tools/call`. The safety property is identical to Pi's: the provider key and the connection
auth stay server-side, and the agent only ever asks Agenta to run a named tool.

User-declared `mcp_servers` are a separate thing and effectively off today. They would reach
Claude through `toAcpMcpServers` as additional ACP stdio servers, but only when
`AGENTA_AGENT_ENABLE_MCP` is set (off by default), so in practice no user MCP server is
attached. See [tools.md](../tools.md#status-and-known-gaps).

## Permissions

Expand Down
23 changes: 17 additions & 6 deletions docs/design/agent-workflows/documentation/adapters/pi.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ variables, so the extension stays inert when none are set and is safe to install

## Tools, the Pi-native way

Pi 0.79.4 does not support MCP. So we do not deliver tools over MCP to Pi. Instead the
extension reads the resolved tool specs from `AGENTA_TOOL_SPECS` and registers each one with
Pi directly through `pi.registerTool`. Pi then sees them as native tools and runs the loop.
Pi 0.79.4 does not support MCP, and `pi-acp` does not forward MCP servers either. So we do not
deliver anything over MCP to Pi: the runner's tool-delivery fork
(`buildSessionMcpServers` in `engines/sandbox_agent/mcp.ts`) returns an empty MCP list for Pi,
and tools come through the extension instead. The extension reads the resolved tool specs from
`AGENTA_TOOL_PUBLIC_SPECS` (public metadata only: name, description, input schema) and
registers each one with Pi directly through `pi.registerTool`. Pi then sees them as native
tools and runs the loop. The private parts of each spec (the `call_ref`, the code, the scoped
secrets, the callback auth) never reach the extension; they stay in runner memory and the
extension relays every call back.

Each registered tool's body does one thing: it POSTs the call back to Agenta's `/tools/call`
with the tool's `callRef` (the callback-tool envelope). The model picks the tool and supplies the
Expand Down Expand Up @@ -158,9 +164,14 @@ And auth comes from the provider key in the sandbox env when present, or from an
The in-process Pi engine (`engines/pi.ts`, selected by the `InProcessPiBackend`) skips sandbox-agent
entirely. It drives Pi's `createAgentSession` directly, with everything in memory: AGENTS.md
injected through the resource loader, the session and settings managers in memory, and a
throwaway working directory. It registers the same tools as Pi `customTools` (the same
POST-back-to-`/tools/call` body) and traces with the same extension logic, just wired in
process rather than loaded from disk.
throwaway working directory. It registers the same tools as Pi `customTools` through
`buildCustomTools`, and traces with the same extension logic, just wired in process rather than
loaded from disk. One difference from the ACP path: there is no file relay. Because the engine
runs in the same process as the runner, each tool body executes directly through
`runResolvedTool` (a gateway tool POSTs to `/tools/call`, a code tool spawns a local
subprocess). The relay only exists on the ACP path, where a separate Pi process or a Daytona
sandbox cannot reach Agenta or hold the private spec. The in-process engine also ignores
`mcp_servers` entirely (`PI_CAPABILITIES.mcpTools` is false).

It returns the same `/run` result as the sandbox-agent path, which is the whole point of the ports:
the workflow author cannot tell which engine ran. It exists for the simplest local case and
Expand Down
Loading
Loading