Usage overview
High level overview of the Durable Agents system and developer APIs.
1. Entity definition (registry.define())
Agents are entities that handle events, defined as a:
handler(ctx, wake)withstateand built in collections
And schemas:
creationSchema-- validated spawn argsinboxSchemas-- typed message contractsoutputSchemas-- what the entity emits (for UI binding)
See Defining entities and EntityDefinition reference.
2. Handler context (ctx)
The context API passed into the handler:
| Property/Method | Purpose |
|---|---|
ctx.firstWake | Boolean -- is this the entity's first activation? |
ctx.entityUrl | Identity -- /type/id |
ctx.entityType | Type name string |
ctx.args | Readonly spawn arguments |
ctx.tags | Entity tags -- key/value metadata |
ctx.db | Full TanStack DB: db.actions for writes, db.collections for reads |
ctx.state | Proxy object keyed by collection name |
ctx.events | Change events that triggered this wake |
ctx.useAgent() | Set up the LLM agent |
ctx.useContext() | Declare context sources with token budgets and cache tiers |
ctx.timelineMessages() | Project the entity timeline into LLM messages |
ctx.insertContext(id, entry) | Insert a durable context entry |
ctx.agent.run() | Execute the agent loop |
ctx.darixTools | Runtime-provided tools to spread into agent config |
ctx.spawn(type, id, args, opts) | Create child entity |
ctx.observe(source, opts) | Subscribe to a source via entity(), cron(), entities(), db() |
ctx.send(url, payload, opts) | Send message to an entity |
ctx.sleep() | Return to idle |
ctx.mkdb(id, schema) | Create cross-entity shared state |
ctx.observe(db(id, schema), opts) | Join existing shared state |
ctx.setTag(key, value) | Set a tag on this entity |
ctx.removeTag(key) | Remove a tag from this entity |
See Writing handlers and HandlerContext reference.
3. Agent configuration
ctx.useAgent({
systemPrompt: string,
model: string, // e.g. 'claude-sonnet-4-5-20250929'
tools: AgentTool[], // [...ctx.darixTools, ...custom]
streamFn?: StreamFn, // optional streaming callback
testResponses?: string[] // for testing without LLM
})
await ctx.agent.run() // blocks until agent finishesSee Configuring the agent and AgentConfig reference.
4. Tool definition
Stateless tools are pure functions:
const myTool: AgentTool = {
name: "calculator",
description: "...",
parameters: Type.Object({ expression: Type.String() }), // TypeBox
execute: async (toolCallId, params) => ({
content: [{ type: "text", text: result }],
details: {},
}),
}Stateful tools are factories receiving ctx for state access:
function createMemoryTool(ctx: HandlerContext): AgentTool {
return {
name: "memory_store",
execute: async (_, params) => {
ctx.db.actions.memory_insert({ row: { key, value } }) // writes to entity state
},
}
}Handler-scoped tools are factories receiving ctx:
function createDispatchTool(ctx: HandlerContext): AgentTool {
return {
execute: async (_, params) => {
const child = await ctx.spawn("worker", id, args, { wake: "runFinished" })
const text = await child.text()
return { content: [{ type: "text", text }] }
},
}
}See Defining tools and AgentTool reference.
5. State collections (ctx.db)
Custom state is accessed through ctx.db:
Writes via ctx.db.actions:
.<name>_insert({ row })-- add new row.<name>_update({ key, updater: (draft) => { ... } })-- Immer-style mutation.<name>_delete({ key })-- remove by primary key
Reads via ctx.db.collections:
.<name>?.get(key)-- read one.<name>?.toArray-- read all (getter, not method)
See Managing state.
6. Entity coordination primitives
spawn(type, id, args, opts)->EntityHandle-- create childopts.initialMessage-- first message to deliveropts.wake--'runFinished',{ on: 'runFinished', includeResponse? }, or{ on: 'change', collections?, debounceMs?, timeoutMs? }
observe(source, opts)->EntityHandle | ObservationHandle-- subscribe viaentity(),cron(),entities(),db()send(url, payload, opts)-- fire-and-forget messagesleep()-- go idle
EntityHandle returned from spawn/observe:
.entityUrl,.type,.db(read-only TanStack DB).run-- Promise that resolves when child completes.text()-- get all completed text output.send(msg)-- send follow-up message.status()--ChildStatus | undefined(object with.status,.entity_url,.entity_type)
See Spawning & coordinating and EntityHandle reference.
7. Shared state (cross-entity)
Define a schema map, then create/connect:
const schema = {
findings: {
schema: z.object({ key: z.string(), text: z.string() }),
type: "shared:finding",
primaryKey: "key",
},
}
// Parent creates:
ctx.mkdb("research-123", schema)
// Children connect:
const shared = await ctx.observe(db("research-123", schema))
shared.findings.insert({ key: "f1", text: "..." })See Shared state and SharedStateHandle reference.
8. Built-in collections
Every entity automatically has 17 ctx.db.collections:
| Collection | Purpose | Key fields |
|---|---|---|
runs | Agent run lifecycle | status: started/completed/failed |
steps | LLM call steps | step_number, model_id, duration_ms |
texts | Text message blocks | status: streaming/completed |
textDeltas | Incremental text chunks | text_id, delta |
toolCalls | Tool invocation lifecycle | tool_name, status, args, result |
reasoning | Extended thinking blocks | status: streaming/completed |
errors | Diagnostic errors | error_code, message |
inbox | Received messages | from, payload, message_type |
wakes | Wake event history | source, timeout, changes |
entityCreated | Bootstrap metadata | entity_type, args, parent_url |
entityStopped | Shutdown signal | timestamp, reason |
childStatus | Child entity status | entity_url, status |
manifests | Wiring declarations | discriminated union: child/source/shared-state/effect/context/schedule |
replayWatermarks | Replay offset tracking | source_id, offset |
tags | Entity tags/labels | key, value |
contextInserted | Context additions | context_id, content |
contextRemoved | Context removals | context_id |
See Built-in collections.
9. CLI (darix)
Interact with the system using the darix CLI:
| Command | Purpose |
|---|---|
darix types | List registered entity types |
darix types inspect <name> | Show type schema |
darix spawn /type/id --args '{...}' | Create entity |
darix send /type/id 'message' | Send message |
darix observe /type/id | Stream entity events |
darix inspect /type/id | Show entity state |
darix ps [--type --status --parent] | List entities |
darix kill /type/id | Stop entity |
See CLI reference.
10. App setup
const registry = createEntityRegistry()
registerMyEntity(registry)
const runtime = createRuntimeHandler({
baseUrl: DARIX_URL, // runtime server
serveEndpoint: `${URL}/webhook`, // callback URL
registry,
})
// Node HTTP server, forward POST /webhook -> runtime.onEnter(req, res)
await runtime.registerTypes() // register all types with runtime serverSee App setup and RuntimeHandler reference.