Skip to main content
Most agent loops live inside one request. ActiveHorizonAgentWorkflow is the durable supervisor for an agent that should keep making progress for minutes or hours — long past any HTTP timeout, any process restart, and any single model call. It is not an open-ended autonomous process. Every turn runs as a bounded child workflow inside four budgets, against an explicit allowlist of workflow types, and an operator can steer or stop it at any point.
The supervisor decides what runs next; it does not execute anything itself. Each turn is a child workflow — commonly SandboxAgentLoopWorkflow, which runs the work inside an isolated sandbox, but any allowlisted workflow type can serve as a turn.

The four budgets

An agent stops when it finishes, or when it hits the first budget to run out. Every value defaults if you omit it, and every value is clamped — an agent cannot be configured to run unbounded. Elapsed time is measured with Temporal’s replay-safe workflow clock, not the host clock, so a worker restart neither loses nor invents elapsed time.

Starting one

POST /v1/workflows/active-horizon-agent/validate takes the same body and checks it without starting anything — worth doing in CI for any recipe you ship.
allowed_workflow_types is a security boundary, not a convenience. A planner may add tasks while the agent runs, and explicitly queued tasks are checked against the same list — so a planner that returns something unexpected cannot launch an arbitrary registered workflow. Leave it empty and nothing dynamic can run at all, which is the safe default rather than the permissive one.

Tasks and the planner

The agent works a queue. Each task names a child workflow and its input:
When the queue drains, the optional planner connector step runs and is expected to return either { "done": true } or { "task": … } — so the agent either finishes or gets its next piece of work. Without a planner, the agent stops when the queue empties.

The ledger is the memory

The supervisor continues as new every checkpoint_every_turns turns (default 10, clamped 1–50) so Temporal history stays bounded. What crosses that boundary is a compact ledger — one entry per completed task — rather than raw transcripts. That is a deliberate constraint: the ledger has to be small enough that a fresh generation can resume from it alone. If an agent needs detail from turn 3 at turn 90, that detail belongs in the ledger entry or in durable storage, not in a transcript the next generation will never see.

Steering a running agent

Every control is a signal against a running workflow, so none of them race with a turn in flight.
Prefer cancel to terminate. Cancel lets the current turn finish and the ledger close out, so the run is still readable afterwards; terminate drops the workflow where it stands and leaves the last turn’s outcome unrecorded. Keep terminate for the case where a turn is stuck and you cannot wait.

Governed defensive-security recipes

A recipe whose recipe_key starts with cyber- runs under a stricter contract than any other agent on the platform. The intent is bounded defensive work — enriching an alert, collecting evidence, reviewing a configuration — under an authorization that a customer has actually granted, with an expiry. Every one of these is enforced by the API before the workflow starts, and the whole scope is hash-bound to the run:
  • authorization_scope is required, and is rejected on any non-cyber- recipe.
  • engagement_id names the engagement, incident or change ticket the work is authorized under.
  • authorized_assets must list 1–100 bounded selectors. *, 0.0.0.0/0 and ::/0 are rejected — there is no way to authorize everything.
  • permitted_actions is a closed allowlist: alert_enrichment, evidence_collection, configuration_review, vulnerability_validation, remediation_planning. Nothing else is accepted, and duplicates are rejected.
  • expires_at must be in the future, no more than 30 days out, and must cover the entire requested active window — an agent cannot outlive its authorization.
  • Tasks may only launch SandboxAgentLoopWorkflow, so the work runs isolated.
  • Shell and bootstrap commands are prohibited outright; the bounded planner passes direct argv.
  • Executables are restricted to a read-only set: pwd, rg, jq, head, tail, wc, sort, cat, stat, sha256sum.
The authorization scope is a customer’s grant, not a configuration default. Widening authorized_assets, adding an action, or extending expires_at requires a new validated release — the scope is hash-bound, so an in-flight agent cannot have its authority broadened underneath it. If a run needs more reach than it was granted, it stops; that is the design, not a failure.

How a run ends

status on the final result says which budget ended it, which is the first thing to look at when an agent stops earlier than expected: A failure_budget_exhausted run is the one worth reading the ledger for: it means turns were failing faster than the agent could make progress, and the ledger entries name which.

Next steps

Sandbox agent sessions

The isolated execution side — where a turn’s work actually runs.

ResponseAutomationV2

The single-ticket workflow, and the contrast with a supervised agent.

Policy engine

The verified-decision gate a mutating turn passes through.

Operations

Running the engine, and what to watch while an agent is live.