Skip to content

Commit 330b1c6

Browse files
committed
docs/agent-workflows-design: functional slice
1 parent a97e608 commit 330b1c6

96 files changed

Lines changed: 15605 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Agent Workflows
2+
3+
This workspace documents the current agent workflow implementation and the work still
4+
needed to make it production-ready.
5+
6+
The source of truth is the code listed in [Ground Truth](ground-truth.md). Design pages at
7+
this level describe the current implementation unless they explicitly say "planned",
8+
"blocked", or "not implemented". Historical work-package notes and old RFCs live in
9+
[trash/](trash/).
10+
11+
## Read In This Order
12+
13+
1. [Ground Truth](ground-truth.md): what the current code does, what is wired, and what is
14+
still missing.
15+
2. [Status](status.md): current cleanup state, decisions, blockers, and next steps.
16+
3. [Meeting Alignment](meeting-alignment.md): where the current work matches the June 18
17+
design discussion, where it diverges, and what still needs to be done.
18+
4. [Architecture](architecture.md): the service, agent runner sidecar, harnesses, and
19+
sandboxes.
20+
5. [Protocol](protocol.md): `/invoke`, `/messages`, `/load-session`, and the runner `/run`
21+
wire contract.
22+
6. [Ports and Adapters](ports-and-adapters.md): the SDK runtime ports, backend adapters,
23+
harness adapters, and browser protocol adapter.
24+
7. [Agent Template](agent-template.md): the intended split between generic agent identity,
25+
harness-specific config, and runtime infrastructure.
26+
8. [Sessions](sessions.md): cold replay, streaming, session ids, and the missing session
27+
store.
28+
9. [Triggers](triggers.md): planned trigger/event integration and the missing Compose.io
29+
POC.
30+
10. [Pi Adapter](adapters/pi.md): Pi-specific tool delivery, prompt layers, tracing, and
31+
usage writeback.
32+
11. [Claude Code Adapter](adapters/claude-code.md): Claude over ACP, MCP tool delivery,
33+
permissions, tracing, and usage.
34+
12. [Agenta Harness](adapters/agenta.md): the experimental Agenta-flavored Pi harness.
35+
13. [SDK Local Tools](sdk-local-tools/): planned and partly implemented work for standalone
36+
SDK tool resolution. This remains blocked by `LocalBackend`.
37+
14. [PR Stack](pr-stack.md): functional breakpoints for reviewable stacked PRs.
38+
15. [Implementation Review](implementation-review.md): high-level cleanup risks and PR
39+
slicing notes.
40+
16. [Open Issues](open-issues.md): deferred decisions that need ownership.
41+
42+
## Current State
43+
44+
The agent workflow runs a coding harness as an Agenta workflow. It supports:
45+
46+
- A batch `/invoke` path that returns the final assistant message.
47+
- An agent-only `/messages` path that accepts Vercel `UIMessage` input and can stream a
48+
Vercel UI Message Stream over SSE.
49+
- A `/load-session` route with the right contract but no durable storage by default.
50+
- Pi and Claude harnesses through the rivet runner.
51+
- Pi and the experimental `agenta` harness through the in-process Pi backend.
52+
- Server-resolved tool specs, code tool execution, callback tools, and MCP plumbing behind
53+
a feature flag.
54+
55+
The main missing pieces are durable server-owned sessions, future session snapshot
56+
interfaces, the agent template/config split, trigger integration, a working standalone
57+
`LocalBackend`, production Agenta harness content, first-class built-in workflow
58+
registration, and the final cleanup of historical work-package names in comments and docs.
59+
60+
## Trash
61+
62+
[trash/](trash/) holds old work-package notes, research spikes, and superseded RFCs. It is
63+
kept for archaeology only. Do not treat it as design truth unless a current page links to a
64+
specific note as background.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# The Agenta harness
2+
3+
`AgentaHarness` is Pi with an opinion. It runs on the same engine as the [Pi
4+
adapter](pi.md) and produces a Pi-shaped config, so it inherits everything Pi does (native
5+
tools, the system-prompt layers, tracing). What it adds is a fixed set of Agenta-shipped
6+
extras that the agent author cannot turn off:
7+
8+
- **Forced tools** — always unioned into the agent's resolved tools. At minimum `read`
9+
(Pi only renders the skills section when `read` is enabled) and `bash` (so skills can run
10+
their helper scripts).
11+
- **Forced skills** — Agenta-shipped Pi skills loaded on every run.
12+
- **A base AGENTS.md preamble** — the author's `instructions` are appended after it.
13+
- **A base persona** — forced onto Pi's `append_system`, with any author-supplied
14+
`append_system` appended after it.
15+
16+
Read the [architecture](../architecture.md), [ports and adapters](../ports-and-adapters.md),
17+
and [Pi adapter](pi.md) pages first. This page assumes them.
18+
19+
## Where the forced bits live
20+
21+
The forced *policy* lives in the SDK harness layer, in one editable module:
22+
`sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py` (`AGENTA_PREAMBLE`,
23+
`AGENTA_FORCED_APPEND_SYSTEM`, `AGENTA_FORCED_TOOLS`, `AGENTA_FORCED_SKILLS`). `AgentaHarness`
24+
(`adapters/harnesses.py`) reads them in `_to_harness_config` and layers them onto the neutral
25+
`SessionConfig`, exactly where `PiHarness` and `ClaudeHarness` do their own translation.
26+
27+
The forced skill *files* live with the runner that runs Pi, under
28+
`services/agent/skills/<name>/` (each a directory with a `SKILL.md`). Skills are real files on
29+
disk because they reference relative scripts and assets, so they cannot ride the wire as
30+
text. The contract between the two halves is the skill **name**: `AGENTA_FORCED_SKILLS` lists
31+
names, and each must match a committed directory under the runner's skills root.
32+
33+
## How a skill reaches the model
34+
35+
1. `AgentaHarness._to_harness_config` puts the forced skill names on the `skills` field of
36+
the `/run` request (`AgentaAgentConfig.wire_tools`).
37+
2. The in-process Pi engine (`engines/pi.ts`) resolves each name against its bundled
38+
`skills/` root (override with `AGENTA_AGENT_SKILLS_DIR`) and passes the directories to Pi's
39+
`DefaultResourceLoader` as `additionalSkillPaths`, with `noSkills: true` so only the
40+
bundled skills load (the run stays hermetic, like `noContextFiles`).
41+
3. Pi loads them, and because the forced `read` tool is enabled, surfaces them in the system
42+
prompt. The model reads a skill's `SKILL.md` on demand (progressive disclosure).
43+
44+
## Two prompt layers, kept distinct
45+
46+
This follows Pi's own split (see `PiAgentConfig`): the **persona** ("who the agent is")
47+
belongs in `append_system`, and **project conventions** belong in `AGENTS.md`. So the Agenta
48+
persona is a forced `append_system`, while the Agenta base preamble plus the author's
49+
instructions are the `AGENTS.md`. An author's own `system` / `append_system` (via
50+
`AgentConfig.harness_options["pi"]`) still apply, layered after the forced persona.
51+
52+
## Selecting it
53+
54+
`agenta` is a harness option alongside `pi` and `claude` (the playground dropdown, the
55+
`harness` field). It runs on the in-process Pi backend (`InProcessPiBackend` now lists
56+
`HarnessType.AGENTA` as supported), so `select_backend` keeps `agenta` on the local Pi path.
57+
58+
## Deferred
59+
60+
Only the in-process Pi (local) path is wired. The ACP/rivet path (and therefore the Daytona
61+
sandbox) does not yet deliver the forced skills — it would teach `runRivet` to read the
62+
`skills` field and lay the bundled skill directories into the sandbox via the existing
63+
bundled-file provisioning. Until then, `agenta` with a non-local sandbox raises
64+
`UnsupportedHarnessError` rather than silently running without its skills.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# The Claude Code adapter
2+
3+
Claude Code is the second harness. It proves the central claim of this PoC: that swapping
4+
the agent is one config value. Where the [Pi adapter](pi.md) does much of its work inside Pi
5+
through an extension, Claude does its work through standard ACP. That makes Claude the
6+
template for any MCP-capable harness rivet can drive.
7+
8+
Read the [architecture](../architecture.md) and [ports and adapters](../ports-and-adapters.md)
9+
pages first.
10+
11+
## Running Claude
12+
13+
The daemon resolves the harness id `claude` to the `claude-agent-acp` adapter, which starts
14+
the `claude` CLI. One operational detail is worth calling out, because it caused a real bug.
15+
The daemon does not ship the `claude` CLI. It downloads it over HTTPS the first time a run
16+
asks for Claude. The sidecar image is a slim Node image with no root certificates, so that
17+
HTTPS download failed until we added `ca-certificates` to the image. With the certs in
18+
place, the download verifies and Claude runs.
19+
20+
Auth is config, like everything else. Claude authenticates with `ANTHROPIC_API_KEY` from the
21+
project vault when present, or with an OAuth token (`CLAUDE_CODE_OAUTH_TOKEN`) otherwise. The
22+
runner turns the common failures into one clear line, so a user sees "add the project's
23+
Anthropic key" rather than a stack trace.
24+
25+
## Tools over MCP
26+
27+
Claude advertises the `mcpTools` capability, so the runner delivers tools to Claude the
28+
standard ACP way, over MCP. This is the branch that the [capability probe](../ports-and-adapters.md)
29+
chooses: deliver over MCP when the harness reports `mcpTools`, not when the harness name is
30+
something in particular.
31+
32+
The mechanism is a small stdio MCP server (`tools/mcp-server.ts`) that the daemon launches
33+
and attaches to the session. Its tool bodies POST back to Agenta's `/tools/call` with the
34+
same callback-tool envelope the Pi path uses. The resolved specs and the callback endpoint reach the
35+
MCP server through its environment, so nothing tool-specific is written to a file the agent
36+
can read. The safety property is identical to Pi's: the provider key and the connection auth
37+
stay server-side, and the agent only ever asks Agenta to run a named tool.
38+
39+
## Permissions
40+
41+
Claude gates tool use behind a permission prompt. In an Agenta run there is no human at the
42+
keyboard to answer it, so the runner answers for it. By default it auto-approves, because the
43+
tools are backend-resolved and trusted. The per-run permission policy (or an env override)
44+
can flip this to deny, which rejects tool use instead. This is handled on
45+
`session.onPermissionRequest`, a hook Pi does not need because Pi does not gate tools this
46+
way.
47+
48+
## Tracing from the event stream
49+
50+
Claude does not self-instrument the way Pi does, because we do not load an Agenta extension
51+
into Claude. So the runner builds the trace itself, from the ACP event stream. It subscribes
52+
to the session's `session/update` notifications and turns them into the same span tree Pi
53+
produces:
54+
55+
```
56+
invoke_agent (AGENT)
57+
turn 0 (CHAIN)
58+
chat <model> (LLM)
59+
execute_tool <name> (TOOL) one per ACP tool_call
60+
```
61+
62+
This is the general path. Any harness rivet drives that does not bring its own
63+
instrumentation gets traced this way. Pi is the exception that traces itself; Claude is the
64+
rule.
65+
66+
## Usage and output
67+
68+
Claude reports usage in two places, so the runner reads both. The per-call input and output
69+
token split rides on the ACP `PromptResponse`, and the cost rides on the `usage_update`
70+
event. The runner combines them into the run total, which then rolls onto the workflow span
71+
the same way Pi's writeback total does.
72+
73+
Output needs one small piece of care. Claude streams text deltas and also periodically
74+
streams a full cumulative snapshot of the message so far. If the runner naively appended
75+
everything, the answer would double. The runner detects a snapshot (a chunk that is a
76+
superset of what it already has) and replaces rather than appends, so the final text is
77+
correct whether a chunk is a delta or a snapshot.
78+
79+
## Models
80+
81+
Claude ignores a model id meant for another provider. Ask it for `gpt-5.5` and it keeps its
82+
own default. The runner handles this honestly: when the harness does not accept the requested
83+
model, the chat span is labelled `chat` rather than falsely claiming a model the run did not
84+
use.
85+
86+
## What Claude demonstrates
87+
88+
Claude is the proof that the seam works. Adding it took a `ClaudeHarness` (which holds its
89+
Pi-versus-Claude config mapping) and no change to the workflow handler above the ports; the
90+
same `RivetBackend` drives it. It also exercises the capability-driven branches the design is
91+
built on: tools over MCP because it reports `mcpTools`, a permission answer because it gates
92+
tools, and event-stream tracing because it does not self-instrument. A future harness that
93+
rivet can drive would reuse this exact path. A future harness that rivet cannot drive would
94+
instead get its own backend beside `RivetBackend` and `InProcessPiBackend`, behind the same
95+
`/run` contract.

0 commit comments

Comments
 (0)