# How the Maka Desktop Build System Streams Sessions, Displays Tool Timelines, and Supports Branching/Recovery

> Discover how the Apache Maka Desktop build system streams sessions, displays tool timelines, and supports branching and recovery for efficient development.

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

---

**The Apache Maka Desktop client streams a live SSE transcript from the Runtime Host, materializes it into a flat timeline of tool activities and reasoning steps, folds consecutive processing spans into collapsible UI blocks, and surfaces branching controls alongside recovery actions for aborted turns.**

The **Maka Desktop build system** combines an Electron-based shell with a React UI layer to deliver real-time AI coding assistance. According to the Apache Maka source code, the architecture relies on a stream-driven pipeline that transforms raw Runtime Host events into render-friendly views while preserving every tool invocation, error state, and branch point.

## Streaming the Session Transcript from Runtime Host

The foundation of the Desktop client is a **Server-Sent Events (SSE)** stream (`text/event-stream`) emitted by the Runtime Host. The UI consumes this stream in [`packages/ui/src/tool-output-stream.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/tool-output-stream.ts), which handles `ToolOutputChunk` objects to build live tool output.

The stream processor performs three critical tasks:

1. **Dedupes and orders** chunks by the `seq` field to handle out-of-order network packets.
2. **Redacts secrets** and enforces size caps to prevent token leakage.
3. **Appends chunks** to the corresponding `ToolActivityItem.outputChunks` array for immediate display.

```typescript
// packages/ui/src/tool-output-stream.ts
export interface ToolOutputChunk {
  seq: number;
  stream: "stdout" | "stderr";
  text: string;
  redacted: boolean;
  createdAt: number;
}

```

The **Live Turn Projection** ([`live-turn-projection.ts`](https://github.com/apache/maka/blob/main/live-turn-projection.ts)) constructs a temporary `TurnViewModel` for the currently streaming turn. This transient model updates the UI in real time before merging into the persisted transcript once the turn settles, ensuring users see tool output the moment it arrives.

## Building the Flat Tool Timeline

Once a turn completes, [`packages/ui/src/materialize.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/materialize.ts) transforms stored messages into UI-ready objects. The `materializeChat()` function maps user, assistant, and system messages into `ChatItem` arrays, while `materializeTools()` extracts every tool activity with status indicators, argument previews, and accumulated output chunks.

The result is a **flat timeline** represented as an ordered array of `TurnTimelineItem` objects. Each item carries a `kind` discriminator ("thinking", "tools", "text", etc.) that preserves the exact chronological order of reasoning steps and tool invocations.

```typescript
// packages/ui/src/materialize.ts
export function materializeChat(
  messages: readonly StoredMessage[], 
  locale: UiLocale = "en"
): ChatItem[] {
  // Maps stored messages into UI objects with tool activities attached
}

```

## Folding the Timeline for Clean Rendering

The render layer never mutates the flat model directly. Instead, it derives a **folded view** just before rendering via [`packages/ui/src/timeline-fold.ts`](https://github.com/apache/maka/blob/main/packages/ui/src/timeline-fold.ts). The `foldTimeline()` function groups consecutive "thinking" and "tools" entries into a single `ProcessingFold` block, preventing visual clutter during long tool runs.

```typescript
// packages/ui/src/timeline-fold.ts
export function foldTimeline(
  items: readonly TurnTimelineItem[]
): FoldedTimelineEntry[] {
  const out: FoldedTimelineEntry[] = [];
  let buffer: FoldedTimelineChild[] | null = null;
  
  const flush = (): void => {
    if (buffer && buffer.length > 0) {
      if (buffer.some((child) => child.kind === 'tools')) {
        out.push({ kind: 'processing', id: 'start', children: buffer });
      } else {
        out.push(...buffer);
      }
    }
    buffer = null;
  };
  
  // Iterate, grouping thinking+tools into ProcessingFold
  flush();
  return out;
}

```

This folding strategy collapses complex multi-step reasoning into a single disclosure unit ("Processing") while preserving the original order as nested children. Pure reasoning runs remain unwrapped, allowing "深度思考" (deep thinking) disclosures to render independently. The folded timeline is memoized per turn in [`chat-turn.tsx`](https://github.com/apache/maka/blob/main/chat-turn.tsx) to prevent unnecessary re-renders.

## Displaying Live Tool Activity

Inside [`packages/ui/src/chat-turn.tsx`](https://github.com/apache/maka/blob/main/packages/ui/src/chat-turn.tsx), the `ChatTurn` component splits the folded timeline at user message boundaries and renders each segment according to type:

- **Thinking** entries render as assistant message bubbles.
- **Tool** entries render as grouped cards showing status icons, progress spinners, and live stdout/stderr streams pulled from `ToolActivityItem.outputChunks`.

The `liveStreaming` prop adds a **global spinner** that covers the entire turn while tools execute. This prevents the UI from displaying an empty "thinking" bubble that would appear abandoned during the initial tool setup phase.

```tsx
// packages/ui/src/chat-turn.tsx
<ChatTurn
  turn={liveTurn}
  liveStreaming={{
    onStreamingSettled: () => console.log('settled'),
    runningStatus: true,
  }}
/>

```

## Branching and Recovery Mechanisms

### Session Branch Context

When a user edits a previous turn, the system creates a **new branch session** tracked by the `SessionContextBranch` interface in [`session-context-layer.tsx`](https://github.com/apache/maka/blob/main/session-context-layer.tsx):

```typescript
// packages/ui/src/session-context-layer.tsx
export interface SessionContextBranch {
  parentSessionId: string;
  parentSessionName: string;
  fromAbortedTurn?: boolean;
}

```

The UI renders a banner showing the parent session name and, when `fromAbortedTurn` is true, displays a localized "从中断前分支" (branch from interruption) label. Clicking the **branch button** on the active streaming tail creates a new session that copies the original transcript up to the selected turn, allowing users to pivot their workflow without losing prior context.

### Recovery from Aborted Turns

When a turn fails due to tool errors or timeouts, the UI preserves the **partial timeline** (including any truncated output) and surfaces **recovery actions**:

- **Regenerate** – Re-run the turn with identical inputs.
- **Branch** – Start a new session from the failure point.

These actions are exposed via the `onSwitchToBypassAndRetry` callback in `ChatTurn`. The aborted-turn UI remains visible even when the provider produced no assistant event (see the handling around lines 61-64 in [`chat-turn.tsx`](https://github.com/apache/maka/blob/main/chat-turn.tsx)), ensuring users can always recover from failures.

```tsx
// In packages/ui/src/chat-turn.tsx
{turn.status === 'failed' && (
  <UiButton
    variant="ghost"
    size="sm"
    onClick={() => props.onSwitchToBypassAndRetry?.(turn.turnId)}
    label="Branch from failure"
  />
)}

```

## Summary

- **Stream-driven architecture**: The Desktop build consumes SSE events from the Runtime Host via [`tool-output-stream.ts`](https://github.com/apache/maka/blob/main/tool-output-stream.ts), deduplicating and redacting chunks in real time.
- **Flat timeline model**: [`materialize.ts`](https://github.com/apache/maka/blob/main/materialize.ts) converts stored messages into `TurnTimelineItem` arrays that preserve exact tool invocation order.
- **UI folding**: [`timeline-fold.ts`](https://github.com/apache/maka/blob/main/timeline-fold.ts) derives `ProcessingFold` blocks to collapse lengthy reasoning sequences without losing structural data.
- **Live rendering**: `ChatTurn` displays tool cards with progress indicators and enforces a global spinner during active streaming to prevent UI abandonment.
- **Branching support**: `SessionContextBranch` tracks parent relationships, while the UI offers explicit branch/regenerate controls for aborted turns via `onSwitchToBypassAndRetry`.

## Frequently Asked Questions

### How does Maka handle out-of-order chunks during session streaming?

The [`tool-output-stream.ts`](https://github.com/apache/maka/blob/main/tool-output-stream.ts) module sequences every `ToolOutputChunk` by its `seq` field before appending to `ToolActivityItem.outputChunks`. This guarantees that even if network jitter delivers stderr before stdout, the UI renders tool output in the exact order produced by the Runtime Host.

### What is the difference between the flat timeline and the folded timeline?

The **flat timeline** is the canonical data model produced by [`materialize.ts`](https://github.com/apache/maka/blob/main/materialize.ts), containing every thinking step and tool invocation as discrete `TurnTimelineItem` objects. The **folded timeline** is a derived, render-time optimization created by `foldTimeline()` that groups consecutive processing items into a single `ProcessingFold` block to reduce visual noise.

### Can users recover a session if a tool invocation crashes mid-stream?

Yes. When a turn status becomes 'failed', `ChatTurn` preserves the partial timeline—including any output chunks received before the crash—and exposes the `onSwitchToBypassAndRetry` callback. Users can either **regenerate** the failed turn or **branch** into a new session that forks from the exact point of failure.

### Where is the Desktop build configuration stored?

The Electron packaging configuration lives in `apps/desktop/electron-builder.config.mjs`. This file defines how the Runtime Host and `@maka/ui` package are bundled into the final Desktop application that ships to end users.