Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1559b78
docs(chat): the twelve chat pages leave the pending list
blove Sep 6, 2026
f25b437
docs(chat): chat-interrupt-panel teaches through the running example
blove Sep 6, 2026
e49c251
docs(chat): chat-debug teaches through the running example
blove Sep 6, 2026
107ef2d
docs(chat): chat-subagent-card teaches through the running example
blove Sep 6, 2026
638070c
docs(chat): chat-input teaches through the running example
blove Sep 6, 2026
a8fa27a
docs(chat): chat-tool-calls teaches through the running example
blove Sep 6, 2026
abd561c
docs(chat): chat-trace teaches through the running example
blove Sep 6, 2026
1c4f78b
docs(chat): generative UI teaches through the running example
blove Sep 6, 2026
7333cf0
docs(chat): message model teaches through the running example
blove Sep 6, 2026
721c850
docs(chat): chat-debug page — launcher vs panel when the agent is nul…
blove Sep 6, 2026
7250c7e
docs(chat): chat-interrupt-panel page — the primitive's reader, graph…
blove Sep 6, 2026
cf3a9f8
docs(chat): chat-subagent-card page — info callout, namespace comment…
blove Sep 6, 2026
5e6f177
docs(chat): thread routing teaches through the running example
blove Sep 6, 2026
04dbe52
docs(chat): client tools teaches through the running example
blove Sep 6, 2026
7d10000
docs(chat): the A2UI overview teaches through the running example
blove Sep 6, 2026
51ef9f6
docs(chat): theming teaches through the running example
blove Sep 6, 2026
01b68fd
docs(chat): chat-trace and chat-subagent-card pages — collapse rule, …
blove Sep 6, 2026
58a1d59
docs(chat): chat-tool-calls page — collapse on complete, valid fence,…
blove Sep 6, 2026
7169b29
docs(chat): generative-ui page — the real idempotence guard, finalize…
blove Sep 6, 2026
813b14d
docs(chat): message-model page — signal comments, current flag, pendi…
blove Sep 6, 2026
7645313
docs(chat): a2ui overview — progressive rendering attributed to the l…
blove Sep 6, 2026
0076a90
docs(chat): client-tools page — seven files, the twin's own graph, at…
blove Sep 6, 2026
02dbd98
docs(chat): thread-routing page — exported signal for the fences, the…
blove Sep 6, 2026
7f88687
docs(chat): theming page — info callout, exact spacing set, radius-in…
blove Sep 6, 2026
d6dffb8
test(website): retire PENDING_PAGES; guard that no walkthrough or doc…
blove Sep 6, 2026
4747b0e
docs(chat): cross-page pass — card icons, checkpointer wording, class…
blove Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
319 changes: 142 additions & 177 deletions apps/website/content/docs/chat/a2ui/overview.mdx

Large diffs are not rendered by default.

157 changes: 91 additions & 66 deletions apps/website/content/docs/chat/components/chat-debug.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
---
description: How the debug example mounts the chat-debug panel against a multi-node LangGraph agent, plus the panel inputs, outputs, sidenav integration and bundle gate.
---

# ChatDebugComponent

`ChatDebugComponent` provides a docked development panel for inspecting an `Agent` or `AgentWithHistory`. It ships from the debug-only secondary entry point so applications can keep debug implementation code out of production bundles unless they explicitly opt in.
`ChatDebugComponent` is a docked development panel for inspecting an agent. It reads the agent's checkpoint history and current state and renders them in a floating panel, and it ships from a debug-only secondary entry point so applications can keep the implementation out of production bundles unless they opt in. The running example mounts the panel on its own, which is the clearest way to see what the panel contributes and what it does not.

**Selector:** `chat-debug`

Expand All @@ -10,97 +14,118 @@
import { ChatDebugComponent } from '@threadplane/chat/debug';
```

## Basic Usage
## What the demo does

```typescript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatDebugComponent } from '@threadplane/chat/debug';
The Run tab mounts `<chat-debug>`, bound to a LangGraph agent, inside the shared example layout shell, `<example-chat-layout>`, which contributes only a full-height background. The panel keeps itself out of the layout — its host renders `display: contents`, and the launcher and the panel are both fixed-position — so the page shows nothing but a small round status pill in the top-right corner. Click the pill and the docked Chat Devtools panel opens, with a Timeline tab listing the agent's checkpoints and a State tab pretty-printing the agent's current state under a Copy button. Pressing Escape or clicking outside the panel closes it.

The demo mounts no chat composition beside the panel, so there is no composer on the page and the Timeline tab shows its empty state: "No checkpoints yet. Send a message to populate the timeline." Everything else is live. The dock buttons in the header move the panel between the left, bottom and right edges, and the choice is persisted, as is the open state and the selected tab.

## How it is built

Three files carry the example: the graph that produces the checkpoints, the provider that points Angular at it, and the component that mounts the panel. Open the Code tab to read them in place.

