Skip to content

feat(amp): add an Amp agent type - #1539

Closed
joahg wants to merge 1 commit into
block:mainfrom
joahg:joah/amp-agent-type
Closed

joahg wants to merge 1 commit into
block:mainfrom
joahg:joah/amp-agent-type

Conversation

@joahg

@joahg joahg commented Oct 22, 2026

Copy link
Copy Markdown
Member

Summary

Adds Amp as a bundled agent type (buzz.amp). It's a desktop plugin, off by default, that runs the local amp CLI as a Buzz agent harness, the same way the Claude Code, Codex, Goose and Pi plugins do. Tracks AGNTOPS-664.

  • Turns: each conversation (a Buzz thread, or the whole channel; always one per DM or Solo chat) is one kept Amp thread. Each turn starts one amp process: amp --execute --stream-json --no-archive-after-execute --title … for a new thread, amp threads continue <T-id> --execute --stream-json after that. Turns in a conversation run one at a time. Stop kills the turn's process.
  • Buzz tools: a per-turn loopback MCP server (host.serveMcp) passed as --mcp-config. The amp process holds no Buzz key.
  • Prompt: Amp has no system-prompt flag, so Buzz's system prompt opens each new thread, and is sent again when it changes.
  • Fallbacks: a reply written in text without send is posted for Amp. A failed turn posts ⚠️ I couldn't finish that: … and tags the owner. A thread Amp can't continue (for example, an archived one) is started again.
  • Settings: Mode (--mode low|medium|high|ultra, also offered for one turn in the composer's model picker), instructions, Workspace (default ~/.buzz), and Sessions scope.
  • Watches and timers: run as one-off threads, which Amp archives.
  • amp-buzz: BUZZ_MANAGED_AGENT=1 is set so Amp plugins that mirror threads to Buzz, such as amp-buzz, stay out of the way.

Amp connects twice when continuing a thread

amp threads continue connects to the MCP server at startup, drops that connection as the thread loads (without a DELETE), then connects again. The native listener admits one session and stops at a second initialize. So while continuing, the Buzz tools refuse initialize with a JSON-RPC error until Amp's stream-json init line arrives. A refused initialize doesn't claim the native session, and Amp retries with backoff. The live test shows four refusals, then a successful connect and tool call.

Not yet supported

No icon, Activity transcript, usage, compact or new-session controls, steering a running turn (a newer mention waits), resuming turns stopped by a reload, or Windows.

Validation

  • bin/pnpm exec vitest run src/bundled/amp: 18 unit tests pass (args, outcome parsing, the initialize gate, owner gating, open then continue, restart when a thread can't continue, failure report, queue and Stop, runOnce).
  • AMP_LIVE=1 bin/pnpm exec vitest run src/bundled/amp/amp.live.test.ts against the real amp (signed in), with a loopback MCP server that follows the native listener's claim rules. Turn 1 opens a kept thread and calls send. Turn 2 continues it and recalls a word from turn 1.
  • bin/pnpm run check, vitest run src/bundled src/app src/plugins (3886 passed), cargo test -p buzzodz-plugins.
  • Not yet exercised: the agent flow in the running desktop app (create an Amp agent, mention it).
  • Pre-push clippy fails locally on src-tauri/src/nsec_backup/persistence.rs and src-tauri/src/terminal/mod.rs (nonminimal_bool). Both files are untouched here and fail the same way on main, so I pushed with --no-verify.

🤖 Generated with Claude Code

A bundled desktop plugin, off by default, that runs the local `amp` CLI as
a Buzz agent harness. Each conversation is one kept Amp thread continued
with `amp threads continue --execute --stream-json`; the Buzz tools are a
per-turn loopback MCP server. While continuing a thread, the Buzz tools
refuse `initialize` until Amp reports the thread open, since Amp connects
twice and the host server admits one session.

AGNTOPS-664

Co-authored-by: Hertz <6dc34705e5eaffade9f039764bf81185f09d3e096c0dedc2fed3b7ba7a549620@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
joahg added a commit that referenced this pull request Nov 4, 2026
## What

`ctx.agents.harness` gives an agent type that a plugin brings the Buzz
side of each turn, built the way the bundled harnesses build it. The
harness still owns its process, sessions and saved state.

- `conversation(delivery, scope)` admits a mention, plugin wake or
attention wake and freezes where it goes: the key (the thread, or the
channel; a DM or Solo chat is always one), the root, whether replies go
at the top level, and a `where` label for titles. It returns `undefined`
when the harness must ignore the delivery: any other trigger, a mention
that isn't the owner's own or attested by them (workflow mentions never
are, per #2360), no channel, or a wake with no conversation to go to.
- `systemPrompt(delivery, { scope, cwd, instructions, tools? })` is
Buzz's system prompt: base prompt, session model, instructions, teams,
`core` memory and repositories folder. `tools: false` adds a section for
sessions without Buzz tools, whose final message the harness posts as
the reply.
- `turnPrompt(delivery, conversation, { cursor? })` returns `{ text,
cursor, restore }`: what the session has not been shown, the event,
plugins' notes and the tools' end-of-turn note. The cursor is opaque:
the plugin saves it with its session and hands it back next turn (none
means a new session). In a channel-scoped conversation it keeps a mark
per thread, like claude-code's `contextRoot`, so a turn in one thread
never hides another thread's earlier messages. A resumed turn gets the
resume note. `restore()` puts the notes back for a turn that never
started, and asking again takes them again.
- `standalonePrompt(agent, text)` gives a watch's or timer's turn its
routing context.
- `tools(agent, conversation?, { read? })` answers the Buzz tools over
MCP, for `ctx.host.serveMcp`.
- `reply(agent, conversation, text, { notifyOwner? })` posts as the
agent in the thread, or at the top level.

The author package also exports the types a harness needs:
`AgentHandle`, `Delivery`, `StandaloneTurn`, `TurnResult`, `McpHandler`,
`HostMcpServer` and the kit's types. `docs/plugin-architecture.md`
documents the kit.

## Why

An installed plugin can already register an agent type and start its CLI
with `host.spawn` and `host.serveMcp`. But the Buzz side of a turn only
existed inside app internals the author package doesn't expose, so every
harness had to be bundled. The first user is an Amp harness that ships
as a plugin in squareup/agents `buzz-plugins` (AGNTOPS-716), replacing
the bundled approach in #1539. Its agents can also run their Amp threads
in orbs.

## Authority

The kit grants nothing `ctx.agents` and `ctx.host` don't already give
the plugin:
- It acts through the `AgentHandle` the plugin was delivered.
- It reads memory through the owner's relay, as `agents.relay` already
allows.
- It reads files for uploads only through the `read` the plugin passes,
which comes from the plugin's own `host.files` grant. Without `read`,
uploading a local file fails.

Bundled harnesses are unchanged.

## Validation (at 58ed3ee, on main 063fccd)

- `src/features/agent-harness/kit.test.ts` has 17 tests covering:
  - owner gating, including refused workflow mentions and other triggers
  - conversation keys for thread, channel scope, DM and Solo
  - wakes: landing, and dropping one with nowhere to go
- turn prompts: unseen messages, notes, restore, asking again, resume,
per-thread marks in a channel conversation (thread A then thread B), an
unreadable cursor
  - the system prompt, with and without tools
  - reply tags in a thread and in Solo
- the tools handler: `tools/list`, `initialize` instructions, a message
with no `jsonrpc` field, and local-file reads going only through the
plugin's `read`
- `src/plugins/author.test.mjs` generates the author package and
typechecks a consumer that registers an agent type using the kit.
- `pnpm check` passes (Biome, tsc, design typecheck and design checks).
- Full `vitest run`: 11 failures across 9 files, all timeouts while the
machine was loaded. All but one pass when rerun alone. The exception,
`browser-host/media-preparation.integration.test.mjs` (tiled HEIC),
fails on main on this machine too.

Deferred: an in-app run of a plugin harness built on the kit. That will
be the Amp plugin, which needs this change in a desktop build.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@joahg

joahg commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

🤖 Superseded: the Amp agent type now ships as an installed plugin (squareup/agents#9092) on the generic plugin harness kit merged in #2364 (AGNTOPS-716).

@joahg joahg closed this Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant