# How Session Message Projection in Apache Maka Builds Conversation State from Runtime Events

> Learn how Apache Maka builds conversation state from runtime events. Discover its deterministic aggregation logic for structured transcript views.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: internals
- Published: 2026-09-02

---

**Apache Maka constructs conversation state by mapping `SessionEvent` objects to canonical `RuntimeEvent` records, then projecting those records into structured transcript views through deterministic aggregation logic.**

This architecture enables real-time, reproducible conversation rendering across the model-tool loop. The source code in `apache/maka` implements a clean separation between event transformation and state projection, ensuring that every conversation turn remains replayable and consistent.

---

## Mapping SessionEvents to RuntimeEvents

The first phase of session message projection occurs in **[`packages/runtime/src/session-event-runtime-mapper.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/session-event-runtime-mapper.ts)**. This file contains the core transformation logic that elevates low-level renderer events into fully-typed, addressable runtime records.

### RuntimeEventMapContext and SessionEventMapMemory

Two supporting structures enable accurate event mapping:

| Structure | Purpose |
|-----------|---------|
| **`RuntimeEventMapContext`** | Immutable container for `sessionId`, `invocationId`, `runId`, `turnId`, and timestamps. Injected into every `RuntimeEvent` for unique identification. |
| **`SessionEventMapMemory`** | Mutable store tracking tool-name ↔ use-id linkage and error state. Allows later events (e.g., `tool_result`) to resolve references to earlier tool invocations. |

### The mapSessionEventToRuntimeEvent Function

The `mapSessionEventToRuntimeEvent(event, ctx, memory)` function (lines 94-102) switches on `event.type` to produce strongly-typed outputs:

```typescript
case 'text_delta':
  return {
    ...base,
    partial: true,
    role: 'model',
    author: 'agent',
    content: { kind: 'text', text: event.text },
    refs: { providerEventId: event.messageId },
  };

```

The **`resolveBase`** helper (lines 90-100) prepends session-wide identifiers and timestamps, guaranteeing that every `RuntimeEvent` is globally unique and temporally ordered.

### Supported Event Types

The mapper handles the complete event taxonomy emitted by Apache Maka's renderer:

- **`text_delta`** – incremental model output fragments
- **`thinking_*`** – model reasoning turns (intermediate computation)
- **`tool_start`**, **`tool_result`** – function call lifecycle events
- **`sandbox_boundary_*`**, **`user_question_*`** – system-level boundary crossings
- **`error`**, **`abort`**, **`complete`** – terminal states and failure modes

Each branch preserves the original `SessionEvent` payload while adding the canonical structure required for downstream projection.

---

## Aggregating RuntimeEvents into Conversation State

Once events are mapped, the projection phase builds user-facing conversation views. Apache Maka maintains **three complementary projections** that serve different consumption patterns.

### Stored-Message Projection ([`model-history.ts`](https://github.com/apache/maka/blob/main/model-history.ts))

**[`packages/runtime/src/model-history.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/model-history.ts)** produces durable, compacted message records from the `RuntimeEvent` ledger. It handles partial event accumulation and final message emission:

```typescript
export function projectMessage(event: RuntimeEvent): MessageProjection {
  // Collapse partial events into a final message shape.
  if (event.partial && event.role === 'model') {
    // Append deltas to the current message buffer.
    // …
  } else {
    // Emit a complete message entry.
    return {
      id: event.id,
      role: event.role,
      author: event.author,
      content: event.content,
      ts: event.ts,
    };
  }
}

```

**Key characteristic**: Idempotency through `RuntimeEvent.id` reuse. Replaying the same ledger yields bit-identical output, enabling reliable session restoration and debugging.

### Live-Turn Projection ([`live-turn-projection.ts`](https://github.com/apache/maka/blob/main/live-turn-projection.ts))

**[`packages/ui/src/live-turn-projection.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/live-turn-projection.ts)** creates an incremental, in-flight view of the current turn before it settles:

```typescript
export function liveTurnProjection(events: RuntimeEvent[]): LiveTurnProjection {
  const turn: LiveTurnProjection = { messages: [], partial: true };
  for (const ev of events) {
    if (ev.partial) {
      // Merge deltas into the last message.
    } else {
      turn.messages.push(projectMessage(ev));
    }
  }
  return turn;
}

```

The `partial: true` flag signals to UI components that the turn is still streaming. When the `complete` event arrives, the flag transitions to `false` and the turn becomes eligible for persistence.

### Transcript Projection ([`transcript-projection.ts`](https://github.com/apache/maka/blob/main/transcript-projection.ts))

**[`packages/ui/src/transcript-projection.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/transcript-projection.ts)** merges stored and live projections into the unified transcript that components like `SessionListPanel` render. This is the primary consumption point for Apache Maka's conversation UI.

---

## End-to-End Session Message Projection Flow

The complete pipeline from renderer emission to UI rendering follows five deterministic stages:

