@tanstack/ai 0.42.0 → 0.43.0
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 +15 -1
- package/dist/esm/activities/chat/adapter.js +23 -16
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/agent-loop-strategies.d.ts +5 -36
- package/dist/esm/activities/chat/agent-loop-strategies.js +75 -21
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
- package/dist/esm/activities/chat/cancel.d.ts +40 -0
- package/dist/esm/activities/chat/cancel.js +54 -0
- package/dist/esm/activities/chat/cancel.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +28 -21
- package/dist/esm/activities/chat/index.js +2100 -1813
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
- package/dist/esm/activities/chat/mcp/manager.js +90 -77
- package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
- package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
- package/dist/esm/activities/chat/messages.js +397 -346
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.js +17 -15
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
- package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
- package/dist/esm/activities/chat/middleware/compose.js +623 -531
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.js +12 -5
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
- package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
- package/dist/esm/activities/chat/middleware/locks.js +71 -0
- package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
- package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
- package/dist/esm/activities/chat/middleware/run-store.js +176 -0
- package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
- package/dist/esm/activities/chat/middleware/validate.js +23 -28
- package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
- package/dist/esm/activities/chat/stream/json-parser.js +39 -25
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
- package/dist/esm/activities/chat/stream/processor.js +1341 -1542
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/stream/strategies.js +69 -53
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
- package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
- package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
- package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.js +85 -47
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateAudio/adapter.js +22 -15
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +4 -0
- package/dist/esm/activities/generateAudio/index.js +141 -105
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.js +22 -15
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +4 -0
- package/dist/esm/activities/generateImage/index.js +155 -111
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +22 -15
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
- package/dist/esm/activities/generateSpeech/index.js +159 -110
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +22 -15
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
- package/dist/esm/activities/generateTranscription/index.js +159 -100
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.js +36 -29
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +143 -19
- package/dist/esm/activities/generateVideo/index.js +456 -279
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.js +60 -48
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/index.js +8 -34
- package/dist/esm/activities/middleware/index.d.ts +1 -1
- package/dist/esm/activities/middleware/run.d.ts +10 -0
- package/dist/esm/activities/middleware/run.js +53 -29
- package/dist/esm/activities/middleware/run.js.map +1 -1
- package/dist/esm/activities/middleware/types.d.ts +44 -6
- package/dist/esm/activities/stream-generation-result.d.ts +4 -1
- package/dist/esm/activities/stream-generation-result.js +79 -44
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.js +22 -15
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +27 -0
- package/dist/esm/activities/summarize/index.js +268 -102
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -1
- package/dist/esm/adapter-internals.js +4 -11
- package/dist/esm/client.d.ts +25 -3
- package/dist/esm/client.js +131 -64
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/custom-events.d.ts +76 -0
- package/dist/esm/custom-events.js +37 -0
- package/dist/esm/custom-events.js.map +1 -0
- package/dist/esm/delivery-detach.d.ts +50 -0
- package/dist/esm/delivery-detach.js +71 -0
- package/dist/esm/delivery-detach.js.map +1 -0
- package/dist/esm/delivery-disconnect.d.ts +62 -0
- package/dist/esm/delivery-disconnect.js +81 -0
- package/dist/esm/delivery-disconnect.js.map +1 -0
- package/dist/esm/extend-adapter.js +19 -17
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +24 -6
- package/dist/esm/index.js +30 -98
- package/dist/esm/interrupt-resume.d.ts +71 -0
- package/dist/esm/interrupt-resume.js +438 -0
- package/dist/esm/interrupt-resume.js.map +1 -0
- package/dist/esm/interrupt-serialization.d.ts +12 -0
- package/dist/esm/interrupt-serialization.js +178 -0
- package/dist/esm/interrupt-serialization.js.map +1 -0
- package/dist/esm/interrupts.d.ts +84 -0
- package/dist/esm/interrupts.js +31 -0
- package/dist/esm/interrupts.js.map +1 -0
- package/dist/esm/locks.d.ts +10 -0
- package/dist/esm/locks.js +2 -0
- package/dist/esm/logger/console-logger.js +101 -78
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/internal-logger.js +104 -89
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +54 -49
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/logger/types.d.ts +1 -1
- package/dist/esm/middlewares/content-guard.js +142 -148
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/middlewares/index.js +2 -6
- package/dist/esm/middlewares/otel.js +598 -732
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/middlewares/usage-attributes.js +47 -40
- package/dist/esm/middlewares/usage-attributes.js.map +1 -1
- package/dist/esm/realtime/event-emitter.js +24 -25
- package/dist/esm/realtime/event-emitter.js.map +1 -1
- package/dist/esm/realtime/index.d.ts +5 -9
- package/dist/esm/realtime/index.js +29 -6
- package/dist/esm/realtime/index.js.map +1 -1
- package/dist/esm/scope.d.ts +47 -0
- package/dist/esm/stream-durability.d.ts +171 -0
- package/dist/esm/stream-durability.js +295 -0
- package/dist/esm/stream-durability.js.map +1 -0
- package/dist/esm/stream-to-response.d.ts +178 -13
- package/dist/esm/stream-to-response.js +663 -115
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js +30 -16
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/system-prompts.js +27 -21
- package/dist/esm/system-prompts.js.map +1 -1
- package/dist/esm/tool-registry.js +72 -45
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/tools/provider-tool.js +14 -5
- package/dist/esm/tools/provider-tool.js.map +1 -1
- package/dist/esm/types.d.ts +321 -42
- package/dist/esm/types.js +2 -0
- package/dist/esm/utilities/ag-ui-wire.js +79 -93
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +26 -4
- package/dist/esm/utilities/chat-params.js +218 -92
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/errors.js +28 -18
- package/dist/esm/utilities/errors.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +46 -41
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/numbers.js +13 -10
- package/dist/esm/utilities/numbers.js.map +1 -1
- package/dist/esm/utilities/provider-executed.js +20 -11
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/sampling-keys.js +31 -19
- package/dist/esm/utilities/sampling-keys.js.map +1 -1
- package/dist/esm/utilities/tool-result.js +42 -30
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/dist/esm/utilities/usage.js +27 -9
- package/dist/esm/utilities/usage.js.map +1 -1
- package/dist/esm/utils.js +26 -18
- package/dist/esm/utils.js.map +1 -1
- package/package.json +10 -6
- package/skills/ai-core/SKILL.md +69 -18
- package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
- package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
- package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
- package/skills/ai-core/chat-experience/SKILL.md +98 -11
- package/skills/ai-core/client-persistence/SKILL.md +277 -0
- package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
- package/skills/ai-core/debug-logging/SKILL.md +1 -1
- package/skills/ai-core/locks/SKILL.md +143 -0
- package/skills/ai-core/media-generation/SKILL.md +144 -12
- package/skills/ai-core/middleware/SKILL.md +258 -33
- package/skills/ai-core/structured-outputs/SKILL.md +1 -1
- package/skills/ai-core/tool-calling/SKILL.md +54 -61
- package/src/activities/chat/agent-loop-strategies.ts +5 -39
- package/src/activities/chat/cancel.ts +81 -0
- package/src/activities/chat/index.ts +1091 -200
- package/src/activities/chat/mcp/manager.ts +4 -4
- package/src/activities/chat/mcp/types.ts +2 -2
- package/src/activities/chat/messages.ts +5 -3
- package/src/activities/chat/middleware/builder.ts +1 -1
- package/src/activities/chat/middleware/compose.ts +186 -9
- package/src/activities/chat/middleware/index.ts +26 -0
- package/src/activities/chat/middleware/locks.ts +102 -0
- package/src/activities/chat/middleware/pending-turn.ts +47 -0
- package/src/activities/chat/middleware/run-disconnect.ts +62 -0
- package/src/activities/chat/middleware/run-store.ts +412 -0
- package/src/activities/chat/middleware/types.ts +62 -1
- package/src/activities/chat/stream/processor.ts +189 -5
- package/src/activities/chat/tools/approval-schema.ts +205 -0
- package/src/activities/chat/tools/tool-calls.ts +106 -13
- package/src/activities/chat/tools/tool-definition.ts +210 -39
- package/src/activities/generateAudio/index.ts +20 -3
- package/src/activities/generateImage/index.ts +20 -3
- package/src/activities/generateSpeech/index.ts +25 -3
- package/src/activities/generateTranscription/index.ts +26 -3
- package/src/activities/generateVideo/index.ts +345 -82
- package/src/activities/middleware/index.ts +2 -0
- package/src/activities/middleware/run.ts +31 -0
- package/src/activities/middleware/types.ts +49 -5
- package/src/activities/stream-generation-result.ts +30 -2
- package/src/activities/summarize/chat-stream-summarize.ts +5 -0
- package/src/activities/summarize/index.ts +200 -10
- package/src/adapter-internals.ts +10 -1
- package/src/client.ts +244 -0
- package/src/custom-events.ts +107 -0
- package/src/delivery-detach.ts +72 -0
- package/src/delivery-disconnect.ts +84 -0
- package/src/index.ts +138 -1
- package/src/interrupt-resume.ts +824 -0
- package/src/interrupt-serialization.ts +183 -0
- package/src/interrupts.ts +146 -0
- package/src/locks.ts +17 -0
- package/src/logger/types.ts +1 -1
- package/src/middlewares/otel.ts +1 -0
- package/src/realtime/index.ts +5 -9
- package/src/scope.ts +47 -0
- package/src/stream-durability.ts +598 -0
- package/src/stream-to-response.ts +1051 -95
- package/src/strip-to-spec-middleware.ts +3 -3
- package/src/types.ts +405 -45
- package/src/utilities/chat-params.ts +245 -55
- package/dist/esm/activities/index.js.map +0 -1
- package/dist/esm/adapter-internals.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/middlewares/index.js.map +0 -1
|
@@ -4,12 +4,12 @@ description: >
|
|
|
4
4
|
Isomorphic tool system: toolDefinition() with Zod schemas,
|
|
5
5
|
.server() and .client() implementations, passing tools to both
|
|
6
6
|
chat() on server and useChat/clientTools on client, tool approval
|
|
7
|
-
flows with needsApproval and
|
|
7
|
+
flows with needsApproval and bound interrupts (resolveInterrupt), lazy tool
|
|
8
8
|
discovery with lazy:true, rendering ToolCallPart and ToolResultPart
|
|
9
9
|
in UI.
|
|
10
10
|
type: sub-skill
|
|
11
11
|
library: tanstack-ai
|
|
12
|
-
library_version: '0.
|
|
12
|
+
library_version: '0.42.0'
|
|
13
13
|
sources:
|
|
14
14
|
- 'TanStack/ai:docs/tools/tools.md'
|
|
15
15
|
- 'TanStack/ai:docs/tools/server-tools.md'
|
|
@@ -108,14 +108,14 @@ function ChatPage() {
|
|
|
108
108
|
connection: fetchServerSentEvents("/api/chat"),
|
|
109
109
|
tools,
|
|
110
110
|
});
|
|
111
|
-
type Messages = InferChatMessages<typeof chatOptions>;
|
|
112
|
-
|
|
113
111
|
const { messages, sendMessage } = useChat(chatOptions);
|
|
112
|
+
// InferChatMessages ties part types to the configured tools when needed:
|
|
113
|
+
// type Messages = InferChatMessages<typeof chatOptions>
|
|
114
114
|
|
|
115
115
|
return (
|
|
116
116
|
<div>
|
|
117
117
|
<span>Cart: {cartCount}</span>
|
|
118
|
-
{
|
|
118
|
+
{messages.map((msg) => (
|
|
119
119
|
<div key={msg.id}>
|
|
120
120
|
{msg.parts.map((part) => {
|
|
121
121
|
if (part.type === "text") return <p>{part.content}</p>;
|
|
@@ -239,9 +239,11 @@ function ChatPage() {
|
|
|
239
239
|
|
|
240
240
|
### Pattern 3: Tool with Approval Flow
|
|
241
241
|
|
|
242
|
-
Set `needsApproval: true` in the definition. Execution pauses
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
Set `needsApproval: true` in the definition. Execution pauses with
|
|
243
|
+
`RUN_FINISHED.outcome.type === 'interrupt'`. The primary client API is bound
|
|
244
|
+
`interrupts` + `resolveInterrupt` / `resolveInterrupts` / `cancel`.
|
|
245
|
+
`addToolApprovalResponse` and `pendingInterrupts` remain as deprecated
|
|
246
|
+
compatibility shims during migration.
|
|
245
247
|
|
|
246
248
|
```typescript
|
|
247
249
|
import { toolDefinition } from '@tanstack/ai'
|
|
@@ -265,59 +267,40 @@ export const sendEmail = sendEmailDef.server(async ({ to, subject, body }) => {
|
|
|
265
267
|
})
|
|
266
268
|
```
|
|
267
269
|
|
|
268
|
-
|
|
270
|
+
Server route must forward `resume` / `parentRunId` (via `chatParamsFromRequest`
|
|
271
|
+
or equivalent). Client -- render bound interrupts:
|
|
269
272
|
|
|
270
273
|
```typescript
|
|
271
274
|
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
|
|
272
275
|
|
|
273
276
|
function ChatPage() {
|
|
274
|
-
const { messages,
|
|
277
|
+
const { messages, interrupts, sendMessage } = useChat({
|
|
275
278
|
connection: fetchServerSentEvents("/api/chat"),
|
|
276
279
|
});
|
|
277
280
|
|
|
278
281
|
return (
|
|
279
282
|
<div>
|
|
283
|
+
{interrupts.map((interrupt) => {
|
|
284
|
+
if (interrupt.kind !== "tool-approval") return null;
|
|
285
|
+
return (
|
|
286
|
+
<div key={interrupt.id}>
|
|
287
|
+
<p>Approve "{interrupt.toolName}"?</p>
|
|
288
|
+
<pre>{JSON.stringify(interrupt.originalArgs, null, 2)}</pre>
|
|
289
|
+
<button onClick={() => interrupt.resolveInterrupt(true)}>
|
|
290
|
+
Approve
|
|
291
|
+
</button>
|
|
292
|
+
<button onClick={() => interrupt.resolveInterrupt(false)}>
|
|
293
|
+
Deny
|
|
294
|
+
</button>
|
|
295
|
+
<button onClick={() => interrupt.cancel()}>Cancel</button>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
})}
|
|
280
299
|
{messages.map((msg) => (
|
|
281
300
|
<div key={msg.id}>
|
|
282
|
-
{msg.parts.map((part) =>
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
part.type === "tool-call" &&
|
|
286
|
-
part.state === "approval-requested" &&
|
|
287
|
-
part.approval
|
|
288
|
-
) {
|
|
289
|
-
return (
|
|
290
|
-
<div key={part.id}>
|
|
291
|
-
<p>Approve "{part.name}"?</p>
|
|
292
|
-
{/* `part.input` is the parsed, typed object (populated once
|
|
293
|
-
the arguments are complete, as they are at approval
|
|
294
|
-
time); `part.arguments` remains the raw JSON string. */}
|
|
295
|
-
<pre>{JSON.stringify(part.input, null, 2)}</pre>
|
|
296
|
-
<button
|
|
297
|
-
onClick={() =>
|
|
298
|
-
addToolApprovalResponse({
|
|
299
|
-
id: part.approval!.id,
|
|
300
|
-
approved: true,
|
|
301
|
-
})
|
|
302
|
-
}
|
|
303
|
-
>
|
|
304
|
-
Approve
|
|
305
|
-
</button>
|
|
306
|
-
<button
|
|
307
|
-
onClick={() =>
|
|
308
|
-
addToolApprovalResponse({
|
|
309
|
-
id: part.approval!.id,
|
|
310
|
-
approved: false,
|
|
311
|
-
})
|
|
312
|
-
}
|
|
313
|
-
>
|
|
314
|
-
Deny
|
|
315
|
-
</button>
|
|
316
|
-
</div>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
return null;
|
|
320
|
-
})}
|
|
301
|
+
{msg.parts.map((part) =>
|
|
302
|
+
part.type === "text" ? <p key={part.content}>{part.content}</p> : null
|
|
303
|
+
)}
|
|
321
304
|
</div>
|
|
322
305
|
))}
|
|
323
306
|
</div>
|
|
@@ -325,14 +308,24 @@ function ChatPage() {
|
|
|
325
308
|
}
|
|
326
309
|
```
|
|
327
310
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
311
|
+
Batch all pending approvals with `resolveInterrupts` (void — submission is
|
|
312
|
+
async; watch `resuming` / `interruptErrors`):
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// Payloadless tool-approvals only
|
|
316
|
+
resolveInterrupts(true)
|
|
317
|
+
|
|
318
|
+
// Or per-item:
|
|
319
|
+
resolveInterrupts((interrupt) => {
|
|
320
|
+
if (interrupt.kind === 'tool-approval') {
|
|
321
|
+
interrupt.resolveInterrupt(true)
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Migration: `pendingInterrupts` aliases `interrupts`; `addToolApprovalResponse`
|
|
327
|
+
forwards to the matching bound approval when present. Prefer the bound methods
|
|
328
|
+
above for new code. See `docs/interrupts/`.
|
|
336
329
|
|
|
337
330
|
### Pattern 4: Lazy Tool Discovery
|
|
338
331
|
|
|
@@ -375,8 +368,8 @@ export async function POST(request: Request) {
|
|
|
375
368
|
adapter: openaiText('gpt-5.5'),
|
|
376
369
|
messages,
|
|
377
370
|
tools: [getProducts, compareProducts],
|
|
378
|
-
// maxIterations bounds model turns, not tool calls.
|
|
379
|
-
//
|
|
371
|
+
// maxIterations bounds model turns, not tool calls. For tool budgets,
|
|
372
|
+
// use middleware onBeforeToolCall + onShouldContinue (see agentic-cycle docs).
|
|
380
373
|
agentLoopStrategy: maxIterations(20),
|
|
381
374
|
})
|
|
382
375
|
return toServerSentEventsResponse(stream)
|
|
@@ -649,7 +642,7 @@ import { anthropicText } from '@tanstack/ai-anthropic'
|
|
|
649
642
|
export async function POST(request: Request) {
|
|
650
643
|
const { messages } = await request.json()
|
|
651
644
|
const stream = chat({
|
|
652
|
-
adapter: anthropicText('claude-sonnet-4-
|
|
645
|
+
adapter: anthropicText('claude-sonnet-4-6'),
|
|
653
646
|
messages,
|
|
654
647
|
tools: [
|
|
655
648
|
codeExecutionTool(
|
|
@@ -685,7 +678,7 @@ import { openaiText } from '@tanstack/ai-openai'
|
|
|
685
678
|
export async function POST(request: Request) {
|
|
686
679
|
const { messages } = await request.json()
|
|
687
680
|
const stream = chat({
|
|
688
|
-
adapter: openaiText('gpt-5.
|
|
681
|
+
adapter: openaiText('gpt-5.5'),
|
|
689
682
|
messages,
|
|
690
683
|
tools: [
|
|
691
684
|
shellTool({
|
|
@@ -4,9 +4,10 @@ import type { AgentLoopStrategy } from '../../types'
|
|
|
4
4
|
* Creates a strategy that continues for a maximum number of **model turns**
|
|
5
5
|
* (iterations), not tool calls.
|
|
6
6
|
*
|
|
7
|
-
* One iteration can still emit many parallel tool calls.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* One iteration can still emit many parallel tool calls. For a tool-call
|
|
8
|
+
* budget, use middleware with `onBeforeToolCall` (per-turn cap) and
|
|
9
|
+
* `onShouldContinue` (cumulative run budget) — see the docs recipe under
|
|
10
|
+
* Agentic Cycle.
|
|
10
11
|
*
|
|
11
12
|
* @param max - Maximum number of model turns to allow
|
|
12
13
|
* @returns AgentLoopStrategy that stops after max iterations
|
|
@@ -26,40 +27,6 @@ export function maxIterations(max: number): AgentLoopStrategy {
|
|
|
26
27
|
return ({ iterationCount }) => iterationCount < max
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
/**
|
|
30
|
-
* Creates a strategy that continues while `toolCallCount < max`.
|
|
31
|
-
*
|
|
32
|
-
* Unlike {@link maxIterations} (which counts model turns), this bounds
|
|
33
|
-
* **emitted** tool calls counted during the run (including ones skipped by
|
|
34
|
-
* `maxToolCallsPerTurn`). Strategies only run between turns, so the turn that
|
|
35
|
-
* crosses `max` is not truncated — the final count (and executions, unless
|
|
36
|
-
* `maxToolCallsPerTurn` is set) may exceed `max`. Pair with
|
|
37
|
-
* `chat({ maxToolCallsPerTurn })` to also cap parallel fan-out inside a single
|
|
38
|
-
* turn.
|
|
39
|
-
*
|
|
40
|
-
* @param max - Maximum cumulative emitted tool calls before stopping further turns
|
|
41
|
-
* @returns AgentLoopStrategy that returns true while `toolCallCount < max`
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* import { chat, combineStrategies, maxIterations, maxToolCalls } from '@tanstack/ai'
|
|
46
|
-
*
|
|
47
|
-
* const stream = chat({
|
|
48
|
-
* adapter: openaiText('gpt-4o'),
|
|
49
|
-
* messages: [...],
|
|
50
|
-
* tools: [weatherTool],
|
|
51
|
-
* maxToolCallsPerTurn: 10,
|
|
52
|
-
* agentLoopStrategy: combineStrategies([
|
|
53
|
-
* maxIterations(20),
|
|
54
|
-
* maxToolCalls(20),
|
|
55
|
-
* ]),
|
|
56
|
-
* })
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
export function maxToolCalls(max: number): AgentLoopStrategy {
|
|
60
|
-
return ({ toolCallCount }) => toolCallCount < max
|
|
61
|
-
}
|
|
62
|
-
|
|
63
30
|
/**
|
|
64
31
|
* Creates a strategy that continues until a specific finish reason is encountered
|
|
65
32
|
*
|
|
@@ -99,7 +66,7 @@ export function untilFinishReason(
|
|
|
99
66
|
* All strategies must return true to continue
|
|
100
67
|
*
|
|
101
68
|
* @param strategies - Array of strategies to combine
|
|
102
|
-
* @returns AgentLoopStrategy that continues only if all strategies
|
|
69
|
+
* @returns AgentLoopStrategy that continues only if all strategies agree
|
|
103
70
|
*
|
|
104
71
|
* @example
|
|
105
72
|
* ```typescript
|
|
@@ -110,7 +77,6 @@ export function untilFinishReason(
|
|
|
110
77
|
* tools: [weatherTool],
|
|
111
78
|
* agentLoopStrategy: combineStrategies([
|
|
112
79
|
* maxIterations(10),
|
|
113
|
-
* maxToolCalls(20),
|
|
114
80
|
* ({ messages }) => messages.length < 100,
|
|
115
81
|
* ]),
|
|
116
82
|
* });
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Out-of-band run cancellation.
|
|
3
|
+
*
|
|
4
|
+
* `ChatClient.stop()` only aborts a local `AbortController`; it sends nothing to
|
|
5
|
+
* the server. A user pressing Stop and a user refreshing the page produce the
|
|
6
|
+
* IDENTICAL TCP close, so intent is **not inferable** from a disconnect. It has
|
|
7
|
+
* to arrive out of band, and there are exactly two bands:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Durable** — {@link requestRunCancel} records the intent on the run's
|
|
10
|
+
* `RunRecord`. This is the only channel that works when the run is being
|
|
11
|
+
* driven by a DIFFERENT host than the one the cancel request reached, which
|
|
12
|
+
* is the normal case for a detached run.
|
|
13
|
+
* 2. **In-process** — abort the run's signal with {@link RUN_CANCEL_REASON}.
|
|
14
|
+
* Core reads the reason back when it builds `AbortInfo`, so
|
|
15
|
+
* `AbortInfo.cancelRequested` is `true` for that abort and `false` for a
|
|
16
|
+
* disconnect. This is the fast path when the cancel reaches the driving host.
|
|
17
|
+
*
|
|
18
|
+
* A cancel endpoint SHOULD do both: record it (so a remote driver observes it)
|
|
19
|
+
* and abort locally (so a co-located driver stops immediately).
|
|
20
|
+
*/
|
|
21
|
+
import type { RunStore } from './middleware/run-store'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Abort reason that marks an abort as an explicit cancellation.
|
|
25
|
+
*
|
|
26
|
+
* Namespaced so an application's own reason string cannot collide with it by
|
|
27
|
+
* accident, and matched with `===` (never a substring test) so an arbitrary
|
|
28
|
+
* provider error message can never be read as a deliberate cancel.
|
|
29
|
+
*/
|
|
30
|
+
export const RUN_CANCEL_REASON = 'tanstack-ai:cancel-requested'
|
|
31
|
+
|
|
32
|
+
/** Whether an abort reason means "the user explicitly cancelled this run". */
|
|
33
|
+
export function isCancelRequestedReason(reason: string | undefined): boolean {
|
|
34
|
+
return reason === RUN_CANCEL_REASON
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Record an explicit cancel on the run record.
|
|
39
|
+
*
|
|
40
|
+
* Deliberately does NOT set a status. The driver is the only actor that knows
|
|
41
|
+
* when the agent has actually stopped and the sandbox has been torn down, so it
|
|
42
|
+
* owns the transition to `'aborted'`. Writing a terminal status here would tell
|
|
43
|
+
* every reader the run is over while the agent is still burning tokens.
|
|
44
|
+
*
|
|
45
|
+
* A no-op for an unknown `runId`, inheriting `RunStore.update`'s documented
|
|
46
|
+
* invariant.
|
|
47
|
+
*/
|
|
48
|
+
export async function requestRunCancel(
|
|
49
|
+
runs: RunStore,
|
|
50
|
+
runId: string,
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
await runs.update(runId, { cancelRequested: true })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether an explicit cancel has been recorded for `runId`.
|
|
57
|
+
*
|
|
58
|
+
* Answers `false` rather than throwing when the store cannot be read. Callers
|
|
59
|
+
* are middleware abort hooks, which are already on a teardown path, and a
|
|
60
|
+
* store failure there must not replace the caller's own reason for tearing
|
|
61
|
+
* down with a store error. The cost of a false negative is that a cancel
|
|
62
|
+
* degrades into a detach — the run record gains `detachedSince`/`sandboxKey`
|
|
63
|
+
* instead of transitioning to `'aborted'`. `@tanstack/ai-sandbox`'s
|
|
64
|
+
* `reapDetachedRuns` recovers that run once the `detachedRunTtlMs` the
|
|
65
|
+
* application passes to that sweep has elapsed — nothing derives it from
|
|
66
|
+
* `withSandbox`, which has no TTL option — so the cost is a delayed teardown
|
|
67
|
+
* rather than a lost one, provided the application actually schedules the
|
|
68
|
+
* sweep, which is its job and not the framework's. Still strictly better than
|
|
69
|
+
* failing the teardown.
|
|
70
|
+
*/
|
|
71
|
+
export async function wasCancelRequested(
|
|
72
|
+
runs: RunStore,
|
|
73
|
+
runId: string,
|
|
74
|
+
): Promise<boolean> {
|
|
75
|
+
try {
|
|
76
|
+
const record = await runs.get(runId)
|
|
77
|
+
return record?.cancelRequested === true
|
|
78
|
+
} catch {
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
}
|