durable-agent
Resumable multi-step agent loop with per-step durability on 0G Storage. Each step has an idempotent key; the step ledger is persisted to 0G Storage so completed steps are never re-run on restart. Every executed step is traced via OpenTelemetry (noop tracer used when OTel is not configured in the base).
What it does
The durable-agent kit adds a resumable multi-step agent loop to your 0G app.
On each run the kit:
- Creates a
JobRunnervia@foundryprotocol/0gkit-jobsbacked byMemoryBackend(in-process, dev-friendly). For cross-process job durability swap inSqliteBackendfrom@foundryprotocol/0gkit-jobs/backends/sqlite. - Loads the step-completion ledger from 0G Storage (if a prior run completed any steps, those roots are already in Storage).
- For each step in the pipeline —
research → act → recordby default:- If the step key is already in the ledger → SKIP (no span emitted).
- Otherwise: open an OTel span, run the step, write the key to the ledger, close the span.
- On failure:
span.setErroris called, the span is closed, and the step is not marked done, so a resume will retry it.
- Persists the updated ledger back to 0G Storage after the run.
Steps are defined via defineAgent({ steps: [...] }) and AgentStep (portable
lib, no hard inference imports). The researchStep is capability-guarded:
if ctx.sealedInference is injected by the adapter it is used; otherwise a
placeholder is returned. No hard dep on any inference package.
OTel note: the tracer is injected by the adapter. Bases that do not
configure @opentelemetry/api receive the built-in makeNoopTracer() which
emits no real spans. Swap in a real tracer from @opentelemetry/api for
production observability.
Cold-start resume caveat: full resume across process restarts requires a
persistent pointer to the ledger root on 0G Storage. The adapter stores this
pointer in the OG_STORAGE_NAMESPACE path. Without a persistent pointer
(e.g. first run, or namespace cleared), the agent starts a fresh run — same
caveat documented by the agent-memory kit.
Compatible bases
react-app · chat · tee-attested-api · mcp-agent · storage-app
Apply
# scaffold-time
npm create 0gkit-app -- --kits durable-agent
# add to an existing project
0g add durable-agent
Environment variables
| Variable | Example | Notes |
|---|---|---|
OG_PRIVATE_KEY | 0xabc123... | Required for 0gkit-jobs signer (signing job receipts) and 0G Storage (uploading/downloading step ledger) |
OG_RPC_URL | https://rpc.0g.ai | 0G chain RPC URL — required for 0G Storage operations |
OG_STORAGE_NAMESPACE | durable-agent | Namespace prefix for step-ledger blobs (default: durable-agent). Each run stored under <ns>/<jobId>/steps |
OG_JOBS_BACKEND | memory | Documents the active jobs backend. memory = MemoryBackend (default). Swap to SqliteBackend for cross-process durability. |
Quick start
0g add durable-agent
Compose steps with defineAgent, then run them through createRunner. Each
step has an idempotent key; the injected AgentJobsBackend is the durable
step ledger — completed steps are skipped on resume. This uses an in-memory
ledger + the built-in no-op tracer (the storage-app adapter swaps in a
0G-Storage-backed backend so resume survives restarts):
import {
defineAgent,
createRunner,
makeNoopTracer,
type AgentJobsBackend,
} from "./lib/agent.js";
import { defaultPipeline } from "./lib/steps.js"; // research → act → record
const done = new Set<string>();
const backend: AgentJobsBackend = {
async getCompletedSteps() {
return done;
},
async markStepDone(key) {
done.add(key);
},
};
const agent = defineAgent({ name: "durable-agent", steps: defaultPipeline });
const runner = createRunner({ agent, backend, tracer: makeNoopTracer() });
await runner.run({ prompt: "Summarize the current state of the 0G network." });
console.log([...done]); // ["research", "act", "record"] — re-running is a no-op
Cold-start caveat: step-skip durability is only as durable as the
AgentJobsBackend. The in-memory ledger above resets on restart; thestorage-appadapter persists it to 0G Storage and resumes from itsroot.
Tiers
- lib —
lib/agent.ts(portabledefineAgent,createRunner,AgentJobsBackend,StepTracer,makeNoopTracerinterfaces + implementations),lib/steps.ts(sampleresearchStep,actStep,recordStep+defaultPipeline). - adapters — one per base (
app/api/agent/route.tsfor Next.js,src/routes/agent.tsfor Hono/tee-attested-api,src/tools/agent.tsfor MCP,src/agent-runner.tsfor storage-app).
Honesty note
Step-level durability in the lib is guaranteed: skipped steps are provably
never executed (the completed ledger snapshot is checked before each step).
Full cold-start resume across process restarts requires a persistent root
pointer in 0G Storage — the adapter stores this, but if the namespace is
cleared or the storage root is lost, the next run starts fresh. The OTel
integration is real when the adapter injects a real tracer from
@opentelemetry/api; the default makeNoopTracer() is a documented noop.