Skip to content

Commit 58471e3

Browse files
bloveclaude
andcommitted
docs: cover error UX, client tools, thread/agent typing, retry budget for recent PRs
Documents user-facing surfaces landed since 0.0.49 that had no narrative docs (API reference is bot-generated; these are the hand-written guides): - chat-subagent-card: correct stale "latest message" claim — the card now renders the full subagent transcript (streaming markdown + reasoning + tool-call cards) on <chat-trace>, auto-expanding while running (#692, #711) - changelog: un-freeze from 0.0.49 → 0.0.52; add classified errors + Retry (#693), injectThreadRouting (#697), full-transcript subagent cards, typed DX - new Error Handling guide: AgentError/AgentErrorKind, toAgentError, isAbortError, agent.retry(), the built-in <chat-error> Retry UX (#693) - new Client Tools guide: tools/action/view/ask with typed ViewProps/ToolArgs and typed agent state via createAgentRef (#685) - provideAgent reference: clientOptions + app-wide LANGGRAPH_CLIENT_OPTIONS retry-budget token with precedence (#681); Typed state via AgentRef (#685) - register both new guides in docs-config nav Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 58f7651 commit 58471e3

6 files changed

Lines changed: 451 additions & 22 deletions

File tree

apps/website/content/docs/chat/components/chat-subagent-card.mdx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# ChatSubagentCardComponent
22

3-
`ChatSubagentCardComponent` is a composition that renders an expandable card for a subagent stream. It displays the subagent's tool call ID, current status (with a color-coded badge), and expands to show the message count and latest message content.
3+
`ChatSubagentCardComponent` is a composition that renders an expandable card for a subagent stream. It displays the subagent's name (or tool call ID), current status (with a color-coded badge) and message count, and expands to show the subagent's **full transcript** — every message rendered as streaming markdown, alongside any reasoning and the subagent's own tool-call cards.
4+
5+
The card is built on the [`<chat-trace>`](/docs/chat/components/chat-trace) primitive, so it auto-expands while the subagent is `running` and collapses once it reaches `complete` (a user toggle always wins).
46

57
**Selector:** `chat-subagent-card`
68

@@ -34,27 +36,33 @@ The `Subagent` type comes from `@threadplane/chat`. It provides reactive state f
3436
| `name` | `string \| undefined` | Optional human-readable name. The card's "Subagent" label uses it when present |
3537
| `status()` | `Signal<'pending' \| 'running' \| 'complete' \| 'error'>` | Current execution status |
3638
| `messages()` | `Signal<Message[]>` | Messages produced by the subagent |
39+
| `toolCalls()` | `Signal<ToolCall[]> \| undefined` | The subagent's own tool calls (name/args/result), referenced by each message's `toolCallIds`. Optional — adapters that don't surface subagent tool calls omit it, and the card defaults to `[]` |
3740
| `state()` | `Signal<Record<string, unknown>>` | Arbitrary subagent state exposed by the runtime |
3841

3942
## Card Behavior
4043

41-
### Collapsed State (Default)
44+
### Header
45+
46+
The card header (the `<chat-trace>` toggle button) shows:
47+
- A chevron that reflects the expanded state
48+
- The subagent `name` if present, otherwise the literal `"Subagent"`, with the `toolCallId` in monospace
49+
- A color-coded status pill
50+
- The message count (e.g., "3 message(s)")
4251

43-
The card header shows:
44-
- An agent icon on the left
45-
- "Subagent" label with the `toolCallId` in monospace
46-
- A color-coded status badge
47-
- A chevron toggle on the right
52+
### Auto-expand and collapse
4853

49-
### Expanded State
54+
Expansion is driven by `<chat-trace>`: the card auto-expands while `status()` is `running` and collapses when it settles to `complete`. Clicking the header toggles it manually, and a manual toggle overrides the automatic behavior.
5055

51-
Clicking the header toggles expansion. The expanded area shows:
52-
- Message count (e.g., "3 message(s)")
53-
- The content of the latest message (either as plain text or serialized JSON)
56+
### Transcript
5457

55-
### Status Badge Colors
58+
When expanded, the card renders the subagent's **entire message list** (not just the latest). For each message it shows, in order:
59+
- Any `reasoning` text, as a muted italic line
60+
- The message `content`, rendered through `<chat-streaming-md>` (streaming markdown)
61+
- A [`<chat-tool-call-card>`](/docs/chat/components/chat-tool-call-card) for each tool call referenced by the message's `toolCallIds`
5662

57-
The status badge uses different chat theme variables based on the current status:
63+
### Status Pill Colors
64+
65+
The status pill is styled via a `data-status` attribute and CSS selectors (the exported `statusColor()` helper is retained for backward compatibility). Colors map to chat theme variables:
5866

5967
| Status | Background | Text Color |
6068
|--------|-----------|------------|
@@ -122,14 +130,16 @@ The card uses the following CSS custom properties:
122130

123131
| Variable | Applied To |
124132
|----------|-----------|
125-
| `--ngaf-chat-surface-alt` | Card background |
126-
| `--ngaf-chat-surface` | Latest message content background |
127-
| `--ngaf-chat-separator` | Card border, section dividers |
128-
| `--ngaf-chat-radius-card` | Card border radius |
129-
| `--ngaf-chat-text` | Subagent label, message content |
130-
| `--ngaf-chat-text-muted` | Agent icon, tool call ID, chevron, message count |
133+
| `--ngaf-chat-text` | Subagent name, message content |
134+
| `--ngaf-chat-text-muted` | Tool call ID, message count, reasoning line |
135+
| `--ngaf-chat-font-mono` | Tool call ID |
136+
| `--ngaf-chat-separator` | Divider between successive transcript messages |
137+
| `--ngaf-chat-warning-bg` / `--ngaf-chat-warning-text` | `running` status pill |
138+
| `--ngaf-chat-success` | `complete` status pill |
139+
| `--ngaf-chat-error-bg` / `--ngaf-chat-error-text` | `error` status pill |
140+
141+
The outer card chrome (background, border, radius) comes from the wrapping [`<chat-trace>`](/docs/chat/components/chat-trace).
131142

132143
## ARIA
133144

134-
- The header button has `aria-expanded` reflecting the current state
135-
- The button has `aria-label="Toggle subagent details"`
145+
- The header button (from `<chat-trace>`) exposes `aria-expanded` reflecting the current state

apps/website/content/docs/chat/getting-started/changelog.mdx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
11
# Changelog
22

33
<Callout type="info" title="Highlight releases only">
4-
This changelog tracks selected highlight releases, not every patch. The published package is at `0.0.49` — see the entry below for the notable additions since `0.0.19`.
4+
This changelog tracks selected highlight releases, not every patch. The published package is at `0.0.52` — see the entry below for the notable additions since `0.0.19`.
55
</Callout>
66

7+
## 0.0.52
8+
9+
### Classified errors and Retry
10+
11+
- New structured `AgentError` on the `Agent` contract: `agent.error()` now returns an `AgentError` (or `undefined`) carrying a machine-readable `kind` (`connection` | `auth` | `server` | `interrupted` | `aborted`), a `retryable` flag, an optional HTTP `status`, and the original `cause`. Both adapters normalize raw failures through `toAgentError()`.
12+
- New `agent.retry()` action re-runs the last request and clears `error`. The built-in `<chat-error>` primitive (auto-rendered by `<chat>`) shows cause-specific copy and a **Retry** button whenever `error.retryable` is true.
13+
- New exports from `@threadplane/chat`: `AgentError`, `AgentErrorKind`, `toAgentError`, `isAbortError`, `AGENT_ERROR_MESSAGES`, plus `ChatErrorComponent` and `extractErrorMessage`. See [Error Handling](/docs/chat/guides/error-handling).
14+
15+
### Thread routing
16+
17+
- New `injectThreadRouting()` helper binds an app-owned active-thread signal to the Angular Router — restoring the thread id from the URL on load, stamping changes back into the URL, and treating a bare URL as the welcome state, with no `localStorage`. See [Thread Routing](/docs/chat/guides/thread-routing).
18+
19+
### Subagent cards
20+
21+
- `<chat-subagent-card>` now renders the subagent's **full transcript** — every message as streaming markdown, with reasoning and the subagent's own tool-call cards — instead of only the latest message. Cards are inline and persistent, auto-expand while running, and collapse on completion. See [ChatSubagentCard](/docs/chat/components/chat-subagent-card).
22+
23+
### TypeScript DX
24+
25+
- Strict-safe, fully typed authoring surface for client tools, `view`, and `ask`, plus typed agent dependency injection. New type helpers exported from `@threadplane/chat`: `ToolArgs`, `ViewProps`, `AgentRef` / `createAgentRef`, and the `StandardSchemaV1` inference aliases.
26+
727
## 0.0.49
828

929
### Citations
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Client Tools
2+
3+
Client tools are tools you declare **in the browser** that the model calls and the browser executes — no server-side implementation. There are three kinds:
4+
5+
| Helper | Kind | What it does |
6+
|---|---|---|
7+
| `action()` | function | Runs an async handler in the browser; its resolved return value becomes the tool result sent back to the model. |
8+
| `view()` | render-only component | The model fills the component's props from the schema; the card renders inline and the call is auto-acknowledged once it mounts. |
9+
| `ask()` | interactive component | The model fills the component's props; the value the component emits back becomes the tool result (human-in-the-loop). |
10+
11+
Tools are arguments-typed by a [Standard Schema](https://standardschema.dev) (e.g. a Zod object). The catalog is shipped to the model by the adapter; the backend graph binds the client stubs and ends its turn so the browser executes them.
12+
13+
<Callout type="info" title="Adapter-neutral">
14+
The same declarations work with `@threadplane/langgraph` and `@threadplane/ag-ui` — only the `provideAgent`/`injectAgent` imports change.
15+
</Callout>
16+
17+
## Declaring a registry
18+
19+
`tools({...})` collects named tools into a frozen registry. Pass it to `<chat>` via `[clientTools]`:
20+
21+
```typescript
22+
import { Component } from '@angular/core';
23+
import { ChatComponent, tools, action, view, ask } from '@threadplane/chat';
24+
import { injectAgent } from '@threadplane/langgraph';
25+
import { z } from 'zod/v4';
26+
import { WeatherCardComponent } from './weather-card.component';
27+
import { ConfirmBookingComponent } from './confirm-booking.component';
28+
29+
const clientTools = tools({
30+
get_weather: action(
31+
'Look up the current weather for a location.',
32+
z.object({ location: z.string() }),
33+
async ({ location }) => ({ location, temperatureF: 68, conditions: 'Sunny' }),
34+
),
35+
weather_card: view(
36+
'Display a weather card for a location.',
37+
z.object({ location: z.string(), temperatureF: z.number(), conditions: z.string() }),
38+
WeatherCardComponent,
39+
),
40+
confirm_booking: ask(
41+
'Ask the user to confirm a booking before finalizing it.',
42+
z.object({ summary: z.string() }),
43+
ConfirmBookingComponent,
44+
),
45+
});
46+
47+
@Component({
48+
selector: 'app-client-tools',
49+
standalone: true,
50+
imports: [ChatComponent],
51+
template: `<chat [agent]="agent" [clientTools]="clientTools" />`,
52+
})
53+
export class ClientToolsComponent {
54+
protected readonly agent = injectAgent();
55+
protected readonly clientTools = clientTools;
56+
}
57+
```
58+
59+
The object keys (`get_weather`, `weather_card`, `confirm_booking`) are the tool names the model sees. `tools()` preserves each tool's precise generic type, so downstream lookups stay typed.
60+
61+
## Typed component props with `ViewProps`
62+
63+
For `view()` and `ask()`, the component's signal inputs are checked against the schema output at compile time — every field the schema produces must be a declared `input()` with an assignable type (the component may declare extra inputs the schema doesn't fill). Derive the input types directly from the schema with `ViewProps<typeof schema>` so the two never drift:
64+
65+
```typescript
66+
import { Component, input } from '@angular/core';
67+
import type { ViewProps } from '@threadplane/chat';
68+
import { z } from 'zod/v4';
69+
70+
export const weatherCardSchema = z.object({
71+
location: z.string(),
72+
temperatureF: z.number(),
73+
conditions: z.string(),
74+
});
75+
76+
// { location: string; temperatureF: number; conditions: string }
77+
type Inputs = ViewProps<typeof weatherCardSchema>;
78+
79+
@Component({
80+
selector: 'app-weather-card',
81+
standalone: true,
82+
template: `<div>{{ location() }}: {{ temperatureF() }}°F, {{ conditions() }}</div>`,
83+
})
84+
export class WeatherCardComponent {
85+
location = input.required<string>();
86+
temperatureF = input.required<number>();
87+
conditions = input.required<string>();
88+
}
89+
```
90+
91+
Under `strict: true`, the typed `view`/`ask` overloads report a compile error at the `view(...)`/`ask(...)` call site if the component's inputs diverge from the schema — mismatches become build errors, not silent runtime failures.
92+
93+
## Typed handler args with `ToolArgs`
94+
95+
For `action()`, the handler argument type is inferred from the schema automatically. When you want to name that type — e.g. to write the handler separately — use `ToolArgs<typeof schema>` (an alias of the schema's inferred output):
96+
97+
```typescript
98+
import { action, type ToolArgs } from '@threadplane/chat';
99+
import { z } from 'zod/v4';
100+
101+
const moveSchema = z.object({ fromDay: z.number(), toDay: z.number() });
102+
103+
async function moveStop(args: ToolArgs<typeof moveSchema>) {
104+
// args is { fromDay: number; toDay: number }
105+
return reorder(args.fromDay, args.toDay);
106+
}
107+
108+
const move = action('Move a stop to another day.', moveSchema, moveStop);
109+
```
110+
111+
## Typed agent state
112+
113+
Tool handlers and components often read agent state. Pair the registry with a typed `AgentRef` so `agent.state()` / `agent.value()` carry your state shape instead of `Record<string, unknown>` — see [Typed state via AgentRef](/docs/langgraph/api/provide-agent#typed-state-via-agentref):
114+
115+
```typescript
116+
import { createAgentRef } from '@threadplane/chat';
117+
import { injectAgent } from '@threadplane/langgraph';
118+
119+
interface ClientToolsState { messages: unknown[]; client_tools: unknown[]; }
120+
export const CLIENT_TOOLS = createAgentRef<ClientToolsState>('client-tools');
121+
122+
// component
123+
protected readonly agent = injectAgent(CLIENT_TOOLS); // LangGraphAgent<ClientToolsState>
124+
```
125+
126+
## API reference
127+
128+
```typescript
129+
import {
130+
tools, action, view, ask,
131+
type ViewProps, type ToolArgs,
132+
type ClientToolDef, type ClientToolRegistry,
133+
} from '@threadplane/chat';
134+
```
135+
136+
| Export | Purpose |
137+
|---|---|
138+
| `action(description, schema, handler)` | Declare a function tool (handler return → result) |
139+
| `view(description, schema, component)` | Declare a render-only component tool (auto-acknowledged) |
140+
| `ask(description, schema, component)` | Declare an interactive component tool (emitted value → result) |
141+
| `tools(map)` | Freeze a name-keyed registry for `[clientTools]` |
142+
| `ViewProps<S>` | Component input prop bag inferred from a schema |
143+
| `ToolArgs<S>` | Handler argument type inferred from a schema |
144+
| `ClientToolDef` / `ClientToolRegistry` | The tool-definition union and frozen-registry types |
145+
146+
## What's next
147+
148+
<CardGroup cols={3}>
149+
<Card
150+
title="Generative UI"
151+
icon="layout"
152+
href="/docs/chat/guides/generative-ui"
153+
>
154+
Render agent-emitted UI specs with `[views]`, distinct from model-called tools.
155+
</Card>
156+
<Card
157+
title="provideAgent()"
158+
icon="code"
159+
href="/docs/langgraph/api/provide-agent"
160+
>
161+
Typed agent state via `AgentRef` and client-tuning options.
162+
</Card>
163+
<Card
164+
title="Error Handling"
165+
icon="alert-triangle"
166+
href="/docs/chat/guides/error-handling"
167+
>
168+
Classified errors and Retry when a tool call or run fails.
169+
</Card>
170+
</CardGroup>

0 commit comments

Comments
 (0)