@truefoundry/assistant-ui-runtime 0.1.5 → 0.1.6
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/README.md +198 -401
- package/dist/chunk-3A2EPLQG.js +93 -0
- package/dist/chunk-3A2EPLQG.js.map +1 -0
- package/dist/chunk-Q2SHKMLM.js +270 -0
- package/dist/chunk-Q2SHKMLM.js.map +1 -0
- package/dist/index.d.ts +24 -29
- package/dist/index.js +264 -237
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +162 -0
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +19 -0
- package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-BfiFf8O1.d.ts +468 -0
- package/package.json +19 -6
- package/src/askUserQuestion.ts +3 -3
- package/src/buildEditedUserMessageContent.test.ts +2 -2
- package/src/collectPending.ts +1 -1
- package/src/convertTurnMessages.test.ts +141 -196
- package/src/convertTurnMessages.ts +130 -76
- package/src/createSubAgent.ts +1 -1
- package/src/draftAgentConfig.test.ts +26 -29
- package/src/extractTurnUserText.ts +1 -1
- package/src/foldPeerThreads.test.ts +1 -1
- package/src/foldPeerThreads.ts +3 -2
- package/src/index.ts +80 -4
- package/src/listPages.ts +21 -0
- package/src/loadSessionSnapshot.test.ts +9 -8
- package/src/loadSessionSnapshot.ts +9 -14
- package/src/mcpAuth.ts +6 -3
- package/src/messageCustomMetadata.ts +1 -1
- package/src/modelMessageContent.ts +1 -1
- package/src/modelMessageImageContent.test.ts +1 -1
- package/src/modelMessageImageContent.ts +7 -6
- package/src/plugins/truefoundry-agent-server-adapter/README.md +178 -0
- package/src/plugins/truefoundry-agent-server-adapter/guards.test.ts +113 -0
- package/src/plugins/truefoundry-agent-server-adapter/guards.ts +130 -0
- package/src/plugins/truefoundry-agent-server-adapter/index.ts +359 -0
- package/src/plugins/truefoundry-agent-server-adapter/types.ts +135 -0
- package/src/plugins/truefoundry-agent-server-adapter/types.typecheck.ts +164 -0
- package/src/private/agentSpec.ts +8 -3
- package/src/private/draftSessionBridge.ts +14 -13
- package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
- package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
- package/src/requiredActionInputs.ts +1 -1
- package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
- package/src/server/eventUtils.ts +120 -0
- package/src/server/events.ts +246 -0
- package/src/server/index.ts +66 -0
- package/src/server/types.ts +319 -0
- package/src/sessionSnapshot.ts +1 -1
- package/src/sessions.ts +5 -21
- package/src/streamTurn.test.ts +172 -155
- package/src/streamTurn.ts +51 -48
- package/src/toolApproval.ts +4 -4
- package/src/toolResponse.ts +4 -4
- package/src/truefoundryExtras.ts +1 -1
- package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
- package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
- package/src/truefoundryThreadListAdapter.test.ts +16 -18
- package/src/truefoundryThreadListAdapter.ts +7 -7
- package/src/turnEventHelpers.ts +1 -1
- package/src/types.ts +2 -16
- package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
- package/src/useTrueFoundryAgentMessages.ts +32 -44
- package/src/useTrueFoundryAgentRuntime.ts +11 -28
- package/src/private/bindDraftAgentSession.test.ts +0 -54
- package/src/private/bindDraftAgentSession.ts +0 -28
- package/src/private/getGatewayFromPrivateClient.ts +0 -13
package/README.md
CHANGED
|
@@ -1,55 +1,72 @@
|
|
|
1
|
-
# truefoundry
|
|
1
|
+
# @truefoundry/assistant-ui-runtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A headless React runtime that connects [assistant-ui](https://www.assistant-ui.com/) to TrueFoundry agent sessions. Bring your own UI and server — the adapter maps sessions, turns, and streaming events onto assistant-ui's external-store runtime.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Built on top of [`@assistant-ui/react`](https://www.assistant-ui.com/), so Thread, Composer, ThreadList, and tool UIs work against a familiar contract out of the box.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Checkout the Demo [here](../../examples/assistant-ui-vite)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Table of contents
|
|
12
|
+
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Quick start](#quick-start)
|
|
15
|
+
- [`useTrueFoundryAgentRuntime` options](#usetruefoundryagentruntime-options)
|
|
16
|
+
- [Agent modes](#agent-modes)
|
|
17
|
+
- [Attachments](#attachments)
|
|
18
|
+
- [Runtime extras](#runtime-extras)
|
|
19
|
+
- [Server port (`AgentChatServer`)](#server-port-agentchatserver)
|
|
20
|
+
- [TrueFoundry gateway plugin](#truefoundry-gateway-plugin)
|
|
21
|
+
- [Exports](#exports)
|
|
22
|
+
- [Architecture](#architecture-source-map)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
---
|
|
13
26
|
|
|
14
27
|
## Installation
|
|
15
28
|
|
|
16
29
|
```bash
|
|
17
|
-
npm install @
|
|
30
|
+
npm install @truefoundry/assistant-ui-runtime @assistant-ui/react
|
|
31
|
+
# or
|
|
32
|
+
pnpm add @truefoundry/assistant-ui-runtime @assistant-ui/react
|
|
33
|
+
# or
|
|
34
|
+
yarn add @truefoundry/assistant-ui-runtime @assistant-ui/react
|
|
18
35
|
```
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
Using the built-in TrueFoundry gateway plugin? Also install the gateway SDK:
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
```bash
|
|
40
|
+
npm install truefoundry-gateway-sdk
|
|
41
|
+
```
|
|
23
42
|
|
|
24
|
-
|
|
43
|
+
**Peers:** React `^18 || ^19`, `@assistant-ui/react` in the host app, and an `AgentChatServer` implementation (plugin or your own). Bundled deps `@assistant-ui/core` and `@assistant-ui/store` are pulled in automatically.
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
import { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
|
|
45
|
+
---
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
apiKey: process.env.TFY_API_KEY!,
|
|
31
|
-
environment: process.env.TFY_GATEWAY_URL!, // https://gateway.truefoundry.ai/<tenant>
|
|
32
|
-
});
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
For production, point `fetch` or `auth` at your own backend proxy so secrets never reach the browser.
|
|
47
|
+
## Quick start
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
The fastest path is a TrueFoundry gateway server + the runtime hook + your Thread UI.
|
|
38
50
|
|
|
39
51
|
```tsx
|
|
40
52
|
"use client";
|
|
41
53
|
|
|
42
54
|
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
43
|
-
import {
|
|
55
|
+
import {
|
|
56
|
+
createTrueFoundryChatServer,
|
|
57
|
+
useTrueFoundryAgentRuntime,
|
|
58
|
+
} from "@truefoundry/assistant-ui-runtime";
|
|
44
59
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
45
60
|
|
|
46
|
-
const
|
|
47
|
-
|
|
61
|
+
const server = createTrueFoundryChatServer({
|
|
62
|
+
apiKey: process.env.TFY_API_KEY!,
|
|
63
|
+
baseUrl: process.env.TFY_GATEWAY_URL!,
|
|
64
|
+
});
|
|
48
65
|
|
|
49
66
|
export function MyAssistant() {
|
|
50
67
|
const runtime = useTrueFoundryAgentRuntime({
|
|
51
|
-
|
|
52
|
-
agentName:
|
|
68
|
+
server,
|
|
69
|
+
agentName: "support-bot",
|
|
53
70
|
});
|
|
54
71
|
|
|
55
72
|
return (
|
|
@@ -60,445 +77,237 @@ export function MyAssistant() {
|
|
|
60
77
|
}
|
|
61
78
|
```
|
|
62
79
|
|
|
63
|
-
|
|
80
|
+
That wires streaming turns, tool approvals, ask-user prompts, MCP auth, and sub-agent nesting through the runtime.
|
|
64
81
|
|
|
65
|
-
|
|
66
|
-
import { MyAssistant } from "@/components/MyAssistant";
|
|
82
|
+
> Prefer a drop-in chat UI? Pair with [`@truefoundry/agent-ui-sdk`](https://www.npmjs.com/package/@truefoundry/agent-ui-sdk) (`AgentChat`) instead of a custom Thread.
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<main className="h-dvh">
|
|
71
|
-
<MyAssistant />
|
|
72
|
-
</main>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 4. Set up UI components
|
|
78
|
-
|
|
79
|
-
See the assistant-ui [Thread UI guide](https://www.assistant-ui.com/docs/ui/thread) for wiring Thread, composer, and primitives.
|
|
84
|
+
---
|
|
80
85
|
|
|
81
86
|
## `useTrueFoundryAgentRuntime` options
|
|
82
87
|
|
|
83
|
-
`UseTrueFoundryAgentRuntimeOptions` extends assistant-ui's `ExternalStoreSharedOptions`.
|
|
88
|
+
`UseTrueFoundryAgentRuntimeOptions` extends assistant-ui's `ExternalStoreSharedOptions`. Adapter-specific fields:
|
|
84
89
|
|
|
85
90
|
| Option | Type | Required | Description |
|
|
86
|
-
|
|
87
|
-
| `
|
|
88
|
-
| `agentName` | `string` |
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Pass the saved agent name as `agentName`:
|
|
91
|
+
| ------ | ---- | -------- | ----------- |
|
|
92
|
+
| `server` | `AgentChatServer` | ✅ | Server implementation. The runtime never reads credentials itself. |
|
|
93
|
+
| `agentName` | `string` | ✅\* | Saved agent to run. \*Or use `agent` for draft / explicit named mode. |
|
|
94
|
+
| `agent` | `NamedAgentConfig \| DraftAgentConfig` | — | Discriminated agent source. Overrides `agentName` when set. |
|
|
95
|
+
| `initialSessionId` | `string` | — | Pin an existing session once on mount (uncontrolled). |
|
|
96
|
+
| `threadId` | `string` | — | Controlled active session id; reactive and URL-syncable. |
|
|
97
|
+
| `onThreadIdChange` | `(threadId: string \| undefined) => void` | — | Fires when the active session changes. |
|
|
98
|
+
| `onError` | `(error: unknown) => void` | — | Invoked on stream / load / turn errors. |
|
|
99
|
+
| `adapters` | `{ attachments?, speech?, dictation?, voice?, feedback? }` | — | Optional assistant-ui adapters forwarded to the runtime. |
|
|
100
|
+
|
|
101
|
+
### Resume / pin a session
|
|
98
102
|
|
|
99
103
|
```tsx
|
|
100
104
|
const runtime = useTrueFoundryAgentRuntime({
|
|
101
|
-
|
|
105
|
+
server,
|
|
102
106
|
agentName: "support-bot",
|
|
107
|
+
initialSessionId: "ses_abc123",
|
|
103
108
|
});
|
|
104
109
|
```
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
Omit `<ThreadList>` if you manage session ids yourself — the session-list adapter only powers that UI. Each gateway session corresponds to one assistant-ui thread.
|
|
107
112
|
|
|
108
|
-
|
|
113
|
+
---
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
import { trueFoundryAttachmentAdapter, useTrueFoundryAgentRuntime } from "truefoundry-agents-assistant-ui-runtime";
|
|
115
|
+
## Agent modes
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
client,
|
|
115
|
-
agentName,
|
|
116
|
-
adapters: { attachments: trueFoundryAttachmentAdapter },
|
|
117
|
-
});
|
|
118
|
-
```
|
|
117
|
+
`agent` / `agentName` control how the runtime sources the agent.
|
|
119
118
|
|
|
120
|
-
|
|
119
|
+
| Mode | Config | Behavior |
|
|
120
|
+
| ---- | ------ | -------- |
|
|
121
|
+
| `named` _(default)_ | `agentName` or `agent: { mode: "named", agentName }` | Runs a saved gateway agent |
|
|
122
|
+
| `draft` | `agent: { mode: "draft", defaultAgentSpec }` | Inline mutable `AgentSpec`, synced via draft sessions |
|
|
121
123
|
|
|
122
124
|
```tsx
|
|
125
|
+
// Named
|
|
123
126
|
const runtime = useTrueFoundryAgentRuntime({
|
|
124
|
-
|
|
125
|
-
agentName,
|
|
126
|
-
adapters: { attachments: trueFoundryAttachmentAdapter, speech, feedback },
|
|
127
|
+
server,
|
|
128
|
+
agentName: "support-bot",
|
|
127
129
|
});
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Resuming a session
|
|
131
130
|
|
|
132
|
-
|
|
131
|
+
// Draft
|
|
133
132
|
const runtime = useTrueFoundryAgentRuntime({
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
server,
|
|
134
|
+
agent: {
|
|
135
|
+
mode: "draft",
|
|
136
|
+
defaultAgentSpec: { model: { name: "gpt-4o" } },
|
|
137
|
+
onAgentSpecChange: (spec) => console.log("spec updated", spec),
|
|
138
|
+
},
|
|
137
139
|
});
|
|
138
140
|
```
|
|
139
141
|
|
|
140
|
-
|
|
142
|
+
---
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
## Attachments
|
|
145
|
+
|
|
146
|
+
Attachments are **opt-in**. Wire the built-in adapter for composer file pick / previews and gateway forwarding on send.
|
|
143
147
|
|
|
144
148
|
```tsx
|
|
149
|
+
import {
|
|
150
|
+
trueFoundryAttachmentAdapter,
|
|
151
|
+
useTrueFoundryAgentRuntime,
|
|
152
|
+
} from "@truefoundry/assistant-ui-runtime";
|
|
153
|
+
|
|
145
154
|
const runtime = useTrueFoundryAgentRuntime({
|
|
146
|
-
|
|
155
|
+
server,
|
|
147
156
|
agentName,
|
|
148
|
-
|
|
157
|
+
adapters: { attachments: trueFoundryAttachmentAdapter },
|
|
149
158
|
});
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
153
|
-
<Thread />
|
|
154
|
-
</AssistantRuntimeProvider>
|
|
155
|
-
);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Each gateway session corresponds to one assistant-ui thread.
|
|
159
|
-
|
|
160
|
-
## Multi-agent (nested sub-agents)
|
|
161
|
-
|
|
162
|
-
TrueFoundry sub-agents are discovered at runtime via `thread.created` and nested under `ToolCallMessagePart.messages`. The gateway always sends `title` as a required `string` on `thread.created` / `thread.done` (never `null`). This runtime copies it onto `metadata.custom.subAgent.title` for the first nested message of each child thread; `name` comes from `agentInfo.name` on the same event.
|
|
163
|
-
|
|
164
|
-
Render nested threads with `MessagePartPrimitive.Messages` inside your tool fallback (recommended — no per-tool registration):
|
|
165
|
-
|
|
166
|
-
```tsx
|
|
167
|
-
import { MessagePartPrimitive, MessagePrimitive } from "@assistant-ui/react";
|
|
168
|
-
import { useAuiState } from "@assistant-ui/store";
|
|
169
|
-
import type { TrueFoundryMessageCustomMetadata } from "truefoundry-agents-assistant-ui-runtime";
|
|
170
|
-
|
|
171
|
-
function NestedSubAgentAssistantMessage() {
|
|
172
|
-
const custom = useAuiState(
|
|
173
|
-
(s) => s.message.metadata.custom as TrueFoundryMessageCustomMetadata,
|
|
174
|
-
);
|
|
175
|
-
const heading = custom.subAgent?.title ?? custom.subAgent?.name;
|
|
176
|
-
|
|
177
|
-
return (
|
|
178
|
-
<>
|
|
179
|
-
{heading != null && (
|
|
180
|
-
<div className="text-sm text-muted-foreground">{heading}</div>
|
|
181
|
-
)}
|
|
182
|
-
<MessagePrimitive.Root data-role="assistant">
|
|
183
|
-
<MessagePrimitive.Parts />
|
|
184
|
-
</MessagePrimitive.Root>
|
|
185
|
-
</>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
<MessagePartPrimitive.Messages
|
|
190
|
-
components={{
|
|
191
|
-
AssistantMessage: NestedSubAgentAssistantMessage,
|
|
192
|
-
UserMessage: () => null,
|
|
193
|
-
}}
|
|
194
|
-
/>
|
|
195
159
|
```
|
|
196
160
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
Alternative: register `defineToolkit({ create_sub_agent: ... })` — all sub-agents share that one system tool name.
|
|
200
|
-
|
|
201
|
-
See the [Multi-Agent Chat UI guide](https://www.assistant-ui.com/docs/tools/multi-agent).
|
|
202
|
-
|
|
203
|
-
## Tool approvals
|
|
204
|
-
|
|
205
|
-
When the agent requests approval, the assistant message carries a `requires-action` status and the tool-call part exposes an `approval`. Respond through assistant-ui's tool-approval UI; the adapter converts the decision back into a gateway `user.tool_approval` input and resumes the turn. Approvals on nested sub-agent threads are scoped to the correct `threadId` automatically.
|
|
206
|
-
|
|
207
|
-
For custom approval chrome (a thread-level bar instead of per-part buttons), use the extras hooks — see [Runtime extras](#runtime-extras) below.
|
|
208
|
-
|
|
209
|
-
## Ask-user tool responses (`tool.response_required`)
|
|
210
|
-
|
|
211
|
-
When the agent calls the client-side `ask_user_question` system tool, the turn ends with `tool.response_required`. The adapter marks the tool call with a human `interrupt` payload (`question`, `options`) resolved from the originating `model.message` via `ToolCallRef.sourceEventId`.
|
|
212
|
-
|
|
213
|
-
Collect the user's answer and call `respondToToolResponse({ toolCallId, content })`. The `content` string is free-form (chosen option text, typed answer, etc.).
|
|
214
|
-
|
|
215
|
-
## Batched resume (approvals + responses)
|
|
216
|
-
|
|
217
|
-
The gateway requires **every** pending `user.tool_approval` and `user.tool_response` across all threads (root + sub-agents) in a **single** `prepareTurn({ input })` call. The adapter stages decisions locally and only sends when nothing is pending anywhere:
|
|
218
|
-
|
|
219
|
-
1. User resolves all tool approvals (`respondToToolApproval`).
|
|
220
|
-
2. User answers all ask-user prompts (`respondToToolResponse`).
|
|
221
|
-
3. Runtime collects `collectRequiredActionInputs(message)` → one mixed `TurnInputItem[]` → `sendTurn({ inputs })`.
|
|
222
|
-
|
|
223
|
-
Do not send partial resumes; the backend rejects incomplete input sets.
|
|
224
|
-
|
|
225
|
-
## MCP auth
|
|
226
|
-
|
|
227
|
-
When MCP OAuth is required, the paused assistant message has `metadata.custom.pendingMcpAuth === true` and structured `metadata.custom.mcpServers` (`{ id, name, authUrl }[]`) — both fields are on `TrueFoundryMessageCustomMetadata`. After the user completes OAuth in the browser, call `resumeMcpAuth()` from extras (or `startRun` with `runConfig.custom.resumeMcpAuth: true`).
|
|
161
|
+
---
|
|
228
162
|
|
|
229
163
|
## Runtime extras
|
|
230
164
|
|
|
231
|
-
Typed escape hatch for adapter-specific state and actions
|
|
165
|
+
Typed escape hatch for adapter-specific state and actions (same pattern as `@assistant-ui/react-google-adk`). Use selector hooks for thread-level UI; use action hooks / `trueFoundryExtras.get(aui)` inside nested sub-agent renderers.
|
|
232
166
|
|
|
233
|
-
###
|
|
167
|
+
### Approvals, ask-user, MCP auth
|
|
234
168
|
|
|
235
169
|
```tsx
|
|
236
170
|
import {
|
|
237
171
|
useTrueFoundryApprovals,
|
|
238
172
|
useTrueFoundryToolResponses,
|
|
239
173
|
useTrueFoundryMcpAuth,
|
|
240
|
-
} from "truefoundry
|
|
241
|
-
|
|
242
|
-
function ApprovalBar() {
|
|
243
|
-
const { pending, respond } = useTrueFoundryApprovals();
|
|
244
|
-
if (pending.length === 0) return null;
|
|
245
|
-
|
|
246
|
-
const item = pending[0]!;
|
|
247
|
-
return (
|
|
248
|
-
<div>
|
|
249
|
-
<p>Allow {item.toolName}?</p>
|
|
250
|
-
<button onClick={() => respond({ approvalId: item.approvalId, approved: true })}>
|
|
251
|
-
Allow
|
|
252
|
-
</button>
|
|
253
|
-
<button onClick={() => respond({ approvalId: item.approvalId, approved: false })}>
|
|
254
|
-
Deny
|
|
255
|
-
</button>
|
|
256
|
-
</div>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function AskUserBar() {
|
|
261
|
-
const { pending, respond } = useTrueFoundryToolResponses();
|
|
262
|
-
if (pending.length === 0) return null;
|
|
263
|
-
|
|
264
|
-
const item = pending[0]!;
|
|
265
|
-
return (
|
|
266
|
-
<div>
|
|
267
|
-
<p>{item.question ?? "Answer required"}</p>
|
|
268
|
-
{(item.options ?? []).map((option) => (
|
|
269
|
-
<button key={option} onClick={() => respond({ toolCallId: item.toolCallId, content: option })}>
|
|
270
|
-
{option}
|
|
271
|
-
</button>
|
|
272
|
-
))}
|
|
273
|
-
</div>
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function McpAuthContinue() {
|
|
278
|
-
const { pending, resume } = useTrueFoundryMcpAuth();
|
|
279
|
-
if (pending == null) return null;
|
|
280
|
-
|
|
281
|
-
return (
|
|
282
|
-
<div>
|
|
283
|
-
{pending.mcpServers.map((server) => (
|
|
284
|
-
<a key={server.id} href={server.authUrl} target="_blank" rel="noreferrer">
|
|
285
|
-
Authorize {server.name}
|
|
286
|
-
</a>
|
|
287
|
-
))}
|
|
288
|
-
<button onClick={() => void resume()}>Continue</button>
|
|
289
|
-
</div>
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Action hooks (any render context, including nested sub-agents)
|
|
295
|
-
|
|
296
|
-
```tsx
|
|
297
|
-
import { useTrueFoundryRespondToToolApproval } from "truefoundry-agents-assistant-ui-runtime";
|
|
174
|
+
} from "@truefoundry/assistant-ui-runtime";
|
|
298
175
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
<button onClick={() => respond({ approvalId, approved: true })}>
|
|
303
|
-
Allow
|
|
304
|
-
</button>
|
|
305
|
-
);
|
|
306
|
-
}
|
|
176
|
+
const { pending, respond } = useTrueFoundryApprovals();
|
|
177
|
+
const { pending: asks, respond: answer } = useTrueFoundryToolResponses();
|
|
178
|
+
const { pending: mcp, resume } = useTrueFoundryMcpAuth();
|
|
307
179
|
```
|
|
308
180
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
```tsx
|
|
312
|
-
import {
|
|
313
|
-
useTrueFoundryRespondToToolApproval,
|
|
314
|
-
useTrueFoundryRespondToToolResponse,
|
|
315
|
-
useTrueFoundryResumeMcpAuth,
|
|
316
|
-
useTrueFoundryCancel,
|
|
317
|
-
} from "truefoundry-agents-assistant-ui-runtime";
|
|
318
|
-
|
|
319
|
-
const respondToApproval = useTrueFoundryRespondToToolApproval();
|
|
320
|
-
const respondToResponse = useTrueFoundryRespondToToolResponse();
|
|
321
|
-
const resumeMcpAuth = useTrueFoundryResumeMcpAuth();
|
|
322
|
-
const cancel = useTrueFoundryCancel();
|
|
323
|
-
|
|
324
|
-
respondToApproval({ approvalId, approved: true });
|
|
325
|
-
respondToResponse({ toolCallId, content: "Option A" });
|
|
326
|
-
void resumeMcpAuth();
|
|
327
|
-
void cancel();
|
|
328
|
-
```
|
|
181
|
+
**Batched resume:** the gateway requires **every** pending `user.tool_approval` and `user.tool_response` across all threads (root + sub-agents) in a **single** resume call. The adapter stages decisions locally and only sends when nothing is pending anywhere — partial resumes are rejected.
|
|
329
182
|
|
|
330
183
|
### Hooks reference
|
|
331
184
|
|
|
332
185
|
| Hook | Returns | Description |
|
|
333
|
-
|
|
334
|
-
| `useTrueFoundryApprovals()` | `{ pending
|
|
335
|
-
| `useTrueFoundryToolResponses()` | `{ pending
|
|
336
|
-
| `useTrueFoundryMcpAuth()` | `{ pending
|
|
337
|
-
| `useTrueFoundryRespondToToolApproval()` | `(r
|
|
338
|
-
| `useTrueFoundryRespondToToolResponse()` | `(r
|
|
339
|
-
| `useTrueFoundryResumeMcpAuth()` | `() => Promise<void>` | Resume
|
|
340
|
-
| `useTrueFoundryCancel()` | `() => Promise<void>` | Cancel the active turn
|
|
341
|
-
| `useTrueFoundryHistoryPagination()` | `{ hasOlderHistory, isLoadingOlderHistory, loadOlderHistory }` | Scroll-up older history
|
|
342
|
-
|
|
343
|
-
Where:
|
|
344
|
-
|
|
345
|
-
- `PendingApproval` = `{ approvalId, threadId, toolName, args, argsText }`
|
|
346
|
-
- `PendingToolResponse` = `{ toolCallId, threadId, toolName, args, argsText, question?, options? }`
|
|
347
|
-
- `RespondToToolApprovalOptions` = `{ approvalId, approved, optionId?, reason? }`
|
|
348
|
-
- `RespondToToolResponseOptions` = `{ toolCallId, content }`
|
|
349
|
-
|
|
350
|
-
> **Read vs. action hooks.** The three `use*Approvals` / `use*ToolResponses` / `use*McpAuth` read hooks subscribe to extras state and re-render when pending items change — use them in thread-level UI (e.g. an approval bar). The four action-only hooks (`useTrueFoundryRespondTo*`, `useTrueFoundryResumeMcpAuth`, `useTrueFoundryCancel`) read the action via `trueFoundryExtras.get(aui)` and do **not** subscribe to state, so they are safe to call from nested sub-agent renderers where only a readonly context is available.
|
|
186
|
+
| ---- | ------- | ----------- |
|
|
187
|
+
| `useTrueFoundryApprovals()` | `{ pending, respond }` | Pending tool approvals + respond |
|
|
188
|
+
| `useTrueFoundryToolResponses()` | `{ pending, respond }` | Pending ask-user prompts + respond |
|
|
189
|
+
| `useTrueFoundryMcpAuth()` | `{ pending, resume }` | Pending MCP OAuth + resume |
|
|
190
|
+
| `useTrueFoundryRespondToToolApproval()` | `(r) => void` | Respond from any render context |
|
|
191
|
+
| `useTrueFoundryRespondToToolResponse()` | `(r) => void` | Answer ask-user from any render context |
|
|
192
|
+
| `useTrueFoundryResumeMcpAuth()` | `() => Promise<void>` | Resume after MCP OAuth |
|
|
193
|
+
| `useTrueFoundryCancel()` | `() => Promise<void>` | Cancel the active turn |
|
|
194
|
+
| `useTrueFoundryHistoryPagination()` | `{ hasOlderHistory, isLoadingOlderHistory, loadOlderHistory }` | Scroll-up older history |
|
|
351
195
|
|
|
352
196
|
### Low-level namespace
|
|
353
197
|
|
|
354
198
|
```tsx
|
|
355
|
-
import { trueFoundryExtras
|
|
199
|
+
import { trueFoundryExtras } from "@truefoundry/assistant-ui-runtime";
|
|
356
200
|
|
|
357
|
-
// Throws outside useTrueFoundryAgentRuntime:
|
|
358
201
|
const extras = trueFoundryExtras.use();
|
|
359
|
-
|
|
360
|
-
// Safe with fallback (returns default outside runtime):
|
|
361
202
|
const pending = trueFoundryExtras.use((e) => e.pendingApprovals, []);
|
|
362
203
|
```
|
|
363
204
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
| Field | Type | Purpose |
|
|
367
|
-
|-------|------|---------|
|
|
368
|
-
| `pendingApprovals` | `PendingApproval[]` | Undecided tool approvals across all threads |
|
|
369
|
-
| `pendingToolResponses` | `PendingToolResponse[]` | Unanswered ask-user / client-side tool prompts |
|
|
370
|
-
| `pendingMcpAuth` | `{ mcpServers } \| null` | MCP OAuth pause state |
|
|
371
|
-
| `respondToToolApproval` | `(r: { approvalId, approved, reason? }) => void` | Stage approval; batch-send when complete |
|
|
372
|
-
| `respondToToolResponse` | `(r: { toolCallId, content }) => void` | Stage answer; batch-send when complete |
|
|
373
|
-
| `resumeMcpAuth` | `() => Promise<void>` | Resume after OAuth |
|
|
374
|
-
| `cancel` | `() => Promise<void>` | Cancel the active turn: calls `session.cancel()` and lets the stream drain to its terminal `turn.done` (reconciles on next session load) |
|
|
375
|
-
| `resetFromTurn` | `(turnId: string) => Promise<void>` | Re-submit a user turn (branch/reset) |
|
|
376
|
-
| `reload` | `() => void` | Retry the current session load |
|
|
377
|
-
| `downloadSandboxFile` | `(path: string) => Promise<Blob>` | Download a file from the session sandbox |
|
|
378
|
-
| `hasOlderHistory` | `boolean` | True when another older `listEvents` page is available |
|
|
379
|
-
| `isLoadingOlderHistory` | `boolean` | True while `loadOlderHistory` is in flight |
|
|
380
|
-
| `loadOlderHistory` | `() => Promise<void>` | Prepend the next older history window (scroll-up) |
|
|
381
|
-
| `draft` | `TrueFoundryDraftRuntimeExtras \| null` | Draft-mode agent spec sync extras |
|
|
382
|
-
|
|
383
|
-
Per-part `respondToApproval` from assistant-ui still works for root-thread tool UIs; extras complements that for global chrome and nested renderers.
|
|
384
|
-
|
|
385
|
-
## Cancellation
|
|
205
|
+
---
|
|
386
206
|
|
|
387
|
-
|
|
207
|
+
## Server port (`AgentChatServer`)
|
|
388
208
|
|
|
389
|
-
|
|
209
|
+
The runtime never holds credentials. It accepts any object implementing `AgentChatServer` — a flat, stateless port with methods like `createSession`, `listSessions`, `prepareAndExecuteTurn`, etc.
|
|
390
210
|
|
|
391
|
-
|
|
211
|
+
**First-party:** use [`createTrueFoundryChatServer`](#truefoundry-gateway-plugin) (requires `truefoundry-gateway-sdk`).
|
|
392
212
|
|
|
393
|
-
|
|
213
|
+
**Your own backend:**
|
|
394
214
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
215
|
+
```tsx
|
|
216
|
+
import type { AgentChatServer } from "@truefoundry/assistant-ui-runtime";
|
|
217
|
+
|
|
218
|
+
const server: AgentChatServer = {
|
|
219
|
+
createSession: async (req) => {
|
|
220
|
+
/* … */
|
|
221
|
+
},
|
|
222
|
+
listSessions: async (req) => {
|
|
223
|
+
/* … */
|
|
224
|
+
},
|
|
225
|
+
getSession: async (req) => {
|
|
226
|
+
/* … */
|
|
227
|
+
},
|
|
228
|
+
updateSession: async (req) => {
|
|
229
|
+
/* … */
|
|
230
|
+
},
|
|
231
|
+
prepareAndExecuteTurn: (req) => {
|
|
232
|
+
/* return AsyncIterable<TurnStreamData> */
|
|
233
|
+
},
|
|
234
|
+
cancelSession: async (req) => {
|
|
235
|
+
/* … */
|
|
236
|
+
},
|
|
237
|
+
listTurns: async (req) => {
|
|
238
|
+
/* … */
|
|
239
|
+
},
|
|
240
|
+
getTurn: async (req) => {
|
|
241
|
+
/* … */
|
|
242
|
+
},
|
|
243
|
+
listEvents: async (req) => {
|
|
244
|
+
/* … */
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
```
|
|
398
248
|
|
|
399
|
-
|
|
249
|
+
`ListResult<T>` is `{ data: T[]; nextPageToken?: string }` — flat token-based pagination. Optional methods: `deleteSession`, `listTurnEvents`, `subscribeToTurn`, `downloadSandboxFile`.
|
|
400
250
|
|
|
401
|
-
|
|
251
|
+
---
|
|
402
252
|
|
|
403
|
-
|
|
404
|
-
2. One (or a few) `listEvents` page(s) for the newest complete user-message group.
|
|
405
|
-
3. Clears `isLoading`, then resumes a running turn via subscribe if needed.
|
|
253
|
+
## TrueFoundry gateway plugin
|
|
406
254
|
|
|
407
|
-
|
|
255
|
+
`createTrueFoundryChatServer` wraps `truefoundry-gateway-sdk` into an `AgentChatServer`. Import from the main entry or the isolated subpath (no React):
|
|
408
256
|
|
|
409
257
|
```tsx
|
|
410
|
-
|
|
411
|
-
|
|
258
|
+
import { createTrueFoundryChatServer } from "@truefoundry/assistant-ui-runtime";
|
|
259
|
+
// or
|
|
260
|
+
import { createTrueFoundryChatServer } from "@truefoundry/assistant-ui-runtime/plugins/truefoundry-agent-server-adapter";
|
|
412
261
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
262
|
+
const server = createTrueFoundryChatServer({
|
|
263
|
+
apiKey: process.env.TFY_API_KEY!,
|
|
264
|
+
baseUrl: process.env.TFY_GATEWAY_URL!,
|
|
265
|
+
});
|
|
417
266
|
```
|
|
418
267
|
|
|
419
|
-
|
|
268
|
+
See the [plugin README](./src/plugins/truefoundry-agent-server-adapter/README.md) for options, named vs draft routing, `Tfy*` types, type guards, and host-spec extension.
|
|
420
269
|
|
|
421
|
-
|
|
270
|
+
---
|
|
422
271
|
|
|
423
|
-
|
|
272
|
+
## Exports
|
|
424
273
|
|
|
425
274
|
| Export | Kind | Purpose |
|
|
426
|
-
|
|
427
|
-
| `useTrueFoundryAgentRuntime` |
|
|
428
|
-
| `
|
|
429
|
-
| `
|
|
430
|
-
| `
|
|
431
|
-
| `
|
|
432
|
-
| `
|
|
433
|
-
| `
|
|
434
|
-
| `
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
| `trueFoundryExtras` | namespace | `createRuntimeExtras` channel — `.use()`, `.get(aui)`, `.provide()`. |
|
|
438
|
-
| `TrueFoundryRuntimeExtras` | type | Shape provided into the runtime extras slot. |
|
|
439
|
-
| `PendingApproval`, `PendingToolResponse` | types | Derived pending items for UI rendering. |
|
|
440
|
-
| `createTrueFoundryThreadListAdapter` | fn | Builds the cursor-paginated `RemoteThreadListAdapter` powering `<ThreadList>` (`list({ after })` → `nextCursor`). Used internally; exported for custom wiring. |
|
|
441
|
-
| `getSession` | fn | `(client, sessionId) => Promise<AgentSession>` convenience wrapper. |
|
|
442
|
-
| `convertTurnsToThreadMessages` | fn | Loads a session's turns and folds them into assistant-ui `ThreadMessage[]` (`ConvertTurnsResult`). |
|
|
443
|
-
| `buildTurnAssistantContent` | fn | Folds a single turn's events into assistant content parts. |
|
|
444
|
-
| `repositoryItemsFromMessages` | fn | Converts messages into `ExportedMessageRepositoryItem[]` for history export. |
|
|
445
|
-
| `getTurnMessageContent` | fn | Extracts the text payload from an `AppendMessage`. |
|
|
446
|
-
| `ConvertTurnsResult` | type | Result of `convertTurnsToThreadMessages` (`messages`, `foldState`, `runningTurn?`, `unstable_resume?`). |
|
|
447
|
-
| `collectApprovalInputs` | fn | Collects decided approvals from a message into `user.tool_approval` inputs. |
|
|
448
|
-
| `collectResponseInputs` | fn | Collects staged answers into `user.tool_response` inputs. |
|
|
449
|
-
| `collectRequiredActionInputs` | fn | Collects both approval + response inputs once nothing is pending. |
|
|
450
|
-
| `messageHasPendingApprovals` | fn | True if a message still has undecided tool approvals. |
|
|
451
|
-
| `messageHasPendingResponses` | fn | True if a message still has unanswered tool responses. |
|
|
452
|
-
| `messageHasPendingRequiredActions` | fn | True if either approvals or responses are still pending. |
|
|
453
|
-
| `findPausedAssistantMessage` | fn | Last assistant message in `requires-action` state. |
|
|
454
|
-
| `toTrueFoundryApprovalInputs` | fn | Applies an approval decision and returns gateway inputs. |
|
|
455
|
-
| `SubAgentArtifact`, `SubAgentCustomMetadata` | types | Shapes attached to sub-agent tool calls / nested messages. |
|
|
456
|
-
| `TrueFoundryMessageCustomMetadata` | type | Typed keys on `ThreadMessage.metadata.custom` written by this adapter. |
|
|
457
|
-
| `ROOT_THREAD_ID` | const | The literal `"main"` — the gateway's root thread id. |
|
|
275
|
+
| ------ | ---- | ------- |
|
|
276
|
+
| `useTrueFoundryAgentRuntime` | Hook | Root runtime — wires external-store + thread list |
|
|
277
|
+
| `createTrueFoundryChatServer` | Function | TrueFoundry gateway → `AgentChatServer` (also via plugin subpath) |
|
|
278
|
+
| `trueFoundryAttachmentAdapter` | Adapter | Opt-in composer attachments |
|
|
279
|
+
| `trueFoundryExtras` | Namespace | Low-level extras access |
|
|
280
|
+
| `useTrueFoundryApprovals` / `ToolResponses` / `McpAuth` / … | Hooks | Pending state + actions |
|
|
281
|
+
| `AgentChatServer`, `Session`, `Turn`, … | Types | Server port + DTOs |
|
|
282
|
+
| `TfyAgentSpec`, `TfySession`, `isTfyToolInfo`, … | Types / guards | Gateway-concrete types from the plugin |
|
|
283
|
+
| `NamedAgentConfig`, `DraftAgentConfig` | Types | Agent source discriminants |
|
|
284
|
+
|
|
285
|
+
---
|
|
458
286
|
|
|
459
287
|
## Architecture (source map)
|
|
460
288
|
|
|
461
|
-
For contributors
|
|
289
|
+
For contributors working inside this package. Source lives in `src/`; the published entry point is `dist/index.js` (built by `tsup`).
|
|
462
290
|
|
|
463
291
|
| File | Responsibility |
|
|
464
|
-
|
|
465
|
-
| `
|
|
466
|
-
| `
|
|
467
|
-
| `
|
|
468
|
-
| `
|
|
469
|
-
| `
|
|
470
|
-
| `
|
|
471
|
-
| `
|
|
472
|
-
| `
|
|
473
|
-
| `convertTurnMessages.ts` | `projectSessionMessages` pure projector; `buildSnapshotFromSession` history ingest; `convertTurnsToThreadMessages` wrapper; stream-event aggregation. |
|
|
474
|
-
| `foldPeerThreads.ts` | `PeerThreadFoldState` — folds peer/sub-agent threads under their spawning tool call. |
|
|
475
|
-
| `messageCustomMetadata.ts` | `TrueFoundryMessageCustomMetadata` — typed `metadata.custom` keys for this adapter. |
|
|
476
|
-
| `modelMessageContent.ts` | `model.message` events → assistant content parts (text, reasoning, tool calls). |
|
|
477
|
-
| `streamTurn.ts` | `streamTurnContent` / `resumeTurnStream` generators over `prepareTurn`/`stream`. |
|
|
478
|
-
| `toolApproval.ts` | Approval state, decision mapping, and `user.tool_approval` input collection. |
|
|
479
|
-
| `toolResponse.ts` | Ask-user response state, staging, and `user.tool_response` input collection. |
|
|
480
|
-
| `askUserQuestion.ts` | `ask_user_question` detection and argument parsing. |
|
|
481
|
-
| `mcpAuth.ts` | MCP auth-required detection and structured authorize UI metadata. |
|
|
482
|
-
| `turnEventHelpers.ts` | Appends approval / response / MCP-auth status onto turn updates. |
|
|
483
|
-
| `createSubAgent.ts` | Detects the `create_sub_agent` system tool call. |
|
|
484
|
-
| `extractTurnUserText.ts` / `lastUserMessageText.ts` | Text extraction helpers. |
|
|
485
|
-
| `sessions.ts` | `getSession` wrapper. |
|
|
486
|
-
| `sessionListStartTimestamp.ts` | Default `listSessions` window (1 year). |
|
|
487
|
-
| `constants.ts` | `ROOT_THREAD_ID = "main"`. |
|
|
488
|
-
| `types.ts` / `turnStreamUpdate.ts` | Shared option and update types. |
|
|
292
|
+
| ---- | -------------- |
|
|
293
|
+
| `server/types.ts` | `AgentChatServer` + `AgentBuilderServer`, session/turn/pagination types |
|
|
294
|
+
| `server/events.ts` | Concrete turn/stream event types |
|
|
295
|
+
| `useTrueFoundryAgentRuntime.ts` | Public hook — external-store + thread-list + extras |
|
|
296
|
+
| `useTrueFoundryAgentMessages.ts` | Reactive session snapshot: load, stream, cancel, resume |
|
|
297
|
+
| `truefoundryExtras.ts` / `hooks.ts` | Extras namespace + consumer hooks |
|
|
298
|
+
| `convertTurnMessages.ts` | Pure projection from snapshot → thread messages |
|
|
299
|
+
| `foldPeerThreads.ts` | Nest peer/sub-agent threads under spawning tool calls |
|
|
300
|
+
| `plugins/truefoundry-agent-server-adapter/` | Gateway SDK → `AgentChatServer` |
|
|
489
301
|
|
|
490
302
|
### Invariants
|
|
491
303
|
|
|
492
304
|
- One gateway **session** ⇄ one assistant-ui **thread** (`session.id` = thread `remoteId`).
|
|
493
|
-
-
|
|
494
|
-
- The runtime never holds credentials —
|
|
495
|
-
-
|
|
496
|
-
-
|
|
497
|
-
- Approval decisions are allow/deny only (`ApprovalDecision`); there is no `optionId` on the gateway wire.
|
|
498
|
-
|
|
499
|
-
## Local development
|
|
305
|
+
- Root thread id is always `"main"` (`ROOT_THREAD_ID`); sub-agents nest under their `create_sub_agent` tool call.
|
|
306
|
+
- The runtime never holds credentials — only a pre-built `AgentChatServer`.
|
|
307
|
+
- A paused turn's resume `input` must include **all** pending approvals + tool responses across every thread in one batch.
|
|
308
|
+
- Two agent modes: **named** (`agentName`) and **draft** (`agent: { mode: "draft", … }`).
|
|
500
309
|
|
|
501
|
-
|
|
310
|
+
### Local development
|
|
502
311
|
|
|
503
312
|
```bash
|
|
504
313
|
pnpm build # tsup → dist/
|
|
@@ -506,30 +315,18 @@ pnpm test # vitest run
|
|
|
506
315
|
pnpm typecheck # tsc --noEmit
|
|
507
316
|
```
|
|
508
317
|
|
|
509
|
-
|
|
318
|
+
### Unsupported assistant-ui features
|
|
510
319
|
|
|
511
|
-
|
|
320
|
+
| Feature | Notes |
|
|
321
|
+
| ------- | ----- |
|
|
322
|
+
| Attachment rendering | Forwarded on send; user bubbles show text only today |
|
|
323
|
+
| Speech / Dictation / Voice | Pass-through only |
|
|
324
|
+
| Feedback | Pass-through only; not persisted to the gateway |
|
|
325
|
+
| Thread rename / archive / delete | Thread-list adapter no-ops |
|
|
326
|
+
| Thread title generation | Returns an empty stream |
|
|
512
327
|
|
|
513
|
-
|
|
328
|
+
---
|
|
514
329
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
| Built-in `AttachmentAdapter` | Ships as `trueFoundryAttachmentAdapter` (opt-in via `adapters.attachments`). Not applied by default. |
|
|
519
|
-
| Speech synthesis (`adapters.speech`) | Pass-through only. Not shipped. |
|
|
520
|
-
| Dictation (`adapters.dictation`) | Pass-through only. Not shipped. |
|
|
521
|
-
| Voice (`adapters.voice`) | Pass-through only. Not shipped. |
|
|
522
|
-
| Feedback (`adapters.feedback`) | Pass-through only. Ratings are not persisted to the gateway. |
|
|
523
|
-
| Message edit (`onEdit`) | Not wired. |
|
|
524
|
-
| Regenerate (`onReload`) | Not wired. |
|
|
525
|
-
| Message delete (`onDelete`) | Not wired. |
|
|
526
|
-
| Client-side tool results (`onAddToolResult`) | Not wired. |
|
|
527
|
-
| Tool call resume (`onResumeToolCall`) | Not wired. |
|
|
528
|
-
| Message queue (`queue`) | Not wired. |
|
|
529
|
-
| Branch switching | Not wired. |
|
|
530
|
-
| Thread rename / archive / delete | Thread-list adapter no-ops. |
|
|
531
|
-
| Thread title generation | Returns an empty stream. |
|
|
532
|
-
| Generative UI message parts | Not mapped from gateway events. |
|
|
533
|
-
| Source citation parts | Not mapped from gateway events. |
|
|
534
|
-
| Message import / external state | `onImport`, `onExportExternalState`, `onLoadExternalState` not wired. |
|
|
535
|
-
| Composer suggestions | `suggestions` not populated. |
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
See [LICENSE](../../LICENSE).
|