@assistant-ui/mcp-docs-server 0.1.34 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/organized/code-examples/waterfall.md +4 -4
- package/.docs/organized/code-examples/with-a2a.md +5 -5
- package/.docs/organized/code-examples/with-ag-ui.md +6 -6
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +7 -7
- package/.docs/organized/code-examples/with-artifacts.md +7 -7
- package/.docs/organized/code-examples/with-assistant-transport.md +73 -57
- package/.docs/organized/code-examples/with-browser-extension.md +7 -7
- package/.docs/organized/code-examples/with-chain-of-thought.md +44 -93
- package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
- package/.docs/organized/code-examples/with-cloud.md +7 -7
- package/.docs/organized/code-examples/with-custom-thread-list.md +7 -7
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +9 -9
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +9 -9
- package/.docs/organized/code-examples/with-eve.md +343 -0
- package/.docs/organized/code-examples/with-expo.md +943 -940
- package/.docs/organized/code-examples/with-external-store.md +5 -5
- package/.docs/organized/code-examples/with-ffmpeg.md +8 -11
- package/.docs/organized/code-examples/with-generative-ui.md +33 -309
- package/.docs/organized/code-examples/with-google-adk.md +5 -5
- package/.docs/organized/code-examples/with-heat-graph.md +4 -4
- package/.docs/organized/code-examples/with-image-generation.md +7 -7
- package/.docs/organized/code-examples/with-interactables.md +169 -341
- package/.docs/organized/code-examples/with-langchain.md +7 -7
- package/.docs/organized/code-examples/with-langgraph.md +23 -160
- package/.docs/organized/code-examples/with-livekit.md +10 -10
- package/.docs/organized/code-examples/with-mcp.md +7 -7
- package/.docs/organized/code-examples/with-opencode.md +106 -580
- package/.docs/organized/code-examples/with-pi.md +2046 -0
- package/.docs/organized/code-examples/with-react-hook-form.md +16 -9
- package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
- package/.docs/organized/code-examples/with-react-ink.md +29 -17
- package/.docs/organized/code-examples/with-react-router.md +12 -12
- package/.docs/organized/code-examples/with-resumable-stream.md +8 -8
- package/.docs/organized/code-examples/with-store.md +14 -10
- package/.docs/organized/code-examples/with-tanstack.md +21 -7
- package/.docs/organized/code-examples/with-tap-runtime.md +5 -5
- package/.docs/organized/code-examples/with-virtualized-thread.md +676 -0
- package/.docs/raw/docs/(docs)/architecture.mdx +13 -12
- package/.docs/raw/docs/(docs)/cli.mdx +4 -2
- package/.docs/raw/docs/(docs)/devtools.mdx +25 -2
- package/.docs/raw/docs/(docs)/index.mdx +5 -2
- package/.docs/raw/docs/(docs)/installation.mdx +5 -2
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +16 -16
- package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +73 -42
- package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +3 -20
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +234 -123
- package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +51 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +70 -38
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +30 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +7 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +26 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +15 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +103 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +16 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +56 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +12 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +12 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +14 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +57 -1
- package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +6 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/interactables-legacy.mdx +55 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/interactables.mdx +151 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +17 -38
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +20 -27
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +40 -25
- package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
- package/.docs/raw/docs/guides/headless-composer-input.mdx +113 -0
- package/.docs/raw/docs/guides/index.mdx +3 -0
- package/.docs/raw/docs/guides/input-history.mdx +55 -0
- package/.docs/raw/docs/guides/latex.mdx +28 -22
- package/.docs/raw/docs/guides/mentions.mdx +32 -7
- package/.docs/raw/docs/guides/slash-commands.mdx +3 -6
- package/.docs/raw/docs/guides/speech.mdx +5 -7
- package/.docs/raw/docs/guides/virtualization.mdx +133 -0
- package/.docs/raw/docs/guides/voice.mdx +3 -2
- package/.docs/raw/docs/ink/hooks.mdx +2 -2
- package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +4 -12
- package/.docs/raw/docs/integrations/auth/better-auth.mdx +7 -10
- package/.docs/raw/docs/integrations/auth/clerk.mdx +3 -9
- package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -8
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +8 -5
- package/.docs/raw/docs/integrations/index.mdx +5 -12
- package/.docs/raw/docs/integrations/observability/helicone.mdx +3 -4
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +3 -5
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -2
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +4 -3
- package/.docs/raw/docs/primitives/composer.mdx +8 -0
- package/.docs/raw/docs/primitives/thread.mdx +24 -0
- package/.docs/raw/docs/react-native/hooks.mdx +1 -1
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +22 -3
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +54 -0
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +108 -4
- package/.docs/raw/docs/runtimes/eve/overview.mdx +100 -0
- package/.docs/raw/docs/runtimes/eve/quickstart.mdx +143 -0
- package/.docs/raw/docs/runtimes/langchain.mdx +386 -18
- package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +1 -1
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -6
- package/.docs/raw/docs/tools/defining-tools.mdx +4 -0
- package/.docs/raw/docs/tools/interactables-legacy.mdx +410 -0
- package/.docs/raw/docs/tools/interactables.mdx +892 -223
- package/.docs/raw/docs/tools/mcp.mdx +4 -4
- package/.docs/raw/docs/tools/multi-agent.mdx +5 -0
- package/.docs/raw/docs/tools/tool-ui.mdx +37 -1
- package/.docs/raw/docs/ui/composer-trigger-popover.mdx +2 -0
- package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
- package/.docs/raw/docs/ui/file.mdx +1 -1
- package/.docs/raw/docs/ui/model-selector.mdx +219 -52
- package/.docs/raw/docs/ui/number-roll.mdx +154 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +38 -0
- package/.docs/raw/docs/ui/reasoning.mdx +3 -3
- package/.docs/raw/docs/ui/streamdown.mdx +2 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
- package/.docs/raw/docs/ui/thread.mdx +52 -0
- package/.docs/raw/docs/ui/tool-fallback.mdx +2 -2
- package/.docs/raw/docs/utilities/react-o11y.mdx +92 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +14 -6
- package/src/tools/tests/mcp-protocol.test.ts +9 -0
|
@@ -15,18 +15,11 @@ Integrations are wiring guides for using third-party services with assistant-ui,
|
|
|
15
15
|
|
|
16
16
|
## Where integrations slot in
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
frameworks │ │ proxies
|
|
24
|
-
(e.g. │ │ (e.g. Helicone,
|
|
25
|
-
Mastra) │ │ Langfuse)
|
|
26
|
-
▼ │
|
|
27
|
-
run on the server, │
|
|
28
|
-
then forward calls ──────┘
|
|
29
|
-
to the provider
|
|
18
|
+
```mermaid
|
|
19
|
+
flowchart LR
|
|
20
|
+
client["client"] --> route["your API route"] --> provider["LLM provider"]
|
|
21
|
+
route -->|"agent frameworks (e.g. Mastra)"| server["run on the server,<br/>then forward calls"]
|
|
22
|
+
server -->|"observability proxies (e.g. Helicone, Langfuse)"| provider
|
|
30
23
|
```
|
|
31
24
|
|
|
32
25
|
Integrations live on the server. **Agent frameworks** like Mastra take over the API route. **Gateways** swap the upstream provider URL. **Observability** logs or traces every call. **Auth** gates the route and scopes per-user data. **Persistence** and **attachments** are adapter recipes for storing chat data outside the default in-memory path.
|
|
@@ -11,10 +11,9 @@ Helicone is independent of which assistant-ui runtime you use. It slots in at th
|
|
|
11
11
|
|
|
12
12
|
## How it works
|
|
13
13
|
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
└─ logs request, response, tokens, cost
|
|
14
|
+
```mermaid
|
|
15
|
+
flowchart LR
|
|
16
|
+
server["your server"] --> proxy["Helicone proxy<br/>(logs request, response, tokens, cost)"] --> provider["OpenAI / Anthropic / etc."]
|
|
18
17
|
```
|
|
19
18
|
|
|
20
19
|
Calls pass through Helicone's edge before reaching the upstream provider. The proxy is transparent: response shape and streaming behavior are unchanged, you just gain a dashboard of every call.
|
|
@@ -13,11 +13,9 @@ Pick Langfuse when you want to see the agent's full call tree inside a single tu
|
|
|
13
13
|
|
|
14
14
|
## How it works
|
|
15
15
|
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
▼
|
|
20
|
-
OpenTelemetry SDK ──► LangfuseSpanProcessor ──► Langfuse
|
|
16
|
+
```mermaid
|
|
17
|
+
flowchart LR
|
|
18
|
+
route["your route"] --> stream["AI SDK streamText<br/>(experimental_telemetry)"] --> otel["OpenTelemetry SDK"] --> proc["LangfuseSpanProcessor"] --> langfuse["Langfuse"]
|
|
21
19
|
```
|
|
22
20
|
|
|
23
21
|
Langfuse subscribes to OpenTelemetry spans the AI SDK already emits when telemetry is enabled. No proxy, no wrapping; the SDK ships spans and Langfuse renders them.
|
|
@@ -15,8 +15,9 @@ This page covers the **AI SDK** path. If you use [`@assistant-ui/react-langgraph
|
|
|
15
15
|
|
|
16
16
|
LangSmith provides a wrapper around the `ai` namespace. You call `wrapAISDK(ai)`, get back the same exports (`generateText`, `streamText`, `generateObject`, `streamObject`), and use those in place of the originals. Every call is then traced.
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
|
|
18
|
+
```mermaid
|
|
19
|
+
flowchart LR
|
|
20
|
+
route["your route"] --> stream["wrapped streamText"] --> client["LangSmith client"] --> langsmith["LangSmith"]
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
## Setup
|
|
@@ -18,9 +18,10 @@ If you're rolling your own auth, replace `auth()` calls with whatever your stack
|
|
|
18
18
|
|
|
19
19
|
## How it works
|
|
20
20
|
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
```mermaid
|
|
22
|
+
flowchart LR
|
|
23
|
+
client["client"] --> threads["/api/threads/*<br/>(RemoteThreadListAdapter)"] --> tt["threads table"]
|
|
24
|
+
client --> messages["/api/messages/*<br/>(ThreadHistoryAdapter)"] --> mt["messages table"]
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
Two adapters, two tables:
|
|
@@ -103,6 +103,14 @@ import { ComposerPrimitive } from "@assistant-ui/react";
|
|
|
103
103
|
|
|
104
104
|
The primitive's behavior (keyboard handling, disabled state, form submission) is merged onto your element. Your styles, your component, primitive wiring.
|
|
105
105
|
|
|
106
|
+
<Callout type="info">
|
|
107
|
+
Own the input DOM entirely, such as a `contentEditable` surface or editor
|
|
108
|
+
library that cannot be expressed through `asChild` or `render`? See
|
|
109
|
+
[Headless Composer Input](/docs/guides/headless-composer-input) for the
|
|
110
|
+
unstable hook that supplies composer text and send gating without
|
|
111
|
+
`ComposerPrimitive.Input`.
|
|
112
|
+
</Callout>
|
|
113
|
+
|
|
106
114
|
### Unstable Trigger Popovers
|
|
107
115
|
|
|
108
116
|
Composer includes an unstable **trigger popover** system for character-triggered popovers (e.g. `@` for mentions, `/` for slash commands). Multiple triggers coexist under a single `TriggerPopoverRoot`.
|
|
@@ -319,6 +319,30 @@ Renders a single message at a specific index in the thread.
|
|
|
319
319
|
|
|
320
320
|
<PrimitivesTypeTable type="ThreadPrimitiveMessageByIndexProps" parameters={ThreadPrimitiveDocs.MessageByIndex.props} />
|
|
321
321
|
|
|
322
|
+
### Unstable_MessageById
|
|
323
|
+
|
|
324
|
+
Renders a single message by id with the same `components` surface as
|
|
325
|
+
`MessageByIndex`. Pair it with `unstable_useThreadMessageIds` for virtualized or
|
|
326
|
+
custom message lists that should stay attached to messages across reordering and
|
|
327
|
+
windowing. Unknown ids render `null`.
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
const messageIds = unstable_useThreadMessageIds();
|
|
331
|
+
|
|
332
|
+
messageIds.map((messageId) => (
|
|
333
|
+
<ThreadPrimitive.Unstable_MessageById
|
|
334
|
+
key={messageId}
|
|
335
|
+
messageId={messageId}
|
|
336
|
+
components={{ Message: MyMessage }}
|
|
337
|
+
/>
|
|
338
|
+
));
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
<Callout type="warn">
|
|
342
|
+
`unstable_useThreadMessageIds` and `ThreadPrimitive.Unstable_MessageById` are
|
|
343
|
+
experimental and may change in any release.
|
|
344
|
+
</Callout>
|
|
345
|
+
|
|
322
346
|
### ScrollToBottom
|
|
323
347
|
|
|
324
348
|
Scrolls the viewport to the bottom. Automatically disabled when already at the bottom. Renders a `<button>` element unless `asChild` is set.
|
|
@@ -80,7 +80,7 @@ const runtime = useLocalRuntime(chatModel, {
|
|
|
80
80
|
| `maxSteps` | `number` | Maximum tool call steps per run |
|
|
81
81
|
| `cloud` | `AssistantCloud` | Optional cloud instance for persistence |
|
|
82
82
|
| `adapters` | `object` | Optional adapter overrides (see below) |
|
|
83
|
-
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run
|
|
83
|
+
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run until a result is added via `addResult` |
|
|
84
84
|
|
|
85
85
|
The `adapters` option accepts the following fields (all optional):
|
|
86
86
|
|
|
@@ -66,9 +66,9 @@ messages are gone on the next page load.
|
|
|
66
66
|
`showThinking: false`, so imported reasoning messages are dropped at conversion
|
|
67
67
|
time, the same way a live run never stores them.
|
|
68
68
|
|
|
69
|
-
`fromAgUiMessages` reconstructs the text, reasoning, and tool calls of each
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
`fromAgUiMessages` reconstructs the text, reasoning, and tool calls of each message. Multimodal user input (`image`, `audio`, `video`, and `document` parts, as well as legacy `binary` parts) is restored as attachments on the user message, so a backend that persists multimodal messages shows them again on reload and re-sends them on the next run. Legacy `binary` parts that only reference a file id are not restored.
|
|
70
|
+
|
|
71
|
+
An assistant message whose tool call has no matching tool result is reconstructed with `requires-action` status, the same status the runtime derives for a pending tool call, so a reloaded human-in-the-loop call (for example an `ask_user` tool) is actionable rather than stuck. This matches how every other external-store runtime surfaces a pending tool call on reload. The AG-UI wire snapshot carries no run outcome, so a tool call that a successful run intentionally left without a result is also surfaced as actionable.
|
|
72
72
|
|
|
73
73
|
## Thread list (experimental)
|
|
74
74
|
|
|
@@ -133,6 +133,25 @@ await runtime.unstable_submitInterruptResponses(
|
|
|
133
133
|
);
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
### Steering away from an interrupt
|
|
137
|
+
|
|
138
|
+
When the user ignores the interrupt UI and just sends a new message, use the `useAgUiSteerAway` hook. Every open interrupt defaults to `status: "cancelled"`, the new message is appended, and the run resumes with `resume: ResumeEntry[]` on the wire. With no pending interrupts it behaves like a normal append.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
const steerAway = useAgUiSteerAway();
|
|
142
|
+
|
|
143
|
+
// the user typed a new message instead of answering the interrupt
|
|
144
|
+
await steerAway("actually, let's do something else");
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The message accepts a plain string or a partial `AppendMessage` (the parent defaults to the current head, which is the interrupted assistant message). Pass `responses` to override the status of specific interrupts; the rest still default to cancelled.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
await steerAway("continue without the file", [
|
|
151
|
+
{ interruptId: "tool-1", status: "resolved", payload: { approved: true } },
|
|
152
|
+
]);
|
|
153
|
+
```
|
|
154
|
+
|
|
136
155
|
## Supported events
|
|
137
156
|
|
|
138
157
|
The runtime parses the AG-UI event stream and maps each event type to assistant-ui state.
|
|
@@ -30,7 +30,7 @@ A non-exhaustive list of `unstable_` exports surfaced in the runtime docs.
|
|
|
30
30
|
| API | Package | Notes |
|
|
31
31
|
| --- | --- | --- |
|
|
32
32
|
| `unstable_createMessageConverter` | `@assistant-ui/react` | Message-format converter used by AssistantTransport and DataStream. |
|
|
33
|
-
| `unstable_humanToolNames` | `@assistant-ui/react` | Tool names that pause
|
|
33
|
+
| `unstable_humanToolNames` | `@assistant-ui/react` | Tool names that pause the run until a result is added via `addResult`. Only available on LocalRuntime; not supported in DataStream. |
|
|
34
34
|
| `unstable_threadListAdapter` | `@assistant-ui/react-langgraph` | LangGraph thread-list adapter slot on `useLangGraphRuntime`. |
|
|
35
35
|
| `unstable_createLangGraphStream` | `@assistant-ui/react-langgraph` | End-to-end cancellation primitive. |
|
|
36
36
|
| `unstable_Provider` | Various adapters | Thread-scoped provider on `RemoteThreadListAdapter`. Must render children synchronously. |
|
|
@@ -206,7 +206,7 @@ const runtime = useDataStreamRuntime({
|
|
|
206
206
|
## Tool integration
|
|
207
207
|
|
|
208
208
|
<Callout type="warn">
|
|
209
|
-
Human-in-the-loop tools (`unstable_humanToolNames`, `human()` interrupts) are not supported in the data stream runtime. Use [`LocalRuntime`](/docs/runtimes/custom/local-runtime) directly if you need
|
|
209
|
+
Human-in-the-loop tools (`unstable_humanToolNames`, `human()` interrupts) are not supported in the data stream runtime. Use [`LocalRuntime`](/docs/runtimes/custom/local-runtime#human-in-the-loop-tools) directly if you need them.
|
|
210
210
|
</Callout>
|
|
211
211
|
|
|
212
212
|
### Frontend tools
|
|
@@ -299,6 +299,54 @@ runtime.thread.import(repo);
|
|
|
299
299
|
|
|
300
300
|
Each message must have an explicit `id` and `parentId`; messages with the same `parentId` create branches. Parents must appear before children in the array.
|
|
301
301
|
|
|
302
|
+
### Exporting a snapshot
|
|
303
|
+
|
|
304
|
+
`thread.import()` has a counterpart, `thread.export()`, which captures the current thread (including its full branch tree) as a serializable `ExportedMessageRepository`. Use it to persist a conversation and re-import it later:
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
// capture the current thread as a serializable snapshot
|
|
308
|
+
const repo = runtime.thread.export();
|
|
309
|
+
await saveToBackend(JSON.stringify(repo));
|
|
310
|
+
|
|
311
|
+
// later, restore it into a runtime
|
|
312
|
+
runtime.thread.import(repo);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The exported shape round-trips through `thread.import()` directly, so the same value is both your persistence format and what you load back.
|
|
316
|
+
|
|
317
|
+
### Persisting branch selection
|
|
318
|
+
|
|
319
|
+
If you store the full branch tree outside assistant-ui, persist the selected branch head too and pass it back as `messageRepository.headId`. `setMessages` still performs the branch switch; `unstable_onBranchChange` is an additional signal that fires after an explicit `switchToBranch` action, such as a BranchPicker click.
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
const runtime = useExternalStoreRuntime({
|
|
323
|
+
messageRepository: {
|
|
324
|
+
messages: storedMessages,
|
|
325
|
+
headId: selectedHeadId,
|
|
326
|
+
},
|
|
327
|
+
setMessages: (messages) => {
|
|
328
|
+
setVisibleMessages(messages);
|
|
329
|
+
},
|
|
330
|
+
unstable_onBranchChange: ({ headId, visibleMessageIds }) => {
|
|
331
|
+
saveSelectedBranch({
|
|
332
|
+
headId,
|
|
333
|
+
visibleMessageIds,
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
onNew,
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`headId` is the canonical persisted head of the visible branch. Optimistic or transient message ids are not surfaced there. `visibleMessageIds` is the currently visible path in order, which can include an optimistic leaf while `headId` points to its persisted ancestor.
|
|
341
|
+
|
|
342
|
+
The callback only fires for explicit branch switches, and consecutive switches that resolve to the same canonical head are de-duped. It does not fire on adapter resync, `messageRepository` reset, append, edit/regenerate, content-only updates, or while the thread is running.
|
|
343
|
+
|
|
344
|
+
<Callout type="warn">
|
|
345
|
+
`unstable_onBranchChange` is under active development and may change without
|
|
346
|
+
notice. It complements `setMessages`; it does not enable branch switching by
|
|
347
|
+
itself.
|
|
348
|
+
</Callout>
|
|
349
|
+
|
|
302
350
|
## Tool calling
|
|
303
351
|
|
|
304
352
|
Handle tool results by updating the matching tool-call entry:
|
|
@@ -717,6 +765,12 @@ useExternalStoreRuntime({
|
|
|
717
765
|
type: "(messages: readonly T[]) => void",
|
|
718
766
|
description: "Update messages (required for branch switching).",
|
|
719
767
|
},
|
|
768
|
+
{
|
|
769
|
+
name: "unstable_onBranchChange",
|
|
770
|
+
type: "(event: ExternalStoreBranchChange) => void",
|
|
771
|
+
description:
|
|
772
|
+
"Called after an explicit branch switch with the canonical persisted head id and visible message path. Complements setMessages and is unstable.",
|
|
773
|
+
},
|
|
720
774
|
{
|
|
721
775
|
name: "onEdit",
|
|
722
776
|
type: "(message: AppendMessage) => Promise<void>",
|
|
@@ -457,18 +457,122 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
|
457
457
|
|
|
458
458
|
See the [tools guide](/docs/tools/defining-tools) for advanced patterns.
|
|
459
459
|
|
|
460
|
-
### Human-in-the-loop
|
|
460
|
+
### Human-in-the-loop tools
|
|
461
461
|
|
|
462
|
-
|
|
462
|
+
Tools listed in `unstable_humanToolNames` are not executed by code. The run pauses on the tool call and the user supplies the result through the tool UI:
|
|
463
463
|
|
|
464
464
|
```ts
|
|
465
465
|
const runtime = useLocalRuntime(MyModelAdapter, {
|
|
466
|
-
unstable_humanToolNames: ["
|
|
466
|
+
unstable_humanToolNames: ["send_email"],
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
The pause is driven by the message status your adapter returns; `LocalRuntime` never sets it for you. When the model requests a human tool call, end the run with `status: { type: "requires-action", reason: "tool-calls" }`. Without that status, the runtime marks the message complete and nothing waits. Only return it while a listed tool call is missing its result: unresolved tool calls that are not listed do not hold the run, so the runtime would invoke your adapter again immediately.
|
|
471
|
+
|
|
472
|
+
```tsx
|
|
473
|
+
const MyModelAdapter: ChatModelAdapter = {
|
|
474
|
+
async run({ messages, abortSignal, unstable_getMessage }) {
|
|
475
|
+
const toolResults = unstable_getMessage().content.flatMap((part) =>
|
|
476
|
+
part.type === "tool-call" && part.result !== undefined
|
|
477
|
+
? [{ toolCallId: part.toolCallId, result: part.result }]
|
|
478
|
+
: [],
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
const result = await fetch("<YOUR_API_ENDPOINT>", {
|
|
482
|
+
method: "POST",
|
|
483
|
+
headers: { "Content-Type": "application/json" },
|
|
484
|
+
body: JSON.stringify({ messages, toolResults }),
|
|
485
|
+
signal: abortSignal,
|
|
486
|
+
});
|
|
487
|
+
const data = await result.json();
|
|
488
|
+
|
|
489
|
+
if (data.toolCall) {
|
|
490
|
+
return {
|
|
491
|
+
content: [
|
|
492
|
+
{
|
|
493
|
+
type: "tool-call",
|
|
494
|
+
toolCallId: data.toolCall.id,
|
|
495
|
+
toolName: data.toolCall.name,
|
|
496
|
+
args: data.toolCall.args,
|
|
497
|
+
argsText: JSON.stringify(data.toolCall.args),
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return { content: [{ type: "text", text: data.text }] };
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
The full loop:
|
|
510
|
+
|
|
511
|
+
1. **The run pauses.** While a listed tool call has no result, the runtime stops invoking your adapter. The unresolved tool call part reports `status.type === "requires-action"` to its renderer.
|
|
512
|
+
2. **The user responds.** The tool UI completes the call with `addResult(...)`. The stock [`ToolFallback`](/docs/ui/tool-fallback) component handles this out of the box: in the requires-action state it shows Allow and Deny buttons that record the decision as the tool result.
|
|
513
|
+
3. **The run resumes.** Once every listed tool call has a result, the runtime invokes your adapter again. The resumed call receives the same `messages` array as before (it ends at the user message; the in-progress assistant message is not part of it), so read the recorded results from `unstable_getMessage().content` as shown above. Content returned by the resumed call is appended to the same assistant message.
|
|
514
|
+
|
|
515
|
+
For a custom confirmation UI, register a human tool whose `render` completes the call with `addResult`. The shape of the result payload is yours to define; the adapter receives it verbatim and translates it for your backend:
|
|
516
|
+
|
|
517
|
+
```tsx
|
|
518
|
+
const toolkit = defineToolkit({
|
|
519
|
+
send_email: {
|
|
520
|
+
type: "human",
|
|
521
|
+
description: "Send an email after the user confirms",
|
|
522
|
+
parameters: z.object({ to: z.string(), subject: z.string() }),
|
|
523
|
+
render: ({ args, result, addResult }) => {
|
|
524
|
+
if (result) {
|
|
525
|
+
return <p>{result.approved ? "Sent" : `Cancelled: ${result.reason}`}</p>;
|
|
526
|
+
}
|
|
527
|
+
return (
|
|
528
|
+
<div>
|
|
529
|
+
<p>
|
|
530
|
+
Send "{args.subject}" to {args.to}?
|
|
531
|
+
</p>
|
|
532
|
+
<button onClick={() => addResult({ approved: true })}>Allow</button>
|
|
533
|
+
<button
|
|
534
|
+
onClick={() =>
|
|
535
|
+
addResult({ approved: false, reason: "User declined" })
|
|
536
|
+
}
|
|
537
|
+
>
|
|
538
|
+
Deny
|
|
539
|
+
</button>
|
|
540
|
+
</div>
|
|
541
|
+
);
|
|
542
|
+
},
|
|
543
|
+
},
|
|
467
544
|
});
|
|
468
545
|
```
|
|
469
546
|
|
|
470
547
|
`unstable_humanToolNames` is unstable; see [stability](/docs/runtimes/concepts/stability).
|
|
471
548
|
|
|
549
|
+
### Approval gates
|
|
550
|
+
|
|
551
|
+
The [server-side approval gate](/docs/tools/tool-ui#server-side-approval-gates) is also supported on `LocalRuntime`, for actions your backend executes after the user authorizes them. Where a human tool asks the user to supply the tool result, an approval gate asks the user to allow or block an action the adapter performs. Emit `approval: { id }` on the tool call part and end the run with the same `requires-action` status:
|
|
552
|
+
|
|
553
|
+
```tsx
|
|
554
|
+
return {
|
|
555
|
+
content: [
|
|
556
|
+
{
|
|
557
|
+
type: "tool-call",
|
|
558
|
+
toolCallId: data.toolCall.id,
|
|
559
|
+
toolName: data.toolCall.name,
|
|
560
|
+
args: data.toolCall.args,
|
|
561
|
+
argsText: JSON.stringify(data.toolCall.args),
|
|
562
|
+
approval: { id: data.toolCall.id },
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
566
|
+
};
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
A tool call with a pending approval pauses the run, whether or not the tool is listed in `unstable_humanToolNames`. Always emit gates in the pending state (`approval: { id }` with no `approved` field); a part that arrives already decided is treated as resolved and the runtime invokes the adapter again immediately. The stock [`ToolFallback`](/docs/ui/tool-fallback) Allow and Deny buttons, or a custom renderer calling `respondToApproval({ approved, reason? })`, record the decision:
|
|
570
|
+
|
|
571
|
+
- **Deny** sets `approval.approved: false` and synthesizes an error result (`{ error: reason || "Tool approval denied" }` with `isError: true`), so the model sees the denial.
|
|
572
|
+
- **Allow** sets `approval.approved: true` and leaves the result empty; performing the action is your adapter's job.
|
|
573
|
+
|
|
574
|
+
Once every pending approval on the message is decided and every listed human tool has a result, the runtime invokes your adapter again. Read the decisions from `unstable_getMessage().content`, perform the approved actions, and return the follow-up response. A tool call that carries an approval is owned by the gate: it does not additionally require a result, even when its name is listed in `unstable_humanToolNames`.
|
|
575
|
+
|
|
472
576
|
## Resuming a run
|
|
473
577
|
|
|
474
578
|
`resumeRun` reconnects to an in-progress assistant run. Useful for page refresh, network reconnect, tab backgrounding, or thread switching when the backend is still generating.
|
|
@@ -743,7 +847,7 @@ const CustomAPIAdapter: ChatModelAdapter = {
|
|
|
743
847
|
name: "unstable_humanToolNames",
|
|
744
848
|
type: "string[]",
|
|
745
849
|
description:
|
|
746
|
-
"Tool names that
|
|
850
|
+
"Tool names that pause the run until the user supplies a result via addResult (unstable).",
|
|
747
851
|
},
|
|
748
852
|
]}
|
|
749
853
|
/>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Eve Runtime
|
|
3
|
+
description: Connect an Eve agent to assistant-ui with useEveAgentRuntime, eve/next, durable sessions, streaming messages, and human-in-the-loop approvals.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
import { VercelIcon } from "@/components/icons/vercel";
|
|
7
|
+
|
|
8
|
+
`@assistant-ui/eve` integrates assistant-ui with [Eve](https://eve.dev/), Vercel's filesystem-first framework for durable agents. It wraps Eve's `useEveAgent` hook and exposes it as an assistant-ui `ExternalStoreRuntime`, so Eve owns the session stream while assistant-ui renders messages, reasoning, dynamic tool calls, and approval requests.
|
|
9
|
+
|
|
10
|
+
## When to use it
|
|
11
|
+
|
|
12
|
+
Pick the Eve runtime when:
|
|
13
|
+
|
|
14
|
+
- You want your agent implementation to live under `agent/` and be served by `eve/next`.
|
|
15
|
+
- You want Eve sessions, continuation tokens, NDJSON streaming, and local development tooling.
|
|
16
|
+
- You want assistant-ui to render Eve messages and human-in-the-loop tool approvals without writing a custom runtime adapter.
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
The Next.js app mounts Eve with `withEve()` and assistant-ui's registry transform with `withAui()`:
|
|
21
|
+
|
|
22
|
+
```ts title="next.config.ts"
|
|
23
|
+
import { withAui } from "@assistant-ui/next";
|
|
24
|
+
import type { NextConfig } from "next";
|
|
25
|
+
import { withEve } from "eve/next";
|
|
26
|
+
|
|
27
|
+
const nextConfig: NextConfig = {};
|
|
28
|
+
|
|
29
|
+
export default withEve(withAui(nextConfig));
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
On the client, `useEveAgentRuntime()` calls Eve's React hook and converts Eve message parts into assistant-ui thread messages:
|
|
33
|
+
|
|
34
|
+
```tsx title="app/page.tsx"
|
|
35
|
+
"use client";
|
|
36
|
+
|
|
37
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
38
|
+
import { useEveAgentRuntime } from "@assistant-ui/eve";
|
|
39
|
+
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
40
|
+
|
|
41
|
+
export default function Home() {
|
|
42
|
+
const runtime = useEveAgentRuntime();
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
46
|
+
<Thread />
|
|
47
|
+
</AssistantRuntimeProvider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Requirements
|
|
53
|
+
|
|
54
|
+
- Node.js 24 or higher.
|
|
55
|
+
- React 18 or 19.
|
|
56
|
+
- An Eve app mounted with `eve/next`.
|
|
57
|
+
- A model credential for the model configured in `agent/agent.ts`.
|
|
58
|
+
|
|
59
|
+
Eve's default gateway model ids route through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). Use `AI_GATEWAY_API_KEY` or Vercel OIDC, or configure a direct provider `LanguageModel`.
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
<PlatformTabs>
|
|
64
|
+
<Tab value="React">
|
|
65
|
+
|
|
66
|
+
<InstallCommand npm={["@assistant-ui/react", "@assistant-ui/eve", "eve"]} />
|
|
67
|
+
|
|
68
|
+
</Tab>
|
|
69
|
+
<Tab value="React Native">
|
|
70
|
+
|
|
71
|
+
Eve's browser hook talks to the Eve HTTP channel. There is not a React Native Eve runtime package yet.
|
|
72
|
+
|
|
73
|
+
</Tab>
|
|
74
|
+
<Tab value="React Ink">
|
|
75
|
+
|
|
76
|
+
Use Eve's terminal UI directly for command-line agent sessions. There is not an Ink Eve runtime package yet.
|
|
77
|
+
|
|
78
|
+
</Tab>
|
|
79
|
+
</PlatformTabs>
|
|
80
|
+
|
|
81
|
+
## Auth note
|
|
82
|
+
|
|
83
|
+
Eve's built-in `eve` channel accepts localhost during development and trusted Vercel OIDC callers. It does not automatically admit browser users in production. Before deploying a public app, add `agent/channels/eve.ts` and wire the channel to your application auth.
|
|
84
|
+
|
|
85
|
+
## Next
|
|
86
|
+
|
|
87
|
+
<Cards>
|
|
88
|
+
<Card
|
|
89
|
+
icon={<VercelIcon width={20} height={20} />}
|
|
90
|
+
title="Quickstart"
|
|
91
|
+
description="Scaffold the Eve template or add Eve to an existing assistant-ui app."
|
|
92
|
+
href="/docs/runtimes/eve/quickstart"
|
|
93
|
+
/>
|
|
94
|
+
<Card
|
|
95
|
+
icon={<VercelIcon width={20} height={20} />}
|
|
96
|
+
title="Eve channel"
|
|
97
|
+
description="Routes, auth, session creation, and stream events in the default Eve HTTP channel."
|
|
98
|
+
href="https://eve.dev/docs/channels/eve"
|
|
99
|
+
/>
|
|
100
|
+
</Cards>
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quickstart
|
|
3
|
+
description: From-template and manual setup paths to a working Eve agent chat in assistant-ui.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Two paths to a running Eve-powered assistant-ui app. The template is fastest; the manual path is what you adapt when integrating into an existing Next.js project.
|
|
7
|
+
|
|
8
|
+
## From the template
|
|
9
|
+
|
|
10
|
+
<PlatformTabs>
|
|
11
|
+
<Tab value="React">
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx create-assistant-ui@latest -t eve my-app
|
|
15
|
+
cd my-app
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Set a model credential:
|
|
19
|
+
|
|
20
|
+
```sh title=".env.local"
|
|
21
|
+
AI_GATEWAY_API_KEY=your-api-key
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm run dev
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Open http://localhost:3000 and send a message. The template includes:
|
|
29
|
+
|
|
30
|
+
- `agent/agent.ts` for Eve runtime config.
|
|
31
|
+
- `agent/instructions.md` for the always-on system prompt.
|
|
32
|
+
- `next.config.ts` with `withEve(withAui(nextConfig))`.
|
|
33
|
+
- `app/page.tsx` with `useEveAgentRuntime()`.
|
|
34
|
+
|
|
35
|
+
</Tab>
|
|
36
|
+
<Tab value="React Native">
|
|
37
|
+
|
|
38
|
+
There is not a React Native Eve template yet.
|
|
39
|
+
|
|
40
|
+
</Tab>
|
|
41
|
+
<Tab value="React Ink">
|
|
42
|
+
|
|
43
|
+
Use Eve's terminal UI directly for command-line agent sessions.
|
|
44
|
+
|
|
45
|
+
</Tab>
|
|
46
|
+
</PlatformTabs>
|
|
47
|
+
|
|
48
|
+
## Manual setup in an existing app
|
|
49
|
+
|
|
50
|
+
<Steps>
|
|
51
|
+
<Step>
|
|
52
|
+
|
|
53
|
+
### Install dependencies
|
|
54
|
+
|
|
55
|
+
<InstallCommand npm={["@assistant-ui/react", "@assistant-ui/eve", "eve"]} />
|
|
56
|
+
|
|
57
|
+
</Step>
|
|
58
|
+
<Step>
|
|
59
|
+
|
|
60
|
+
### Mount Eve in Next.js
|
|
61
|
+
|
|
62
|
+
```ts title="next.config.ts"
|
|
63
|
+
import { withAui } from "@assistant-ui/next";
|
|
64
|
+
import type { NextConfig } from "next";
|
|
65
|
+
import { withEve } from "eve/next";
|
|
66
|
+
|
|
67
|
+
const nextConfig: NextConfig = {};
|
|
68
|
+
|
|
69
|
+
export default withEve(withAui(nextConfig));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
</Step>
|
|
73
|
+
<Step>
|
|
74
|
+
|
|
75
|
+
### Add an Eve agent
|
|
76
|
+
|
|
77
|
+
```ts title="agent/agent.ts"
|
|
78
|
+
import { defineAgent } from "eve";
|
|
79
|
+
|
|
80
|
+
export default defineAgent({
|
|
81
|
+
model: "anthropic/claude-sonnet-4.6",
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```md title="agent/instructions.md"
|
|
86
|
+
You are a concise assistant. Use tools when they are available.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
</Step>
|
|
90
|
+
<Step>
|
|
91
|
+
|
|
92
|
+
### Create the runtime
|
|
93
|
+
|
|
94
|
+
```tsx title="app/page.tsx"
|
|
95
|
+
"use client";
|
|
96
|
+
|
|
97
|
+
import { Thread } from "@/components/assistant-ui/thread";
|
|
98
|
+
import { useEveAgentRuntime } from "@assistant-ui/eve";
|
|
99
|
+
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
100
|
+
|
|
101
|
+
export default function Home() {
|
|
102
|
+
const runtime = useEveAgentRuntime();
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
106
|
+
<Thread />
|
|
107
|
+
</AssistantRuntimeProvider>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
</Step>
|
|
113
|
+
</Steps>
|
|
114
|
+
|
|
115
|
+
## Production auth
|
|
116
|
+
|
|
117
|
+
The default Eve channel is convenient for local development. For production browser users, define `agent/channels/eve.ts` and replace the default auth policy with your app's auth.
|
|
118
|
+
|
|
119
|
+
```ts title="agent/channels/eve.ts"
|
|
120
|
+
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
121
|
+
import { eveChannel } from "eve/channels/eve";
|
|
122
|
+
|
|
123
|
+
export default eveChannel({
|
|
124
|
+
auth: [localDev(), vercelOidc()],
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
That example keeps the development defaults. Swap in your Clerk, Auth.js, OIDC, or JWT verification before going live.
|
|
129
|
+
|
|
130
|
+
## Next
|
|
131
|
+
|
|
132
|
+
<Cards>
|
|
133
|
+
<Card
|
|
134
|
+
title="Eve overview"
|
|
135
|
+
description="Architecture, requirements, and runtime behavior."
|
|
136
|
+
href="/docs/runtimes/eve/overview"
|
|
137
|
+
/>
|
|
138
|
+
<Card
|
|
139
|
+
title="API reference"
|
|
140
|
+
description="useEveAgentRuntime and message conversion helpers."
|
|
141
|
+
href="/docs/api-reference/integrations/eve"
|
|
142
|
+
/>
|
|
143
|
+
</Cards>
|