### A graph with several nodes per turn

The backend is deliberately multi-step, because one node per turn produces one checkpoint and very little to look at. `generate` answers with the model, `process` appends a synthetic message derived from the answer, and `summarize` asks the model for a one-sentence summary of the conversation so far. The system prompt read by `generate` frames the assistant as an aviation helper working over a mock dataset of ten United States airports and four airlines.

<ExampleCode file="graph.py" region="pipeline-nodes" title="graph.py — the three pipeline nodes" />

Each node returns a partial state update, and each of those updates becomes a checkpoint on the thread.

### Wiring the nodes into a linear pipeline

The nodes are registered on a `StateGraph` over `MessagesState` and chained: `generate` to `process` to `summarize` to `generate_title`, then to the end. `generate_title` is a background node that summarizes the first user message into a thread title; it returns an empty update, so it changes the message list not at all while still adding a step to the run.

<ExampleCode file="graph.py" region="graph-wiring" title="graph.py — the compiled pipeline" />

<Callout type="info" title="No checkpointer is compiled in">
The last line calls `compile()` with no checkpointer. The checkpoints the Timeline tab reads come from the LangGraph API server, which persists thread state for every graph it serves. `langgraph dev` refuses to load a graph that compiles its own saver, and a deployment ignores one — see [Persistence](/docs/langgraph/guides/persistence).
</Callout>

### The agent provider

`provideAgent()` registers the agent once for the whole application. The example resolves its connection details at runtime from the host that serves the demo, which is why its factory reads them rather than hard-coding them, and it sits alongside `provideChat({})`, which supplies the chat library's own providers.

