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

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. 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:

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)

packages/runtime/src/model-history.ts produces durable, compacted message records from the RuntimeEvent ledger. It handles partial event accumulation and final message emission:

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)

packages/ui/src/live-turn-projection.ts creates an incremental, in-flight view of the current turn before it settles:

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)

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:
  5. UI layer combines projections via 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:

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 Core SessionEvent → RuntimeEvent transformation
Stored-message history packages/runtime/src/model-history.ts Durable message projection with compaction
Live-turn view packages/ui/src/live-turn-projection.ts Incremental in-flight turn construction
Transcript assembly packages/ui/src/transcript-projection.ts Unified UI-ready conversation view
Integration tests 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 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 SessionEvents (like text_delta). 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.

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 and 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 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →