# Understanding the Lifecycle Concepts of sessionId, turnId, and runId in Apache Maka

> Explore Apache Maka's sessionId, turnId, and runId lifecycle concepts. Understand how these identifiers manage conversations, model executions, and request-response pairs for effective data flow.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: deep-dive
- Published: 2026-08-26

---

**Apache Maka employs a three-tier identifier hierarchy where `sessionId` represents the top-level conversation container, `runId` tracks logical model executions within that session, and `turnId` captures atomic request-response pairs, each with distinct creation scopes and persistence lifecycles.**

Apache Maka's runtime architecture separates conversational AI workflows into three distinct identification layers. Understanding the lifecycle concepts of `sessionId`, `turnId`, and `runId` in Maka is essential for debugging persistence, replaying executions, and building responsive user interfaces. These identifiers form a strict parent-child-grandchild relationship that drives how the framework stores events, manages state, and renders interactions.

## Session ID: The Top-Level Conversation Container

The **`sessionId`** represents the overarching interactive conversation between a user and a Runtime Host. It functions as the root anchor for all activity within a single chat window, CLI session, or automated workflow.

According to the source code, the session ID is generated when a user opens a new conversation and is stored in the session-scoped **Task Ledger**. As documented in [`docs/session-task-ledger-lifecycle.md`](https://github.com/apache/maka/blob/main/docs/session-task-ledger-lifecycle.md), this identifier persists until the user explicitly ends the session or an automatic timeout/archival policy triggers. All subsequent runs and turns inherit this same `sessionId`, making it the primary key for session-level metadata and task persistence.

The session remains valid across multiple model invocations, ensuring that task history and context remain accessible regardless of how many individual runs execute within the conversation.

## Run ID: The Logical Execution Context

The **`runId`** identifies a logical execution of a model or agent within a session. Unlike the session identifier, which spans the entire conversation, a run represents a specific model invocation that may span multiple turns—such as multi-turn tool-calling sessions or long-running autonomous goals.

In `src/scripts/computer-use/real-model.mjs`, the framework assigns a fresh `runId` using `randomUUID()` the first time a model invocation is created for a given session. The identifier is recorded in the **Runtime Store** ([`src/packages/storage/src/sqlite-runtime-store.ts`](https://github.com/apache/maka/blob/main/src/packages/storage/src/sqlite-runtime-store.ts)) and attached to every **RuntimeEvent** belonging to that execution.

A run remains valid for its entire execution lifecycle. The run is considered *terminal* when the model reports a final status—such as completing a goal, returning from a tool call, or receiving a user abort signal. After termination, the `runId` is closed, though the session continues. A new model invocation within the same session receives a fresh `runId`, allowing the framework to separate distinct execution phases while maintaining session continuity.

## Turn ID: The Atomic Interaction Unit

The **`turnId`** represents the smallest unit of interaction that Maka tracks—a single request-response pair. This atomic identifier enables precise UI rendering, event ordering, and scroll positioning.

At the start of each turn, the system generates a fresh `turnId` using `crypto.randomUUID()` (or static constants for testing). As implemented in [`src/packages/ui/src/use-turn-virtualizer.ts`](https://github.com/apache/maka/blob/main/src/packages/ui/src/use-turn-virtualizer.ts), the turn ID is stored on turn-level events and used by the UI to locate corresponding DOM elements via `data-turn-id` attributes.

The turn lifecycle exists only for the duration of that specific interaction. Once the turn's output is rendered and persisted, the `turnId` is never reused. New turns within the same run receive unique identifiers, enabling precise scroll-and-highlight behavior without confusing the boundaries between separate turns or runs.

## Hierarchy and Relationships

The three identifiers form a strict containment hierarchy:

- **Session ID** → Parent container for all activity
- **Run ID** → Child of a session; may contain many turns  
- **Turn ID** → Grandchild of a session (and child of a run)

A typical execution flow follows this pattern:

1. Create session → `sessionId = "s-abc123"` (persists throughout)
2. Start run → `runId = "r-xyz789"` (valid until terminal status)
3. Execute turns → `turnId = "t-1"`, `turnId = "t-2"` (atomic, single-use)

When a new model invocation is required—whether triggered by a user command or autonomous goal—a fresh `runId` is allocated while the original `sessionId` is preserved. Each turn inside that run receives its own `turnId`, ensuring that persistence, replay, and UI virtualization operate independently at each layer.

## Implementation in Source Code

### Deriving Identifiers in the Runtime Ledger

In `src/scripts/computer-use/direct-runtime-ledger.mjs`, the framework demonstrates how the three identifiers relate during ledger creation:

```javascript
// src/scripts/computer-use/direct-runtime-ledger.mjs
export function createDirectRuntimeTurnLedger({ sessionId, turnId, text, newId, now }) {
  const invocationId = `${sessionId}-invocation`;
  const runId = `${sessionId}-run`;       // run ID derived from the session
  // …
}

```

This function shows the derivation pattern where the `runId` is constructed from the `sessionId`, establishing the parent-child relationship at the code level.

### Generating Fresh IDs for Model Invocation

When initiating actual model execution, `src/scripts/computer-use/real-model.mjs` generates cryptographically secure identifiers:

```javascript
// src/scripts/computer-use/real-model.mjs
const runId = randomUUID();             // fresh run ID
const turnId = crypto.randomUUID();      // fresh turn ID
const runResult = await runModel({
  sessionId,
  runId,
  turnId,
});

```

This separation ensures that each model invocation receives a unique execution context (`runId`) while maintaining the conversation context (`sessionId`).

### Persisting Runtime Events

The SQLite runtime store enforces identifier relationships during persistence. In [`src/packages/storage/src/sqlite-runtime-store.ts`](https://github.com/apache/maka/blob/main/src/packages/storage/src/sqlite-runtime-store.ts), the `importRuntimeEvent` method validates that events belong to the correct session and run:

```typescript
// src/packages/storage/src/sqlite-runtime-store.ts
async importRuntimeEvent(sessionId: string, runId: string, canonicalEvent: RuntimeEvent) {
  if (sessionId !== canonicalEvent.sessionId || runId !== canonicalEvent.runId) {
    throw new Error(`RuntimeEvent ${canonicalEvent.id} appears more than once in run ${runId}`);
  }
  // …
}

```

This validation ensures data integrity across the identifier hierarchy.

### UI Virtualization by Turn

The UI layer leverages `turnId` for precise rendering control. In [`src/packages/ui/src/use-turn-virtualizer.ts`](https://github.com/apache/maka/blob/main/src/packages/ui/src/use-turn-virtualizer.ts):

```typescript
// src/packages/ui/src/use-turn-virtualizer.ts
const heights = input.sessionId && layoutKey
    ? turnHeightIndex.lookup(input.sessionId, layoutKey)
    : undefined;
// … later
const index = turnIds.indexOf(turnId);   // locate a specific turn for scrolling

```

This implementation allows the interface to scroll to specific turns using stable identifiers without reloading entire sessions or runs.

## Summary

- **`sessionId`** persists for the entire conversation lifetime, storing session-scoped tasks and metadata in the Task Ledger.
- **`runId`** represents discrete model executions within a session, tracked in the Runtime Store, and terminates when the model reports final status.
- **`turnId`** provides atomic tracking of individual request-response pairs, enabling precise UI virtualization and scroll positioning.
- The hierarchy—Session → Run → Turn—allows Maka to persist state independently, replay executions by `runId`, and render turns without confusion between execution contexts.

## Frequently Asked Questions

### What is the relationship between sessionId and runId in Maka?

The `sessionId` acts as the parent container that persists for the entire conversation, while `runId` represents child executions spawned within that session. Multiple `runId` values can exist sequentially within a single `sessionId`, but each run belongs to exactly one session. This relationship is enforced in the SQLite runtime store, which validates that every runtime event matches both its session and run identifiers.

### How long does a turnId persist in Apache Maka?

A `turnId` exists only for the duration of its specific request-response cycle. Generated at the start of each turn using `crypto.randomUUID()`, the identifier is attached to runtime events and UI elements, then retired once the turn completes. The identifier is never reused, ensuring that scroll positions and event logs maintain precise references to historical interactions without ambiguity.

### Can multiple runIds exist simultaneously within the same session?

While a session can contain many runs over its lifetime, each `runId` represents a distinct execution phase that begins with a model invocation and ends with a terminal status. After a run terminates—whether through completion, tool return, or user abort—a new model invocation generates a fresh `runId`. The architecture does not support overlapping active runs within the same session context; instead, runs proceed sequentially while the `sessionId` maintains continuity.

### Where are these identifiers stored in the Maka codebase?

The `sessionId` is persisted in the session-scoped Task Ledger as described in [`docs/session-task-ledger-lifecycle.md`](https://github.com/apache/maka/blob/main/docs/session-task-ledger-lifecycle.md). The `runId` and associated events are stored in the Runtime Store implemented in [`src/packages/storage/src/sqlite-runtime-store.ts`](https://github.com/apache/maka/blob/main/src/packages/storage/src/sqlite-runtime-store.ts). The `turnId` is utilized by the UI layer in [`src/packages/ui/src/use-turn-virtualizer.ts`](https://github.com/apache/maka/blob/main/src/packages/ui/src/use-turn-virtualizer.ts) for DOM element tracking and layout management. Collectively, these storage locations implement the hierarchical relationship defined in [`ARCHITECTURE.md`](https://github.com/apache/maka/blob/main/ARCHITECTURE.md).