The agent() lifecycle
packages/core/src/runtime.ts
This is the heart of the engine. Every agent() call walks the same fixed 13-step sequence — and the order is load-bearing. The journal lookup happens before any model is spawned (that's resume), the budget and agent-cap gates fail fast as values, and the semaphore slot is always released in a finally. Click through each step; the panel shows the real code for that stage.
seq++ — the replay key
Every agent() call grabs a monotonic seq from the runtime. It's the primary key in the journal — the entire resume mechanism keys off it. Same script + same args ⇒ identical seq order, which is exactly why scripts must be deterministic.
this number is everythingconst agent = async (prompt, opts = {}) => {
const mySeq = seq++; // monotonic, per-runtime
const phase = opts.phase ?? currentPhase;
const label = opts.label ?? `agent-${mySeq}`;
const key = `${mySeq}:${phase}:${label}`;Why the order matters
A few invariants fall straight out of this sequence:
- Journal-before-spawn is the whole resume story. By the time control reaches the adapter, we've already proven this seq has no cached result. See Journal & resume.
- Gates throw values, not exceptions. Budget and agent-cap produce a tagged
WorkflowErrorwrapped inWorkflowThrow, so your script body cantry/catchit like any error while library code keeps threadingResult. spawned++is synchronous. The cap is claimed the instant the check passes, so a burst of concurrent launches can't all slip pastmaxAgents.release()lives infinally. Success or throw, the semaphore slot goes back to the next waiter. Omitting it would deadlock the run.
The shape in one glance
Everything the runtime does is observable through events — there are no side channels.