Seed Agents is a local-first, account-scoped agent system. The desktop app uses it to configure an agent server, store provider secrets, create agents, work in durable sessions, stream model responses, delegate work to children, and inspect everything that executed.
Design principles
Signed control plane: every HTTP action is wrapped in a signed DAG-CBOR envelope. See the signed API.
Account isolation: persisted state belongs to one Seed account. Access needs ownership or an accepted agent-level reader or writer collaboration.
Durable sessions: sessions are append-only event logs with replay by sequence number.
Live clients: desktop clients subscribe over a signed WebSocket protocol and receive live changes.
Secret redaction: API keys are encrypted at rest and never returned in API responses.
Visible tools: tool calls and tool results are durable session events, rendered in the UI.
Shared hypermedia behavior: read uses SDK code shared with the CLI's URL resolution. It does not shell out to the CLI.
Inspectable operation: durable session events, the signed read actions, and diagnostic logs support debugging local workflows. There is no unauthenticated inspection surface.
Few verbs, one address space: the five verbs read, write, call, delegate, plan, plus the two session verbs status and continue_session, are the whole model-facing surface. A new feature arrives as a new address or a new callable. It never adds a tool to the provider payload.
Configuration is content: an agent's tools and memory are documents in its Space. The agent and its owner can both address and read them.
The log is symmetric: the user holds the same verbs the agent does, every event in the Log names its actor, and there is no side channel between them.
Everything that executes is a run: turns, children, and scripts are rows in one tree, and that tree is also the queue. Waiting costs nothing and a crash is recoverable. See Runs.
Each term above has its own page, listed in the Agents glossary.
Major components
Seed app (desktop) and Seed web app, sharing frontend/packages/ui/src/agents
├─ Local agents server subprocess (desktop only; same artifact as the Docker image)
│ configured with the desktop's typed HM API bridge plus its daemon's direct IPFS endpoint
├─ Agents routes: list, detail, session
├─ Assistant sidebar: sessions of any agent on any configured server
├─ Provider and create-agent dialogs
├─ daemon-backed signing for the selected account
├─ signed CBOR HTTP client
├─ signed WebSocket subscription hook
└─ chat message renderer shared with the desktop assistant panel
Agents service (Bun)
├─ /api/message signed action API
├─ /agents/ws signed subscription API
├─ SQLite persistence (state, and the runs table that is also the queue)
├─ AES-GCM secret storage
├─ Pi SDK-backed model execution loop
├─ the verbs (read / write / call / delegate / plan, plus status / continue_session)
├─ tool documents in ~/tools + the <space> index in every system prompt
├─ run queue: leases, boot sweep, park/resume, wake sources
├─ QuickJS script engine with a content-keyed journal
└─ diagnostic logging
Shared Seed libraries
├─ @seed-hypermedia/client for Ed25519 signatures/principals and canonical DAG-CBOR
│ (the service imports them through the @shm/shared/blobs and @shm/shared/cbor re-exports)
├─ @seed-hypermedia/client for URL resolution and markdown conversion
└─ desktop daemon for selected-account signingThe desktop app signs through the Seed daemon. The service itself is described on the agents service page, and persistence covers the SQLite tables.
End-to-end user flow
User opens the desktop Agents page.
Desktop reads the default agent server URL and checks /agents/api/health.
Desktop opens a signed WebSocket subscription for the selected account.
User configures a model provider in the Model providers dialog.
Desktop sends signed SetSecret and SetModelProvider actions.
User creates an agent in the Create agent dialog.
Desktop sends signed CreateAgent.
Server persists the agent and broadcasts account changes.
User opens agent detail and creates or opens a session.
Desktop subscribes to sessions/<sessionId> over WebSocket.
A writer sends a message with signed MessageSession. Other accepted writers may send at the same time.
Server immediately appends each durable user message with its acting account and exact signer, broadcasts it, and creates a runs row. The first turn is claimed inline on the interactive queue. Concurrent turns stay queued in append order, because only one model turn may own a session. Session status mirrors run state, so it reads streaming while any of those turns are live.
Server creates an in-memory Pi SDK session. Its configuration comes from the Seed provider record, the encrypted secret, the agent's system prompt (its own instructions plus the shared runtime prompt and its <space> index), and the tool set: the verbs, plus any callables the transcript shows this thread has already expanded.
Pi runs the provider and model loop and emits streaming, tool, and final events.
Server emits session-partial service events for model text deltas, cumulative usage, and the current activity phase.
WebSocket sends appendPartial events to subscribed desktop clients.
Desktop renders the partial through the shared assistant markdown renderer.
Tool calls and results are translated from Pi events and appended as durable Seed events stamped actor: 'agent'. A call for a tool the thread has not expanded returns that tool's contract instead of an error (touch-expand). Once the contract is in the transcript, the tool is promoted for the rest of the thread.
If the turn used delegate, each child gets its own run row: a model child with its own session, or a script child in the QuickJS engine. The parent's run parks on them without holding resources and resumes when they resolve.
The final assistant message is appended as a durable event. The run finalizes: it rolls child usage up, settles plan steps whose children all succeeded, and records any obligation it ended without meeting.
Session status re-derives to idle, or to error when the latest run failed.
Completed capabilities
Server
Bun standalone service with configurable host, port, db, and data dir. See operations.
/api/message and /agents/api/message signed CBOR action routes.
/api/health and /agents/api/health JSON health routes.
/agents/ws signed WebSocket subscription endpoint.
No browser UI and no unauthenticated data routes. Everything else is a 404.
Graceful shutdown for WebSockets and SQLite.
Persistence
SQLite schema, version gate, and prepend-only migrations.
Accounts and local account authorization table.
Provider config table.
AES-GCM encrypted secrets.
Agent definitions and per-agent state directories.
Sessions and durable session events.
Tool documents per agent (tool_documents), addressed by CID.
Runs, run journals, and outstanding event waits (runs, run_journal, run_event_waits).
Idempotency table for client request and message IDs.
Agent runtime
Agent create, list, get, update, and delete.
Agent invitations, acceptance and decline, revocation, and reader and writer collaborator roles. Readers can inspect the complete agent. Writers can also change it and interact with it. Only owners manage access or delete the agent.
Session create, get, list, message, stop, retry, and delete.
Cross-agent session listing (ListSessions) with composite keyset pagination.
Pi SDK-backed model execution for OpenAI-compatible, Anthropic, and Google provider mappings. See model providers.
Text streaming translated from Pi events into Seed WebSocket partials.
Durable user, assistant, error, and tool events, each carrying its actor.
The verbs, registered as Seed-owned Pi custom tools. Callables are dispatched through call and never exposed to the provider.
Tool result size limiting (256 KiB).
The run queue: two queues, lease-based claiming, boot sweep, retry classification with backoff, cancellation cascade, and timer and event wakes.
Seed app and web UI
Agents list, server, detail, and session routes with sidebar, menu, and shortcut integration. See desktop and web UI.
Local agents server lifecycle: attaches to an already-running server in development, and spawns the bundled binary in a packaged app.
Assistant sidebar backed by agent sessions, with no separate chat runtime. It lists sessions from every configured server, including the local one.
Default and multi-server settings.
Provider management dialog for every provider type in the registry, with API-key and ChatGPT-subscription sign-in.
Create-agent dialog with configured-provider selection.
Agent detail page with editable name, model, and system prompt, and a Settings panel to invite collaborators and manage members.
Session page with debounced inline title editing, optimistic user messages, durable events, live assistant partials, and shared chat rendering.
A mounted remote session page, or the selected Assistant-sidebar session, keeps hm:// documents and comments that the agent created or referenced subscribed on the desktop's local node. This includes recursive target discovery for comments and exact versions for document write results. Background sessions do not sync.
User and assistant bubbles, markdown, streaming cursor, and tool-call bubbles shared with the desktop assistant panel.
Tools tab over ListAgentTools: the callable grants an owner can toggle, and, for an authored lambda, a dialog with its full document: contract, source, and content address.
The pinned run card with its Activity drawer, nested child sessions, and the parked-run Answer action that sends a SignalRun.
The composer's wrench palette, which runs read, write, and call as the user through InvokeSessionTool.
WebSocket diagnostic logs and defensive message parsing.
Known incomplete areas
Anthropic and Google are mapped through Pi but have no real-provider smoke coverage yet, so they are not production-complete.
Signed-action timestamps reject requests more than five minutes from server time. There is no nonce cache, so a captured request can be replayed inside that window. See security.
No production KMS or OS-keychain storage for the secret key.
Grants stop at the callable set plus a single publish grant. There is no per-address or per-destination policy engine, and memory writes are ungated by design.
Providers can be deleted (DeleteModelProvider, which also removes the API-key secret), but there is no general secret-deletion action.
Triggers and plans are still SQLite rows, not documents in the Space. The event-bus milestone that moves them is only partly built.
No full WebSocket heartbeat, backpressure, or subscription-limit protocol.
No long-term retention or pruning policy for events, runs, or journals.
See the roadmap for what is planned.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime