Skip to content
ElectricElectric
PricingBlog
Main Navigation
Go to Cloud
Cloud
Go to Cloud
On this page

← Blog

Electric Agents 0.6

By Sam Willis and Kyle Mathews

Are you an LLM? You can read better optimized documentation at /blog/2026/06/16/electric-agents-0-6-from-runtime-to-app.md for this page in Markdown format

Electric Agents 0.6 is out today. It rounds out the platform we launched in April with a broader SDK and runtime surface for building agentic systems.

  • Core APIs. Long-lived entities, StreamDB state, local and remote runners, spawning and forking, wakes, signals, schedules, self-sends, app APIs, and multi-agent coordination patterns.
  • Apps in development. Desktop and mobile devtools for inspecting and controlling agent systems. Download canary builds from GitHub releases or build from source.
  • Cloud next. Managed Agents servers in Electric Cloud are coming soon.

Get started with Electric Agents

Run the Quickstart, read the docs, or revisit the original launch post.

The Electric Agents stack ​

The ecosystem is converging on a thesis: the agent is the log. If you accept that, the rest of the stack follows.

Electric Agents stack: Durable Streams, StreamDB, TanStack DB, Agents runtime, and Agents apps.

If the agent is the log, the log has to outlive any single process or device — and if multiple readers need to observe it (UIs, supervisors, other agents), it has to sync. Durable Streams are append-only logs that do both.

Raw logs aren't ergonomic. You want typed projections — messages, runs, tool calls, errors, children — that update live as events land. StreamDB projects those collections over the stream.

Projections need queries: filter, join, aggregate for a UI timeline or an agent's context window. TanStack DB is the reactive query layer.

At this point you have durable, syncable, queryable agent state. What's missing is compute. Something has to run the LLM loop, call tools, and write the results back. And something has to track which entities exist, route messages, schedule wakes, and dispatch work. The Agents runtime is that control plane.

Runners do the compute; the runtime coordinates. A runner can live on your laptop, in your infrastructure, in CI, or anywhere you control. Because coordination and compute are separate, you can start a session on your machine, leave the runner there, then open the same session from your phone to send a follow-up or stop it.

Managed Agents servers in Electric Cloud are coming soon — hosted coordination, user-owned compute.

Every entity is a StreamDB ​

In Electric Agents, the agent is the durable stream, not the process currently handling it.

  • An Electric Agents entity is a long-lived, addressable thing: an assistant, worker, coding session, support ticket, lead researcher, orchestrator, monitor, or any agent type you define.
  • Every entity has a durable stream, which is the log of what happened.
  • Every entity also has a typed StreamDB projection. That projection gives you live state: timeline, inbox, runs, tool calls, context, errors, children, signals, and custom collections.
  • The process that handles a wake can come and go. The entity persists. It can sleep, wake, replay, fork, spawn children, and be observed by apps or other agents.
Entity: /assistant/helper
handler.ts
1registry.define("assistant", {
2 async handler(ctx) {
3 ctx.useAgent({
4 systemPrompt: "You are a helpful assistant.",
5 model: "claude-sonnet-4-5-20250929",
6 tools: [calculatorTool, ...ctx.darixTools],
7 })
8 await ctx.agent.run()
9 },
10})
Stream
sleeping — zero compute

Every agent is fully introspectable. The stream is a complete audit trail — what you see above is the actual data model.

Demos and videos ​

Logical entities ​

See how Electric Agents models agents as durable, addressable entities. Each entity is backed by a durable stream and has its own URL, so apps, agents, and runtime APIs can all refer to the same long-lived thing.

Custom entities ​

Define your own entity types with custom state, handlers, permissions, and tools.

State ​

Inspect an entity's live StreamDB state: messages, runs, tool calls, child status, and custom collections.

Forking and spawning ​

Spawning starts a fresh child entity. Forking branches an existing entity from a historical point in its stream, preserving the useful context while opening an alternative path forward.

Child agents ​

Use child agents to split work into separately owned streams that the parent can observe and coordinate. A spawned child can receive instructions, perform operations, and report back through its own durable state.

Signals ​

Signals give operators and apps a control plane for active work: SIGINT, pause, resume, kill, and handler-level lifecycle events.

PG sync triggers ​

Postgres changes can flow through sync and wake an agent when the data it cares about changes.

Send to self ​

An agent can send itself a future message to sleep now and continue work later.

Cron ​

Cron schedules wake agents on recurring intervals without keeping a worker process alive.

Core APIs ​

Define ​

Define entity types with schemas, handlers, permissions, and tools. This is where you decide what kind of long-lived thing exists in the system, what state it owns, and what code runs when it wakes. SDK

ts
const registry = createEntityRegistry()