@Component({
selector: 'app-debug-page',
standalone: true,
imports: [ChatDebugComponent],
providers: [
provideAgent({
apiUrl: '/api/langgraph',
assistantId: 'chat',
threadId: signal(localStorage.getItem('threadId')),
onThreadId: (id) => localStorage.setItem('threadId', id),
}),
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<chat-debug
[agent]="chat"
dock="right"
[defaultOpen]="true"
(replayRequested)="replay($event)"
(forkRequested)="fork($event)"
/>
`,
})
export class DebugPageComponent {
protected readonly replayCheckpointId = signal<string | null>(null);
protected readonly forkSourceCheckpointId = signal<string | null>(null);

protected readonly chat = injectAgent();

replay(checkpointId: string) {
this.replayCheckpointId.set(checkpointId);
// Route this into your app's replay workflow.
}

fork(checkpointId: string) {
this.forkSourceCheckpointId.set(checkpointId);
// Route this into your app's thread fork workflow.
}
}
<ExampleCode file="app.config.ts" />

Your own application passes the two values directly instead:

```typescript
provideAgent({
apiUrl: 'https://your-deployment.langgraph.app',
assistantId: 'debug',
}),
provideChat({}),
```

### Mounting the panel

The component wraps `<chat-debug>` in `<example-chat-layout>` and binds one field. `injectAgent()` returns the agent registered above, and it is handed straight to `[agent]`. Nothing else is bound, so the panel runs on its defaults: docked right, closed on first load, with the floating launcher visible.

<ExampleCode file="debug.component.ts" />

The agent returned by `injectAgent()` exposes a `history()` signal as well as `state()`, which is what makes the Timeline tab appear; an agent without `history()` gets the State tab alone.

## Inputs

| Input | Type | Default | Description |
| --- | --- | --- | --- |
| `agent` | `Agent \| AgentWithHistory` | **Required** | Agent state. Agents with `history()` also enable the Timeline tab. |
| `dock` | `'right' \| 'bottom' \| 'left'` | `'right'` | Initial dock position. |
| `defaultOpen` | `boolean` | `false` | Initial open state when no persisted state exists. |
| `agent` | `DebugAgent \| DebugAgentWithHistory \| null` | `null` | Agent to inspect. The panel renders nothing while this is `null`; the floating launcher still appears, but opening it shows no content. An agent that also exposes `history()` enables the Timeline tab. |
| `dock` | `'right' \| 'bottom' \| 'left'` | `'right'` | Initial dock position, used when no persisted position exists. When a sibling `<chat-sidebar>` is on the page and the user has not clicked a dock button this session, an auto-dock effect forces `bottom` on open. |
| `defaultOpen` | `boolean` | `false` | Initial open state, used when no persisted state exists. |
| `launcher` | `'floating' \| 'none'` | `'floating'` | Shows the built-in floating launcher, or hides it when another surface opens the panel. |
| `storageKey` | `string` | `'chat-debug'` | Local storage key prefix for persisted open/dock/tab state. |
| `storageKey` | `string` | `'chat-debug'` | Local storage key prefix for the persisted open, dock and tab state. |

`DebugAgent` is a structural type: signals for `messages`, `status`, `isLoading`, `error`, `toolCalls` and `state`. `DebugAgentWithHistory` adds `history`. The agents returned by `injectAgent()` satisfy the second.

## Outputs

| Output | Type | Description |
| --- | --- | --- |
| `replayRequested` | `string` | Emits a checkpoint id when the built-in Timeline tab requests replay. |
| `forkRequested` | `string` | Emits a checkpoint id when the built-in Timeline tab requests fork. |
| `replayRequested` | `string` | Emits a checkpoint id when Replay is pressed on a selected checkpoint in the Timeline tab. |
| `forkRequested` | `string` | Emits a checkpoint id when Fork is pressed on a selected checkpoint in the Timeline tab. |
| `openChange` | `boolean` | Emits when the panel opens or closes. |
| `dockChange` | `'right' \| 'bottom' \| 'left'` | Emits when the dock position changes. |

`replayRequested` and `forkRequested` are integration hooks. The debug panel does not mutate the agent by itself; the host app decides whether a checkpoint opens a replay view, starts a forked thread, or maps to a backend-specific time travel operation.

## Sidenav Integration

`ChatSidenavComponent` can own the launcher for apps that already use the sidenav footer. Pass the same agent and keep `[debug]` enabled:
`replayRequested` and `forkRequested` are integration hooks, and the example demonstrates the panel without them. The panel never mutates the agent by itself: selecting a checkpoint reveals a Replay button, a Fork button and a before-and-after diff of that step's state, and the host application decides whether an emitted id opens a replay view, starts a forked thread, or maps to a backend-specific time travel operation.

```html
<chat-sidenav
[agent]="chat"
[debug]="true"
<chat-debug
[agent]="agent"
(replayRequested)="replay($event)"
(forkRequested)="fork($event)"
/>
```

The footer button is labelled in expanded and drawer modes. In collapsed mode it uses the status dot only. The dot pulses while `agent.status()` is `running`.
## Sidenav integration

`ChatSidenavComponent` can own the launcher for applications that already use the sidenav footer. Pass the same agent and leave `[debug]` at its default of `true`:

```html
<chat-sidenav [agent]="agent" [debug]="true" />
```

The footer button appears only when an agent is bound and the debug entry point is included in the build. It is labeled "Devtools" in expanded and drawer modes; in collapsed mode it is the status dot alone, and the dot pulses while `agent.status()` is `running`. Pressing it lazily imports `ChatDebugComponent`, mounts it with `launcher="none"` and the storage key `chat-sidenav-debug`, and opens it. The sidenav does not re-expose `replayRequested` or `forkRequested`, so mount `<chat-debug>` yourself when you need those hooks.

## Production Bundles
## Production bundles

The debug implementation lives under `@threadplane/chat/debug`; the main `@threadplane/chat` entry point no longer exports it. In the canonical Angular demo, normal production builds set `THREADPLANE_CHAT_DEBUG=false` and externalize `@threadplane/chat/debug`, so the debug implementation is absent from the emitted bundle. The `production-debug` build opts back in with `THREADPLANE_CHAT_DEBUG=true`.
The implementation lives under `@threadplane/chat/debug`; the main `@threadplane/chat` entry point does not export it. Importing it yourself, as the example does, always puts it in the bundle. The sidenav's lazy import is different: it is gated on an internal flag that is true whenever `ngDevMode` is true, and otherwise only when a `THREADPLANE_CHAT_DEBUG` compile-time constant is defined as `true`. The canonical Angular demo defines that constant per build configuration — `false` for `production`, and `true` for a separate `production-debug` configuration.

Keep debug controls for your app outside the debug panel. The debug panel intentionally exposes a small fixed surface so consumers do not expect demo-specific controls to appear in their own applications.
Keep debug controls of your own outside the panel. The panel intentionally exposes a small fixed surface, so consumers do not expect application-specific controls to appear in it.

## See also
## What's Next

- [Time travel](/docs/langgraph/guides/time-travel) — what the `replayRequested` / `forkRequested` checkpoint hooks plug into, and how to wire replay and fork against the agent's history.
- [`<chat-sidenav>`](/docs/chat/concepts/primitives-vs-compositions#compositions) — the layout composition that can own the debug launcher (see Sidenav Integration above).
<CardGroup cols={2}>
<Card title="Time travel" href="/docs/langgraph/guides/time-travel">
What the replay and fork checkpoint hooks plug into, and how to wire them against the agent's history.
</Card>
<Card title="Persistence" href="/docs/langgraph/guides/persistence">
Where the checkpoints come from, and why this graph compiles without a checkpointer.
</Card>
<Card title="ChatSidenav" href="/docs/chat/components/chat-sidenav">
The layout composition that can own the debug launcher.
</Card>
<Card title="ChatTrace" href="/docs/chat/components/chat-trace">
An in-conversation view of graph execution, for surfaces your users see.
</Card>
</CardGroup>
Loading
Loading