Lifecycle of an AgentRun in Maka: From Construction to Finalization
The lifecycle of an AgentRun in Maka comprises ten distinct stages—from construction and invocation opening through session registration, execution, and final ledger persistence—ensuring every agent turn is durable, traceable, and auditable.
In Apache Maka, an AgentRun represents a durable, traceable execution of a single turn (or agent invocation) within a session. Understanding the lifecycle of an AgentRun in Maka is essential for developers building reliable agent orchestration systems, as each stage guarantees immutable event recording and recoverable state management.
Stage 1: Construction and Identifier Assignment
The lifecycle begins when the runtime instantiates the AgentRun class defined in packages/runtime/src/agent-run.ts. During construction (lines 68‑86), the system fixes critical identifiers that remain immutable throughout the run’s lifetime.
// packages/runtime/src/agent-run.ts (lines 68-86)
const run = new AgentRun(input);
During this phase, the run’s run ID, session ID, turn ID, and invocation ID are permanently assigned. The constructor also assembles lineage metadata and validates durability settings to ensure the run can survive process restarts. These identifiers serve as the foundation for all subsequent ledger entries and correlation across distributed traces.
Stage 2: Invocation Opening and Initial Event Recording
Once constructed, the run enters the opening phase via begin() (or specialized variants beginOperation() and beginContinuation()). This method first invokes openInvocation() (lines 101‑124), which writes the invocation‑opened fact to the RuntimeEventStore.
// Lines 101-124: Establishing the immutable opening record
await run.begin();
// Internally calls openInvocation() and commitInvocationOpening() (lines 123-128)
Immediately following invocation opening, the system records the initial user event. The begin() method calls buildInitialRuntimeEvent (lines 110‑122) to synthesize the root runtime event, which is then persisted via recordRuntimeEvents. This event becomes the root of the run’s event stream, establishing the causal chain for all subsequent processing.
Stage 3: Session Registration and State Management
Before execution begins, the run must register with the session manager. The runtime calls hooks.reserveRun at line 201 to register the new run in the SessionManager, enabling the system to track active runs per session.
// Line 201: Reserving the run in the session
await hooks.reserveRun(run.id, run.metadata);
Following reservation, the session status transitions to running via hooks.updateStatus, and a turn‑state entry is appended via hooks.appendTurnState (lines 203‑205). These hooks ensure the session projection remains consistent with the runtime’s ground truth.
Stage 4: Building Prior Runtime Context
For runs that continue from previous turns, the lifecycle includes a context reconstruction phase. The buildPriorRuntimeContext() method (lines 254‑259) reconstructs the execution context from historical records in the RuntimeEventStore.
// Lines 254-259: Reconstructing context for continuations
const priorContext = await run.buildPriorRuntimeContext();
This step allows the agent to access conversation history and prior tool outputs without reloading the entire session state, optimizing performance for multi-turn interactions.
Stage 5: The Execution Loop and Event Processing
The core execution phase follows an event-driven loop centered on acceptMappedEvent(sessionEvent, runtimeEvent) (lines 608‑635). While the run remains active, this method processes incoming events from the AI backend and user interactions.
// Lines 608-635: Main event processing loop
while (run.isActive) {
await run.acceptMappedEvent(sessionEvent, runtimeEvent);
}
Non‑terminal events—such as tool calls, partial outputs, and streaming chunks—are either buffered via recordRuntimePartial or written immediately to the store. Terminal events (completion, abort, or error) trigger recordRuntimeEvents with strict durability guarantees, followed by recordSessionEvent to update the session projection with the final state.
Stage 6: Stopping and Terminal Claims
External callers can halt a run before natural completion by invoking stop(source) (lines 224‑235). This method stakes a terminal claim with owner = ‘stop’, marking the run as stopped in memory.
// Lines 224-235: Initiating a stop
run.stop('user-request');
The claim is later cashed by settleStopTerminal() (lines 380‑410), which ensures a terminal fact is persisted to the ledger even if the backend stream never emits a natural completion event. This mechanism prevents "zombie runs" and guarantees that every reserved run eventually reaches a terminal state observable in the AgentRunStore.
Stage 7: Finalization and Ledger Persistence
When the backend stream ends or the run is otherwise finished, finalize() (lines 540‑572) executes the cleanup sequence.
// Lines 540-572: Cleanup and persistence
await run.finalize();
This method flushes any buffered partial events, commits the final terminal RuntimeEvent (or synthesizes an abort if none was produced), and deregisters the run from the session via hooks.unregisterRun. Finally, it patches session headers with definitive status and timestamps, completing the observable lifecycle.
Throughout all stages, the AgentRunStore receives durable records including run‑created, run‑started, model‑call‑attempt, model‑projection‑transition, history‑compact‑checkpoint, and terminal facts. Errors in ledger writing propagate as DurableStoreWriteError or ToolLedgerCorruptionError, enabling the runtime to retry or abort safely.
Summary
- Construction fixes immutable identifiers (run ID, session ID, turn ID, invocation ID) and validates durability settings.
- Invocation opening writes the "invocation‑opened" fact and records the root user event to establish the event stream.
- Session registration reserves the run in the
SessionManagerand updates session status to running. - Context building reconstructs prior state from
RuntimeEventStorefor continuation scenarios. - Execution loop processes non‑terminal and terminal events through
acceptMappedEvent, with immediate persistence for terminal outcomes. - Stopping creates terminal claims via
stop()and settles them viasettleStopTerminal()to guarantee ledger completion. - Finalization flushes buffers, commits terminal facts, unregisters from the session, and updates headers.
- Ledger persistence spans the entire lifecycle, with error types like
DurableStoreWriteErrorensuring system safety.
Frequently Asked Questions
What is the difference between stopping and finalizing an AgentRun?
Stopping an AgentRun via stop(source) (lines 224‑235) initiates an early termination by staking a terminal claim, but does not immediately persist the final state. Finalization via finalize() (lines 540‑572) actually commits the terminal event to the RuntimeEventStore, flushes buffers, and deregisters the run from the session. Stopping is the request; finalization is the durable completion.
How does Maka ensure an AgentRun remains durable throughout its lifecycle?
Durability is ensured through multiple mechanisms: immutable identifiers assigned at construction; synchronous writing of the "invocation‑opened" fact; the AgentRunStore ledger that records every state transition; and error propagation via DurableStoreWriteError or ToolLedgerCorruptionError if persistence fails. Additionally, settleStopTerminal() guarantees that even manually stopped runs produce a terminal fact in the ledger.
Can a run access previous conversation history, and how?
Yes, runs that continue from previous turns invoke buildPriorRuntimeContext() (lines 254‑259), which queries the RuntimeEventStore to reconstruct the prior context. This allows the agent to reference historical events without reloading the entire session state, optimizing performance while maintaining traceability.
What happens if the backend stream fails without emitting a terminal event?
If the backend stream fails or the run is manually stopped before natural completion, the settleStopTerminal() method (lines 380‑410) ensures a terminal fact is still written to the ledger. This "cashing" of the terminal claim prevents data loss and maintains the immutable audit trail required for reproducible agent execution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →