# Durable agentic flow

> Compose checkpoints, child tools, durable timers, and approval signals in an ordinary handler.

An agent loop becomes durable when every restart boundary lives in PostgreSQL. Workhorse provides
those boundaries while your application continues to own model calls, tool code, and prompts.

<Tabs items={["TypeScript", "Python", "Go"]}>
  <Tab value="TypeScript">
    ```ts
    const plan = await context.checkpoint("plan", () => callModel(prompt));
    await context.setProgress({ stage: "planned" });
    const tools = await context.runChildrenAll(toolRequests);
    await context.sleep("model-cooldown", cooldownMs);
    const approval = await context.waitForSignal<{ approved: boolean }>("approval");
    ```
  </Tab>
  <Tab value="Python">
    ```python
    plan = context.checkpoint("plan", lambda: call_model(prompt))
    context.set_progress({"stage": "planned"})
    tools = context.run_children_all(tool_requests)
    context.sleep("model-cooldown", cooldown_ms)
    approval = context.wait_for_signal("approval")
    ```
  </Tab>
  <Tab value="Go">
    ```go
    plan, err := handler.Checkpoint("plan", func() (any, error) { return callModel(prompt) })
    progress, err := handler.SetProgress(map[string]any{"stage": "planned"})
    tools, err := handler.CreateChildrenAll(toolRequests)
    err = handler.Sleep("model-cooldown", cooldown)
    approval, err := handler.WaitForSignal("approval")
    ```
  </Tab>
</Tabs>

Python handlers use `set_progress` and `get_progress`; Go handlers use `SetProgress` and
`GetProgress`. All three surfaces write the same fenced projection, so the dashboard sees one
language-independent progress value.

Each child name and request must stay stable across replay. Checkpoints reuse stored results, timers
keep their first wake target, and the approval signal arrives through `Queue.sendSignal`. Every
boundary releases the lease instead of retaining an in-memory continuation.

Model and tool calls remain at least once. Use provider idempotency, an outbox, an inbox, or a
compensation path when an external effect cannot safely repeat.

## Run the repository example

`pnpm example:agentic-flow` builds the publishable packages and runs
`typescript/examples/agentic-flow.mjs` against the configured database. The example creates a
parent, joins tool children, crosses a durable timer, delivers an idempotent approval signal, and
prints the final result and progress.

The example combines `HandlerContext.setProgress`, `HandlerContext.runChildrenAll`,
`Queue.syncRateLimitPolicies`, a per-tenant `concurrencyKey`, `HandlerContext.sleep`, and
`HandlerContext.waitForSignal`. Python names progress updates `HandlerContext.set_progress`, while
Go uses `HandlerContext.SetProgress`; the repository example reads `DATABASE_URL_TEST_PACKED`.

## Next

- [Child jobs](/docs/child-jobs) — delegate and join tool work
- [Signals](/docs/signals) — deliver approval from another process
- [Durable execution](/docs/durable-execution) — make replayed stages safe

---

Exact example contracts and durable boundaries:
[architecture reference](https://github.com/stablemates/workhorse/blob/main/docs/architecture.md#agentic-flow-example).
