Development
How to run, test, and change the Seed Agents service and its shared UI, with a code map and the rules that keep the model-facing surface coherent.

This page is for people and coding agents changing Seed Agents. It names where things are, how to run and validate them, and the conventions that keep the runtime coherent. Read the root AGENTS.md, then agents/AGENTS.md for the service (Bun only, never pnpm; bun check && bun test before every commit) and frontend/AGENTS.md for UI work. General repo setup is in contributing.

Commands

The whole stack in one mprocs TUI (Docker web backends, desktop, web, and the agents server, one pane per process; q stops everything):

./dev up

The agents server alone:

direnv exec . bash -lc 'cd agents && bun src/main.ts' # plain direnv exec . bash -lc 'cd agents && bun run dev' # hot reload, dev web backends, subscription auth on

Validate the service:

direnv exec . bash -lc 'cd agents && bun check && bun test' # typecheck + formatter, then the suite direnv exec . bash -lc 'cd agents && bun run test:build' # the compiled binary boots direnv exec . bash -lc 'cd agents && bun run test:docker' # the image boots direnv exec . bash -lc 'cd agents && bun run test:trigger' # real daemon, mention trigger fires once direnv exec . bash -lc 'cd agents && bun run protocol:check' # the protocol surface did not change silently

Validate the frontend:

direnv exec . bash -lc 'pnpm typecheck' direnv exec . bash -lc 'pnpm test' direnv exec . bash -lc 'pnpm format:check'

Build the deployment image and run the desktop:

docker build -t seedhypermedia/agents:dev . -f ./agents/Dockerfile direnv exec . bash -lc './dev run-desktop'

pnpm audit fails today on repository dependency advisories unrelated to this feature. Report that. Do not claim it passed.

Local URLs

The dev shell sets SEED_AGENTS_HTTP_PORT=3051 in .env.vars, so the dev server never shares a port with the 3050 default a packaged build uses.

what

dev URL

server base

http://localhost:3051

health

http://localhost:3051/agents/api/health

signed API

POST http://localhost:3051/api/message

WebSocket

ws://localhost:3051/agents/ws

latency snapshot

http://localhost:3051/api/perf

Code map

The service, in agents/:

    src/main.ts: the Bun HTTP and WebSocket server, CORS, the webhook route, health and version, live event fan-out.

    src/api-service.ts: the heart of the service: action dispatch, persistence operations, the Pi-backed model loop, the verb implementations, the Space index, trigger firing, subscription verification.

    src/auth.ts: signed envelope verification, the five-minute timestamp window, capability-based delegation.

    src/cbor.ts: DAG-CBOR request and response helpers and the protocol version header.

    src/config.ts: every environment variable and CLI flag, with defaults.

    src/sqlite.ts and src/sqlite-schema.sql: open, schema gate, migrations; the canonical schema.

    src/runs.ts: durable run records and the dispatch queue: leases, fair-share claiming, retries, cancellation cascade, timer wakes.

    src/run-events.ts: waiting runs and what wakes them.

    src/workflow-host.ts: the QuickJS script engine: lint, realm prelude, journaled effect pump, replay; src/workflow-worker-host.ts runs it in a worker behind a flag.

    src/tool-documents.ts: tools as content-addressed tool documents: the lambda ABI, builtin materialization, the MCP projection, authoring validation, contract markdown.

    src/mcp.ts: remote MCP servers: connect, discover, proxy, the lazy per-run connection pool.

    src/web-tools.ts: self-hosted web_search and the tiered web reader behind read https://….

    src/agent-memory.ts: the per-agent memory filesystem and the signed memory actions.

    src/code-exec.ts: sandboxed execution in microsandbox microVMs, boot-per-call and the warm pool.

    src/activity-monitor.ts, src/activity-triggers.ts, src/schedule-monitor.ts, src/schedule-triggers.ts: the trigger monitors and matching.

    src/provider-oauth.ts: the ChatGPT subscription sign-in flow.

    src/json-schema.ts: the bounded JSON Schema validator for typed results and authored contracts.

    src/perf.ts, src/session-perf.ts: the latency recorder behind /api/perf.

    src/protocol-compat.ts, src/protocol-surface.ts: shims for older clients and the surface snapshot the CI gate diffs.

    protocol/src/index.ts: the canonical protocol types for actions, responses, session events, and WebSocket events, published as the private package @seed-hypermedia/agents-protocol; protocol/PROTOCOL.md holds the versioning rules and changelog.

    protocol/src/tool-registry.ts: the verbs and the callable tools: model-facing descriptions, JSON schemas, render metadata. Every word of a description is prompt.

    protocol/src/write-guides.ts: the per-resource guides an agent reads at ~/tools/write/<resource>.

    protocol/src/delegation.ts, protocol/src/reasoning.ts, protocol/src/model-capabilities.ts: thoroughness presets, the reasoning-level matrix, image-input support.

    e2e/run.ts and e2e/live-gate.ts: the record/replay model gate and the live gate against a real server and model.

The shared UI, in frontend/packages/ui/src/agents/: client.ts signs and sends actions, models.ts holds the React Query hooks and signed subscriptions, platform.ts is the seam each app implements, and the pages and pieces are listed on the desktop and web UI page. The desktop's platform is frontend/apps/desktop/src/agents-platform.ts (its pages/agents.tsx only re-exports the shared list page); the web's in frontend/apps/web/app/web-agents-platform.ts and web-assistant-host.tsx. Routes are in frontend/packages/shared/src/routes.ts.

Shared Hypermedia behaviour the service reuses from @seed-hypermedia/client: resource-read.ts (resolveIdWithClient, shared with the CLI), hm-resolver.ts, blocks-to-markdown.ts and markdown-to-blocks.ts, explore-query.ts, and the blob signing and DAG-CBOR primitives (imported through the @shm/shared/blobs and @shm/shared/cbor re-exports).

Test map

The whole service suite runs from agents/ with bun test:

    api-service.test.ts: the big one: actions, ownership, sessions, delegation, obligations, plans.

    verbs.test.ts: the five verbs: address dispatch, touch-expand, promotion, user-invoked verbs.

    tool-documents.test.ts: CIDs, builtin materialization, lambda authoring validation.

    runs.test.ts, run-time.test.ts: queue claiming, leases, sweeps, parks and wakes.

    workflow-host.test.ts, workflow-worker.test.ts: the script engine: lint, journal replay, fuel and caps, the worker transport.

    activity-triggers.test.ts, trigger-events.test.ts, activity-trigger-race.test.ts, schedule-triggers.test.ts: trigger matching, firing idempotency, the comment/citation sibling race.

    agent-memory.test.ts, session-attachments.test.ts, code-exec.test.ts, exec-pool.test.ts, exec-verify.test.ts, web-tools.test.ts, agent-tools-api.test.ts, mcp.test.ts, session-continuation.test.ts, write-link-validation.test.ts.

    auth.test.ts, sqlite.test.ts, main.test.ts, config.test.ts, json-schema.test.ts, poll-loop.test.ts, provider-oauth.test.ts, protocol-surface.test.ts, statements.test.ts, perf.test.ts, session-perf.test.ts.

    e2e-replay.test.ts shells out to e2e/run.ts. It currently skips: the cassettes predate the verb collapse (e2e/recordings/STALE.md), so a green run is not model-gate coverage. See operations.

Frontend: the shared UI's agents tests live in frontend/packages/ui/src/__tests__/, the desktop's in frontend/apps/desktop/src/__tests__/, and the web app's in frontend/apps/web/app/__tests__/. New page or hook tests belong beside those.

Conventions

    Normalize user and network input at API boundaries. Internal APIs expect normalized values.

    Never hold a SQLite write transaction around a model, provider, or tool network call.

    Never log secrets, signed bodies, or full session or model content.

    Protocol types live in agents/protocol/src/index.ts. Do not recreate mirrors in the desktop or the service. A change to the surface must pass bun run protocol:check, and a breaking one bumps the protocol version per protocol/PROTOCOL.md.

    Provider responses stay redacted.

    Prefer broad tests that exercise real behaviour, and existing files over tiny one-off modules.

Adding an API action

    Add the request and response types to agents/protocol/src/index.ts.

    Dispatch it in Service.message() with validation and account-ownership checks.

    Add idempotency if client retries could duplicate side effects.

    Emit service events if live clients need updates.

    Add the hook in models.ts and any UI.

    Add tests, run protocol:check, and update the signed API.

Adding a WebSocket event

    Extend AgentWSEvent in the protocol package.

    Emit the service event where the change originates and map it in the fan-out in main.ts.

    Handle it in the subscription hook in models.ts.

Changing the database

    Edit sqlite-schema.sql, the fresh-install baseline.

    Prepend the migration to the migrations array in sqlite.ts (the array is reversed, so the newest literal applies last). Never edit or reorder a migration that has shipped.

    Keep the two equivalent: baseline plus every migration must produce the schema in sqlite-schema.sql. sqlite.test.ts synthesizes an old baseline, applies the migrations, and asserts the resulting tables and columns; add yours to those assertions. It is not a full schema diff.

    Never silently accept an unknown or future schema version.

    Update persistence.

Changing the model-facing surface

The verbs (the five working verbs plus status and continue_session) are the whole provider-facing surface. New capability arrives as an address, an option, or a callable, never as another verb without a design discussion.

    A new address form for read or write goes in the verb's description in the tool registry and in the address dispatch in api-service.ts. Edit the description as prompt.

    A new callable goes in callableToolRegistry with runtimes including agent-service. It is reachable through call and never added to the provider payload directly. The next listing materializes it as a tool document for every agent, and the CID change is the version bump.

    Keep touch-expand intact: a wrong or unexpanded call answers with the contract, and never with an error.

    Anything promoted into the provider payload must be filtered against the agent's enabled callables, because promotion is derived from durable events and an unfiltered allowlist would let a hallucinated tool name activate a real one.

    Grants are publish plus the callable set. Do not add a grant for a verb.

    Update tools and security, and if you coined a word for the mechanism, add a term page and list it in the glossary.

Adding a provider

    Add the PROVIDER_SPECS entry in api-service.ts and the matching PROVIDER_METADATA entry in the UI's provider-registry.ts.

    If the model needs reasoning control, add its generation to reasoning.ts with a note on how the levels were verified. If it takes images, add it to model-capabilities.ts.

    Preserve the session lifecycle and WebSocket partials. Map Pi events into ordered message, tool_call, and tool_result events.

    Add mocked network tests for success, streaming, text-before-tool ordering, tools, missing key, and provider errors.

    Confirm decrypted secrets stay in memory and never reach Pi auth files.

Which page to update

Documentation is part of the change, in the same commit.

Manual acceptance checklist

After a core change: start the server and the app, open Agents, confirm the server is online, configure a provider, create an agent, open a session, send a message, confirm the subscription succeeds and the reply streams and persists across a reload. Then ask it to read a URL, read ~/tools/, and read ~/memory/. Confirm tool rows appear and that a call of an unexpanded tool comes back as the contract. Run a verb from the wrench palette and confirm the You chip and that the agent sees it. Give it a task worth a checklist and a delegation. Confirm the run card shows the plan, the child attaches to the running step, and the parent resumes with the result.

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime