@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
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { normalizeToolResult } from '../../../utilities/tool-result'
|
|
2
2
|
import { tanstackMetadata } from '../../../utilities/merge-metadata'
|
|
3
3
|
import { isProviderExecutedToolCall } from '../../../utilities/provider-executed'
|
|
4
|
+
import { mergeStreams } from '../../../utilities/merge-streams'
|
|
4
5
|
import type { AdapterYieldChunk } from '../../../utilities/adapter-yield-chunk'
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
StandardSchemaValidationError,
|
|
8
|
+
isStandardSchema,
|
|
9
|
+
parseWithStandardSchema,
|
|
10
|
+
validateWithStandardSchema,
|
|
11
|
+
} from './schema-converter'
|
|
6
12
|
import type { ToolApprovalResolution } from '../../../interrupts'
|
|
7
13
|
import type {
|
|
8
14
|
AnyTool,
|
|
@@ -13,12 +19,15 @@ import type {
|
|
|
13
19
|
ModelMessage,
|
|
14
20
|
RunFinishedEvent,
|
|
15
21
|
StreamChunk,
|
|
22
|
+
TextOptions,
|
|
16
23
|
Tool,
|
|
17
24
|
ToolCall,
|
|
18
25
|
ToolCallArgsEvent,
|
|
19
26
|
ToolCallEndEvent,
|
|
20
27
|
ToolCallStartEvent,
|
|
21
28
|
ToolExecutionContext,
|
|
29
|
+
ToolInputResponse,
|
|
30
|
+
ToolResultOutcome,
|
|
22
31
|
ToolOutputState,
|
|
23
32
|
} from '../../../types'
|
|
24
33
|
import type {
|
|
@@ -462,6 +471,8 @@ export interface ToolResult {
|
|
|
462
471
|
toolName: string
|
|
463
472
|
result: any
|
|
464
473
|
state?: 'output-available' | 'output-error'
|
|
474
|
+
/** Set when the user or middleware cancelled or denied the tool call; state is output-error. */
|
|
475
|
+
outcome?: ToolResultOutcome
|
|
465
476
|
/** Duration of tool execution in milliseconds (only for server-executed tools) */
|
|
466
477
|
duration?: number
|
|
467
478
|
/**
|
|
@@ -490,9 +501,38 @@ export interface ClientToolRequest {
|
|
|
490
501
|
input: any
|
|
491
502
|
}
|
|
492
503
|
|
|
504
|
+
/** Form or sampling input that paused a server tool. */
|
|
505
|
+
export interface McpInputRequest {
|
|
506
|
+
toolCallId: string
|
|
507
|
+
toolName: string
|
|
508
|
+
kind: 'form' | 'sampling'
|
|
509
|
+
request: unknown
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
interface McpInputRequiredThrow {
|
|
513
|
+
name: 'MCPInputRequiredError'
|
|
514
|
+
kind: 'form' | 'sampling'
|
|
515
|
+
request: unknown
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function isMcpInputRequired(value: unknown): value is McpInputRequiredThrow {
|
|
519
|
+
if (typeof value !== 'object' || value === null) return false
|
|
520
|
+
if (!('name' in value) || value.name !== 'MCPInputRequiredError') {
|
|
521
|
+
return false
|
|
522
|
+
}
|
|
523
|
+
if (!('kind' in value)) return false
|
|
524
|
+
const kindIsFormOrSampling =
|
|
525
|
+
value.kind === 'form' || value.kind === 'sampling'
|
|
526
|
+
if (!kindIsFormOrSampling) return false
|
|
527
|
+
return 'request' in value
|
|
528
|
+
}
|
|
529
|
+
|
|
493
530
|
export interface ToolResumeExecutionState {
|
|
531
|
+
clientToolErrors?: ReadonlyMap<string, string>
|
|
494
532
|
deniedToolResults?: ReadonlyMap<string, unknown>
|
|
495
533
|
cancelledToolCallIds?: ReadonlySet<string>
|
|
534
|
+
/** Answers to `mcp_input` interrupts, by tool call id. */
|
|
535
|
+
inputResponses?: ReadonlyMap<string, ToolInputResponse>
|
|
496
536
|
}
|
|
497
537
|
|
|
498
538
|
function approvalResolution(
|
|
@@ -527,6 +567,8 @@ interface ExecuteToolCallsResult {
|
|
|
527
567
|
needsApproval: Array<ApprovalRequest>
|
|
528
568
|
/** Tools that need client-side execution */
|
|
529
569
|
needsClientExecution: Array<ClientToolRequest>
|
|
570
|
+
/** Server tools that paused for MCP form or sampling input */
|
|
571
|
+
inputRequired: Array<McpInputRequest>
|
|
530
572
|
/** Interrupts raised by subagents that run as tools */
|
|
531
573
|
subagentInterrupts: Array<Interrupt>
|
|
532
574
|
}
|
|
@@ -639,6 +681,7 @@ export async function* executeServerTool<TContext = unknown>(
|
|
|
639
681
|
pendingEvents: Array<CustomEvent | StreamChunk>,
|
|
640
682
|
results: Array<ToolResult>,
|
|
641
683
|
middlewareHooks?: ToolExecutionMiddlewareHooks,
|
|
684
|
+
inputRequired?: Array<McpInputRequest>,
|
|
642
685
|
subagentInterrupts?: Array<Interrupt>,
|
|
643
686
|
): AsyncGenerator<CustomEvent | StreamChunk, void, void> {
|
|
644
687
|
const startTime = Date.now()
|
|
@@ -744,6 +787,18 @@ export async function* executeServerTool<TContext = unknown>(
|
|
|
744
787
|
throw error
|
|
745
788
|
}
|
|
746
789
|
|
|
790
|
+
// Same shape as MCPInputRequiredError. Pause instead of a tool error.
|
|
791
|
+
if (isMcpInputRequired(error)) {
|
|
792
|
+
if (!inputRequired) throw error
|
|
793
|
+
inputRequired.push({
|
|
794
|
+
toolCallId: toolCall.id,
|
|
795
|
+
toolName,
|
|
796
|
+
kind: error.kind,
|
|
797
|
+
request: error.request,
|
|
798
|
+
})
|
|
799
|
+
return
|
|
800
|
+
}
|
|
801
|
+
|
|
747
802
|
const message = error instanceof Error ? error.message : 'Unknown error'
|
|
748
803
|
results.push({
|
|
749
804
|
toolCallId: toolCall.id,
|
|
@@ -768,17 +823,35 @@ export async function* executeServerTool<TContext = unknown>(
|
|
|
768
823
|
}
|
|
769
824
|
}
|
|
770
825
|
|
|
771
|
-
function buildClientToolResult(
|
|
826
|
+
async function buildClientToolResult(
|
|
772
827
|
toolCallId: string,
|
|
773
828
|
toolName: string,
|
|
774
829
|
tool: AnyTool,
|
|
775
830
|
rawResult: unknown,
|
|
776
831
|
input?: unknown,
|
|
777
|
-
|
|
832
|
+
errorText?: string,
|
|
833
|
+
): Promise<ToolResult> {
|
|
834
|
+
if (errorText !== undefined) {
|
|
835
|
+
return {
|
|
836
|
+
toolCallId,
|
|
837
|
+
toolName,
|
|
838
|
+
result: { error: errorText },
|
|
839
|
+
input,
|
|
840
|
+
state: 'output-error',
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
778
844
|
try {
|
|
779
845
|
let result = rawResult
|
|
780
846
|
if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {
|
|
781
|
-
|
|
847
|
+
const validation = await validateWithStandardSchema<unknown>(
|
|
848
|
+
tool.outputSchema,
|
|
849
|
+
result,
|
|
850
|
+
)
|
|
851
|
+
if (!validation.success) {
|
|
852
|
+
throw new StandardSchemaValidationError(validation.issues)
|
|
853
|
+
}
|
|
854
|
+
result = validation.data
|
|
782
855
|
}
|
|
783
856
|
|
|
784
857
|
const parsed =
|
|
@@ -816,6 +889,9 @@ function buildClientToolResult(
|
|
|
816
889
|
* @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
|
|
817
890
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
818
891
|
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
892
|
+
* @param toolExecution - `'parallel'` (default) prepares every call in call
|
|
893
|
+
* order, then starts the server tools together. `'sequential'` runs one
|
|
894
|
+
* call at a time. Results come back in call order either way.
|
|
819
895
|
*/
|
|
820
896
|
export async function* executeToolCalls<TContext = unknown>(
|
|
821
897
|
toolCalls: Array<ToolCall>,
|
|
@@ -831,11 +907,12 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
831
907
|
userContext?: TContext,
|
|
832
908
|
abortSignal?: AbortSignal,
|
|
833
909
|
resumeState?: ToolResumeExecutionState,
|
|
910
|
+
toolExecution: NonNullable<TextOptions['toolExecution']> = 'parallel',
|
|
834
911
|
): AsyncGenerator<CustomEvent | StreamChunk, ExecuteToolCallsResult, void> {
|
|
835
912
|
const results: Array<ToolResult> = []
|
|
836
913
|
const needsApproval: Array<ApprovalRequest> = []
|
|
837
914
|
const needsClientExecution: Array<ClientToolRequest> = []
|
|
838
|
-
const
|
|
915
|
+
const inputRequired: Array<McpInputRequest> = []
|
|
839
916
|
|
|
840
917
|
// Create tool lookup map
|
|
841
918
|
const toolMap = new Map<string, AnyTool>()
|
|
@@ -843,6 +920,66 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
843
920
|
toolMap.set(tool.name, tool)
|
|
844
921
|
}
|
|
845
922
|
|
|
923
|
+
const runsInOrder = toolExecution === 'sequential'
|
|
924
|
+
// Parallel mode collects the server runs here. `mergeStreams` starts them
|
|
925
|
+
// together after the loop.
|
|
926
|
+
const runs: Array<AsyncGenerator<CustomEvent | StreamChunk, void, void>> = []
|
|
927
|
+
// Errors thrown by a run or a before-hook. They are rethrown only after every
|
|
928
|
+
// started tool has finished, so no tool outlives the batch.
|
|
929
|
+
const failures: Array<unknown> = []
|
|
930
|
+
// Each call's subagent interrupts, so they come back in call order.
|
|
931
|
+
const interruptsByCall = new Map<string, Array<Interrupt>>()
|
|
932
|
+
|
|
933
|
+
// A tool that has not started when the run aborts never starts. It gets an
|
|
934
|
+
// error result, so every call of the batch still has a result.
|
|
935
|
+
async function* runServerTool(
|
|
936
|
+
toolCall: ToolCall,
|
|
937
|
+
tool: AnyTool,
|
|
938
|
+
toolName: string,
|
|
939
|
+
input: unknown,
|
|
940
|
+
context: ToolExecutionContext<TContext>,
|
|
941
|
+
pendingEvents: Array<CustomEvent | StreamChunk>,
|
|
942
|
+
): AsyncGenerator<CustomEvent | StreamChunk, void, void> {
|
|
943
|
+
try {
|
|
944
|
+
if (abortSignal?.aborted) {
|
|
945
|
+
results.push({
|
|
946
|
+
toolCallId: toolCall.id,
|
|
947
|
+
toolName,
|
|
948
|
+
result: { error: 'Operation aborted' },
|
|
949
|
+
input,
|
|
950
|
+
state: 'output-error',
|
|
951
|
+
duration: 0,
|
|
952
|
+
})
|
|
953
|
+
await middlewareHooks?.onAfterToolCall?.({
|
|
954
|
+
toolCall,
|
|
955
|
+
tool,
|
|
956
|
+
toolName,
|
|
957
|
+
toolCallId: toolCall.id,
|
|
958
|
+
ok: false,
|
|
959
|
+
duration: 0,
|
|
960
|
+
error: new Error('Operation aborted'),
|
|
961
|
+
})
|
|
962
|
+
return
|
|
963
|
+
}
|
|
964
|
+
const interrupts: Array<Interrupt> = []
|
|
965
|
+
interruptsByCall.set(toolCall.id, interrupts)
|
|
966
|
+
yield* executeServerTool(
|
|
967
|
+
toolCall,
|
|
968
|
+
tool,
|
|
969
|
+
toolName,
|
|
970
|
+
input,
|
|
971
|
+
context,
|
|
972
|
+
pendingEvents,
|
|
973
|
+
results,
|
|
974
|
+
middlewareHooks,
|
|
975
|
+
inputRequired,
|
|
976
|
+
interrupts,
|
|
977
|
+
)
|
|
978
|
+
} catch (error) {
|
|
979
|
+
failures.push(error)
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
846
983
|
// Batch gating: when any tool in the batch still needs an approval decision,
|
|
847
984
|
// defer all execution so side effects don't happen before the user decides.
|
|
848
985
|
const hasPendingApprovals = toolCalls.some((tc) => {
|
|
@@ -891,6 +1028,7 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
891
1028
|
toolName,
|
|
892
1029
|
result: { error: 'Tool execution cancelled' },
|
|
893
1030
|
state: 'output-error',
|
|
1031
|
+
outcome: 'cancelled',
|
|
894
1032
|
})
|
|
895
1033
|
continue
|
|
896
1034
|
}
|
|
@@ -942,10 +1080,12 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
942
1080
|
|
|
943
1081
|
// Create a ToolExecutionContext for this tool call with event emission
|
|
944
1082
|
const pendingEvents: Array<CustomEvent | StreamChunk> = []
|
|
1083
|
+
const inputResponse = resumeState?.inputResponses?.get(toolCall.id)
|
|
945
1084
|
const context = {
|
|
946
1085
|
toolCallId: toolCall.id,
|
|
947
1086
|
context: userContext,
|
|
948
1087
|
abortSignal,
|
|
1088
|
+
...(inputResponse !== undefined ? { inputResponse } : {}),
|
|
949
1089
|
emitCustomEvent: (
|
|
950
1090
|
eventName: string,
|
|
951
1091
|
value: Record<string, any>,
|
|
@@ -980,14 +1120,16 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
980
1120
|
if (approved) {
|
|
981
1121
|
input = editedApprovalArgs(resolution) ?? input
|
|
982
1122
|
// Approved - check if client has executed
|
|
983
|
-
|
|
1123
|
+
const clientError = resumeState?.clientToolErrors?.get(toolCall.id)
|
|
1124
|
+
if (clientResults.has(toolCall.id) || clientError !== undefined) {
|
|
984
1125
|
results.push(
|
|
985
|
-
buildClientToolResult(
|
|
1126
|
+
await buildClientToolResult(
|
|
986
1127
|
toolCall.id,
|
|
987
1128
|
toolName,
|
|
988
1129
|
tool,
|
|
989
1130
|
clientResults.get(toolCall.id),
|
|
990
1131
|
input,
|
|
1132
|
+
clientError,
|
|
991
1133
|
),
|
|
992
1134
|
)
|
|
993
1135
|
} else {
|
|
@@ -1008,6 +1150,7 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
1008
1150
|
deniedApprovalResult(resolution),
|
|
1009
1151
|
input,
|
|
1010
1152
|
state: 'output-error',
|
|
1153
|
+
outcome: 'denied',
|
|
1011
1154
|
})
|
|
1012
1155
|
}
|
|
1013
1156
|
} else {
|
|
@@ -1021,14 +1164,16 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
1021
1164
|
}
|
|
1022
1165
|
} else {
|
|
1023
1166
|
// No approval needed - check if client has executed
|
|
1024
|
-
|
|
1167
|
+
const clientError = resumeState?.clientToolErrors?.get(toolCall.id)
|
|
1168
|
+
if (clientResults.has(toolCall.id) || clientError !== undefined) {
|
|
1025
1169
|
results.push(
|
|
1026
|
-
buildClientToolResult(
|
|
1170
|
+
await buildClientToolResult(
|
|
1027
1171
|
toolCall.id,
|
|
1028
1172
|
toolName,
|
|
1029
1173
|
tool,
|
|
1030
1174
|
clientResults.get(toolCall.id),
|
|
1031
1175
|
input,
|
|
1176
|
+
clientError,
|
|
1032
1177
|
),
|
|
1033
1178
|
)
|
|
1034
1179
|
} else {
|
|
@@ -1048,50 +1193,7 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
1048
1193
|
const approvalId = `approval_${toolCall.id}`
|
|
1049
1194
|
const resolution = approvalResolution(approvals, toolCall.id)
|
|
1050
1195
|
|
|
1051
|
-
|
|
1052
|
-
if (resolution !== undefined) {
|
|
1053
|
-
const approved = isApproved(resolution)
|
|
1054
|
-
|
|
1055
|
-
if (approved) {
|
|
1056
|
-
input = editedApprovalArgs(resolution) ?? input
|
|
1057
|
-
// Apply middleware before-hook for approved tools
|
|
1058
|
-
if (middlewareHooks) {
|
|
1059
|
-
const decision = await applyBeforeToolCallDecision(
|
|
1060
|
-
toolCall,
|
|
1061
|
-
tool,
|
|
1062
|
-
input,
|
|
1063
|
-
toolName,
|
|
1064
|
-
middlewareHooks,
|
|
1065
|
-
results,
|
|
1066
|
-
)
|
|
1067
|
-
if (!decision.proceed) continue
|
|
1068
|
-
input = decision.input
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
yield* executeServerTool(
|
|
1072
|
-
toolCall,
|
|
1073
|
-
tool,
|
|
1074
|
-
toolName,
|
|
1075
|
-
input,
|
|
1076
|
-
context,
|
|
1077
|
-
pendingEvents,
|
|
1078
|
-
results,
|
|
1079
|
-
middlewareHooks,
|
|
1080
|
-
subagentInterrupts,
|
|
1081
|
-
)
|
|
1082
|
-
} else {
|
|
1083
|
-
// User declined
|
|
1084
|
-
results.push({
|
|
1085
|
-
toolCallId: toolCall.id,
|
|
1086
|
-
toolName,
|
|
1087
|
-
result:
|
|
1088
|
-
resumeState?.deniedToolResults?.get(toolCall.id) ??
|
|
1089
|
-
deniedApprovalResult(resolution),
|
|
1090
|
-
input,
|
|
1091
|
-
state: 'output-error',
|
|
1092
|
-
})
|
|
1093
|
-
}
|
|
1094
|
-
} else {
|
|
1196
|
+
if (resolution === undefined) {
|
|
1095
1197
|
// Need approval
|
|
1096
1198
|
needsApproval.push({
|
|
1097
1199
|
toolCallId: toolCall.id,
|
|
@@ -1099,36 +1201,81 @@ export async function* executeToolCalls<TContext = unknown>(
|
|
|
1099
1201
|
input,
|
|
1100
1202
|
approvalId,
|
|
1101
1203
|
})
|
|
1204
|
+
continue
|
|
1102
1205
|
}
|
|
1103
|
-
|
|
1206
|
+
if (!isApproved(resolution)) {
|
|
1207
|
+
// User declined
|
|
1208
|
+
results.push({
|
|
1209
|
+
toolCallId: toolCall.id,
|
|
1210
|
+
toolName,
|
|
1211
|
+
result:
|
|
1212
|
+
resumeState?.deniedToolResults?.get(toolCall.id) ??
|
|
1213
|
+
deniedApprovalResult(resolution),
|
|
1214
|
+
input,
|
|
1215
|
+
state: 'output-error',
|
|
1216
|
+
outcome: 'denied',
|
|
1217
|
+
})
|
|
1218
|
+
continue
|
|
1219
|
+
}
|
|
1220
|
+
// Approved: run it like any other server tool below.
|
|
1221
|
+
input = editedApprovalArgs(resolution) ?? input
|
|
1104
1222
|
}
|
|
1105
1223
|
|
|
1106
|
-
// CASE 3:
|
|
1224
|
+
// CASE 3: Server tool, approved or with no approval
|
|
1107
1225
|
if (middlewareHooks) {
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1226
|
+
let decision: Awaited<ReturnType<typeof applyBeforeToolCallDecision>>
|
|
1227
|
+
try {
|
|
1228
|
+
decision = await applyBeforeToolCallDecision(
|
|
1229
|
+
toolCall,
|
|
1230
|
+
tool,
|
|
1231
|
+
input,
|
|
1232
|
+
toolName,
|
|
1233
|
+
middlewareHooks,
|
|
1234
|
+
results,
|
|
1235
|
+
)
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
// The calls prepared so far still run, as they would one at a time.
|
|
1238
|
+
failures.push(error)
|
|
1239
|
+
break
|
|
1240
|
+
}
|
|
1116
1241
|
if (!decision.proceed) continue
|
|
1117
1242
|
input = decision.input
|
|
1118
1243
|
}
|
|
1119
1244
|
|
|
1120
|
-
|
|
1245
|
+
const run = runServerTool(
|
|
1121
1246
|
toolCall,
|
|
1122
1247
|
tool,
|
|
1123
1248
|
toolName,
|
|
1124
1249
|
input,
|
|
1125
1250
|
context,
|
|
1126
1251
|
pendingEvents,
|
|
1127
|
-
results,
|
|
1128
|
-
middlewareHooks,
|
|
1129
|
-
subagentInterrupts,
|
|
1130
1252
|
)
|
|
1253
|
+
if (!runsInOrder) {
|
|
1254
|
+
runs.push(run)
|
|
1255
|
+
continue
|
|
1256
|
+
}
|
|
1257
|
+
yield* run
|
|
1258
|
+
if (failures.length > 0) break
|
|
1131
1259
|
}
|
|
1132
1260
|
|
|
1133
|
-
|
|
1261
|
+
yield* mergeStreams(runs)
|
|
1262
|
+
if (failures.length > 0) throw failures[0]
|
|
1263
|
+
|
|
1264
|
+
// Parallel tools finish in any order. The model gets the results, input
|
|
1265
|
+
// requests, and interrupts in the order it made the calls.
|
|
1266
|
+
const callOrder = new Map(toolCalls.map((tc, index) => [tc.id, index]))
|
|
1267
|
+
const byCallOrder = (a: { toolCallId: string }, b: { toolCallId: string }) =>
|
|
1268
|
+
(callOrder.get(a.toolCallId) ?? 0) - (callOrder.get(b.toolCallId) ?? 0)
|
|
1269
|
+
results.sort(byCallOrder)
|
|
1270
|
+
inputRequired.sort(byCallOrder)
|
|
1271
|
+
|
|
1272
|
+
return {
|
|
1273
|
+
results,
|
|
1274
|
+
needsApproval,
|
|
1275
|
+
needsClientExecution,
|
|
1276
|
+
inputRequired,
|
|
1277
|
+
subagentInterrupts: toolCalls.flatMap(
|
|
1278
|
+
(tc) => interruptsByCall.get(tc.id) ?? [],
|
|
1279
|
+
),
|
|
1280
|
+
}
|
|
1134
1281
|
}
|
|
@@ -99,6 +99,7 @@ export interface ServerTool<
|
|
|
99
99
|
outputSchema?: TOutput
|
|
100
100
|
needsApproval?: TNeedsApproval
|
|
101
101
|
approvalSchema?: TApprovalSchema
|
|
102
|
+
execution?: 'task'
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/**
|
|
@@ -127,6 +128,7 @@ export interface ClientTool<
|
|
|
127
128
|
outputSchema?: TOutput
|
|
128
129
|
needsApproval?: TNeedsApproval
|
|
129
130
|
approvalSchema?: TApprovalSchema
|
|
131
|
+
execution?: 'task'
|
|
130
132
|
lazy?: boolean
|
|
131
133
|
metadata?: Record<string, unknown>
|
|
132
134
|
execute?: ToolExecuteFunction<TInput, TOutput, TContext>
|
|
@@ -158,6 +160,7 @@ export interface ToolDefinitionInstance<
|
|
|
158
160
|
outputSchema: TOutput
|
|
159
161
|
needsApproval?: TNeedsApproval
|
|
160
162
|
approvalSchema: TApprovalSchema
|
|
163
|
+
execution?: 'task'
|
|
161
164
|
readonly [toolApprovalCapability]?: {
|
|
162
165
|
needsApproval: TNeedsApproval
|
|
163
166
|
approvalSchema: TApprovalSchema
|
|
@@ -221,6 +224,7 @@ export type ToolDefinitionConfig<
|
|
|
221
224
|
outputSchema?: TOutput
|
|
222
225
|
lazy?: boolean
|
|
223
226
|
metadata?: Record<string, unknown>
|
|
227
|
+
execution?: 'task'
|
|
224
228
|
} & ApprovalConfig<TNeedsApproval, TApprovalSchema>
|
|
225
229
|
|
|
226
230
|
/**
|
|
@@ -353,6 +357,7 @@ export function toolDefinition<
|
|
|
353
357
|
const outputSchema = config.outputSchema as TOutput
|
|
354
358
|
const approvalSchema = config.approvalSchema as TApprovalSchema
|
|
355
359
|
const needsApproval = config.needsApproval as TNeedsApproval | undefined
|
|
360
|
+
const execution = config.execution
|
|
356
361
|
|
|
357
362
|
const definition: ToolDefinition<
|
|
358
363
|
TInput,
|
|
@@ -367,6 +372,7 @@ export function toolDefinition<
|
|
|
367
372
|
outputSchema,
|
|
368
373
|
approvalSchema,
|
|
369
374
|
needsApproval,
|
|
375
|
+
execution,
|
|
370
376
|
server<TContext = unknown>(
|
|
371
377
|
execute: ToolExecuteFunction<TInput, TOutput, TContext>,
|
|
372
378
|
): ServerTool<
|
|
@@ -385,6 +391,7 @@ export function toolDefinition<
|
|
|
385
391
|
outputSchema,
|
|
386
392
|
approvalSchema,
|
|
387
393
|
needsApproval,
|
|
394
|
+
execution,
|
|
388
395
|
execute,
|
|
389
396
|
}
|
|
390
397
|
},
|
|
@@ -407,6 +414,7 @@ export function toolDefinition<
|
|
|
407
414
|
outputSchema,
|
|
408
415
|
approvalSchema,
|
|
409
416
|
needsApproval,
|
|
417
|
+
execution,
|
|
410
418
|
...(execute !== undefined && { execute }),
|
|
411
419
|
}
|
|
412
420
|
},
|
|
@@ -131,6 +131,10 @@ export interface EvaluateAdapterResult {
|
|
|
131
131
|
model: string
|
|
132
132
|
answers: Record<string, WireAnswer>
|
|
133
133
|
usage: TokenUsage
|
|
134
|
+
/** Provider response id, for example to look the request up later. */
|
|
135
|
+
id?: string
|
|
136
|
+
/** Upstream provider that served the request, when a router reports it. */
|
|
137
|
+
provider?: string
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
/**
|
|
@@ -100,6 +100,10 @@ export interface EvaluateResultMeta {
|
|
|
100
100
|
/** Resolved model id from the provider. */
|
|
101
101
|
model: string
|
|
102
102
|
usage: TokenUsage
|
|
103
|
+
/** Provider response id, when the adapter returns one. */
|
|
104
|
+
id?: string
|
|
105
|
+
/** Upstream provider that served the request, when the adapter returns one. */
|
|
106
|
+
provider?: string
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
/**
|
|
@@ -576,6 +580,8 @@ export async function decide<
|
|
|
576
580
|
return withMeta(answers, {
|
|
577
581
|
model: result.model,
|
|
578
582
|
usage: result.usage,
|
|
583
|
+
...(result.id !== undefined && { id: result.id }),
|
|
584
|
+
...(result.provider !== undefined && { provider: result.provider }),
|
|
579
585
|
})
|
|
580
586
|
} catch (error) {
|
|
581
587
|
const duration = Date.now() - startTime
|
|
@@ -445,7 +445,7 @@ export interface ListVoicesActivityOptions<
|
|
|
445
445
|
* import { elevenlabsSpeech } from '@tanstack/ai-elevenlabs'
|
|
446
446
|
*
|
|
447
447
|
* const { voices } = await listVoices({
|
|
448
|
-
* adapter: elevenlabsSpeech('
|
|
448
|
+
* adapter: elevenlabsSpeech('eleven_v4'),
|
|
449
449
|
* origins: ['generated', 'cloned'],
|
|
450
450
|
* })
|
|
451
451
|
* ```
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { snapToDurationOption } from './snap'
|
|
1
2
|
import type {
|
|
2
3
|
ModelInputModalitiesByName,
|
|
3
4
|
VideoGenerationOptions,
|
|
@@ -25,6 +26,12 @@ export type DurationOptions<T extends string | number | undefined> =
|
|
|
25
26
|
}
|
|
26
27
|
| { kind: 'none' }
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Spellings of one clip length: the number `6`, the string `"6"`, or the
|
|
31
|
+
* template `"6s"`.
|
|
32
|
+
*/
|
|
33
|
+
export type VideoDurationSpell<N extends number> = N | `${N}` | `${N}s`
|
|
34
|
+
|
|
28
35
|
/**
|
|
29
36
|
* Configuration for video adapter instances
|
|
30
37
|
*
|
|
@@ -126,10 +133,15 @@ export interface VideoAdapter<
|
|
|
126
133
|
availableDurations: () => DurationOptions<TModelDurationByName[TModel]>
|
|
127
134
|
|
|
128
135
|
/**
|
|
129
|
-
* Coerce
|
|
130
|
-
*
|
|
136
|
+
* Coerce `input` to the closest duration this model accepts.
|
|
137
|
+
* `input` may be seconds (`7`), a numeric string (`"7"`), a template
|
|
138
|
+
* (`"6s"`), or a keyword the model lists (`"auto"`).
|
|
139
|
+
* Returns `undefined` when the model has no duration field, or when
|
|
140
|
+
* `input` is a keyword that model does not list.
|
|
131
141
|
*/
|
|
132
|
-
snapDuration: (
|
|
142
|
+
snapDuration: (
|
|
143
|
+
input: number | string,
|
|
144
|
+
) => TModelDurationByName[TModel] | undefined
|
|
133
145
|
}
|
|
134
146
|
|
|
135
147
|
/**
|
|
@@ -208,11 +220,13 @@ export abstract class BaseVideoAdapter<
|
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
223
|
+
* Uses `availableDurations()`. Adapters that declare a duration map only
|
|
224
|
+
* need to override that method.
|
|
213
225
|
*/
|
|
214
|
-
snapDuration(
|
|
215
|
-
|
|
226
|
+
snapDuration(
|
|
227
|
+
input: number | string,
|
|
228
|
+
): TModelDurationByName[TModel] | undefined {
|
|
229
|
+
return snapToDurationOption(input, this.availableDurations())
|
|
216
230
|
}
|
|
217
231
|
|
|
218
232
|
protected generateId(): string {
|
|
@@ -173,10 +173,11 @@ export type VideoCreateOptions<
|
|
|
173
173
|
/** Video size — format depends on the provider (e.g., "16:9", "1280x720") */
|
|
174
174
|
size?: VideoSizeForAdapter<TAdapter>
|
|
175
175
|
/**
|
|
176
|
-
* Video duration
|
|
177
|
-
*
|
|
178
|
-
* Pass `adapter.snapDuration(
|
|
179
|
-
* value.
|
|
176
|
+
* Video duration. Adapters that declare a per-model duration map narrow
|
|
177
|
+
* this to that model's union (for example `4 | 6 | 8` for Veo 3, or
|
|
178
|
+
* `4 | "4" | "4s"` for Sora). Pass `adapter.snapDuration(input)` to coerce
|
|
179
|
+
* a raw value. `input` may be seconds, a `"6s"` template, or `"auto"` when
|
|
180
|
+
* the model lists it.
|
|
180
181
|
*/
|
|
181
182
|
duration?: VideoDurationForAdapter<TAdapter>
|
|
182
183
|
/**
|