1. **Renderer emits** a `SessionEvent` (e.g., `text_delta` with incremental text)
2. **RuntimeKernel receives** the event and invokes `mapSessionEventToRuntimeEvent` with context and memory
3. **Canonical ledger** (`RuntimeEventLog`) appends the resulting `RuntimeEvent`
4. **Projection helpers** read the ledger:
   - [`model-history.ts`](https://github.com/apache/maka/blob/main/model-history.ts) produces stored-message projections
   - [`live-turn-projection.ts`](https://github.com/apache/maka/blob/main/live-turn-projection.ts) builds the in-flight view
5. **UI layer** combines projections via [`transcript-projection.ts`](https://github.com/apache/maka/blob/main/transcript-projection.ts) for final rendering

The entire flow is **pure and side-effect-free**: transformation logic never mutates external state, and projection logic only reads the immutable ledger.

---

## Working Code Example

This runnable pattern demonstrates the complete session message projection pipeline:

```typescript
import {
  mapSessionEventToRuntimeEvent,
  createSessionEventMapMemory,
} from '@maka/runtime/session-event-runtime-mapper';
import { RuntimeEventLog } from '@maka/runtime/runtime-event-log';
import { transcriptProjection } from '@maka/ui/transcript-projection';

const ctx = {
  sessionId: 'sess-1',
  invocationId: 'sess-1-inv',
  runId: 'run-1',
  turnId: 'turn-1',
};

const memory = createSessionEventMapMemory();
const ledger = new RuntimeEventLog();

// Simulated stream of SessionEvents from the renderer:
for (const sessEvt of rendererEvents) {
  const rtEvt = mapSessionEventToRuntimeEvent(sessEvt, ctx, memory);
  ledger.append(rtEvt);
}

// Build the UI transcript:
const transcript = transcriptProjection(ledger.readAll());
console.log(transcript);

```

The example exercises the exact transformation path: raw renderer events → canonical runtime events → structured conversation transcript.

---

## Key Source Files

| Component | File Path | Purpose |
|-----------|-----------|---------|
| Session-event mapper | [`packages/runtime/src/session-event-runtime-mapper.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/session-event-runtime-mapper.ts) | Core `SessionEvent` → `RuntimeEvent` transformation |
| Stored-message history | [`packages/runtime/src/model-history.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/model-history.ts) | Durable message projection with compaction |
| Live-turn view | [`packages/ui/src/live-turn-projection.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/live-turn-projection.ts) | Incremental in-flight turn construction |
| Transcript assembly | [`packages/ui/src/transcript-projection.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/transcript-projection.ts) | Unified UI-ready conversation view |
| Integration tests | [`packages/runtime-host/src/__tests__/session-revision-protocol.test.ts`](https://github.com/apache/maka/blob/main/packages/runtime-host/src/__tests__/session-revision-protocol.test.ts) | Pipeline correctness validation |

---

## Summary

- **Session message projection** in Apache Maka converts low-level `SessionEvent` objects into canonical `RuntimeEvent` records via `mapSessionEventToRuntimeEvent`
- **Immutable context injection** (`RuntimeEventMapContext`) and **mutable memory** (`SessionEventMapMemory`) enable accurate, stateful event transformation
- **Three projection layers**—stored-message, live-turn, and transcript—serve persistence, real-time streaming, and UI rendering respectively
- **Idempotent, deterministic design** guarantees that ledger replay produces identical conversation state, supporting debugging and session restoration
- **Pure transformation architecture** maintains clean separation between event ingestion and state consumption

---

## Frequently Asked Questions

### What is the difference between SessionEvent and RuntimeEvent in Apache Maka?

**`SessionEvent`** is the raw, renderer-native format emitted by Apache Maka's backend components. **`RuntimeEvent`** is the canonical, strongly-typed representation that all downstream systems consume. The [`session-event-runtime-mapper.ts`](https://github.com/apache/maka/blob/main/session-event-runtime-mapper.ts) file performs this elevation, adding session identifiers, timestamps, and structural normalization that the projection layer requires.

### How does Apache Maka handle partial message updates during streaming?

The mapper sets `partial: true` on `RuntimeEvent` objects derived from delta-type `SessionEvent`s (like `text_delta`). [`live-turn-projection.ts`](https://github.com/apache/maka/blob/main/live-turn-projection.ts) accumulates these deltas into the current message buffer, exposing incremental state to the UI. When the terminal `complete` event arrives, `partial` becomes `false` and the finalized message moves to persistent storage via [`model-history.ts`](https://github.com/apache/maka/blob/main/model-history.ts).

### Can conversation state be rebuilt from the RuntimeEventLog?

Yes—this is a core design guarantee of Apache Maka's session message projection. Because `RuntimeEvent.id` values are stable and the projection functions are pure, replaying any `RuntimeEventLog` sequence through [`model-history.ts`](https://github.com/apache/maka/blob/main/model-history.ts) and [`transcript-projection.ts`](https://github.com/apache/maka/blob/main/transcript-projection.ts) yields identical conversation state. This enables session restoration, collaborative debugging, and deterministic testing.

### Where does tool call information persist across async boundaries?

`SessionEventMapMemory` in [`session-event-runtime-mapper.ts`](https://github.com/apache/maka/blob/main/session-event-runtime-mapper.ts) maintains the tool-name ↔ use-id mapping. When `tool_start` occurs, the tool name is stored in memory keyed by use-id. Later `tool_result` events retrieve this mapping to construct complete `RuntimeEvent` records with proper references, even when the renderer emits these events asynchronously.