Build durable agents without the infrastructure

Substructure is an open-source engine for building durable, long-running AI agents using only a stateless HTTP endpoint hosted on your infrastructure, in your code.

Substructure

  • Drives the agent loop by calling your endpoint
  • Makes your agents durable and resilient
  • Handles LLM calls and event streaming

Your endpoint

  • Makes agent decisions and executes tools
  • Runs on your infrastructure
Get started

Deploy it anywhere

Define an agent as a worker, deploy it to the platform you already use, and drive it from any backend.

import Substructure from "@substructure.ai/sdk";

const sub = new Substructure();
const { agent } = sub;

const assistant = agent({ id: "assistant" })
  .use(agent.jsonState())
  .use(agent.systemMessage("Helpful assistant."))
  .use(agent.messageHistory())
  .use(agent.llmLoop({
    request: { model: "anthropic/claude-sonnet-4" },
  }));

const worker = sub.worker({ agents: [assistant] });

export default {
  fetch(req: Request, env: Env) {
    return worker.fetchHandler({
      signingSecret: env.SIGNING_SECRET,
    })(req);
  },
};

Architecture

Substructure sits between the browser or backend client and your agent, driving the agentic loop by calling your HTTP endpoint and streaming real-time events back to clients.

Clientbrowser or backend
Substructureembedded or cloud-hosted
Your Agentyour code, your infra

TypeScript SDK

Substructure ships a TypeScript SDK for building agents. It is a middleware based SDK and aims to be composable and easily extendable.

Workers don't hold state between calls. The server sends the current state with every trigger and persists whatever the worker returns. Inline a JSON object for simple agents, or keep the payload in your own database and load it in middleware.

const dbState = agent.middleware({
  handler: async (req, next) => {
    const state = await db.load(req.sessionId);
    const result = await next({ ...req, state });
    await db.save(req.sessionId, result.state);
    return result;
  },
});

const research = agent({ id: "researcher" })
  .use(dbState)
  .use(messageHistory())
  .use(agent.llmLoop({
    request: { model: "anthropic/claude-sonnet-4-6" },
  }));
examples/hybrid-state

CLI

Provision apps, watch sessions, and debug agents from the terminal.

claude-code · ~/acme/support-bot
$subs cloud link
↳ wrote subs.toml
$wrangler deploy
↳ https://support-bot.acme.workers.dev
$subs cloud webhook set https://support-bot.acme.workers.dev
↳ webhook enabled
$subs cloud webhook secret | wrangler secret put SIGNING_SECRET
↳ webhook signing secret synced
$export SUBSTRUCTURE_API_KEY=$(subs cloud keys create local)
$tsx client.ts
↳ { reply: "hello, world" }
✓ Live in 47s.

Dashboard

Spend and events for every session, streamed live to the dashboard.

substructure.ai/apps/researcher/sessions
Substructure dashboard showing a session's agent and cumulative spend, a spend-over-time bar chart, and the real-time event log

Events

Every agent run is an append-only log of events: tool calls, LLM responses, sub-agent spawns, errors. The log drives the next decision, streams to subscribers in real time, and stays around as a complete record of what happened.

Decision loop

Substructure sends your worker a trigger and the current state. The worker makes decisions, runs tools, and returns actions. That's the whole loop.

f(user.message,state)(call.llm,state′)

Feature list

Here's what the runtime takes care of.

  • Durable agent sessions
  • Crash recovery and automatic retries
  • Agent state persistence
  • LLM call orchestration
  • Tool call dispatch
  • Sub-agent lifecycle management
  • Cost tracking per session
  • Real-time event streaming to clients
  • Resumable client connections
  • JWT auth for browser clients
  • API key auth for backends
  • Client-side tool execution
  • Human-in-the-loop interrupts
  • Embeddable or standalone server

Get started

Substructure is open source. Write your agent as an HTTP worker, deploy it, and connect it to the runtime.