@assistant-ui/mcp-docs-server 0.1.33 → 0.1.35
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 +7 -7
- package/.docs/organized/code-examples/with-a2a.md +8 -8
- package/.docs/organized/code-examples/with-ag-ui.md +12 -12
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
- package/.docs/organized/code-examples/with-artifacts.md +40 -34
- package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
- package/.docs/organized/code-examples/with-browser-extension.md +9 -9
- package/.docs/organized/code-examples/with-chain-of-thought.md +72 -51
- package/.docs/organized/code-examples/with-cloud-standalone.md +10 -10
- package/.docs/organized/code-examples/with-cloud.md +10 -10
- package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +12 -12
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +12 -12
- package/.docs/organized/code-examples/with-expo.md +66 -31
- package/.docs/organized/code-examples/with-external-store.md +8 -8
- package/.docs/organized/code-examples/with-ffmpeg.md +13 -13
- package/.docs/organized/code-examples/with-generative-ui.md +98 -368
- package/.docs/organized/code-examples/with-google-adk.md +9 -9
- package/.docs/organized/code-examples/with-heat-graph.md +7 -7
- package/.docs/organized/code-examples/with-image-generation.md +10 -10
- package/.docs/organized/code-examples/with-interactables.md +10 -10
- package/.docs/organized/code-examples/with-langchain.md +10 -10
- package/.docs/organized/code-examples/with-langgraph.md +33 -29
- package/.docs/organized/code-examples/with-livekit.md +12 -12
- package/.docs/organized/code-examples/with-mcp.md +11 -11
- package/.docs/organized/code-examples/with-opencode.md +109 -583
- package/.docs/organized/code-examples/with-pi.md +2044 -0
- package/.docs/organized/code-examples/with-react-hook-form.md +11 -11
- package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
- package/.docs/organized/code-examples/with-react-ink.md +309 -102
- package/.docs/organized/code-examples/with-react-router.md +14 -14
- package/.docs/organized/code-examples/with-resumable-stream.md +11 -11
- package/.docs/organized/code-examples/with-store.md +70 -66
- package/.docs/organized/code-examples/with-tanstack.md +25 -11
- package/.docs/organized/code-examples/with-tap-runtime.md +8 -8
- package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
- package/.docs/raw/docs/(docs)/architecture.mdx +65 -53
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +3 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +14 -3
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +93 -9
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +21 -86
- package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
- package/.docs/raw/docs/guides/index.mdx +3 -0
- package/.docs/raw/docs/guides/input-history.mdx +55 -0
- package/.docs/raw/docs/guides/virtualization.mdx +63 -0
- package/.docs/raw/docs/ink/adapters.mdx +23 -1
- package/.docs/raw/docs/ink/hooks.mdx +22 -19
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +14 -8
- package/.docs/raw/docs/react-native/hooks.mdx +26 -18
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +39 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +3 -3
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +46 -4
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +54 -12
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +62 -4
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +120 -4
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -9
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +13 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +5 -5
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +3 -3
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
- package/.docs/raw/docs/tools/backend.mdx +19 -11
- package/.docs/raw/docs/tools/defining-tools.mdx +177 -52
- package/.docs/raw/docs/tools/index.mdx +7 -12
- package/.docs/raw/docs/tools/mcp.mdx +83 -15
- package/.docs/raw/docs/tools/multi-agent.mdx +5 -5
- package/.docs/raw/docs/tools/tool-ui.mdx +64 -28
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +4 -4
- package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
- package/.docs/raw/docs/ui/mermaid.mdx +16 -9
- 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 +2 -2
- 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 +16 -0
- package/.docs/raw/docs/utilities/react-o11y.mdx +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.js.map +1 -1
- package/dist/prepare-docs/copy-raw.js.map +1 -1
- package/dist/prepare-docs/prepare.js.map +1 -1
- package/dist/stdio.js.map +1 -1
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +4 -4
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/assistant-frame.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/make-assistant-visible.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/model-context.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/motivation.mdx +0 -0
- /package/.docs/raw/docs/{(docs)/copilots → copilots}/use-assistant-instructions.mdx +0 -0
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"@assistant-ui/react-ink": "workspace:*",
|
|
18
18
|
"@assistant-ui/react-ink-markdown": "workspace:*",
|
|
19
19
|
"ink": "^7.0.5",
|
|
20
|
-
"react": "^19.2.
|
|
20
|
+
"react": "^19.2.7"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/react": "^19.2.
|
|
24
|
-
"esbuild": "^0.28.
|
|
23
|
+
"@types/react": "^19.2.17",
|
|
24
|
+
"esbuild": "^0.28.1",
|
|
25
25
|
"tsx": "^4.22.4",
|
|
26
26
|
"typescript": "^6.0.3"
|
|
27
27
|
}
|
|
@@ -37,74 +37,40 @@ import { Box, Text } from "ink";
|
|
|
37
37
|
import {
|
|
38
38
|
AssistantRuntimeProvider,
|
|
39
39
|
useLocalRuntime,
|
|
40
|
-
|
|
40
|
+
StatusBarPrimitive,
|
|
41
|
+
Tools,
|
|
42
|
+
useAui,
|
|
41
43
|
} from "@assistant-ui/react-ink";
|
|
42
44
|
import { Thread } from "./components/thread.js";
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Simulate streaming with a markdown-rich response
|
|
55
|
-
const response = `## Response to your question
|
|
56
|
-
|
|
57
|
-
You asked: **"${userText}"**
|
|
58
|
-
|
|
59
|
-
Here's a quick example in JavaScript:
|
|
60
|
-
|
|
61
|
-
\`\`\`js
|
|
62
|
-
const promise = new Promise((resolve, reject) => {
|
|
63
|
-
setTimeout(() => resolve("done!"), 1000);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
promise.then(result => console.log(result));
|
|
67
|
-
\`\`\`
|
|
68
|
-
|
|
69
|
-
### Key points
|
|
70
|
-
|
|
71
|
-
- A \`Promise\` represents an **async operation**
|
|
72
|
-
- It can be *pending*, *fulfilled*, or *rejected*
|
|
73
|
-
- Use \`.then()\` and \`.catch()\` to handle results
|
|
74
|
-
|
|
75
|
-
> Promises are the foundation of modern async JavaScript.
|
|
76
|
-
|
|
77
|
-
| Method | Purpose |
|
|
78
|
-
|--------|---------|
|
|
79
|
-
| \`.then()\` | Handle success |
|
|
80
|
-
| \`.catch()\` | Handle errors |
|
|
81
|
-
| \`.finally()\` | Run cleanup |
|
|
82
|
-
|
|
83
|
-
Learn more at [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).`;
|
|
84
|
-
const words = response.split(" ");
|
|
85
|
-
|
|
86
|
-
let accumulated = "";
|
|
87
|
-
for (const word of words) {
|
|
88
|
-
accumulated += (accumulated ? " " : "") + word;
|
|
89
|
-
yield { content: [{ type: "text" as const, text: accumulated }] };
|
|
90
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
});
|
|
45
|
+
import { createScriptedAdapter, MODEL_NAME } from "./scripted-adapter.js";
|
|
46
|
+
import toolkit from "./tools.js";
|
|
47
|
+
|
|
48
|
+
const StatusBar = () => (
|
|
49
|
+
<StatusBarPrimitive.Root>
|
|
50
|
+
<Text dimColor>
|
|
51
|
+
model: <StatusBarPrimitive.ModelName name={MODEL_NAME} /> ·{" "}
|
|
52
|
+
<StatusBarPrimitive.MessageCount /> · <StatusBarPrimitive.Status />
|
|
53
|
+
</Text>
|
|
54
|
+
</StatusBarPrimitive.Root>
|
|
55
|
+
);
|
|
94
56
|
|
|
95
57
|
export const App = () => {
|
|
96
|
-
const adapter = useMemo(() =>
|
|
58
|
+
const adapter = useMemo(() => createScriptedAdapter(), []);
|
|
97
59
|
const runtime = useLocalRuntime(adapter);
|
|
60
|
+
const aui = useAui({
|
|
61
|
+
tools: Tools({ toolkit }),
|
|
62
|
+
});
|
|
98
63
|
|
|
99
64
|
return (
|
|
100
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
65
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
101
66
|
<Box flexDirection="column" padding={1}>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</
|
|
67
|
+
<Box>
|
|
68
|
+
<Text bold color="cyan">
|
|
69
|
+
demo-agent
|
|
70
|
+
</Text>
|
|
71
|
+
<Text dimColor>{" ~/acme-app"}</Text>
|
|
72
|
+
</Box>
|
|
73
|
+
<StatusBar />
|
|
108
74
|
<Box marginTop={1}>
|
|
109
75
|
<Thread />
|
|
110
76
|
</Box>
|
|
@@ -122,62 +88,68 @@ import { Box, Text } from "ink";
|
|
|
122
88
|
import {
|
|
123
89
|
ThreadPrimitive,
|
|
124
90
|
ComposerPrimitive,
|
|
125
|
-
|
|
91
|
+
MessagePrimitive,
|
|
92
|
+
ErrorPrimitive,
|
|
93
|
+
LoadingPrimitive,
|
|
94
|
+
LiveChecklist,
|
|
126
95
|
} from "@assistant-ui/react-ink";
|
|
127
96
|
import { MarkdownText } from "@assistant-ui/react-ink-markdown";
|
|
128
97
|
|
|
129
|
-
const UserMessage = () =>
|
|
130
|
-
|
|
131
|
-
s.message.parts
|
|
132
|
-
.filter((p) => p.type === "text")
|
|
133
|
-
.map((p) => ("text" in p ? p.text : ""))
|
|
134
|
-
.join(""),
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
return (
|
|
98
|
+
const UserMessage = () => (
|
|
99
|
+
<MessagePrimitive.Root>
|
|
138
100
|
<Box marginBottom={1}>
|
|
139
101
|
<Text bold color="green">
|
|
140
102
|
You:{" "}
|
|
141
103
|
</Text>
|
|
142
|
-
<
|
|
104
|
+
<MessagePrimitive.Content
|
|
105
|
+
renderText={({ part }) => <Text wrap="wrap">{part.text}</Text>}
|
|
106
|
+
/>
|
|
143
107
|
</Box>
|
|
144
|
-
|
|
145
|
-
|
|
108
|
+
</MessagePrimitive.Root>
|
|
109
|
+
);
|
|
146
110
|
|
|
147
|
-
const AssistantMessage = () =>
|
|
148
|
-
|
|
149
|
-
s.message.parts
|
|
150
|
-
.filter((p) => p.type === "text")
|
|
151
|
-
.map((p) => ("text" in p ? p.text : ""))
|
|
152
|
-
.join(""),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
return (
|
|
111
|
+
const AssistantMessage = () => (
|
|
112
|
+
<MessagePrimitive.Root>
|
|
156
113
|
<Box flexDirection="column" marginBottom={1}>
|
|
157
114
|
<Text bold color="blue">
|
|
158
115
|
AI:
|
|
159
116
|
</Text>
|
|
160
|
-
<
|
|
117
|
+
<MessagePrimitive.Content
|
|
118
|
+
renderText={({ part }) => <MarkdownText text={part.text} />}
|
|
119
|
+
renderReasoning={({ part }) => (
|
|
120
|
+
<Text dimColor italic>
|
|
121
|
+
{part.text}
|
|
122
|
+
</Text>
|
|
123
|
+
)}
|
|
124
|
+
/>
|
|
125
|
+
<LiveChecklist title="Plan" showProgress marginTop={1} />
|
|
126
|
+
<ErrorPrimitive.Root>
|
|
127
|
+
<ErrorPrimitive.Message />
|
|
128
|
+
</ErrorPrimitive.Root>
|
|
161
129
|
</Box>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
130
|
+
</MessagePrimitive.Root>
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const Loading = () => (
|
|
134
|
+
<LoadingPrimitive.Root marginBottom={1}>
|
|
135
|
+
<LoadingPrimitive.Spinner />
|
|
136
|
+
<Text> </Text>
|
|
137
|
+
<LoadingPrimitive.Text>Working</LoadingPrimitive.Text>
|
|
138
|
+
<Text> </Text>
|
|
139
|
+
<LoadingPrimitive.ElapsedTime />
|
|
140
|
+
</LoadingPrimitive.Root>
|
|
141
|
+
);
|
|
174
142
|
|
|
175
143
|
export const Thread = () => {
|
|
176
144
|
return (
|
|
177
145
|
<ThreadPrimitive.Root>
|
|
178
146
|
<ThreadPrimitive.Empty>
|
|
179
|
-
<Box marginBottom={1}>
|
|
180
|
-
<Text
|
|
147
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
148
|
+
<Text>
|
|
149
|
+
Working in this project. <Text color="yellow">fetchUser()</Text> is
|
|
150
|
+
flaky in prod and has no retry logic.
|
|
151
|
+
</Text>
|
|
152
|
+
<Text dimColor>{' try: "make fetchUser retry on failure"'}</Text>
|
|
181
153
|
</Box>
|
|
182
154
|
</ThreadPrimitive.Empty>
|
|
183
155
|
|
|
@@ -187,13 +159,14 @@ export const Thread = () => {
|
|
|
187
159
|
}
|
|
188
160
|
</ThreadPrimitive.Messages>
|
|
189
161
|
|
|
190
|
-
<
|
|
162
|
+
<Loading />
|
|
191
163
|
|
|
192
164
|
<Box borderStyle="round" borderColor="gray" paddingX={1}>
|
|
193
165
|
<Text color="gray">{"> "}</Text>
|
|
194
166
|
<ComposerPrimitive.Input
|
|
195
167
|
submitOnEnter
|
|
196
|
-
|
|
168
|
+
multiLine
|
|
169
|
+
placeholder="Type a message... (Enter to send)"
|
|
197
170
|
autoFocus
|
|
198
171
|
/>
|
|
199
172
|
</Box>
|
|
@@ -242,6 +215,240 @@ render(<App />);
|
|
|
242
215
|
|
|
243
216
|
```
|
|
244
217
|
|
|
218
|
+
## src/scripted-adapter.ts
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import type {
|
|
222
|
+
ChatModelAdapter,
|
|
223
|
+
ThreadAssistantMessagePart,
|
|
224
|
+
} from "@assistant-ui/react-ink";
|
|
225
|
+
|
|
226
|
+
export const MODEL_NAME = "demo-agent";
|
|
227
|
+
|
|
228
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
229
|
+
|
|
230
|
+
const RETRY_PATCH = `--- a/src/retry.ts
|
|
231
|
+
+++ b/src/retry.ts
|
|
232
|
+
@@ -1,6 +1,18 @@
|
|
233
|
+
export async function fetchUser(id: string) {
|
|
234
|
+
- const res = await fetch(\`/api/users/\${id}\`);
|
|
235
|
+
- return res.json();
|
|
236
|
+
+ return retry(() => fetch(\`/api/users/\${id}\`).then((r) => r.json()));
|
|
237
|
+
+}
|
|
238
|
+
+
|
|
239
|
+
+export async function retry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
|
|
240
|
+
+ let lastError: unknown;
|
|
241
|
+
+ for (let i = 0; i <= attempts; i++) {
|
|
242
|
+
+ try {
|
|
243
|
+
+ return await fn();
|
|
244
|
+
+ } catch (err) {
|
|
245
|
+
+ lastError = err;
|
|
246
|
+
+ await new Promise((r) => setTimeout(r, 2 ** i * 100));
|
|
247
|
+
+ }
|
|
248
|
+
+ }
|
|
249
|
+
+ throw lastError;
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
const RETRY_PATCH_FIX = `--- a/src/retry.ts
|
|
254
|
+
+++ b/src/retry.ts
|
|
255
|
+
@@ -6,7 +6,7 @@ export async function retry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
|
|
256
|
+
let lastError: unknown;
|
|
257
|
+
- for (let i = 0; i <= attempts; i++) {
|
|
258
|
+
+ for (let i = 0; i < attempts; i++) {
|
|
259
|
+
try {
|
|
260
|
+
return await fn();
|
|
261
|
+
} catch (err) {
|
|
262
|
+
`;
|
|
263
|
+
|
|
264
|
+
const isTaskRequest = (text: string) =>
|
|
265
|
+
/fetch|retry|resilient|flaky|backoff|robust|network|\byes\b|sure|\bok\b|go ahead|do it|please/i.test(
|
|
266
|
+
text,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const lastUserText = (
|
|
270
|
+
messages: readonly { role: string; content: readonly { type: string }[] }[],
|
|
271
|
+
) => {
|
|
272
|
+
const last = messages.filter((m) => m.role === "user").at(-1);
|
|
273
|
+
return (
|
|
274
|
+
last?.content
|
|
275
|
+
.filter((p): p is { type: "text"; text: string } => p.type === "text")
|
|
276
|
+
.map((p) => p.text)
|
|
277
|
+
.join("") ?? ""
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
async function* streamText(
|
|
282
|
+
parts: ThreadAssistantMessagePart[],
|
|
283
|
+
index: number,
|
|
284
|
+
text: string,
|
|
285
|
+
delay: number,
|
|
286
|
+
): AsyncGenerator<{ content: ThreadAssistantMessagePart[] }> {
|
|
287
|
+
for (const word of text.split(" ")) {
|
|
288
|
+
const p = parts[index] as Extract<
|
|
289
|
+
ThreadAssistantMessagePart,
|
|
290
|
+
{ type: "text" | "reasoning" }
|
|
291
|
+
>;
|
|
292
|
+
parts[index] = { ...p, text: p.text ? `${p.text} ${word}` : word };
|
|
293
|
+
yield { content: [...parts] };
|
|
294
|
+
await sleep(delay);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function* runAgentScript(): AsyncGenerator<{
|
|
299
|
+
content: ThreadAssistantMessagePart[];
|
|
300
|
+
}> {
|
|
301
|
+
const parts: ThreadAssistantMessagePart[] = [];
|
|
302
|
+
const emit = () => ({ content: [...parts] });
|
|
303
|
+
const setResult = (result: unknown, isError = false) => {
|
|
304
|
+
parts[parts.length - 1] = {
|
|
305
|
+
...parts[parts.length - 1],
|
|
306
|
+
result,
|
|
307
|
+
...(isError ? { isError: true } : {}),
|
|
308
|
+
} as ThreadAssistantMessagePart;
|
|
309
|
+
};
|
|
310
|
+
const pushToolCall = (
|
|
311
|
+
toolCallId: string,
|
|
312
|
+
toolName: string,
|
|
313
|
+
args: Record<string, string>,
|
|
314
|
+
) =>
|
|
315
|
+
parts.push({
|
|
316
|
+
type: "tool-call",
|
|
317
|
+
toolCallId,
|
|
318
|
+
toolName,
|
|
319
|
+
args,
|
|
320
|
+
argsText: JSON.stringify(args),
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const reasoningIdx = parts.push({ type: "reasoning", text: "" }) - 1;
|
|
324
|
+
yield* streamText(
|
|
325
|
+
parts,
|
|
326
|
+
reasoningIdx,
|
|
327
|
+
"Let me look at how fetchUser is implemented, wrap it in retry-with-backoff, and run the tests to make sure it holds up.",
|
|
328
|
+
20,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
pushToolCall("read-1", "read_file", { path: "src/retry.ts" });
|
|
332
|
+
yield emit();
|
|
333
|
+
await sleep(500);
|
|
334
|
+
setResult("12 lines read");
|
|
335
|
+
yield emit();
|
|
336
|
+
await sleep(300);
|
|
337
|
+
|
|
338
|
+
pushToolCall("patch-1", "apply_patch", {
|
|
339
|
+
path: "src/retry.ts",
|
|
340
|
+
patch: RETRY_PATCH,
|
|
341
|
+
});
|
|
342
|
+
yield emit();
|
|
343
|
+
await sleep(600);
|
|
344
|
+
setResult({ applied: true });
|
|
345
|
+
yield emit();
|
|
346
|
+
await sleep(300);
|
|
347
|
+
|
|
348
|
+
pushToolCall("test-1", "run_tests", { command: "pnpm test retry" });
|
|
349
|
+
yield emit();
|
|
350
|
+
await sleep(700);
|
|
351
|
+
setResult(
|
|
352
|
+
"FAIL src/retry.test.ts - retry() ran 4 times, expected 3 (off-by-one in loop bound)",
|
|
353
|
+
true,
|
|
354
|
+
);
|
|
355
|
+
yield emit();
|
|
356
|
+
await sleep(400);
|
|
357
|
+
|
|
358
|
+
const noteIdx = parts.push({ type: "text", text: "" }) - 1;
|
|
359
|
+
yield* streamText(
|
|
360
|
+
parts,
|
|
361
|
+
noteIdx,
|
|
362
|
+
"The loop bound was off by one. Tightening it and re-running.",
|
|
363
|
+
20,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
pushToolCall("patch-2", "apply_patch", {
|
|
367
|
+
path: "src/retry.ts",
|
|
368
|
+
patch: RETRY_PATCH_FIX,
|
|
369
|
+
});
|
|
370
|
+
yield emit();
|
|
371
|
+
await sleep(500);
|
|
372
|
+
setResult({ applied: true });
|
|
373
|
+
yield emit();
|
|
374
|
+
await sleep(300);
|
|
375
|
+
|
|
376
|
+
pushToolCall("test-2", "run_tests", { command: "pnpm test retry" });
|
|
377
|
+
yield emit();
|
|
378
|
+
await sleep(600);
|
|
379
|
+
setResult("PASS src/retry.test.ts (3 passed)");
|
|
380
|
+
yield emit();
|
|
381
|
+
await sleep(300);
|
|
382
|
+
|
|
383
|
+
const summary = `### Done
|
|
384
|
+
|
|
385
|
+
Added a \`retry()\` helper and routed \`fetchUser\` through it:
|
|
386
|
+
|
|
387
|
+
- exponential backoff (\`100ms\`, \`200ms\`, \`400ms\`)
|
|
388
|
+
- rethrows the **last error** after exhausting attempts
|
|
389
|
+
- fixed an off-by-one that ran one extra attempt
|
|
390
|
+
|
|
391
|
+
Tests are green.`;
|
|
392
|
+
const summaryIdx = parts.push({ type: "text", text: "" }) - 1;
|
|
393
|
+
yield* streamText(parts, summaryIdx, summary, 15);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function* runProposalReply(): AsyncGenerator<{
|
|
397
|
+
content: ThreadAssistantMessagePart[];
|
|
398
|
+
}> {
|
|
399
|
+
const response = `I'm set up to work in this repo. The obvious thing to fix is the flaky \`fetchUser()\` call in \`src/retry.ts\` — it has no retry logic, so a single network blip fails the request.
|
|
400
|
+
|
|
401
|
+
Want me to wrap it in retry-with-backoff? Just say *make fetchUser retry on failure*.`;
|
|
402
|
+
let acc = "";
|
|
403
|
+
for (const word of response.split(" ")) {
|
|
404
|
+
acc += (acc ? " " : "") + word;
|
|
405
|
+
yield { content: [{ type: "text", text: acc }] };
|
|
406
|
+
await sleep(20);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export const createScriptedAdapter = (): ChatModelAdapter => ({
|
|
411
|
+
run({ messages }) {
|
|
412
|
+
return isTaskRequest(lastUserText(messages))
|
|
413
|
+
? runAgentScript()
|
|
414
|
+
: runProposalReply();
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## src/tools.tsx
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
import { Box, Text } from "ink";
|
|
424
|
+
import {
|
|
425
|
+
defineToolkit,
|
|
426
|
+
DiffView,
|
|
427
|
+
type ToolCallMessagePartComponent,
|
|
428
|
+
} from "@assistant-ui/react-ink";
|
|
429
|
+
|
|
430
|
+
const ApplyPatchToolUI: ToolCallMessagePartComponent<
|
|
431
|
+
{ path: string; patch: string },
|
|
432
|
+
unknown
|
|
433
|
+
> = ({ args }) => {
|
|
434
|
+
if (!args?.patch) return null;
|
|
435
|
+
return (
|
|
436
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
437
|
+
<Text color="cyan">edit {args.path}</Text>
|
|
438
|
+
<DiffView patch={args.patch} showLineNumbers />
|
|
439
|
+
</Box>
|
|
440
|
+
);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
export default defineToolkit({
|
|
444
|
+
apply_patch: {
|
|
445
|
+
type: "backend",
|
|
446
|
+
render: ApplyPatchToolUI,
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
|
|
245
452
|
## tsconfig.json
|
|
246
453
|
|
|
247
454
|
```json
|
|
@@ -773,30 +773,30 @@ export default function Home() {
|
|
|
773
773
|
"dependencies": {
|
|
774
774
|
"@assistant-ui/react": "workspace:*",
|
|
775
775
|
"@assistant-ui/react-markdown": "workspace:*",
|
|
776
|
-
"@react-router/node": "^7.
|
|
777
|
-
"@react-router/serve": "^7.
|
|
776
|
+
"@react-router/node": "^7.17.0",
|
|
777
|
+
"@react-router/serve": "^7.17.0",
|
|
778
778
|
"class-variance-authority": "^0.7.1",
|
|
779
779
|
"clsx": "^2.1.1",
|
|
780
780
|
"eventsource-parser": "^3.1.0",
|
|
781
|
-
"isbot": "^5.1.
|
|
782
|
-
"lucide-react": "^1.
|
|
783
|
-
"openai": "^6.
|
|
784
|
-
"react": "^19.2.
|
|
785
|
-
"react-dom": "^19.2.
|
|
786
|
-
"react-router": "^7.
|
|
781
|
+
"isbot": "^5.1.42",
|
|
782
|
+
"lucide-react": "^1.18.0",
|
|
783
|
+
"openai": "^6.42.0",
|
|
784
|
+
"react": "^19.2.7",
|
|
785
|
+
"react-dom": "^19.2.7",
|
|
786
|
+
"react-router": "^7.17.0",
|
|
787
787
|
"remark-gfm": "^4.0.1",
|
|
788
788
|
"tailwind-merge": "^3.6.0"
|
|
789
789
|
},
|
|
790
790
|
"devDependencies": {
|
|
791
|
-
"@react-router/dev": "^7.
|
|
792
|
-
"@tailwindcss/vite": "^4.3.
|
|
793
|
-
"@types/node": "^25.9.
|
|
794
|
-
"@types/react": "^19.2.
|
|
791
|
+
"@react-router/dev": "^7.17.0",
|
|
792
|
+
"@tailwindcss/vite": "^4.3.1",
|
|
793
|
+
"@types/node": "^25.9.3",
|
|
794
|
+
"@types/react": "^19.2.17",
|
|
795
795
|
"@types/react-dom": "^19.2.3",
|
|
796
|
-
"tailwindcss": "^4.3.
|
|
796
|
+
"tailwindcss": "^4.3.1",
|
|
797
797
|
"tw-animate-css": "^1.4.0",
|
|
798
798
|
"typescript": "^6.0.3",
|
|
799
|
-
"vite": "^8.0.
|
|
799
|
+
"vite": "^8.0.16",
|
|
800
800
|
"vite-tsconfig-paths": "^6.1.1"
|
|
801
801
|
}
|
|
802
802
|
}
|
|
@@ -514,22 +514,22 @@ export default withAui(nextConfig);
|
|
|
514
514
|
"start": "next start"
|
|
515
515
|
},
|
|
516
516
|
"dependencies": {
|
|
517
|
-
"@ai-sdk/openai": "^3.0.
|
|
518
|
-
"@ai-sdk/react": "^3.0.
|
|
517
|
+
"@ai-sdk/openai": "^3.0.71",
|
|
518
|
+
"@ai-sdk/react": "^3.0.206",
|
|
519
519
|
"@assistant-ui/react": "workspace:*",
|
|
520
520
|
"@assistant-ui/react-ai-sdk": "workspace:*",
|
|
521
521
|
"@assistant-ui/react-markdown": "workspace:*",
|
|
522
522
|
"@assistant-ui/store": "workspace:*",
|
|
523
523
|
"@assistant-ui/tap": "workspace:*",
|
|
524
524
|
"@assistant-ui/ui": "workspace:*",
|
|
525
|
-
"ai": "^6.0.
|
|
525
|
+
"ai": "^6.0.204",
|
|
526
526
|
"assistant-stream": "workspace:*",
|
|
527
527
|
"class-variance-authority": "^0.7.1",
|
|
528
528
|
"clsx": "^2.1.1",
|
|
529
|
-
"lucide-react": "^1.
|
|
530
|
-
"next": "^16.2.
|
|
531
|
-
"react": "^19.2.
|
|
532
|
-
"react-dom": "^19.2.
|
|
529
|
+
"lucide-react": "^1.18.0",
|
|
530
|
+
"next": "^16.2.9",
|
|
531
|
+
"react": "^19.2.7",
|
|
532
|
+
"react-dom": "^19.2.7",
|
|
533
533
|
"redis": "^6.0.0",
|
|
534
534
|
"tailwind-merge": "^3.6.0",
|
|
535
535
|
"zod": "^4.4.3"
|
|
@@ -537,12 +537,12 @@ export default withAui(nextConfig);
|
|
|
537
537
|
"devDependencies": {
|
|
538
538
|
"@assistant-ui/next": "workspace:*",
|
|
539
539
|
"@assistant-ui/x-buildutils": "workspace:*",
|
|
540
|
-
"@tailwindcss/postcss": "^4.3.
|
|
541
|
-
"@types/node": "^25.9.
|
|
542
|
-
"@types/react": "^19.2.
|
|
540
|
+
"@tailwindcss/postcss": "^4.3.1",
|
|
541
|
+
"@types/node": "^25.9.3",
|
|
542
|
+
"@types/react": "^19.2.17",
|
|
543
543
|
"@types/react-dom": "^19.2.3",
|
|
544
544
|
"postcss": "^8.5.15",
|
|
545
|
-
"tailwindcss": "^4.3.
|
|
545
|
+
"tailwindcss": "^4.3.1",
|
|
546
546
|
"tw-animate-css": "^1.4.0",
|
|
547
547
|
"typescript": "^6.0.3"
|
|
548
548
|
}
|