Your agent is 10 steps deep when the process dies. Now what?
A durable TypeScript runtime for agents and automations. LLM calls, tool results, and side effects are stored as they complete. Crash? Restart? Pick up where you left off. No platform, no infrastructure, no vendor lock-in. Just a library you import and own.
Your agent calls an LLM. Parses the response. Calls a tool. Then the process dies. Did the LLM call go through? Will it run again on restart and bill you twice?
ctx.effect() stores the result when it completes successfully. Next attempt? Returns what already worked instead of running again. No replay engine, no duplicate LLM calls in the common case, no rebuilding state by hand.
await ctx.effect({
key: 'llm-analyze',
run: () => llm.chat('Summarize this document'),
})
// completed once? retries return the stored result.
// common case: $0.15 well spent, not $0.30. Most durable execution frameworks force deterministic, replayable code. No Math.random(), no new Date(), no branching on external state.
Yella doesn't replay anything. Your handler re-executes from the top on every retry. Effects that already completed return their stored results, the function doesn't run again.
Write normal async functions. Use any library. Branch however you want. There's no SDK to learn, no execution model to study. If your code runs in Node.js today, it runs with Yella tomorrow.
Your agent branched three times and failed on step 8. Correlation IDs link a single event through every handler it triggered. One config line enables a live dashboard with traces, graph views, deferred pauses, and dead-letter visibility.
Handlers re-execute from the top, but completed effects are replayed from storage. That makes failed steps inspectable and repairable without building your own workflow UI.
createEngine({ server: { port: 3400 } })
You write try/catch. You add a database to track what already ran. You build retry logic with exponential backoff. You handle idempotency with deduplication keys. You handle crashes by hoping they don't happen. Six months in, half your codebase is a workflow engine you never intended to build.
You don't need a visual builder, a hosted service, or a framework that owns your control flow. You need a library you import. One file. No infrastructure. No accounts to create, no dashboards to manage, no billing surprises. Your code, your process, your server.
import { createEngine } from '@yellatech/engine'
const engine = createEngine({ store: { type: 'sqlite', path: './agent.db' } })
engine.process({
name: 'research',
on: 'topic:assigned',
run: async (ctx) => {
// already called? returns the stored result.
await ctx.effect({
key: 'llm-analyze',
run: () => llm.chat(`Analyze: ${ctx.payload.topic}`),
})
return ctx.ok({ analysis: true }, { emit: 'topic:analyzed' })
},
})
engine.emit('topic:assigned', { topic: 'durable execution models' })
// drain() waits for all runs to finish, only needed in scripts and tests.
// in a server, emit() returns immediately and the dispatcher handles the rest.
await engine.drain() A connector framework built on Engine. Standard interface for Discord, email, webhooks, cron: with all of Engine's guarantees. Get started with Conduit.
Engineering updates, design decisions, and deep dives. Read the latest.