registry.define("assistant", {
  description: "A project-aware assistant",
  state: {
    notes: {
      schema: z.object({ id: z.string(), text: z.string() }),
      primaryKey: "id",
    },
  },
  async handler(ctx) {
    // ...
  },
})

Run ​

Run an agent loop that persists runs, text, reasoning, tool calls, and errors to the entity stream. The handler can do normal application work before or after the LLM loop; ctx.useAgent() and ctx.agent.run() record the agent trace. See writing handlers and configuring the agent loop. Runtime

ts
async handler(ctx) {
  ctx.useAgent({
    systemPrompt: "You are a helpful assistant.",
    model: "claude-sonnet-4-6",
    tools: [...ctx.electricTools, searchDocsTool],
  })

  await ctx.agent.run()
}

Spawn ​

Spawn a fresh child entity without parent history. Use this for workers, fan-out, and multi-layer agent trees where the parent wants another entity to own a separate stream of work. See spawning and coordinating. SDK

ts
const worker = await ctx.spawn(
  "worker",
  "audit-docs",
  { tools: ["read", "search"] },
  {
    initialMessage: "Audit the docs for missing release notes.",
    wake: { on: "runFinished", includeResponse: true },
  }
)

Fork ​

Fork an entity with history. Use this when you want a second path through the same session. The fork keeps the useful context and branches the durable stream, so you can try another approach without overwriting the original. See spawning and coordinating. Runtime App

ts
const fork = await ctx.forkSelf("variant-a", {
  initialMessage: { text: "Try the shorter implementation path." },
  tags: { branch: "variant-a" },
})

Send ​

Send messages to any entity. Because messages are durable inbox entries, the same API works for user input, parent-to-child steering, and send-to-self continuation after a delay. SDK

ts
await ctx.send(
  "/worker/audit-docs",
  { files: ["website/docs/agents/index.md"] },
  { type: "review_request" }
)

await ctx.send(
  ctx.entityUrl,
  { step: "continue-after-indexing" },
  { type: "self", afterMs: 60_000 }
)

Wake ​

Wake entities from inbox messages, child completion, state changes, cron, future sends, event sources, and Postgres sync triggers. Wakes are how agents scale to zero: no process has to stay alive just to notice that something changed. See waking entities. Runtime

ts
await ctx.observe(entity("/worker/audit-docs"), {
  wake: { on: "runFinished", includeResponse: true },
})

await client.registerWake({
  subscriberUrl: "/monitor/docs",
  sourceUrl: "/worker/audit-docs/main",
  condition: { on: "change", collections: ["runs", "texts"] },
})

Observe ​

Observe entities, shared state, entity lists, timelines, and child state in real time from handlers or apps. Observing loads the target stream into a typed local DB. Those collections can drive UI, coordination logic, or debugging tools. Use the runtime client and React client APIs. SDK App APIs

ts
const client = createAgentsClient({ baseUrl: "http://localhost:4437" })
const db = await client.observe(entity("/assistant/release-post"))

console.log(db.collections.texts.toArray)

Schedule ​

Schedule work with cron and future sends. Agents can sleep until the next scheduled wake. Recurring jobs and delayed follow-ups live in the entity's durable manifest, not in an external timer you have to reconcile. Runtime

ts
await client.upsertCronSchedule({
  entityUrl: "/assistant/release-post",
  id: "daily-checkin",
  expression: "0 9 * * *",
  timezone: "Europe/London",
  payload: "Review open launch tasks.",
})

await client.upsertFutureSendSchedule({
  entityUrl: "/assistant/release-post",
  id: "follow-up",
  fireAt: new Date(Date.now() + 60_000).toISOString(),
  payload: "Continue after the preview build finishes.",
})

Signal ​

Signal running agents: interrupt, pause, resume, kill, or deliver handler-level lifecycle signals. Signals give apps and operators a control plane for active work without treating the agent as an opaque process. See the signals guide and CLI reference. Runtime CLI App

ts
await client.signalEntity({
  entityUrl: "/horton/release-post",
  signal: "SIGINT",
  reason: "User wants to redirect the current run.",
})

Coordinate ​

Coordinate with shared state and multi-agent patterns such as orchestrator/worker, blackboard, reactive observers, map-reduce, and pipelines. Coordination is explicit state and explicit streams, so parent agents, child agents, and UI clients can inspect the work. SDK

ts
const board = ctx.mkdb("release-board", {
  tasks: {
    schema: z.object({ id: z.string(), status: z.string() }),
    type: "shared:task",
    primaryKey: "id",
  },
})

board.tasks.insert({ id: "screenshots", status: "needed" })

const reviewer = await ctx.spawn("worker", "reviewer", {})
await ctx.observe(entity(reviewer.entityUrl), {
  wake: { on: "change", collections: ["texts"] },
})

