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 fragmentsthinking_*– model reasoning turns (intermediate computation)tool_start,tool_result– function call lifecycle eventssandbox_boundary_*,user_question_*– system-level boundary crossingserror,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:
- Renderer emits a
SessionEvent(e.g.,text_deltawith incremental text) - RuntimeKernel receives the event and invokes
mapSessionEventToRuntimeEventwith context and memory - Canonical ledger (
RuntimeEventLog) appends the resultingRuntimeEvent - Projection helpers read the ledger:
model-history.tsproduces stored-message projectionslive-turn-projection.tsbuilds the in-flight view
- UI layer combines projections via
transcript-projection.tsfor 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
SessionEventobjects into canonicalRuntimeEventrecords viamapSessionEventToRuntimeEvent - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →