The package map
teaspill is thirteen packages in one workspace. This page is the machine-room tour: what each package does, what it exports, and whether it's something you use to build agents or something you'd only open as a contributor. It goes one level deeper than Concepts — naming the outbox, the reconciler, and the internal seams — so treat it as the map you keep open while reading the source.
| Package | In one line |
|---|---|
agents-sdk | Define agents in TypeScript, serve them, register them. |
frontend-sdk | Materialize timelines, watch the catalog, send commands from your app. |
cli | The teaspill command — run the stack, drive agents, mint keys. |
schema | The canonical event vocabulary and the addressing helpers everything shares. |
reference-deployment | A working, copy-me deployment of the two planes you run. |
gateway | The single front door: auth plus the proxy every client goes through. |
coordination | The durable-agent engine on Restate — object, outbox, control, cron. |
catalog | The Postgres registry of every entity, synced live via Electric. |
executor | The workspace plane — sandboxed filesystems and shells for tools. |
harness-native | The harness contract plus the native, teaspill-owned model loop. |
harness-casdk | The Claude Agent SDK harness, with durable sessions. |
conformance | A reusable suite asserting teaspill's durability guarantees. |
chaos | Failure injection: kill services mid-run, re-check the invariants. |
You'll use these
agents-sdk
The developer-facing surface for defining agents. Its primary export, defineAgent, takes a typed definition — spawn/message schemas, durable state, tools, an optional onWake hook — and compiles it into a running durable agent. You pick a harness with native(...) or claudeAgentSdk(...); swapping between them changes nothing else about the agent. serve(...) stands up the endpoint and registerDeployment(...) announces it to the gateway. The package also enforces the additive-only rule for state revisions at build time — a breaking schema change at an unchanged revision throws loudly rather than corrupting state at runtime — and exports mintReadToken for issuing browser read tokens. This is the first package a user touches; start with the Building agents guide.
frontend-sdk
The browser and app-facing SDK, framework-agnostic with optional React bindings. Three clients cover the three route families: createActionsClient sends spawn/send/control commands, createAgentTimeline folds an entity's timeline into rendered messages and live token deltas, and createAgentCatalog subscribes to catalog rows over Electric shapes. The heart of it is a pure reducer you can also run on a server: it deduplicates events by seq, joins a long timeline late from a snapshot (fast-join with a stream offset), and surfaces a forward gap as drift instead of silently dropping it. Writes always go through the gateway. Reach for it whenever you're putting agent activity on a screen — see Frontend integration.
cli
The teaspill binary — the dev loop plus entity inspection. It's a thin consumer of the SDKs, never reimplementing stream reading or the actions client. teaspill dev brings the compose stack up, waits on gateway health, and registers your local deployment with backoff (the fix for the register-before-ready race). The rest drive the platform: agents ls, spawn, send, control, logs (follow a timeline, optionally with deltas or from a snapshot), and keys create|ls|revoke for API-key administration against the operator database. Every command body is a plain function over injected dependencies, so parsing and sequencing are unit-tested without a live stack. The full command surface is the CLI reference.
schema
The canonical event schema and token-delta framing — the frozen v1 contract every other package builds on. It defines the event envelope (v, entityId, seq, ts, type, payload), the fifteen event types (from entity_spawned at seq 0 through run_finished, child_finished, archived, and the catch-all opaque), and the sibling delta stream. finalizeEvent is the single seq allocator's stamp; checkSeqContiguity and checkTimelineInvariants are the structural guards. It's also the public home of the addressing helpers — entityUrl, parseEntityUrl, timelineStreamPath, workspaceKey, and friends. You mostly meet it indirectly through the other SDKs, but you'll import its types and helpers directly the moment you handle raw events. The Event schema reference documents every type.
reference-deployment
A complete, working deployment of the two planes you run yourself — an agent-loop service and an executor-host service — plus the compose overlay that adds them to the base stack. It plays three roles at once. It's the getting-started example: copy this package to bootstrap your own deployment, since every seam is wired from public package APIs and nothing internal. It's the stack the live conformance and chaos suites run against, serving the deterministic conformance agents. And it's the home of the deployment-side pieces the SDK leaves open — the ingress workspace client (with abort-to-kill), the ingress tool clients, and catalog-backed child listing. If you want a real example of loose-message normalization or reconciler wiring, read this package. See Self-hosting.
The platform
These are the services you deploy and run — you don't import them into your agent code. As a user you care that they're up and configured; as a contributor this is where the durability machinery lives.
gateway
The platform's single entrypoint. Every external caller — your app, a UI, the CLI, a developer service — talks to teaspill through the gateway; Restate, Postgres, Electric, and the durable streams server are never exposed directly. It authenticates with API keys (Authorization: Bearer), routes commands to Restate ingress (/api/spawn, /api/.../send, /api/.../control), and byte-exact proxies the read paths — /streams/* for timelines and /shapes/* for Electric — preserving long-poll parking, offsets, and caching headers so reads stay resumable. An optional short-lived JWT read path lets a browser read stream and shape routes directly without proxying every request, while writes never bypass your backend. Its full route table and error semantics are the Gateway HTTP API reference; operators should read the Self-hosting networking rules before registering a service.
coordination
You only touch this package as a contributor — it's the durable-agent engine, and defineAgent specializes it for you. It ships the agent virtual object template on Restate: one object per agent type, keyed by instance id, processing one wake at a time. Inside it are the mechanisms Concepts keeps behind plain language. The projection outbox is the system's only seq allocator — it stages events into durable state and flushes them to the stream through an idempotent producer, replaying in order from the first unconfirmed record and trimming only after a confirm, which is what makes projection exactly-once. The reconciler detects, alerts on, and requests recovery from outbox drift, while the agent object executes the actual repair. The interrupt seam, cron, and the archive tick live here too. This is the densest package in the repo; the Durable agents and Projections & the catalog concepts are the gentle version.
catalog
Mostly a contributor and operator concern: the Postgres schema and migrations for the catalog. It owns three tables — entities (the registry: url, tenant, type, status, tags, parent, head sequence, and the archived snapshot that makes resurrection possible), entity_tags (a normalized index so Electric where clauses stay fast), and api_keys (the gateway's auth store). Coordination writes entity rows from inside agent handlers; the gateway reads keys and proxies Electric shapes over the entity tables. It uses Drizzle with checked-in SQL migrations, including the hand-written operational setup Electric requires (REPLICA IDENTITY FULL and the updated_at trigger). You run its migrations when you stand up a stack; you edit it when you change what the catalog stores.
executor
The workspace plane — the sandboxed environments where an agent's tools actually run commands. Each workspace is a Restate virtual object fronting a real environment, serialized per workspace, delegating to a stateless executor-host service behind a minimal adapter seam. Three adapters ship behind that seam: a dev-only local, a guarded local-unrestricted, and docker — one container per workspace, a named volume that outlives the container, and idle teardown. The long-exec protocol uses durable awakeables so a command survives an agent-loop restart, with a host-unresponsive backstop and a concurrent kill escape hatch. The minimal seam is deliberate: it's what lets a remote VM adapter slot in later. Contributors work in the adapters and containment logic; operators must read the Docker socket trust boundary in Self-hosting.
harness-native
The harness contract itself, plus the native model loop teaspill owns end to end. As a contributor this is the frozen Harness interface every harness implements — run(...) returns timeline events (never allocating seq, since the outbox is the sole allocator), a state delta, and usage. The load-bearing clauses live here: every side-effecting tool call goes through Restate ingress under an idempotency key so it's exactly-once under any retry, and emitDelta is fire-and-forget that never blocks a run. The native harness journals every model call and every tool call as its own durable step — the finest-grained durability of the two — and works with any provider. It's dependency-light on purpose so the other harness and the SDK can import the contract. The user-facing view is Harnesses.
harness-casdk
The Claude Agent SDK harness — Claude Code semantics (sessions, compaction) for agents that want them. It implements the same frozen Harness interface as the native loop through three layers: tools execute through an in-process MCP server bound to the same exactly-once idempotency key; a durable session per entity is persisted and can be repaired on load after a crash; and canonical timeline events remain the authority, with a cold rebuild that reconstructs the session from the timeline if it's ever lost. The Claude Agent SDK dependency is exact-pinned and loads lazily — selecting or compiling one of these agents never pulls it in. You select it with claudeAgentSdk(...) in your agent definition; as a contributor, this is where the durable-session translation lives. See Harnesses.
Quality kits
These two packages exist to prove teaspill keeps its promises. You'll meet them when you're contributing or hardening a deployment, rarely otherwise.
conformance
A reusable acceptance suite of named scenarios, each asserting one durability invariant of a running stack — spawn-and-respond, parallel fan-out (the dropped-parent-wake regression), crash-resume, projection continuity across a streams-server restart, and workspace-exec durability. Every scenario has a one-sentence invariant, a driver, and a pure check that returns violations rather than throwing, so other tooling can inspect exactly what broke. Each runs two ways: offline against the real coordination and executor primitives plus faithful fakes (these run in CI with no stack), and live end-to-end through the developer surfaces, gated on a stack URL. Ready-made implementations of the conformance agents ship in the reference deployment — deploy that stack and point the suite at it. The chaos suite is built on top of this kit.
chaos
The failure-injection suite, and the acceptance test for teaspill's durability decisions. For each of five faults it drives a conformance scenario, injects the fault mid-flight — killing the agent-loop, the executor, the streams server, Restate, or the gateway — then re-asserts the mapped invariant. The point is stated in the package itself: assert the invariant, not just no-crash. Faults one through four have offline invariant tests that run in CI against the real primitives, so continuous integration exercises the exactly-once, seq-gapless, awakeable-timeout, and durable-resume logic rather than a skipped shell; the fifth is live-only, with its offline coverage living in the gateway package. Live runs shell out to docker compose to kill and restart real containers, so they're doubly gated and meant only for a disposable dev stack.