Documentation menu
Start here
IntroductionGetting startedWrite an agentProject anatomyUnderstand Ayjnt
Harness engineeringTwo runtimesHuman interfacesHow agents workHost bridgeAgent capabilities
Callable methodsState & SQLiteSessions & memorySchedulingWorkflowsDurable executionInter-agent RPCSub-agentsToolsInterfaces & integrations
Browser clientRouting & middlewareVoiceBrowser toolsMCPEmailObservabilityCLI
Command overviewnewdevrunbuildcompilemigratedeployAPI reference
AgentAgentClientWorkflow classesLocal & cloudMigrationsExamplesDurable execution
Choose queues, retries, fibers, or workflows based on the recovery guarantee the work needs.
Start from a fresh project
This guide is self-contained. Create an empty Ayjnt project, then replace the starter agent with the files shown below. Run commands from the new project directory unless a step says otherwise.
bunx ayjnt new try-durable-execution --empty
cd try-durable-execution
bun install
# remove agents/alive once you are ready to add the guide's filesFour execution tools
keepAliveWhile() protects in-memory work from idle eviction but does not make it recoverable. Fibers add a durable run ledger and stash() checkpoints. Workflows add durable steps and a full instance lifecycle.
| Primitive | Use it for |
|---|---|
| retry() | A fallible operation that can be tried again now |
| queue() | FIFO work owned by one agent |
| runFiber() / startFiber() | Long work with checkpoints and recovery hooks |
| AgentWorkflow | Durable multi-step processes, approvals, and external control |
import { Agent, callable } from "ayjnt";
export default class ImporterAgent extends Agent {
@callable()
async importFile(fileId: string) {
return this.runFiber(
`import:${fileId}`,
async (fiber) => {
const rows = await this.retry(
() => downloadRows(fileId),
{ maxAttempts: 3 },
);
fiber.stash({ downloadedRows: rows.length });
return this.queue("indexRows", { fileId, rows });
},
);
}
async indexRows(payload: { fileId: string; rows: Row[] }) {
await saveRows(payload.fileId, payload.rows);
}
}Make effects idempotent
A recovery path may execute again. Give external writes idempotency keys, record completion before notifying clients, and keep non-durable progress messages separate from durable state transitions.
await chargeCustomer({
customerId,
amount,
// A retry receives the same key instead of creating a second charge.
idempotencyKey: `order:${orderId}:charge`,
});Run it and verify the result
Interrupt one run after its first checkpoint, restart development, and verify that the selected durable primitive resumes or retries safely.
bun run build
bun run dev
# the local app is now available at http://localhost:8787