Connect ​

Connect external tools and systems with MCP servers, webhook-source subscriptions, webhooks, and PG sync-driven triggers. Agents can subscribe to operational systems and wake when those systems change. Runtime

ts
await client.subscribeToWebhookSource({
  entityUrl: "/horton/release-post",
  id: "github-pr",
  sourceKey: "github",
  bucketKey: "repo",
  params: { repo: "electric-sql/electric" },
  lifetime: { kind: "until_entity_stopped" },
})

const todos = await client.observe(
  pgSync({ table: "todos", where: "project_id = $1", params: ["agents"] })
)

Inspect ​

Inspect every entity as a StreamDB: timeline, inbox, runs, tool calls, child status, errors, signals, attachments, and custom collections. The runtime exposes these as TanStack DB collections, so app code can query the agent's state instead of scraping logs. See the built-in collections reference and the TanStack DB query docs. App APIs App

ts
import { eq, queryOnce } from "@durable-streams/state/db"

const db = await client.observe(entity("/horton/release-post"))

const runs = await queryOnce((q) => q.from({ run: db.collections.runs }))

const toolCalls = await queryOnce((q) =>
  q.from({ toolCall: db.collections.toolCalls })
)

const attachments = await queryOnce((q) =>
  q
    .from({ manifest: db.collections.manifests })
    .where(({ manifest }) => eq(manifest.kind, "attachment"))
)

Apps in development ​

The desktop and mobile apps are built on the same APIs you use: observe(entities()) to list agents, observe(entity(...)) to load one, the runtime client to spawn, signal, and send. The app is a subscriber, not special infrastructure.

We're using them to dogfood the SDK and runtime, and to build toward our own software factory: agents that shepherd PRs and issues, keep work moving, and let everyone connect to the same durable session.

  • Custom agent types: build entities with @electric-ax/agents-runtime and inspect them in the desktop app.
  • State explorer: see each entity's runs, inbox, manifests, and custom state in one view.
  • Entity timeline: replay a run event by event, then fork from a point in the timeline to try another path.
  • Cloud or self-hosted: use Electric Cloud when available, or point the app at an agents-server you run yourself.
  • Remote sessions: open sessions started by CI, webhooks, issues, cron, or another machine.
  • MCP servers: add MCP servers with native OAuth. Workspace mcp.json files are respected.
  • Model providers: use an API key from your keychain, or sign in to Codex. Anthropic, OpenAI, DeepSeek, and Moonshot are supported.
  • Skills and slash commands: use /quickstart to get started, then save commands for your workflows.
  • Phone handoff: open a run on iOS or Android to steer it, send a message, or check progress.
  • Desktop workflow extras: pick a working directory, split the tile workspace, attach files and screenshots to chat, discover local dev servers, and install the electric CLI system-wide.

You can download app canaries from GitHub releases, or build the apps yourself from the repo.

How to try it ​

Run the quickstart from the CLI:

sh
npx electric-ax agents quickstart

Open the UI, spawn Horton, send a message, and watch the timeline update as the agent thinks, calls tools, and responds.

Then define your own entity type and connect it to the same runtime and apps:

ts
import {
  createEntityRegistry,
  createRuntimeHandler,
} from "@electric-ax/agents-runtime"

const registry = createEntityRegistry()

registry.define("assistant", {
  description: "A general-purpose AI assistant",
  async handler(ctx) {
    ctx.useAgent({
      systemPrompt: "You are a helpful assistant.",
      model: "claude-sonnet-4-6",
      tools: [...ctx.electricTools],
    })

    await ctx.agent.run()
  },
})

export const runtime = createRuntimeHandler({
  baseUrl: process.env.ELECTRIC_AGENTS_URL ?? "http://localhost:4437",
  serveEndpoint: "http://localhost:3000/webhook",
  registry,
})

Once registered, assistant appears as an entity type in the app. Spawn /assistant/my-agent, send it a message, and use the same timeline, state, and devtools surfaces that Horton uses.

Coming next ​

  • Managed Agents servers in Electric Cloud.
  • More examples and docs for app builders: PG sync triggers, webhook sources, MCP, attachments, sandbox profiles, and multi-agent patterns.
  • More app development polish: desktop builds, smoother downloads and updates, and richer mobile distribution.

Next steps ​

  • Run npx electric-ax agents quickstart.
  • Read the Electric Agents docs.
  • Watch the demos in this post.
  • Download app canaries from GitHub releases, or build them from source.
  • Join the Electric Discord and tell us what you build.

ElectricElectric

AboutContactLegalDocsDemosBlogSign up
TanStack DBPGliteXBlueskyDiscordGitHub

© 2026 Electric DB Inc. Released under the Apache 2.0 License.

✨ Markdown