Workers

Agent runtime

The satellite that hosts the 'agent builder' surface. One Durable Object per (agent id, conversation id). Each instance drives its own inference loop, dispatches tool calls (built-in, unified-connector, custom MCP), and persists state across turns.

Updated today

Overview

At a glance
Worker
ultron-agent-runtime
Durable Object
AgentInstance
DO key
(agent_id, conversation_id)
D1
ultron-agent-builder-db (agent defs, integrations, deployments)
R2
Artifacts (uploaded files for RAG, deployment bundles)
Cron
* * * * * (every minute)
Model traffic
Routed through an AI Gateway slug for cache, logs, rate limits
Vectorize indices
Per-agent agent-rag-<agent_id>, created on demand

Where the chat-v2 route in the app is the user-facing inference loop, the agent runtime is the satellite that the "build your own agent" feature delegates to. The two run on different schemas, with different integration projects, so a misbehaving experimental agent can never touch the production app state.

Worker shape

wrangler.toml (key bindings)toml
1name = "ultron-agent-runtime"
2main = "src/index.ts"
3
4[[durable_objects.bindings]]
5class_name = "AgentInstance"
6name = "AGENT_INSTANCE"
7
8[[d1_databases]]
9binding = "AGENT_DB"
10database_name = "ultron-agent-builder-db"
11
12[[r2_buckets]]
13binding = "AGENT_ASSETS"
14bucket_name = "ultron-agent-builder-assets"
15
16[ai]
17binding = "AI"
18
19[vars]
20AI_GATEWAY_SLUG = "ultron-agent-builder"
21COMPOSIO_PROJECT_NAME = "ultron_agent_builder"
22COMPOSIO_API_BASE = "https://backend.composio.dev/api/v3"
23
24[triggers]
25crons = ["* * * * *"]

AgentInstance DO

One DO instance per (agent, conversation) pair.

src/index.ts (DO sketch)ts
1export class AgentInstance {
2 state: DurableObjectState
3 env: Env
4
5 async fetch(req: Request) {
6 // Routes:
7 // POST /turn start a new assistant turn
8 // POST /tool-result return a tool call result and resume the loop
9 // GET /transcript read the full transcript
10 // POST /reset drop conversation state
11 }
12
13 // Driven by /turn:
14 async runLoop(input: string) {
15 let messages = await this.loadMessages()
16 messages.push({ role: 'user', content: input })
17
18 while (true) {
19 const resp = await this.callModel(messages)
20 if (resp.tool_uses?.length) {
21 const results = await this.dispatchTools(resp.tool_uses)
22 messages = messages.concat(resp, results)
23 continue
24 }
25 await this.persist(messages.concat(resp))
26 return resp
27 }
28 }
29}
Note
State lives in the DO's own SQLite storage. Each agent's conversation is a separate DO instance, so writes are serial per agent and reads are local.

Tool surfaces

What an agent built in the runtime can reach for.

SurfaceWhat it gives
Built-in primitivesBrain read, web search, calculator, document generation. Same shape as chat v2's native tools but scoped to the agent's policy.
Unified connector (per-agent)OAuth surfaces under the siloed agent-builder integration project. Gmail, Slack, Notion, Google Workspace, etc. Each agent has its own connected accounts.
Custom MCP serversAgent owner can register an MCP endpoint with credentials; the runtime connects, lists tools, exposes them to the model.
Workspace skillsSkills registered to the agent's owning workspace appear as callable tools in the loop.

Per-agent scheduling

How an agent fires itself on a schedule.

Each agent definition row in AGENT_DB can carry a cron expression plus a cron_next_run_at timestamp. The worker's 1-minute scheduled handler scans for agents whose cron_next_run_at <= now() and enqueues a turn for each, then advances cron_next_run_at.

src/index.ts (scheduled handler concept)ts
1async scheduled(_event, env) {
2 const due = await env.AGENT_DB.prepare(
3 `SELECT id, owner_user_id, cron_expr
4 FROM agents
5 WHERE cron_expr IS NOT NULL
6 AND cron_next_run_at <= datetime('now')`
7 ).all()
8 for (const row of due.results) {
9 const stub = env.AGENT_INSTANCE.idFromName(`${row.id}:cron`)
10 const obj = env.AGENT_INSTANCE.get(stub)
11 await obj.fetch(new Request('https://internal/turn', {
12 method: 'POST',
13 body: JSON.stringify({ source: 'cron' }),
14 }))
15 // advance cron_next_run_at by the cron interval
16 }
17}

Per-agent RAG

Vector indices are created per agent on demand.

When the agent owner toggles RAG on, the runtime calls the platform's vector-index management API to create a new index named agent-rag-<agent_id>. Uploaded files in AGENT_ASSETS are chunked + embedded and inserted into that index. Inference-time, the inference loop queries the index for relevant chunks before each model call.

File map

cf-workers/ultron-agent-runtime
wrangler.toml
src
index.tsDO class, fetch + scheduled handlers, AI Gateway client
tools.tstool dispatch + connector adapter
rag.tsembedding + index management
migrations
0001_agents.sql
0002_integrations.sql