Skip to content

feat(agents2): plugin agent types on a new Agents2 page - #693

Draft
baxen wants to merge 10 commits into
mainfrom
murderbot/agents2
Draft

baxen wants to merge 10 commits into
mainfrom
murderbot/agents2

Conversation

@baxen

@baxen baxen commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Combines Logan's agent-attention/v1 coplan spec with the plugin agent-type idea from #621. It adds a new Agents2 page and leaves the existing harness agents untouched.

What's here

  • Agent types are plugins. A type registers on ctx.agents2: { id, title, defaults, run, CardBack, tabs }.
  • Local store. It matches the agent-attention/v1 objects: interest/<id> and watch/<id> slugs, each { value, modifiedAt }. Next to them is a plugin-owned config. Each write changes exactly one object, behind a small interface, so a relay backend can replace localStorage later.
  • Runtime.
    • Event watches are matched against the owner's live relay stream (spec steps 1–4). Directly addressed events go to a mention trigger instead.
    • Timers follow the spec's schedule rules: armed_at, interval, max_occurrences, expires_at, and no burst catch-up. Timer state is stored separately from config.
    • Classifier watches are skipped and shown as unsupported.
  • Minimal run lifetime. run({ trigger, agent, config, signal }) gets one stable agent handle (pubkey, name, owner, publish) and an AbortSignal that fires on timeout or agent removal. Runs execute one at a time per agent. There is no per-run identity fencing (unlike Add plugin agent types: agents a plugin defines and the app runs #621).
  • Native identity. crates/agent-controller/src/app_agent.rs plus the Tauri commands in src-tauri/src/app_agents.rs.
    • Keys live in the OS credential store and require the owner's NIP-OA attestation.
    • Only kinds 0/5/7/9/40003 are signed, with size bounds, and posted via NIP-98 <relay>/events.
    • Desktop only; the browser shows an unavailable state.
  • Agents2 page.
    • A grid with a "New agent" card first and no running/stopped states.
    • Hovering a card flips it to the type's CardBack.
    • Clicking a card opens an identity column next to the type's tabs (details and edit).
  • Profile panel. If the pubkey is an Agents2 agent, the panel shows the type's tabs and hides Memories. Otherwise it is unchanged.
  • Example type. A bundled responder that replies with configured text when a watch or mention fires.

FOUNDATION files (edited per baxen's direction; please review closely)

Checks

  • pnpm typecheck passes, and biome check is clean.
  • Full vitest run passes. pages.integration counts were updated for the new page.
  • cargo test -p buzz-agent-controller passes, including the new app_agent tests.
  • cargo test for src-tauri: everything passes except 3 host_command tests. Those failed only in the parallel run and pass when run alone; this PR doesn't touch them.

Status: ready to try, not validated. Not done yet: an in-app run of create/flip/edit/responder, an independent agent review, and review of auth/signing.

Non-goals

Relay storage for attention, classifiers, and migrating harness agents.

Possible split

  1. relay subscribeLive
  2. native app-agent identity
  3. agents2 service + store
  4. page + profile tabs
  5. responder example

hMurderbot added 10 commits October 6, 2026 20:43
Agents are plugins from the start: a type registers run, a card back, and
tabs on ctx.agents2. Attention follows agent-attention/v1 in a local store
behind a one-object-per-write interface. Identity keys stay native; run gets
a stable agent handle and an AbortSignal (timeout or removal).

Signed-off-by: hMurderbot <219c58afff8ac2c76e4e527a329aa61c2ebbb896786b1916d4f9cca5140ecd04@buzz.block.builderlab.xyz>
…alog

The desktop catalog comes from plugin-manager's bundled list, so bundles
missing there never activate in the app even though the web catalog has them.

Signed-off-by: hMurderbot <219c58afff8ac2c76e4e527a329aa61c2ebbb896786b1916d4f9cca5140ecd04@buzz.block.builderlab.xyz>
…host

The harness AgentHost holds an exclusive lock on its agent storage, so a
second Buzz app (e.g. one running main alongside this branch) made every
Agents2 create/delete/publish fail with "Another Buzz app owns this agent
storage". Agents2 keys only need the OS credential store, whose bundle lock
is per-operation and safe across processes, so AppAgentHost now holds its
own PlatformCredentials. Also drops Option::take_if (MSRV 1.77).
Create asked the harness command agent_control_create_authorize for the
owner's NIP-OA tag. That command checks the harness AgentHost's pending
request, so it failed with the harness storage lock error while another
Buzz app was open, and would have refused Agents2's key anyway. Add
app_agent_create_authorize, which attests only the key Agents2 prepared,
for the owner it was prepared for.
Drops the card flips. The page now has three frames:

- Inventory: agents as tiles (portrait, name, the type's summary line) in a
  listbox. Click or arrow keys select; Enter or double-click opens; Esc clears.
- Peek: a side panel beside the grid with identity, the type's Peek, what
  wakes the agent, and Open.
- Build: an identity rail (inline rename, type, key, delete) and the type's
  tabs followed by the app's Attention tab.

New agents are made in the side panel (name, type cards with descriptions)
and go straight to Build.

Attention is the app's: every Agents2 agent is woken through
agent-attention/v1, so AttentionPanel (interests with their watches and
timers, switches, forms, raw JSON fallback) replaces the plugin-hosted
AttentionEditor. The profile panel gets the same Attention tab.

Contract: `Back` becomes `Peek`, and types may add `summary(agent)`.
Responder is updated to match.
Runtime
- Only chat kinds (9, 40003, 40007, 46010) address an agent, so reactions to
  or deletions of its messages no longer wake it. DMs (kind 4) are no longer
  treated as mentions: run could neither read nor answer one.
- One owner's agents no longer wake each other by replying: a sibling's event
  reaches another agent only as a fresh mention, never as a reply or a watch.
- Runners are long-lived, one per saved agent. Edits no longer abort the
  in-flight run, drop the queue or reset the rate window; each job reads the
  agent and config as it starts, and watches recompile when attention changes.
  A run aborts only when its agent is removed or its type is replaced.
- Agent state has one owner: timer run state moves into each record (so it goes
  with its slug or agent), seen/wrote live on the runner, and records whose key
  is gone are dropped when identities load.
- create no longer fails after the agent exists when its profile publish fails.
- The relay snapshot exposes the community origin, so create stops parsing the
  partition key.

Native
- App agent identities lock read-modify-write across app instances.
- Pending creates are kept by pubkey, so concurrent creates don't collide.
- list and publish read the identity file off the async runtime.

Contract
- Peek receives only the agent. Type tabs receive save(config): the app owns
  the name and attention.
- Responder is written the way an external plugin must be: ctx.react and
  type-only imports, plain HTML controls.

UI
- agentSections() builds the type's tabs plus Attention for both the Build view
  and the profile panel.
- Attention model helpers move to attention.ts; editing an existing watch is
  its JSON only, the forms add.
- Key shown with formatPublicKey; "Copied" resets.
… of view

- load() prunes only records that existed before it listed identities, and
  merges identities created while the list was in flight.
- drain() pauses (without consuming jobs) while the agent is not visible;
  update() resumes queued runners after publishing the snapshot. A type that
  cannot run still drops the queue.
- Disposal clears the shown agents without reconciling runners.
- Document that tab save() replaces the type's whole config.
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