@tanstack/ai 0.61.0 → 0.64.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 +1 -0
- package/dist/esm/activities/chat/agents/define-agent.d.ts +17 -5
- package/dist/esm/activities/chat/agents/define-agent.js.map +1 -1
- package/dist/esm/activities/chat/agents/spawn.d.ts +2 -0
- package/dist/esm/activities/chat/agents/spawn.js +10 -33
- package/dist/esm/activities/chat/agents/spawn.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +2 -0
- package/dist/esm/activities/chat/index.js +166 -60
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +43 -22
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +1 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js +6 -2
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +11 -9
- package/dist/esm/activities/chat/stream/processor.js +38 -18
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +8 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +6 -5
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +20 -3
- package/dist/esm/activities/chat/tools/tool-calls.js +126 -36
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/evaluate/adapter.d.ts +4 -0
- package/dist/esm/activities/evaluate/adapter.js.map +1 -1
- package/dist/esm/activities/evaluate/index.d.ts +4 -0
- package/dist/esm/activities/evaluate/index.js +3 -1
- package/dist/esm/activities/evaluate/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +1 -1
- package/dist/esm/activities/generateSpeech/index.js +1 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +14 -6
- package/dist/esm/activities/generateVideo/adapter.js +6 -3
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +5 -4
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.d.ts +12 -3
- package/dist/esm/activities/generateVideo/snap.js +47 -8
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/generateVoice/index.d.ts +1 -1
- package/dist/esm/activities/generateVoice/index.js +1 -1
- package/dist/esm/activities/generateVoice/index.js.map +1 -1
- package/dist/esm/activities/index.d.ts +2 -2
- package/dist/esm/activities/index.js +2 -2
- package/dist/esm/adapter-internals.d.ts +1 -0
- package/dist/esm/adapter-internals.js +2 -1
- package/dist/esm/client.d.ts +1 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/interrupt-resume.js +29 -4
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/middlewares/otel.d.ts +5 -2
- package/dist/esm/middlewares/otel.js +114 -0
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +70 -4
- package/dist/esm/utilities/ag-ui-wire.js +8 -4
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/merge-streams.d.ts +6 -0
- package/dist/esm/utilities/merge-streams.js +37 -0
- package/dist/esm/utilities/merge-streams.js.map +1 -0
- package/dist/esm/utilities/reasoning-encrypted-value.d.ts +8 -0
- package/dist/esm/utilities/reasoning-encrypted-value.js +11 -1
- package/dist/esm/utilities/reasoning-encrypted-value.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +2 -1
- package/dist/esm/utilities/tool-result.js +4 -1
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/chat-experience/SKILL.md +120 -0
- package/skills/ai-core/media-generation/SKILL.md +3 -3
- package/skills/ai-core/tool-calling/SKILL.md +103 -0
- package/src/activities/chat/agents/define-agent.ts +20 -3
- package/src/activities/chat/agents/spawn.ts +22 -43
- package/src/activities/chat/index.ts +248 -65
- package/src/activities/chat/messages.ts +54 -7
- package/src/activities/chat/middleware/types.ts +1 -0
- package/src/activities/chat/stream/message-updaters.ts +8 -0
- package/src/activities/chat/stream/processor.ts +57 -27
- package/src/activities/chat/tools/schema-converter.ts +17 -5
- package/src/activities/chat/tools/tool-calls.ts +215 -68
- package/src/activities/chat/tools/tool-definition.ts +8 -0
- package/src/activities/evaluate/adapter.ts +4 -0
- package/src/activities/evaluate/index.ts +6 -0
- package/src/activities/generateSpeech/index.ts +1 -1
- package/src/activities/generateVideo/adapter.ts +21 -7
- package/src/activities/generateVideo/index.ts +5 -4
- package/src/activities/generateVideo/snap.ts +64 -6
- package/src/activities/generateVoice/index.ts +1 -1
- package/src/activities/index.ts +2 -1
- package/src/adapter-internals.ts +1 -0
- package/src/client.ts +1 -0
- package/src/index.ts +1 -0
- package/src/interrupt-resume.ts +55 -4
- package/src/middlewares/otel.ts +161 -3
- package/src/types.ts +66 -5
- package/src/utilities/ag-ui-wire.ts +17 -2
- package/src/utilities/merge-streams.ts +34 -0
- package/src/utilities/reasoning-encrypted-value.ts +12 -0
- package/src/utilities/tool-result.ts +7 -1
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
import { subagentHostMessageId } from '../../utilities/subagent-wire'
|
|
42
42
|
import { withDurabilityBatchHint } from '../../utilities/durability-batch'
|
|
43
43
|
import { normalizeStreamChunk } from '../../utilities/normalize-stream-chunk'
|
|
44
|
+
import { isRedactedThinkingId } from '../../utilities/reasoning-encrypted-value'
|
|
44
45
|
import { restorePublicUsage } from '../../utilities/restore-inbound-chunk'
|
|
45
46
|
import type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'
|
|
46
47
|
import {
|
|
@@ -113,6 +114,7 @@ import type {
|
|
|
113
114
|
import type {
|
|
114
115
|
ApprovalRequest,
|
|
115
116
|
ClientToolRequest,
|
|
117
|
+
McpInputRequest,
|
|
116
118
|
ToolResult,
|
|
117
119
|
} from './tools/tool-calls'
|
|
118
120
|
import type { ApprovalSchemaConfig } from './tools/tool-definition'
|
|
@@ -148,6 +150,7 @@ import type {
|
|
|
148
150
|
ToolCallEndEvent,
|
|
149
151
|
ToolCallResultEvent,
|
|
150
152
|
ToolCallStartEvent,
|
|
153
|
+
ToolInputResponse,
|
|
151
154
|
UIMessage,
|
|
152
155
|
} from '../../types'
|
|
153
156
|
import type {
|
|
@@ -521,6 +524,8 @@ export interface TextActivityOptions<
|
|
|
521
524
|
abortController?: TextOptions['abortController']
|
|
522
525
|
/** Strategy for controlling the agent loop */
|
|
523
526
|
agentLoopStrategy?: TextOptions['agentLoopStrategy']
|
|
527
|
+
/** How the server tools of one model turn run. Default `'parallel'`. */
|
|
528
|
+
toolExecution?: TextOptions['toolExecution']
|
|
524
529
|
/**
|
|
525
530
|
* Optional configuration for lazy-tool discovery (tools marked `lazy: true`).
|
|
526
531
|
* Tunes how much of each lazy tool's description appears in the discovery
|
|
@@ -851,8 +856,7 @@ class TextEngine<
|
|
|
851
856
|
private currentMessageCreatedAt: Date | null = null
|
|
852
857
|
private streamIdentityCaptured = false
|
|
853
858
|
private accumulatedContent = ''
|
|
854
|
-
private accumulatedThinking:
|
|
855
|
-
[]
|
|
859
|
+
private accumulatedThinking: NonNullable<ModelMessage['thinking']> = []
|
|
856
860
|
/**
|
|
857
861
|
* Arrival order of this iteration's thinking steps, text and tool calls.
|
|
858
862
|
* A ModelMessage keeps `thinking` apart from `content`/`toolCalls`, so a
|
|
@@ -864,6 +868,7 @@ class TextEngine<
|
|
|
864
868
|
private turnParts: Array<TurnPart> | null = []
|
|
865
869
|
private currentThinkingContent = ''
|
|
866
870
|
private currentThinkingSignature = ''
|
|
871
|
+
private currentThinkingRedacted = false
|
|
867
872
|
private eventOptions?: Record<string, unknown> | undefined
|
|
868
873
|
private eventToolNames?: Array<string>
|
|
869
874
|
private finishedEvent: RunFinishedEvent | null = null
|
|
@@ -880,8 +885,10 @@ class TextEngine<
|
|
|
880
885
|
private readonly initialClientToolResults: Map<string, any>
|
|
881
886
|
private readonly resumeApprovals = new Map<string, ToolApprovalResolution>()
|
|
882
887
|
private readonly resumeClientToolResults = new Map<string, any>()
|
|
888
|
+
private readonly resumeClientToolErrors = new Map<string, string>()
|
|
883
889
|
private readonly resumeDeniedToolResults = new Map<string, unknown>()
|
|
884
890
|
private readonly resumeCancelledToolCallIds = new Set<string>()
|
|
891
|
+
private readonly resumeInputResponses = new Map<string, ToolInputResponse>()
|
|
885
892
|
private readonly resumeGenericInterrupts = new Map<
|
|
886
893
|
string,
|
|
887
894
|
ChatResumeGenericResolution
|
|
@@ -952,6 +959,8 @@ class TextEngine<
|
|
|
952
959
|
message: string
|
|
953
960
|
code?: string
|
|
954
961
|
cause?: unknown
|
|
962
|
+
/** Full model text, when a structured-output parse failed (#1485). */
|
|
963
|
+
rawText?: string
|
|
955
964
|
} | null = null
|
|
956
965
|
private combinedCompleteEmitted = false
|
|
957
966
|
private readonly finalStructuredOutput?: {
|
|
@@ -1189,10 +1198,54 @@ class TextEngine<
|
|
|
1189
1198
|
message: string
|
|
1190
1199
|
code?: string
|
|
1191
1200
|
cause?: unknown
|
|
1201
|
+
rawText?: string
|
|
1192
1202
|
} | null {
|
|
1193
1203
|
return this.finalizationError
|
|
1194
1204
|
}
|
|
1195
1205
|
|
|
1206
|
+
private async runTerminalHook(): Promise<void> {
|
|
1207
|
+
if (this.terminalHookCalled || this.isCancelled()) return
|
|
1208
|
+
|
|
1209
|
+
this.terminalHookCalled = true
|
|
1210
|
+
|
|
1211
|
+
if (this.finalizationError) {
|
|
1212
|
+
const errForHook = new Error(
|
|
1213
|
+
this.finalizationError.message,
|
|
1214
|
+
this.finalizationError.cause !== undefined
|
|
1215
|
+
? { cause: this.finalizationError.cause }
|
|
1216
|
+
: undefined,
|
|
1217
|
+
)
|
|
1218
|
+
if (this.finalizationError.code !== undefined) {
|
|
1219
|
+
Object.defineProperty(errForHook, 'code', {
|
|
1220
|
+
value: this.finalizationError.code,
|
|
1221
|
+
enumerable: true,
|
|
1222
|
+
})
|
|
1223
|
+
}
|
|
1224
|
+
if (this.finalizationError.rawText !== undefined) {
|
|
1225
|
+
Object.defineProperty(errForHook, 'rawText', {
|
|
1226
|
+
value: this.finalizationError.rawText,
|
|
1227
|
+
enumerable: true,
|
|
1228
|
+
})
|
|
1229
|
+
}
|
|
1230
|
+
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
1231
|
+
error: errForHook,
|
|
1232
|
+
duration: Date.now() - this.streamStartTime,
|
|
1233
|
+
})
|
|
1234
|
+
return
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
this.addTerminalAssistantMessages()
|
|
1238
|
+
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
1239
|
+
finishReason: this.lastFinishReason,
|
|
1240
|
+
duration: Date.now() - this.streamStartTime,
|
|
1241
|
+
content: this.accumulatedContent,
|
|
1242
|
+
usage: rebuildTokenUsage(
|
|
1243
|
+
this.finishedEvent?.usage,
|
|
1244
|
+
tanstackMetadata(this.finishedEvent ?? undefined)?.usage,
|
|
1245
|
+
),
|
|
1246
|
+
})
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1196
1249
|
async *run(): AsyncGenerator<StreamChunk> {
|
|
1197
1250
|
this.beforeRun()
|
|
1198
1251
|
this.logger.agentLoop('run started', {
|
|
@@ -1238,7 +1291,8 @@ class TextEngine<
|
|
|
1238
1291
|
}
|
|
1239
1292
|
|
|
1240
1293
|
const pendingPhase = yield* this.checkForPendingToolCalls()
|
|
1241
|
-
if (pendingPhase === 'wait') {
|
|
1294
|
+
if (pendingPhase === 'stop' || pendingPhase === 'wait') {
|
|
1295
|
+
await this.runTerminalHook()
|
|
1242
1296
|
return
|
|
1243
1297
|
}
|
|
1244
1298
|
|
|
@@ -1347,46 +1401,11 @@ class TextEngine<
|
|
|
1347
1401
|
}
|
|
1348
1402
|
}
|
|
1349
1403
|
|
|
1350
|
-
//
|
|
1351
|
-
//
|
|
1352
|
-
//
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
this.toolPhase !== 'wait' &&
|
|
1356
|
-
!this.isCancelled()
|
|
1357
|
-
) {
|
|
1358
|
-
if (this.finalizationError) {
|
|
1359
|
-
this.terminalHookCalled = true
|
|
1360
|
-
const errForHook = new Error(
|
|
1361
|
-
this.finalizationError.message,
|
|
1362
|
-
this.finalizationError.cause !== undefined
|
|
1363
|
-
? { cause: this.finalizationError.cause }
|
|
1364
|
-
: undefined,
|
|
1365
|
-
)
|
|
1366
|
-
if (this.finalizationError.code !== undefined) {
|
|
1367
|
-
Object.defineProperty(errForHook, 'code', {
|
|
1368
|
-
value: this.finalizationError.code,
|
|
1369
|
-
enumerable: true,
|
|
1370
|
-
})
|
|
1371
|
-
}
|
|
1372
|
-
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
1373
|
-
error: errForHook,
|
|
1374
|
-
duration: Date.now() - this.streamStartTime,
|
|
1375
|
-
})
|
|
1376
|
-
} else {
|
|
1377
|
-
this.addTerminalAssistantMessages()
|
|
1378
|
-
this.terminalHookCalled = true
|
|
1379
|
-
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
1380
|
-
finishReason: this.lastFinishReason,
|
|
1381
|
-
duration: Date.now() - this.streamStartTime,
|
|
1382
|
-
content: this.accumulatedContent,
|
|
1383
|
-
usage: rebuildTokenUsage(
|
|
1384
|
-
this.finishedEvent?.usage,
|
|
1385
|
-
tanstackMetadata(this.finishedEvent ?? undefined)?.usage,
|
|
1386
|
-
),
|
|
1387
|
-
})
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1404
|
+
// A client-tool or approval wait ends this server-side chat invocation.
|
|
1405
|
+
// Structured-output finalization stays paused until the caller submits
|
|
1406
|
+
// the result in a new invocation, but middleware must still observe one
|
|
1407
|
+
// terminal hook for the current invocation.
|
|
1408
|
+
await this.runTerminalHook()
|
|
1390
1409
|
} catch (error: unknown) {
|
|
1391
1410
|
if (
|
|
1392
1411
|
error instanceof Error &&
|
|
@@ -1508,6 +1527,7 @@ class TextEngine<
|
|
|
1508
1527
|
this.turnParts = []
|
|
1509
1528
|
this.currentThinkingContent = ''
|
|
1510
1529
|
this.currentThinkingSignature = ''
|
|
1530
|
+
this.currentThinkingRedacted = false
|
|
1511
1531
|
|
|
1512
1532
|
this.finishedEvent = null
|
|
1513
1533
|
this.streamedToolErrorResults.clear()
|
|
@@ -1976,6 +1996,7 @@ class TextEngine<
|
|
|
1976
1996
|
...(this.currentThinkingSignature && {
|
|
1977
1997
|
signature: this.currentThinkingSignature,
|
|
1978
1998
|
}),
|
|
1999
|
+
...(this.currentThinkingRedacted && { redacted: true }),
|
|
1979
2000
|
})
|
|
1980
2001
|
if (this.turnParts) {
|
|
1981
2002
|
const placeholder = [...this.turnParts]
|
|
@@ -1993,6 +2014,7 @@ class TextEngine<
|
|
|
1993
2014
|
}
|
|
1994
2015
|
this.currentThinkingContent = ''
|
|
1995
2016
|
this.currentThinkingSignature = ''
|
|
2017
|
+
this.currentThinkingRedacted = false
|
|
1996
2018
|
}
|
|
1997
2019
|
}
|
|
1998
2020
|
|
|
@@ -2020,6 +2042,7 @@ class TextEngine<
|
|
|
2020
2042
|
if (typeof chunk.signature === 'string' && chunk.signature !== '') {
|
|
2021
2043
|
this.noteThinkingStepPosition()
|
|
2022
2044
|
this.currentThinkingSignature = chunk.signature
|
|
2045
|
+
this.currentThinkingRedacted = isRedactedThinkingId(chunk.stepId)
|
|
2023
2046
|
}
|
|
2024
2047
|
}
|
|
2025
2048
|
|
|
@@ -2049,6 +2072,7 @@ class TextEngine<
|
|
|
2049
2072
|
}
|
|
2050
2073
|
this.noteThinkingStepPosition()
|
|
2051
2074
|
this.currentThinkingSignature = chunk.encryptedValue
|
|
2075
|
+
this.currentThinkingRedacted = isRedactedThinkingId(chunk.entityId)
|
|
2052
2076
|
}
|
|
2053
2077
|
|
|
2054
2078
|
/**
|
|
@@ -2181,9 +2205,12 @@ class TextEngine<
|
|
|
2181
2205
|
this.middlewareCtx.context,
|
|
2182
2206
|
this.toolAbortSignal,
|
|
2183
2207
|
{
|
|
2208
|
+
clientToolErrors: this.resumeClientToolErrors,
|
|
2184
2209
|
deniedToolResults: this.resumeDeniedToolResults,
|
|
2185
2210
|
cancelledToolCallIds: this.resumeCancelledToolCallIds,
|
|
2211
|
+
inputResponses: this.resumeInputResponses,
|
|
2186
2212
|
},
|
|
2213
|
+
this.params.toolExecution,
|
|
2187
2214
|
)
|
|
2188
2215
|
|
|
2189
2216
|
// Consume the async generator, yielding custom events and collecting the return value
|
|
@@ -2207,11 +2234,12 @@ class TextEngine<
|
|
|
2207
2234
|
}),
|
|
2208
2235
|
)
|
|
2209
2236
|
|
|
2210
|
-
|
|
2237
|
+
const pauseForDecision =
|
|
2211
2238
|
executionResult.needsApproval.length > 0 ||
|
|
2212
2239
|
executionResult.needsClientExecution.length > 0 ||
|
|
2240
|
+
executionResult.inputRequired.length > 0 ||
|
|
2213
2241
|
executionResult.subagentInterrupts.length > 0
|
|
2214
|
-
) {
|
|
2242
|
+
if (pauseForDecision) {
|
|
2215
2243
|
this.discardDeferredToolCallRunFinishedChunks()
|
|
2216
2244
|
|
|
2217
2245
|
if (allResults.length > 0) {
|
|
@@ -2228,6 +2256,7 @@ class TextEngine<
|
|
|
2228
2256
|
executionResult.needsApproval,
|
|
2229
2257
|
executionResult.needsClientExecution,
|
|
2230
2258
|
[],
|
|
2259
|
+
executionResult.inputRequired,
|
|
2231
2260
|
executionResult.subagentInterrupts,
|
|
2232
2261
|
)
|
|
2233
2262
|
this.setToolPhase(emitted ? 'wait' : 'stop')
|
|
@@ -2365,9 +2394,12 @@ class TextEngine<
|
|
|
2365
2394
|
this.middlewareCtx.context,
|
|
2366
2395
|
this.toolAbortSignal,
|
|
2367
2396
|
{
|
|
2397
|
+
clientToolErrors: this.resumeClientToolErrors,
|
|
2368
2398
|
deniedToolResults: this.resumeDeniedToolResults,
|
|
2369
2399
|
cancelledToolCallIds: this.resumeCancelledToolCallIds,
|
|
2400
|
+
inputResponses: this.resumeInputResponses,
|
|
2370
2401
|
},
|
|
2402
|
+
this.params.toolExecution,
|
|
2371
2403
|
)
|
|
2372
2404
|
|
|
2373
2405
|
// Consume the async generator, yielding custom events and collecting the return value
|
|
@@ -2414,16 +2446,18 @@ class TextEngine<
|
|
|
2414
2446
|
finishEvent,
|
|
2415
2447
|
toolCalls,
|
|
2416
2448
|
afterToolRequests,
|
|
2449
|
+
executionResult.inputRequired,
|
|
2417
2450
|
)
|
|
2418
2451
|
this.setToolPhase('wait')
|
|
2419
2452
|
return
|
|
2420
2453
|
}
|
|
2421
2454
|
|
|
2422
|
-
|
|
2455
|
+
const pauseForDecision =
|
|
2423
2456
|
executionResult.needsApproval.length > 0 ||
|
|
2424
2457
|
executionResult.needsClientExecution.length > 0 ||
|
|
2458
|
+
executionResult.inputRequired.length > 0 ||
|
|
2425
2459
|
executionResult.subagentInterrupts.length > 0
|
|
2426
|
-
) {
|
|
2460
|
+
if (pauseForDecision) {
|
|
2427
2461
|
if (allResults.length > 0) {
|
|
2428
2462
|
for (const chunk of afterToolBoundaryChunks) {
|
|
2429
2463
|
yield* this.pipeThroughMiddleware(chunk)
|
|
@@ -2435,6 +2469,7 @@ class TextEngine<
|
|
|
2435
2469
|
executionResult.needsApproval,
|
|
2436
2470
|
executionResult.needsClientExecution,
|
|
2437
2471
|
[],
|
|
2472
|
+
executionResult.inputRequired,
|
|
2438
2473
|
executionResult.subagentInterrupts,
|
|
2439
2474
|
)
|
|
2440
2475
|
this.setToolPhase(emitted ? 'wait' : 'stop')
|
|
@@ -2554,7 +2589,7 @@ class TextEngine<
|
|
|
2554
2589
|
),
|
|
2555
2590
|
)
|
|
2556
2591
|
type Segment = {
|
|
2557
|
-
thinking:
|
|
2592
|
+
thinking: NonNullable<ModelMessage['thinking']>
|
|
2558
2593
|
text: string
|
|
2559
2594
|
callIds: Array<string>
|
|
2560
2595
|
}
|
|
@@ -2853,6 +2888,7 @@ class TextEngine<
|
|
|
2853
2888
|
GenericInterruptRequest<InterruptDefinition<any, any, any, any>>
|
|
2854
2889
|
> = [],
|
|
2855
2890
|
genericInterruptIds: ReadonlyArray<string> = [],
|
|
2891
|
+
inputRequired: ReadonlyArray<McpInputRequest> = [],
|
|
2856
2892
|
childInterrupts: ReadonlyArray<Interrupt> = [],
|
|
2857
2893
|
): Array<Interrupt> {
|
|
2858
2894
|
const interrupts: Array<Interrupt> = []
|
|
@@ -2971,6 +3007,30 @@ class TextEngine<
|
|
|
2971
3007
|
})
|
|
2972
3008
|
}
|
|
2973
3009
|
|
|
3010
|
+
for (const pendingInput of inputRequired) {
|
|
3011
|
+
const id = `mcp_input_${pendingInput.toolCallId}`
|
|
3012
|
+
interrupts.push({
|
|
3013
|
+
id,
|
|
3014
|
+
reason: 'mcp_input',
|
|
3015
|
+
message: `Input required to run ${pendingInput.toolName}`,
|
|
3016
|
+
toolCallId: pendingInput.toolCallId,
|
|
3017
|
+
metadata: {
|
|
3018
|
+
toolName: pendingInput.toolName,
|
|
3019
|
+
// A generic binding with no definition id. `useChat` shows it as a
|
|
3020
|
+
// generic interrupt with `resolveInterrupt` and `cancel`. The answer
|
|
3021
|
+
// is not validated here. The MCP server checks it.
|
|
3022
|
+
[interruptBindingMetadataKey]: {
|
|
3023
|
+
v: INTERRUPT_BINDING_VERSION,
|
|
3024
|
+
kind: 'generic',
|
|
3025
|
+
interruptId: id,
|
|
3026
|
+
},
|
|
3027
|
+
[INTERRUPT_PAYLOAD_METADATA_KEY]: {
|
|
3028
|
+
kind: pendingInput.kind,
|
|
3029
|
+
request: pendingInput.request,
|
|
3030
|
+
},
|
|
3031
|
+
},
|
|
3032
|
+
})
|
|
3033
|
+
}
|
|
2974
3034
|
// A subagent tool call raised these. They keep their `subagentRunId`.
|
|
2975
3035
|
interrupts.push(...childInterrupts)
|
|
2976
3036
|
|
|
@@ -2995,6 +3055,7 @@ class TextEngine<
|
|
|
2995
3055
|
GenericInterruptRequest<InterruptDefinition<any, any, any, any>>
|
|
2996
3056
|
> = [],
|
|
2997
3057
|
genericInterruptIds?: ReadonlyArray<string>,
|
|
3058
|
+
inputRequired: ReadonlyArray<McpInputRequest> = [],
|
|
2998
3059
|
childInterrupts?: ReadonlyArray<Interrupt>,
|
|
2999
3060
|
): StreamChunk {
|
|
3000
3061
|
return {
|
|
@@ -3007,6 +3068,7 @@ class TextEngine<
|
|
|
3007
3068
|
clientRequests,
|
|
3008
3069
|
genericRequests,
|
|
3009
3070
|
genericInterruptIds,
|
|
3071
|
+
inputRequired,
|
|
3010
3072
|
childInterrupts,
|
|
3011
3073
|
),
|
|
3012
3074
|
},
|
|
@@ -3157,6 +3219,7 @@ class TextEngine<
|
|
|
3157
3219
|
genericRequests: ReadonlyArray<
|
|
3158
3220
|
GenericInterruptRequest<InterruptDefinition<any, any, any, any>>
|
|
3159
3221
|
> = [],
|
|
3222
|
+
inputRequired: ReadonlyArray<McpInputRequest> = [],
|
|
3160
3223
|
childInterrupts: ReadonlyArray<Interrupt> = [],
|
|
3161
3224
|
): AsyncGenerator<StreamChunk, boolean, void> {
|
|
3162
3225
|
yield* this.emitSyntheticRunStarted(finishEvent)
|
|
@@ -3171,6 +3234,7 @@ class TextEngine<
|
|
|
3171
3234
|
clientRequests,
|
|
3172
3235
|
genericRequests,
|
|
3173
3236
|
genericInterruptIds,
|
|
3237
|
+
inputRequired,
|
|
3174
3238
|
childInterrupts,
|
|
3175
3239
|
),
|
|
3176
3240
|
)
|
|
@@ -3207,6 +3271,7 @@ class TextEngine<
|
|
|
3207
3271
|
requests?: ReadonlyArray<
|
|
3208
3272
|
GenericInterruptRequest<InterruptDefinition<any, any, any, any>>
|
|
3209
3273
|
>,
|
|
3274
|
+
inputRequired: ReadonlyArray<McpInputRequest> = [],
|
|
3210
3275
|
): AsyncGenerator<StreamChunk, boolean, void> {
|
|
3211
3276
|
this.middlewareCtx.phase = phase
|
|
3212
3277
|
const boundaryRequests =
|
|
@@ -3242,6 +3307,7 @@ class TextEngine<
|
|
|
3242
3307
|
actionable.approvals,
|
|
3243
3308
|
actionable.clientRequests,
|
|
3244
3309
|
boundaryRequests,
|
|
3310
|
+
inputRequired,
|
|
3245
3311
|
)
|
|
3246
3312
|
return true
|
|
3247
3313
|
}
|
|
@@ -3289,6 +3355,7 @@ class TextEngine<
|
|
|
3289
3355
|
} else if (
|
|
3290
3356
|
!tool.execute &&
|
|
3291
3357
|
!clientToolResults.has(toolCall.id) &&
|
|
3358
|
+
!this.resumeClientToolErrors.has(toolCall.id) &&
|
|
3292
3359
|
!this.resumeCancelledToolCallIds.has(toolCall.id)
|
|
3293
3360
|
) {
|
|
3294
3361
|
clientRequests.push({
|
|
@@ -3396,9 +3463,15 @@ class TextEngine<
|
|
|
3396
3463
|
content: wireContent,
|
|
3397
3464
|
role: 'tool' as const,
|
|
3398
3465
|
}
|
|
3466
|
+
// An outcome always comes with output-error.
|
|
3399
3467
|
chunks.push(
|
|
3400
3468
|
(result.state === 'output-error'
|
|
3401
|
-
? withTanstackMetadata(resultChunk, {
|
|
3469
|
+
? withTanstackMetadata(resultChunk, {
|
|
3470
|
+
state: result.state,
|
|
3471
|
+
...(result.outcome !== undefined && {
|
|
3472
|
+
toolResultOutcome: result.outcome,
|
|
3473
|
+
}),
|
|
3474
|
+
})
|
|
3402
3475
|
: resultChunk) as StreamChunk,
|
|
3403
3476
|
)
|
|
3404
3477
|
|
|
@@ -3419,22 +3492,63 @@ class TextEngine<
|
|
|
3419
3492
|
return false
|
|
3420
3493
|
}
|
|
3421
3494
|
})
|
|
3422
|
-
|
|
3423
|
-
const
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3495
|
+
const isDeniedResult = result.outcome === 'denied'
|
|
3496
|
+
const existingToolResultIdx = isDeniedResult
|
|
3497
|
+
? this.messages.findIndex(
|
|
3498
|
+
(message) =>
|
|
3499
|
+
message.role === 'tool' &&
|
|
3500
|
+
message.toolCallId === result.toolCallId,
|
|
3501
|
+
)
|
|
3502
|
+
: -1
|
|
3503
|
+
const resultMessageIdx =
|
|
3504
|
+
existingToolResultIdx >= 0 ? existingToolResultIdx : placeholderIdx
|
|
3505
|
+
|
|
3506
|
+
// Only a denied result keeps the old message's fields. A replaced
|
|
3507
|
+
// pendingExecution placeholder must not leak into the real result.
|
|
3508
|
+
const existingToolMessage =
|
|
3509
|
+
existingToolResultIdx >= 0
|
|
3510
|
+
? this.messages[existingToolResultIdx]
|
|
3511
|
+
: undefined
|
|
3512
|
+
const errorField = result.state === 'output-error' && {
|
|
3513
|
+
error: toolResultErrorText(parseToolOutput(wireContent)),
|
|
3430
3514
|
}
|
|
3515
|
+
const replacementMessage: ModelMessage =
|
|
3516
|
+
existingToolMessage?.role === 'tool'
|
|
3517
|
+
? {
|
|
3518
|
+
...existingToolMessage,
|
|
3519
|
+
content,
|
|
3520
|
+
toolCallId: result.toolCallId,
|
|
3521
|
+
...errorField,
|
|
3522
|
+
}
|
|
3523
|
+
: {
|
|
3524
|
+
role: 'tool',
|
|
3525
|
+
content,
|
|
3526
|
+
toolCallId: result.toolCallId,
|
|
3527
|
+
...errorField,
|
|
3528
|
+
}
|
|
3529
|
+
const newToolMessage: ModelMessage =
|
|
3530
|
+
result.outcome !== undefined
|
|
3531
|
+
? withTanstackMetadata(replacementMessage, {
|
|
3532
|
+
toolResultOutcome: result.outcome,
|
|
3533
|
+
})
|
|
3534
|
+
: replacementMessage
|
|
3431
3535
|
|
|
3432
|
-
if (
|
|
3433
|
-
|
|
3434
|
-
...this.messages.slice(0,
|
|
3536
|
+
if (resultMessageIdx >= 0) {
|
|
3537
|
+
const replacedMessages = [
|
|
3538
|
+
...this.messages.slice(0, resultMessageIdx),
|
|
3435
3539
|
newToolMessage,
|
|
3436
|
-
...this.messages.slice(
|
|
3540
|
+
...this.messages.slice(resultMessageIdx + 1),
|
|
3437
3541
|
]
|
|
3542
|
+
this.messages = isDeniedResult
|
|
3543
|
+
? replacedMessages.filter(
|
|
3544
|
+
(message, index) =>
|
|
3545
|
+
index === resultMessageIdx ||
|
|
3546
|
+
!(
|
|
3547
|
+
message.role === 'tool' &&
|
|
3548
|
+
message.toolCallId === result.toolCallId
|
|
3549
|
+
),
|
|
3550
|
+
)
|
|
3551
|
+
: replacedMessages
|
|
3438
3552
|
} else {
|
|
3439
3553
|
this.messages = [...this.messages, newToolMessage]
|
|
3440
3554
|
}
|
|
@@ -3465,7 +3579,10 @@ class TextEngine<
|
|
|
3465
3579
|
}
|
|
3466
3580
|
|
|
3467
3581
|
// Only mark as complete if NOT pending execution
|
|
3468
|
-
if (
|
|
3582
|
+
if (
|
|
3583
|
+
!hasPendingExecution &&
|
|
3584
|
+
!this.resumeDeniedToolResults.has(message.toolCallId)
|
|
3585
|
+
) {
|
|
3469
3586
|
completedToolIds.add(message.toolCallId)
|
|
3470
3587
|
}
|
|
3471
3588
|
}
|
|
@@ -3853,6 +3970,9 @@ class TextEngine<
|
|
|
3853
3970
|
// Track whether a RUN_ERROR has been yielded to streaming consumers so
|
|
3854
3971
|
// we don't emit a duplicate synthetic one at the end.
|
|
3855
3972
|
let runErrorYielded = false
|
|
3973
|
+
// The full model text. Adapter errors carry only a 200-character preview,
|
|
3974
|
+
// so a Promise caller could not recover the answer (#1485).
|
|
3975
|
+
let rawText = ''
|
|
3856
3976
|
|
|
3857
3977
|
// Pipe chunks through middleware; yield to consumer only when yieldChunks=true
|
|
3858
3978
|
for await (const raw of providerStream) {
|
|
@@ -3964,14 +4084,20 @@ class TextEngine<
|
|
|
3964
4084
|
await this.runOnUsageFromChunk(chunk)
|
|
3965
4085
|
}
|
|
3966
4086
|
|
|
4087
|
+
if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {
|
|
4088
|
+
rawText += chunk.delta
|
|
4089
|
+
}
|
|
4090
|
+
|
|
3967
4091
|
if (chunk.type === EventType.RUN_ERROR) {
|
|
3968
4092
|
// RunErrorEvent already exposes `message` and `code` after narrowing.
|
|
4093
|
+
// A native stream has no adapter error to capture, but its RUN_ERROR
|
|
4094
|
+
// can carry the provider's error body as `rawEvent` (#1005).
|
|
4095
|
+
const cause = fallbackAdapterError ?? chunk.rawEvent
|
|
3969
4096
|
this.finalizationError = {
|
|
3970
4097
|
message: chunk.message,
|
|
3971
4098
|
...(chunk.code ? { code: chunk.code } : {}),
|
|
3972
|
-
...(
|
|
3973
|
-
|
|
3974
|
-
: {}),
|
|
4099
|
+
...(cause !== undefined ? { cause } : {}),
|
|
4100
|
+
...(rawText ? { rawText } : {}),
|
|
3975
4101
|
}
|
|
3976
4102
|
}
|
|
3977
4103
|
|
|
@@ -4155,6 +4281,7 @@ class TextEngine<
|
|
|
4155
4281
|
message: `Failed to parse structured output as JSON. Content: ${detail}`,
|
|
4156
4282
|
code: 'structured-output-parse-failed',
|
|
4157
4283
|
cause: err,
|
|
4284
|
+
rawText,
|
|
4158
4285
|
}
|
|
4159
4286
|
}
|
|
4160
4287
|
}
|
|
@@ -4265,6 +4392,7 @@ class TextEngine<
|
|
|
4265
4392
|
resumeToolState: {
|
|
4266
4393
|
approvals: this.resumeApprovals,
|
|
4267
4394
|
clientToolResults: this.resumeClientToolResults,
|
|
4395
|
+
clientToolErrors: this.resumeClientToolErrors,
|
|
4268
4396
|
deniedToolResults: this.resumeDeniedToolResults,
|
|
4269
4397
|
cancelledToolCallIds: this.resumeCancelledToolCallIds,
|
|
4270
4398
|
},
|
|
@@ -4407,6 +4535,26 @@ class TextEngine<
|
|
|
4407
4535
|
: []
|
|
4408
4536
|
})
|
|
4409
4537
|
pending.push(...genericPending)
|
|
4538
|
+
// An `mcp_input_*` answer is valid only for a server tool call that is
|
|
4539
|
+
// still pending in history.
|
|
4540
|
+
const mcpInputCallIds = new Map<string, string>()
|
|
4541
|
+
for (const toolCall of pendingToolCalls) {
|
|
4542
|
+
const interruptId = `mcp_input_${toolCall.id}`
|
|
4543
|
+
if (!resumeInterruptIds.has(interruptId)) continue
|
|
4544
|
+
if (!toolsByCallId.get(toolCall.id)?.execute) continue
|
|
4545
|
+
mcpInputCallIds.set(interruptId, toolCall.id)
|
|
4546
|
+
pending.push({
|
|
4547
|
+
interruptId,
|
|
4548
|
+
payload: { id: interruptId },
|
|
4549
|
+
binding: {
|
|
4550
|
+
v: INTERRUPT_BINDING_VERSION,
|
|
4551
|
+
kind: 'generic',
|
|
4552
|
+
interruptId,
|
|
4553
|
+
interruptedRunId,
|
|
4554
|
+
generation: 0,
|
|
4555
|
+
},
|
|
4556
|
+
})
|
|
4557
|
+
}
|
|
4410
4558
|
const validated = await validateInterruptResumeBatch({
|
|
4411
4559
|
threadId: this.threadId,
|
|
4412
4560
|
interruptedRunId,
|
|
@@ -4436,6 +4584,16 @@ class TextEngine<
|
|
|
4436
4584
|
})
|
|
4437
4585
|
|
|
4438
4586
|
const genericResolutions = validated.resumeToolState.genericInterrupts
|
|
4587
|
+
for (const [interruptId, toolCallId] of mcpInputCallIds) {
|
|
4588
|
+
const resolution = genericResolutions?.get(interruptId)
|
|
4589
|
+
if (!resolution) continue
|
|
4590
|
+
this.resumeInputResponses.set(
|
|
4591
|
+
toolCallId,
|
|
4592
|
+
resolution.status === 'resolved'
|
|
4593
|
+
? { status: 'resolved', payload: resolution.payload }
|
|
4594
|
+
: { status: 'cancelled' },
|
|
4595
|
+
)
|
|
4596
|
+
}
|
|
4439
4597
|
if (genericPending.length > 0 && genericResolutions) {
|
|
4440
4598
|
const resolutions = genericPending
|
|
4441
4599
|
.sort((left, right) => {
|
|
@@ -4485,6 +4643,7 @@ class TextEngine<
|
|
|
4485
4643
|
this.earlyTermination = true
|
|
4486
4644
|
} else if (policy.toolResume === 'cancel') {
|
|
4487
4645
|
for (const request of pendingToolCalls) {
|
|
4646
|
+
if (this.resumeDeniedToolResults.has(request.id)) continue
|
|
4488
4647
|
this.resumeCancelledToolCallIds.add(request.id)
|
|
4489
4648
|
}
|
|
4490
4649
|
}
|
|
@@ -4626,6 +4785,11 @@ class TextEngine<
|
|
|
4626
4785
|
this.resumeClientToolResults.set(toolCallId, result)
|
|
4627
4786
|
}
|
|
4628
4787
|
}
|
|
4788
|
+
if (state?.clientToolErrors) {
|
|
4789
|
+
for (const [toolCallId, errorText] of state.clientToolErrors) {
|
|
4790
|
+
this.resumeClientToolErrors.set(toolCallId, errorText)
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4629
4793
|
if (state?.deniedToolResults) {
|
|
4630
4794
|
for (const [toolCallId, result] of state.deniedToolResults) {
|
|
4631
4795
|
this.resumeDeniedToolResults.set(toolCallId, result)
|
|
@@ -4685,6 +4849,7 @@ class TextEngine<
|
|
|
4685
4849
|
this.earlyTermination = true
|
|
4686
4850
|
} else if (policy.toolResume === 'cancel') {
|
|
4687
4851
|
for (const toolCall of this.getPendingToolCallsFromMessages()) {
|
|
4852
|
+
if (this.resumeDeniedToolResults.has(toolCall.id)) continue
|
|
4688
4853
|
this.resumeCancelledToolCallIds.add(toolCall.id)
|
|
4689
4854
|
}
|
|
4690
4855
|
}
|
|
@@ -4839,6 +5004,7 @@ class TextEngine<
|
|
|
4839
5004
|
results: Array<ToolResult>
|
|
4840
5005
|
needsApproval: Array<ApprovalRequest>
|
|
4841
5006
|
needsClientExecution: Array<ClientToolRequest>
|
|
5007
|
+
inputRequired: Array<McpInputRequest>
|
|
4842
5008
|
subagentInterrupts: Array<Interrupt>
|
|
4843
5009
|
},
|
|
4844
5010
|
void
|
|
@@ -4849,6 +5015,7 @@ class TextEngine<
|
|
|
4849
5015
|
results: Array<ToolResult>
|
|
4850
5016
|
needsApproval: Array<ApprovalRequest>
|
|
4851
5017
|
needsClientExecution: Array<ClientToolRequest>
|
|
5018
|
+
inputRequired: Array<McpInputRequest>
|
|
4852
5019
|
subagentInterrupts: Array<Interrupt>
|
|
4853
5020
|
},
|
|
4854
5021
|
void
|
|
@@ -5287,6 +5454,16 @@ async function* runRoutedSubagents(
|
|
|
5287
5454
|
yield* runChatEngine(options, engineRef)
|
|
5288
5455
|
return
|
|
5289
5456
|
}
|
|
5457
|
+
// ponytail: a router cannot write input yet. Add it to the router pick if
|
|
5458
|
+
// someone needs it.
|
|
5459
|
+
const withInput = bag.agents.find(
|
|
5460
|
+
(agent: DefinedAgent) => agent.inputSchema !== undefined,
|
|
5461
|
+
)
|
|
5462
|
+
if (withInput) {
|
|
5463
|
+
throw new Error(
|
|
5464
|
+
`Subagent "${withInput.name}" has an inputSchema. inputSchema needs tool mode: remove subagents.router so the model writes the input.`,
|
|
5465
|
+
)
|
|
5466
|
+
}
|
|
5290
5467
|
const threadId = options.threadId ?? `thread-${Date.now()}`
|
|
5291
5468
|
const runId = options.runId ?? `run-${Date.now()}`
|
|
5292
5469
|
const messages = options.messages ?? []
|
|
@@ -5742,6 +5919,12 @@ async function runAgenticStructuredOutput<TSchema extends SchemaInput>(
|
|
|
5742
5919
|
enumerable: true,
|
|
5743
5920
|
})
|
|
5744
5921
|
}
|
|
5922
|
+
if (finalizationError.rawText !== undefined) {
|
|
5923
|
+
Object.defineProperty(err, 'rawText', {
|
|
5924
|
+
value: finalizationError.rawText,
|
|
5925
|
+
enumerable: true,
|
|
5926
|
+
})
|
|
5927
|
+
}
|
|
5745
5928
|
throw err
|
|
5746
5929
|
}
|
|
5747
5930
|
|