@ai-sdk/workflow 1.0.0-beta.10 → 1.0.0-beta.101
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/CHANGELOG.md +782 -0
- package/dist/index.d.mts +200 -112
- package/dist/index.mjs +879 -344
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -11
- package/src/create-language-model-tool-result-output.ts +85 -0
- package/src/do-stream-step.ts +46 -9
- package/src/index.ts +5 -3
- package/src/providers/mock.ts +8 -8
- package/src/serializable-schema.ts +5 -1
- package/src/stream-text-iterator.ts +150 -62
- package/src/test/agent-e2e-workflows.ts +89 -9
- package/src/to-ui-message-chunk.ts +13 -10
- package/src/workflow-agent.ts +1215 -611
- package/src/workflow-chat-transport.ts +17 -15
- package/src/telemetry.ts +0 -199
package/src/workflow-agent.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
JSONValue,
|
|
3
2
|
LanguageModelV4CallOptions,
|
|
4
3
|
LanguageModelV4Prompt,
|
|
5
4
|
LanguageModelV4StreamPart,
|
|
@@ -7,48 +6,74 @@ import type {
|
|
|
7
6
|
SharedV4ProviderOptions,
|
|
8
7
|
} from '@ai-sdk/provider';
|
|
9
8
|
import {
|
|
10
|
-
|
|
9
|
+
getErrorMessage,
|
|
10
|
+
validateTypes,
|
|
11
|
+
type Context,
|
|
12
|
+
type HasRequiredKey,
|
|
13
|
+
type InferToolSetContext,
|
|
14
|
+
} from '@ai-sdk/provider-utils';
|
|
15
|
+
import {
|
|
16
|
+
Output,
|
|
17
|
+
experimental_filterActiveTools as filterActiveTools,
|
|
11
18
|
type FinishReason,
|
|
12
19
|
type LanguageModelResponseMetadata,
|
|
13
20
|
type LanguageModelUsage,
|
|
21
|
+
type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
|
|
14
22
|
type ModelMessage,
|
|
15
|
-
Output,
|
|
16
23
|
type StepResult,
|
|
17
24
|
type StopCondition,
|
|
18
|
-
type
|
|
19
|
-
type
|
|
25
|
+
type GenerateTextOnStepEndCallback,
|
|
26
|
+
type ActiveTools,
|
|
20
27
|
type ToolCallRepairFunction,
|
|
21
28
|
type ToolChoice,
|
|
22
29
|
type ToolSet,
|
|
23
30
|
type UIMessage,
|
|
24
|
-
LanguageModel,
|
|
31
|
+
type LanguageModel,
|
|
32
|
+
type Prompt,
|
|
33
|
+
type TelemetryOptions as CoreTelemetryOptions,
|
|
34
|
+
type Instructions,
|
|
25
35
|
} from 'ai';
|
|
26
36
|
import {
|
|
37
|
+
createRestrictedTelemetryDispatcher,
|
|
38
|
+
collectToolApprovals,
|
|
27
39
|
convertToLanguageModelPrompt,
|
|
28
40
|
mergeAbortSignals,
|
|
29
41
|
mergeCallbacks,
|
|
30
42
|
standardizePrompt,
|
|
43
|
+
validateApprovedToolApprovals,
|
|
31
44
|
} from 'ai/internal';
|
|
45
|
+
import { createLanguageModelToolResultOutput } from './create-language-model-tool-result-output.js';
|
|
32
46
|
import { streamTextIterator } from './stream-text-iterator.js';
|
|
33
|
-
import type { CompatibleLanguageModel } from './types.js';
|
|
34
47
|
|
|
35
48
|
// Re-export for consumers
|
|
36
49
|
export type { CompatibleLanguageModel } from './types.js';
|
|
37
50
|
|
|
38
51
|
/**
|
|
39
52
|
* Callback function to be called after each step completes.
|
|
40
|
-
* Alias for the AI SDK's
|
|
53
|
+
* Alias for the AI SDK's GenerateTextOnStepEndCallback, using
|
|
41
54
|
* WorkflowAgent-consistent naming.
|
|
42
55
|
*/
|
|
56
|
+
export type WorkflowAgentOnStepEndCallback<
|
|
57
|
+
TTools extends ToolSet = ToolSet,
|
|
58
|
+
TRuntimeContext extends Context = Context,
|
|
59
|
+
> = GenerateTextOnStepEndCallback<TTools, TRuntimeContext>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Callback function to be called after each step completes.
|
|
63
|
+
* Deprecated alias for `WorkflowAgentOnStepEndCallback`.
|
|
64
|
+
*
|
|
65
|
+
* @deprecated Use `WorkflowAgentOnStepEndCallback` instead.
|
|
66
|
+
*/
|
|
43
67
|
export type WorkflowAgentOnStepFinishCallback<
|
|
44
68
|
TTools extends ToolSet = ToolSet,
|
|
45
|
-
|
|
69
|
+
TRuntimeContext extends Context = Context,
|
|
70
|
+
> = WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
46
71
|
|
|
47
72
|
/**
|
|
48
73
|
* Infer the type of the tools of a workflow agent.
|
|
49
74
|
*/
|
|
50
75
|
export type InferWorkflowAgentTools<WORKFLOW_AGENT> =
|
|
51
|
-
WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS> ? TOOLS : never;
|
|
76
|
+
WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS, any> ? TOOLS : never;
|
|
52
77
|
|
|
53
78
|
/**
|
|
54
79
|
* Infer the UI message type of a workflow agent.
|
|
@@ -89,54 +114,14 @@ export interface OutputSpecification<OUTPUT, PARTIAL> {
|
|
|
89
114
|
*/
|
|
90
115
|
export type ProviderOptions = SharedV4ProviderOptions;
|
|
91
116
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Identifier for this function. Used to group telemetry data by function.
|
|
103
|
-
*/
|
|
104
|
-
functionId?: string;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Additional information to include in the telemetry data.
|
|
108
|
-
*/
|
|
109
|
-
metadata?: Record<
|
|
110
|
-
string,
|
|
111
|
-
| string
|
|
112
|
-
| number
|
|
113
|
-
| boolean
|
|
114
|
-
| Array<string | number | boolean>
|
|
115
|
-
| null
|
|
116
|
-
| undefined
|
|
117
|
-
>;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Enable or disable input recording. Enabled by default.
|
|
121
|
-
*
|
|
122
|
-
* You might want to disable input recording to avoid recording sensitive
|
|
123
|
-
* information, to reduce data transfers, or to increase performance.
|
|
124
|
-
*/
|
|
125
|
-
recordInputs?: boolean;
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Enable or disable output recording. Enabled by default.
|
|
129
|
-
*
|
|
130
|
-
* You might want to disable output recording to avoid recording sensitive
|
|
131
|
-
* information, to reduce data transfers, or to increase performance.
|
|
132
|
-
*/
|
|
133
|
-
recordOutputs?: boolean;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Custom tracer for the telemetry.
|
|
137
|
-
*/
|
|
138
|
-
tracer?: unknown;
|
|
139
|
-
}
|
|
117
|
+
type WorkflowAgentToolsContextParameter<TTools extends ToolSet> =
|
|
118
|
+
HasRequiredKey<InferToolSetContext<TTools>> extends true
|
|
119
|
+
? { toolsContext: InferToolSetContext<TTools> }
|
|
120
|
+
: { toolsContext?: never };
|
|
121
|
+
export type TelemetryOptions<
|
|
122
|
+
TRuntimeContext extends Context = Context,
|
|
123
|
+
TTools extends ToolSet = ToolSet,
|
|
124
|
+
> = CoreTelemetryOptions<TRuntimeContext, TTools>;
|
|
140
125
|
|
|
141
126
|
/**
|
|
142
127
|
* A transformation that is applied to the stream.
|
|
@@ -253,7 +238,10 @@ export interface GenerationSettings {
|
|
|
253
238
|
/**
|
|
254
239
|
* Information passed to the prepareStep callback.
|
|
255
240
|
*/
|
|
256
|
-
export interface PrepareStepInfo<
|
|
241
|
+
export interface PrepareStepInfo<
|
|
242
|
+
TTools extends ToolSet = ToolSet,
|
|
243
|
+
TRuntimeContext extends Context = Context,
|
|
244
|
+
> {
|
|
257
245
|
/**
|
|
258
246
|
* The current model configuration (string or function).
|
|
259
247
|
* The function should return a LanguageModelV4 instance.
|
|
@@ -268,7 +256,7 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
|
|
|
268
256
|
/**
|
|
269
257
|
* All previous steps with their results.
|
|
270
258
|
*/
|
|
271
|
-
steps: StepResult<TTools,
|
|
259
|
+
steps: StepResult<TTools, TRuntimeContext>[];
|
|
272
260
|
|
|
273
261
|
/**
|
|
274
262
|
* The messages that will be sent to the model.
|
|
@@ -277,16 +265,29 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
|
|
|
277
265
|
messages: LanguageModelV4Prompt;
|
|
278
266
|
|
|
279
267
|
/**
|
|
280
|
-
* The context
|
|
268
|
+
* The runtime context that flows through the agent loop.
|
|
269
|
+
* Treat the value as immutable; return a new `runtimeContext` from
|
|
270
|
+
* `prepareStep` to update it for the current and subsequent steps.
|
|
281
271
|
*/
|
|
282
|
-
|
|
272
|
+
runtimeContext: TRuntimeContext;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Per-tool context, keyed by tool name. Each tool receives only its own
|
|
276
|
+
* validated entry as `context` during execution.
|
|
277
|
+
* Treat the value as immutable; return a new `toolsContext` from
|
|
278
|
+
* `prepareStep` to update it for the current and subsequent steps.
|
|
279
|
+
*/
|
|
280
|
+
toolsContext: InferToolSetContext<TTools>;
|
|
283
281
|
}
|
|
284
282
|
|
|
285
283
|
/**
|
|
286
284
|
* Return type from the prepareStep callback.
|
|
287
285
|
* All properties are optional - only return the ones you want to override.
|
|
288
286
|
*/
|
|
289
|
-
export interface PrepareStepResult
|
|
287
|
+
export interface PrepareStepResult<
|
|
288
|
+
TTools extends ToolSet = ToolSet,
|
|
289
|
+
TRuntimeContext extends Context = Context,
|
|
290
|
+
> extends Partial<GenerationSettings> {
|
|
290
291
|
/**
|
|
291
292
|
* Override the model for this step.
|
|
292
293
|
*/
|
|
@@ -315,32 +316,53 @@ export interface PrepareStepResult extends Partial<GenerationSettings> {
|
|
|
315
316
|
activeTools?: string[];
|
|
316
317
|
|
|
317
318
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
319
|
+
* Updated runtime context for the current and subsequent steps.
|
|
320
|
+
* Returning a value replaces the agent's runtime context.
|
|
321
|
+
*/
|
|
322
|
+
runtimeContext?: TRuntimeContext;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Updated per-tool context for the current and subsequent steps.
|
|
326
|
+
* Returning a value replaces the agent's tools context.
|
|
320
327
|
*/
|
|
321
|
-
|
|
328
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
/**
|
|
325
332
|
* Callback function called before each step in the agent loop.
|
|
326
333
|
* Use this to modify settings, manage context, or implement dynamic behavior.
|
|
327
334
|
*/
|
|
328
|
-
export type PrepareStepCallback<
|
|
329
|
-
|
|
330
|
-
|
|
335
|
+
export type PrepareStepCallback<
|
|
336
|
+
TTools extends ToolSet = ToolSet,
|
|
337
|
+
TRuntimeContext extends Context = Context,
|
|
338
|
+
> = (
|
|
339
|
+
info: PrepareStepInfo<TTools, TRuntimeContext>,
|
|
340
|
+
) =>
|
|
341
|
+
| PrepareStepResult<TTools, TRuntimeContext>
|
|
342
|
+
| undefined
|
|
343
|
+
| Promise<PrepareStepResult<TTools, TRuntimeContext> | undefined>;
|
|
331
344
|
|
|
332
345
|
/**
|
|
333
346
|
* Options passed to the prepareCall callback.
|
|
334
347
|
*/
|
|
335
348
|
export interface PrepareCallOptions<
|
|
336
349
|
TTools extends ToolSet = ToolSet,
|
|
350
|
+
TRuntimeContext extends Context = Context,
|
|
337
351
|
> extends Partial<GenerationSettings> {
|
|
338
352
|
model: LanguageModel;
|
|
339
353
|
tools: TTools;
|
|
340
|
-
instructions?:
|
|
354
|
+
instructions?: Instructions;
|
|
341
355
|
toolChoice?: ToolChoice<TTools>;
|
|
342
|
-
|
|
343
|
-
|
|
356
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
357
|
+
/**
|
|
358
|
+
* Runtime context that flows through the agent loop.
|
|
359
|
+
* Treat as immutable; return a new `runtimeContext` to update it for the call.
|
|
360
|
+
*/
|
|
361
|
+
runtimeContext?: TRuntimeContext;
|
|
362
|
+
/**
|
|
363
|
+
* Per-tool context, keyed by tool name.
|
|
364
|
+
*/
|
|
365
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
344
366
|
messages: ModelMessage[];
|
|
345
367
|
}
|
|
346
368
|
|
|
@@ -350,172 +372,217 @@ export interface PrepareCallOptions<
|
|
|
350
372
|
* Note: `tools` cannot be overridden via prepareCall because they are
|
|
351
373
|
* bound at construction time for type safety.
|
|
352
374
|
*/
|
|
353
|
-
export type PrepareCallResult<
|
|
354
|
-
|
|
355
|
-
|
|
375
|
+
export type PrepareCallResult<
|
|
376
|
+
TTools extends ToolSet = ToolSet,
|
|
377
|
+
TRuntimeContext extends Context = Context,
|
|
378
|
+
> = Partial<Omit<PrepareCallOptions<TTools, TRuntimeContext>, 'tools'>>;
|
|
356
379
|
|
|
357
380
|
/**
|
|
358
381
|
* Callback called once before the agent loop starts to transform call parameters.
|
|
359
382
|
*/
|
|
360
|
-
export type PrepareCallCallback<
|
|
361
|
-
|
|
362
|
-
|
|
383
|
+
export type PrepareCallCallback<
|
|
384
|
+
TTools extends ToolSet = ToolSet,
|
|
385
|
+
TRuntimeContext extends Context = Context,
|
|
386
|
+
> = (
|
|
387
|
+
options: PrepareCallOptions<TTools, TRuntimeContext>,
|
|
388
|
+
) =>
|
|
389
|
+
| PrepareCallResult<TTools, TRuntimeContext>
|
|
390
|
+
| Promise<PrepareCallResult<TTools, TRuntimeContext>>;
|
|
363
391
|
|
|
364
392
|
/**
|
|
365
393
|
* Configuration options for creating a {@link WorkflowAgent} instance.
|
|
366
394
|
*/
|
|
367
|
-
export
|
|
395
|
+
export type WorkflowAgentOptions<
|
|
368
396
|
TTools extends ToolSet = ToolSet,
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
397
|
+
TRuntimeContext extends Context = Context,
|
|
398
|
+
> = GenerationSettings &
|
|
399
|
+
WorkflowAgentToolsContextParameter<TTools> & {
|
|
400
|
+
/**
|
|
401
|
+
* The id of the agent.
|
|
402
|
+
*/
|
|
403
|
+
id?: string;
|
|
374
404
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
405
|
+
/**
|
|
406
|
+
* The model provider to use for the agent.
|
|
407
|
+
*
|
|
408
|
+
* This should be a string compatible with the Vercel AI Gateway (e.g., 'anthropic/claude-opus'),
|
|
409
|
+
* or a LanguageModelV4 instance from a provider.
|
|
410
|
+
*/
|
|
411
|
+
model: LanguageModel;
|
|
382
412
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
413
|
+
/**
|
|
414
|
+
* A set of tools available to the agent.
|
|
415
|
+
* Tools can be implemented as workflow steps for automatic retries and persistence,
|
|
416
|
+
* or as regular workflow-level logic using core library features like sleep() and Hooks.
|
|
417
|
+
*/
|
|
418
|
+
tools?: TTools;
|
|
389
419
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
420
|
+
/**
|
|
421
|
+
* Agent instructions. Can be a string, a SystemModelMessage, or an array of SystemModelMessages.
|
|
422
|
+
* Supports provider-specific options (e.g., caching) when using the SystemModelMessage form.
|
|
423
|
+
*/
|
|
424
|
+
instructions?: Instructions;
|
|
395
425
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
426
|
+
/**
|
|
427
|
+
* Optional system prompt to guide the agent's behavior.
|
|
428
|
+
* @deprecated Use `instructions` instead.
|
|
429
|
+
*/
|
|
430
|
+
system?: string;
|
|
401
431
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
432
|
+
/**
|
|
433
|
+
* The tool choice strategy. Default: 'auto'.
|
|
434
|
+
*/
|
|
435
|
+
toolChoice?: ToolChoice<TTools>;
|
|
406
436
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
437
|
+
/**
|
|
438
|
+
* Optional telemetry configuration.
|
|
439
|
+
*/
|
|
440
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
411
441
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Default runtime context for every stream call on this agent.
|
|
444
|
+
*
|
|
445
|
+
* The runtime context flows through `prepareStep`, lifecycle callbacks,
|
|
446
|
+
* and step results.
|
|
447
|
+
* Treat as immutable; return a new `runtimeContext` from `prepareStep`
|
|
448
|
+
* to update it between steps.
|
|
449
|
+
*
|
|
450
|
+
* In workflow context, keep values serializable so they can cross workflow
|
|
451
|
+
* and step boundaries.
|
|
452
|
+
*
|
|
453
|
+
* Per-stream `runtimeContext` values passed to `stream()` override this default.
|
|
454
|
+
*/
|
|
455
|
+
runtimeContext?: TRuntimeContext;
|
|
420
456
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Default stop condition for the agent loop. When the condition is an array,
|
|
459
|
+
* any of the conditions can be met to stop the generation.
|
|
460
|
+
*
|
|
461
|
+
* Per-stream `stopWhen` values passed to `stream()` override this default.
|
|
462
|
+
*/
|
|
463
|
+
stopWhen?:
|
|
464
|
+
| StopCondition<NoInfer<ToolSet>, any>
|
|
465
|
+
| Array<StopCondition<NoInfer<ToolSet>, any>>;
|
|
430
466
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
467
|
+
/**
|
|
468
|
+
* Default set of active tools that limits which tools the model can call,
|
|
469
|
+
* without changing the tool call and result types in the result.
|
|
470
|
+
*
|
|
471
|
+
* Per-stream `activeTools` values passed to `stream()` override this default.
|
|
472
|
+
*/
|
|
473
|
+
activeTools?: ActiveTools<NoInfer<TTools>>;
|
|
438
474
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
475
|
+
/**
|
|
476
|
+
* Default output specification for structured outputs.
|
|
477
|
+
* Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
|
|
478
|
+
*
|
|
479
|
+
* Per-stream `output` values passed to `stream()` override this default.
|
|
480
|
+
*/
|
|
481
|
+
output?: OutputSpecification<any, any>;
|
|
446
482
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
483
|
+
/**
|
|
484
|
+
* Default function that attempts to repair a tool call that failed to parse.
|
|
485
|
+
*
|
|
486
|
+
* Per-stream `experimental_repairToolCall` values passed to `stream()` override this default.
|
|
487
|
+
*/
|
|
488
|
+
experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
|
|
453
489
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
490
|
+
/**
|
|
491
|
+
* Default custom download function to use for URLs.
|
|
492
|
+
*
|
|
493
|
+
* Per-stream `experimental_download` values passed to `stream()` override this default.
|
|
494
|
+
*/
|
|
495
|
+
experimental_download?: DownloadFunction;
|
|
460
496
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Default callback function called before each step in the agent loop.
|
|
499
|
+
* Use this to modify settings, manage context, or inject messages dynamically
|
|
500
|
+
* for every stream call on this agent instance.
|
|
501
|
+
*
|
|
502
|
+
* Per-stream `prepareStep` values passed to `stream()` override this default.
|
|
503
|
+
*/
|
|
504
|
+
prepareStep?: PrepareStepCallback<TTools, TRuntimeContext>;
|
|
469
505
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Callback function to be called after each step completes.
|
|
508
|
+
*/
|
|
509
|
+
onStepEnd?: WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
474
510
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Callback function to be called after each step completes.
|
|
513
|
+
*
|
|
514
|
+
* @deprecated Use `onStepEnd` instead.
|
|
515
|
+
*/
|
|
516
|
+
onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools, TRuntimeContext>;
|
|
479
517
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
518
|
+
/**
|
|
519
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
520
|
+
*/
|
|
521
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext>;
|
|
484
522
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
523
|
+
/**
|
|
524
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
525
|
+
*
|
|
526
|
+
* @deprecated Use `onEnd` instead.
|
|
527
|
+
*/
|
|
528
|
+
onFinish?: WorkflowAgentOnFinishCallback<TTools, TRuntimeContext>;
|
|
489
529
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
530
|
+
/**
|
|
531
|
+
* Callback called when the agent starts streaming, before any LLM calls.
|
|
532
|
+
*/
|
|
533
|
+
experimental_onStart?: WorkflowAgentOnStartCallback<
|
|
534
|
+
TTools,
|
|
535
|
+
TRuntimeContext
|
|
536
|
+
>;
|
|
494
537
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
538
|
+
/**
|
|
539
|
+
* Callback called before each step (LLM call) begins.
|
|
540
|
+
*/
|
|
541
|
+
experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
|
|
542
|
+
TTools,
|
|
543
|
+
TRuntimeContext
|
|
544
|
+
>;
|
|
499
545
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
546
|
+
/**
|
|
547
|
+
* Callback called before a tool's execute function runs.
|
|
548
|
+
*/
|
|
549
|
+
onToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TTools>;
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Callback called after a tool execution completes.
|
|
553
|
+
*/
|
|
554
|
+
onToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TTools>;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Prepare the parameters for the stream call.
|
|
558
|
+
* Called once before the agent loop starts. Use this to transform
|
|
559
|
+
* model, tools, instructions, or other settings based on runtime context.
|
|
560
|
+
*/
|
|
561
|
+
prepareCall?: PrepareCallCallback<TTools, TRuntimeContext>;
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Whether to allow system messages inside the `prompt` or `messages` fields.
|
|
565
|
+
* When `false` (the default), system messages in `prompt` or `messages` are
|
|
566
|
+
* rejected to prevent prompt-injection attacks. Set to `true` only when you
|
|
567
|
+
* intentionally interleave system messages with user messages.
|
|
568
|
+
*
|
|
569
|
+
* @default false
|
|
570
|
+
*/
|
|
571
|
+
allowSystemInMessages?: boolean;
|
|
572
|
+
};
|
|
507
573
|
|
|
508
574
|
/**
|
|
509
575
|
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
510
576
|
*/
|
|
511
|
-
export type
|
|
577
|
+
export type WorkflowAgentOnEndCallback<
|
|
512
578
|
TTools extends ToolSet = ToolSet,
|
|
579
|
+
TRuntimeContext extends Context = Context,
|
|
513
580
|
OUTPUT = never,
|
|
514
581
|
> = (event: {
|
|
515
582
|
/**
|
|
516
583
|
* Details for all steps.
|
|
517
584
|
*/
|
|
518
|
-
readonly steps: StepResult<TTools,
|
|
585
|
+
readonly steps: StepResult<TTools, TRuntimeContext>[];
|
|
519
586
|
|
|
520
587
|
/**
|
|
521
588
|
* The final messages including all tool calls and results.
|
|
@@ -532,15 +599,25 @@ export type WorkflowAgentOnFinishCallback<
|
|
|
532
599
|
*/
|
|
533
600
|
readonly finishReason: FinishReason;
|
|
534
601
|
|
|
602
|
+
/**
|
|
603
|
+
* The total token usage across all steps.
|
|
604
|
+
*/
|
|
605
|
+
readonly usage: LanguageModelUsage;
|
|
606
|
+
|
|
535
607
|
/**
|
|
536
608
|
* The total token usage across all steps.
|
|
537
609
|
*/
|
|
538
610
|
readonly totalUsage: LanguageModelUsage;
|
|
539
611
|
|
|
540
612
|
/**
|
|
541
|
-
*
|
|
613
|
+
* The runtime context at the end of the agent loop.
|
|
614
|
+
*/
|
|
615
|
+
readonly runtimeContext: TRuntimeContext;
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* The per-tool context at the end of the agent loop.
|
|
542
619
|
*/
|
|
543
|
-
readonly
|
|
620
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
544
621
|
|
|
545
622
|
/**
|
|
546
623
|
* The generated structured output. It uses the `output` specification.
|
|
@@ -549,6 +626,17 @@ export type WorkflowAgentOnFinishCallback<
|
|
|
549
626
|
readonly output: OUTPUT;
|
|
550
627
|
}) => PromiseLike<void> | void;
|
|
551
628
|
|
|
629
|
+
/**
|
|
630
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
631
|
+
*
|
|
632
|
+
* @deprecated Use `WorkflowAgentOnEndCallback` instead.
|
|
633
|
+
*/
|
|
634
|
+
export type WorkflowAgentOnFinishCallback<
|
|
635
|
+
TTools extends ToolSet = ToolSet,
|
|
636
|
+
TRuntimeContext extends Context = Context,
|
|
637
|
+
OUTPUT = never,
|
|
638
|
+
> = WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
639
|
+
|
|
552
640
|
/**
|
|
553
641
|
* Callback that is invoked when an error occurs during streaming.
|
|
554
642
|
*/
|
|
@@ -570,36 +658,57 @@ export type WorkflowAgentOnAbortCallback<TTools extends ToolSet = ToolSet> =
|
|
|
570
658
|
/**
|
|
571
659
|
* Callback that is called when the agent starts streaming, before any LLM calls.
|
|
572
660
|
*/
|
|
573
|
-
export type WorkflowAgentOnStartCallback
|
|
661
|
+
export type WorkflowAgentOnStartCallback<
|
|
662
|
+
TTools extends ToolSet = ToolSet,
|
|
663
|
+
TRuntimeContext extends Context = Context,
|
|
664
|
+
> = (event: {
|
|
574
665
|
/** The model being used */
|
|
575
666
|
readonly model: LanguageModel;
|
|
576
667
|
/** The messages being sent */
|
|
577
668
|
readonly messages: ModelMessage[];
|
|
669
|
+
/** Shared runtime context for this agent loop */
|
|
670
|
+
readonly runtimeContext: TRuntimeContext;
|
|
671
|
+
/** Per-tool context map for this agent loop */
|
|
672
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
578
673
|
}) => PromiseLike<void> | void;
|
|
579
674
|
|
|
580
675
|
/**
|
|
581
676
|
* Callback that is called before each step (LLM call) begins.
|
|
582
677
|
*/
|
|
583
|
-
export type WorkflowAgentOnStepStartCallback<
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
678
|
+
export type WorkflowAgentOnStepStartCallback<
|
|
679
|
+
TTools extends ToolSet = ToolSet,
|
|
680
|
+
TRuntimeContext extends Context = Context,
|
|
681
|
+
> = (event: {
|
|
682
|
+
/** The current step number (0-based) */
|
|
683
|
+
readonly stepNumber: number;
|
|
684
|
+
/** The model being used for this step */
|
|
685
|
+
readonly model: LanguageModel;
|
|
686
|
+
/** The messages being sent for this step */
|
|
687
|
+
readonly messages: ModelMessage[];
|
|
688
|
+
/** Results from all previously finished steps */
|
|
689
|
+
readonly steps: ReadonlyArray<StepResult<TTools, TRuntimeContext>>;
|
|
690
|
+
/** Shared runtime context for this step */
|
|
691
|
+
readonly runtimeContext: TRuntimeContext;
|
|
692
|
+
/** Per-tool context map for this step */
|
|
693
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
694
|
+
}) => PromiseLike<void> | void;
|
|
594
695
|
|
|
595
696
|
/**
|
|
596
697
|
* Callback that is called before a tool's execute function runs.
|
|
597
698
|
*/
|
|
598
|
-
export type
|
|
699
|
+
export type WorkflowAgentOnToolExecutionStartCallback<
|
|
700
|
+
TTools extends ToolSet = ToolSet,
|
|
701
|
+
> = (event: {
|
|
599
702
|
/** The tool call being executed */
|
|
600
703
|
readonly toolCall: ToolCall;
|
|
601
704
|
/** The current step number (0-based) */
|
|
602
705
|
readonly stepNumber: number;
|
|
706
|
+
/** Messages sent to the language model for the step that produced the call */
|
|
707
|
+
readonly messages: ModelMessage[];
|
|
708
|
+
/** Tool-specific context passed to the tool */
|
|
709
|
+
readonly toolContext:
|
|
710
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
711
|
+
| undefined;
|
|
603
712
|
}) => PromiseLike<void> | void;
|
|
604
713
|
|
|
605
714
|
/**
|
|
@@ -607,7 +716,9 @@ export type WorkflowAgentOnToolCallStartCallback = (event: {
|
|
|
607
716
|
* Uses a discriminated union pattern: check `success` to determine
|
|
608
717
|
* whether `output` or `error` is available.
|
|
609
718
|
*/
|
|
610
|
-
export type
|
|
719
|
+
export type WorkflowAgentOnToolExecutionEndCallback<
|
|
720
|
+
TTools extends ToolSet = ToolSet,
|
|
721
|
+
> = (
|
|
611
722
|
event:
|
|
612
723
|
| {
|
|
613
724
|
/** The tool call that was executed */
|
|
@@ -616,6 +727,12 @@ export type WorkflowAgentOnToolCallFinishCallback = (
|
|
|
616
727
|
readonly stepNumber: number;
|
|
617
728
|
/** Execution time in milliseconds */
|
|
618
729
|
readonly durationMs: number;
|
|
730
|
+
/** Messages sent to the language model for the step that produced the call */
|
|
731
|
+
readonly messages: ModelMessage[];
|
|
732
|
+
/** Tool-specific context passed to the tool */
|
|
733
|
+
readonly toolContext:
|
|
734
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
735
|
+
| undefined;
|
|
619
736
|
/** Whether the tool call succeeded */
|
|
620
737
|
readonly success: true;
|
|
621
738
|
/** The tool result */
|
|
@@ -629,6 +746,12 @@ export type WorkflowAgentOnToolCallFinishCallback = (
|
|
|
629
746
|
readonly stepNumber: number;
|
|
630
747
|
/** Execution time in milliseconds */
|
|
631
748
|
readonly durationMs: number;
|
|
749
|
+
/** Messages sent to the language model for the step that produced the call */
|
|
750
|
+
readonly messages: ModelMessage[];
|
|
751
|
+
/** Tool-specific context passed to the tool */
|
|
752
|
+
readonly toolContext:
|
|
753
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
754
|
+
| undefined;
|
|
632
755
|
/** Whether the tool call succeeded */
|
|
633
756
|
readonly success: false;
|
|
634
757
|
/** The error that occurred */
|
|
@@ -642,6 +765,7 @@ export type WorkflowAgentOnToolCallFinishCallback = (
|
|
|
642
765
|
*/
|
|
643
766
|
export type WorkflowAgentStreamOptions<
|
|
644
767
|
TTools extends ToolSet = ToolSet,
|
|
768
|
+
TRuntimeContext extends Context = Context,
|
|
645
769
|
OUTPUT = never,
|
|
646
770
|
PARTIAL_OUTPUT = never,
|
|
647
771
|
> = Partial<GenerationSettings> &
|
|
@@ -714,13 +838,6 @@ export type WorkflowAgentStreamOptions<
|
|
|
714
838
|
| StopCondition<NoInfer<ToolSet>, any>
|
|
715
839
|
| Array<StopCondition<NoInfer<ToolSet>, any>>;
|
|
716
840
|
|
|
717
|
-
/**
|
|
718
|
-
* Maximum number of sequential LLM calls (steps), e.g. when you use tool calls.
|
|
719
|
-
* A maximum number can be set to prevent infinite loops in the case of misconfigured tools.
|
|
720
|
-
* By default, it's unlimited (the agent loops until completion).
|
|
721
|
-
*/
|
|
722
|
-
maxSteps?: number;
|
|
723
|
-
|
|
724
841
|
/**
|
|
725
842
|
* The tool choice strategy. Default: 'auto'.
|
|
726
843
|
* Overrides the toolChoice from the constructor if provided.
|
|
@@ -731,19 +848,37 @@ export type WorkflowAgentStreamOptions<
|
|
|
731
848
|
* Limits the tools that are available for the model to call without
|
|
732
849
|
* changing the tool call and result types in the result.
|
|
733
850
|
*/
|
|
734
|
-
activeTools?:
|
|
851
|
+
activeTools?: ActiveTools<NoInfer<TTools>>;
|
|
735
852
|
|
|
736
853
|
/**
|
|
737
|
-
* Optional telemetry configuration
|
|
854
|
+
* Optional telemetry configuration.
|
|
738
855
|
*/
|
|
739
|
-
|
|
856
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
740
857
|
|
|
741
858
|
/**
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
859
|
+
* Runtime context that flows through the agent loop.
|
|
860
|
+
*
|
|
861
|
+
* Treat as immutable; return a new `runtimeContext` from `prepareStep`
|
|
862
|
+
* to update it between steps.
|
|
863
|
+
*
|
|
864
|
+
* In workflow context, keep values serializable so they can cross workflow
|
|
865
|
+
* and step boundaries.
|
|
866
|
+
*
|
|
867
|
+
* Overrides the constructor-level `runtimeContext` if provided.
|
|
868
|
+
*/
|
|
869
|
+
runtimeContext?: TRuntimeContext;
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Per-tool context, keyed by tool name. Each tool receives only its own
|
|
873
|
+
* validated entry as `context` during execution. Tools that declare a
|
|
874
|
+
* `contextSchema` validate their entry against the schema.
|
|
875
|
+
*
|
|
876
|
+
* In workflow context, keep values serializable so they can cross workflow
|
|
877
|
+
* and step boundaries.
|
|
878
|
+
*
|
|
879
|
+
* Overrides the constructor-level `toolsContext` if provided.
|
|
745
880
|
*/
|
|
746
|
-
|
|
881
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
747
882
|
|
|
748
883
|
/**
|
|
749
884
|
* Optional specification for parsing structured outputs from the LLM response.
|
|
@@ -801,7 +936,14 @@ export type WorkflowAgentStreamOptions<
|
|
|
801
936
|
/**
|
|
802
937
|
* Callback function to be called after each step completes.
|
|
803
938
|
*/
|
|
804
|
-
|
|
939
|
+
onStepEnd?: WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Callback function to be called after each step completes.
|
|
943
|
+
*
|
|
944
|
+
* @deprecated Use `onStepEnd` instead.
|
|
945
|
+
*/
|
|
946
|
+
onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools, TRuntimeContext>;
|
|
805
947
|
|
|
806
948
|
/**
|
|
807
949
|
* Callback that is invoked when an error occurs during streaming.
|
|
@@ -813,7 +955,15 @@ export type WorkflowAgentStreamOptions<
|
|
|
813
955
|
* Callback that is called when the LLM response and all request tool executions
|
|
814
956
|
* (for tools that have an `execute` function) are finished.
|
|
815
957
|
*/
|
|
816
|
-
|
|
958
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Callback that is called when the LLM response and all request tool executions
|
|
962
|
+
* (for tools that have an `execute` function) are finished.
|
|
963
|
+
*
|
|
964
|
+
* @deprecated Use `onEnd` instead.
|
|
965
|
+
*/
|
|
966
|
+
onFinish?: WorkflowAgentOnFinishCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
817
967
|
|
|
818
968
|
/**
|
|
819
969
|
* Callback that is called when the operation is aborted.
|
|
@@ -823,22 +973,28 @@ export type WorkflowAgentStreamOptions<
|
|
|
823
973
|
/**
|
|
824
974
|
* Callback called when the agent starts streaming, before any LLM calls.
|
|
825
975
|
*/
|
|
826
|
-
experimental_onStart?: WorkflowAgentOnStartCallback
|
|
976
|
+
experimental_onStart?: WorkflowAgentOnStartCallback<
|
|
977
|
+
TTools,
|
|
978
|
+
TRuntimeContext
|
|
979
|
+
>;
|
|
827
980
|
|
|
828
981
|
/**
|
|
829
982
|
* Callback called before each step (LLM call) begins.
|
|
830
983
|
*/
|
|
831
|
-
experimental_onStepStart?: WorkflowAgentOnStepStartCallback
|
|
984
|
+
experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
|
|
985
|
+
TTools,
|
|
986
|
+
TRuntimeContext
|
|
987
|
+
>;
|
|
832
988
|
|
|
833
989
|
/**
|
|
834
990
|
* Callback called before a tool's execute function runs.
|
|
835
991
|
*/
|
|
836
|
-
|
|
992
|
+
onToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TTools>;
|
|
837
993
|
|
|
838
994
|
/**
|
|
839
995
|
* Callback called after a tool execution completes.
|
|
840
996
|
*/
|
|
841
|
-
|
|
997
|
+
onToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TTools>;
|
|
842
998
|
|
|
843
999
|
/**
|
|
844
1000
|
* Callback function called before each step in the agent loop.
|
|
@@ -858,7 +1014,7 @@ export type WorkflowAgentStreamOptions<
|
|
|
858
1014
|
* }
|
|
859
1015
|
* ```
|
|
860
1016
|
*/
|
|
861
|
-
prepareStep?: PrepareStepCallback<TTools>;
|
|
1017
|
+
prepareStep?: PrepareStepCallback<TTools, TRuntimeContext>;
|
|
862
1018
|
|
|
863
1019
|
/**
|
|
864
1020
|
* Timeout in milliseconds for the stream operation.
|
|
@@ -910,6 +1066,12 @@ export interface ToolResult {
|
|
|
910
1066
|
output: unknown;
|
|
911
1067
|
}
|
|
912
1068
|
|
|
1069
|
+
type WorkflowToolExecutionResult = {
|
|
1070
|
+
modelResult: LanguageModelV4ToolResultPart;
|
|
1071
|
+
rawOutput: unknown;
|
|
1072
|
+
isError: boolean;
|
|
1073
|
+
};
|
|
1074
|
+
|
|
913
1075
|
/**
|
|
914
1076
|
* Result of the WorkflowAgent.stream method.
|
|
915
1077
|
*/
|
|
@@ -988,7 +1150,10 @@ export interface WorkflowAgentStreamResult<
|
|
|
988
1150
|
* });
|
|
989
1151
|
* ```
|
|
990
1152
|
*/
|
|
991
|
-
export class WorkflowAgent<
|
|
1153
|
+
export class WorkflowAgent<
|
|
1154
|
+
TBaseTools extends ToolSet = ToolSet,
|
|
1155
|
+
TRuntimeContext extends Context = Context,
|
|
1156
|
+
> {
|
|
992
1157
|
/**
|
|
993
1158
|
* The id of the agent.
|
|
994
1159
|
*/
|
|
@@ -999,52 +1164,66 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
999
1164
|
* The tool set configured for this agent.
|
|
1000
1165
|
*/
|
|
1001
1166
|
public readonly tools: TBaseTools;
|
|
1002
|
-
private instructions?:
|
|
1003
|
-
| string
|
|
1004
|
-
| SystemModelMessage
|
|
1005
|
-
| Array<SystemModelMessage>;
|
|
1167
|
+
private instructions?: Instructions;
|
|
1006
1168
|
private generationSettings: GenerationSettings;
|
|
1007
1169
|
private toolChoice?: ToolChoice<TBaseTools>;
|
|
1008
|
-
private telemetry?:
|
|
1009
|
-
private
|
|
1170
|
+
private telemetry?: TelemetryOptions<TRuntimeContext, TBaseTools>;
|
|
1171
|
+
private runtimeContext?: TRuntimeContext;
|
|
1172
|
+
private toolsContext?: InferToolSetContext<TBaseTools>;
|
|
1010
1173
|
private stopWhen?:
|
|
1011
1174
|
| StopCondition<ToolSet, any>
|
|
1012
1175
|
| Array<StopCondition<ToolSet, any>>;
|
|
1013
|
-
private activeTools?:
|
|
1176
|
+
private activeTools?: ActiveTools<TBaseTools>;
|
|
1014
1177
|
private output?: OutputSpecification<any, any>;
|
|
1015
1178
|
private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
|
|
1016
1179
|
private experimentalDownload?: DownloadFunction;
|
|
1017
|
-
private prepareStep?: PrepareStepCallback<TBaseTools>;
|
|
1018
|
-
private
|
|
1019
|
-
private
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
private
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1180
|
+
private prepareStep?: PrepareStepCallback<TBaseTools, TRuntimeContext>;
|
|
1181
|
+
private allowSystemInMessages: boolean;
|
|
1182
|
+
private constructorOnStepEnd?: WorkflowAgentOnStepEndCallback<
|
|
1183
|
+
TBaseTools,
|
|
1184
|
+
TRuntimeContext
|
|
1185
|
+
>;
|
|
1186
|
+
private constructorOnEnd?: WorkflowAgentOnEndCallback<
|
|
1187
|
+
TBaseTools,
|
|
1188
|
+
TRuntimeContext
|
|
1189
|
+
>;
|
|
1190
|
+
private constructorOnStart?: WorkflowAgentOnStartCallback<
|
|
1191
|
+
TBaseTools,
|
|
1192
|
+
TRuntimeContext
|
|
1193
|
+
>;
|
|
1194
|
+
private constructorOnStepStart?: WorkflowAgentOnStepStartCallback<
|
|
1195
|
+
TBaseTools,
|
|
1196
|
+
TRuntimeContext
|
|
1197
|
+
>;
|
|
1198
|
+
private constructorOnToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TBaseTools>;
|
|
1199
|
+
private constructorOnToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TBaseTools>;
|
|
1200
|
+
private prepareCall?: PrepareCallCallback<TBaseTools, TRuntimeContext>;
|
|
1201
|
+
|
|
1202
|
+
constructor(options: WorkflowAgentOptions<TBaseTools, TRuntimeContext>) {
|
|
1027
1203
|
this.id = options.id;
|
|
1028
1204
|
this.model = options.model;
|
|
1029
1205
|
this.tools = (options.tools ?? {}) as TBaseTools;
|
|
1030
1206
|
// `instructions` takes precedence over deprecated `system`
|
|
1031
1207
|
this.instructions = options.instructions ?? options.system;
|
|
1032
1208
|
this.toolChoice = options.toolChoice;
|
|
1033
|
-
this.telemetry = options.
|
|
1034
|
-
this.
|
|
1209
|
+
this.telemetry = options.telemetry;
|
|
1210
|
+
this.runtimeContext = options.runtimeContext;
|
|
1211
|
+
this.toolsContext = options.toolsContext;
|
|
1035
1212
|
this.stopWhen = options.stopWhen;
|
|
1036
1213
|
this.activeTools = options.activeTools;
|
|
1037
1214
|
this.output = options.output;
|
|
1038
1215
|
this.experimentalRepairToolCall = options.experimental_repairToolCall;
|
|
1039
1216
|
this.experimentalDownload = options.experimental_download;
|
|
1040
1217
|
this.prepareStep = options.prepareStep;
|
|
1041
|
-
this.
|
|
1042
|
-
|
|
1218
|
+
this.constructorOnStepEnd = options.onStepEnd ?? options.onStepFinish;
|
|
1219
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1220
|
+
this.constructorOnEnd = onEnd;
|
|
1043
1221
|
this.constructorOnStart = options.experimental_onStart;
|
|
1044
1222
|
this.constructorOnStepStart = options.experimental_onStepStart;
|
|
1045
|
-
this.
|
|
1046
|
-
this.
|
|
1223
|
+
this.constructorOnToolExecutionStart = options.onToolExecutionStart;
|
|
1224
|
+
this.constructorOnToolExecutionEnd = options.onToolExecutionEnd;
|
|
1047
1225
|
this.prepareCall = options.prepareCall;
|
|
1226
|
+
this.allowSystemInMessages = options.allowSystemInMessages ?? false;
|
|
1048
1227
|
|
|
1049
1228
|
// Extract generation settings
|
|
1050
1229
|
this.generationSettings = {
|
|
@@ -1072,8 +1251,15 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1072
1251
|
OUTPUT = never,
|
|
1073
1252
|
PARTIAL_OUTPUT = never,
|
|
1074
1253
|
>(
|
|
1075
|
-
options: WorkflowAgentStreamOptions<
|
|
1254
|
+
options: WorkflowAgentStreamOptions<
|
|
1255
|
+
TTools,
|
|
1256
|
+
TRuntimeContext,
|
|
1257
|
+
OUTPUT,
|
|
1258
|
+
PARTIAL_OUTPUT
|
|
1259
|
+
>,
|
|
1076
1260
|
): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
|
|
1261
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1262
|
+
|
|
1077
1263
|
// Call prepareCall to transform parameters before the agent loop
|
|
1078
1264
|
let effectiveModel: LanguageModel = this.model;
|
|
1079
1265
|
let effectiveInstructions = options.system ?? this.instructions;
|
|
@@ -1081,11 +1267,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1081
1267
|
options.prompt;
|
|
1082
1268
|
let effectiveMessages: Array<ModelMessage> | undefined = options.messages;
|
|
1083
1269
|
let effectiveGenerationSettings = { ...this.generationSettings };
|
|
1084
|
-
let
|
|
1085
|
-
|
|
1270
|
+
let effectiveRuntimeContext: TRuntimeContext = (options.runtimeContext ??
|
|
1271
|
+
this.runtimeContext ??
|
|
1272
|
+
{}) as TRuntimeContext;
|
|
1273
|
+
let effectiveToolsContext: Record<string, Context | undefined> =
|
|
1274
|
+
(options.toolsContext ?? this.toolsContext ?? {}) as unknown as Record<
|
|
1275
|
+
string,
|
|
1276
|
+
Context | undefined
|
|
1277
|
+
>;
|
|
1086
1278
|
let effectiveToolChoiceFromPrepare = options.toolChoice ?? this.toolChoice;
|
|
1087
|
-
let effectiveTelemetryFromPrepare =
|
|
1088
|
-
options.experimental_telemetry ?? this.telemetry;
|
|
1279
|
+
let effectiveTelemetryFromPrepare = options.telemetry ?? this.telemetry;
|
|
1089
1280
|
|
|
1090
1281
|
// Resolve messages for prepareCall: use messages directly, or convert prompt
|
|
1091
1282
|
const resolvedMessagesForPrepareCall: ModelMessage[] =
|
|
@@ -1101,11 +1292,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1101
1292
|
tools: this.tools,
|
|
1102
1293
|
instructions: effectiveInstructions,
|
|
1103
1294
|
toolChoice: effectiveToolChoiceFromPrepare as ToolChoice<TBaseTools>,
|
|
1104
|
-
|
|
1105
|
-
|
|
1295
|
+
telemetry: effectiveTelemetryFromPrepare,
|
|
1296
|
+
runtimeContext: effectiveRuntimeContext,
|
|
1297
|
+
toolsContext: effectiveToolsContext as InferToolSetContext<TBaseTools>,
|
|
1106
1298
|
messages: resolvedMessagesForPrepareCall,
|
|
1107
1299
|
...effectiveGenerationSettings,
|
|
1108
|
-
} as PrepareCallOptions<TBaseTools>);
|
|
1300
|
+
} as PrepareCallOptions<TBaseTools, TRuntimeContext>);
|
|
1109
1301
|
|
|
1110
1302
|
if (prepared.model !== undefined) effectiveModel = prepared.model;
|
|
1111
1303
|
if (prepared.instructions !== undefined)
|
|
@@ -1114,13 +1306,18 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1114
1306
|
effectiveMessages = prepared.messages as Array<ModelMessage>;
|
|
1115
1307
|
effectivePrompt = undefined; // messages from prepareCall take precedence
|
|
1116
1308
|
}
|
|
1117
|
-
if (prepared.
|
|
1118
|
-
|
|
1309
|
+
if (prepared.runtimeContext !== undefined)
|
|
1310
|
+
effectiveRuntimeContext = prepared.runtimeContext;
|
|
1311
|
+
if (prepared.toolsContext !== undefined)
|
|
1312
|
+
effectiveToolsContext = prepared.toolsContext as Record<
|
|
1313
|
+
string,
|
|
1314
|
+
Context | undefined
|
|
1315
|
+
>;
|
|
1119
1316
|
if (prepared.toolChoice !== undefined)
|
|
1120
1317
|
effectiveToolChoiceFromPrepare =
|
|
1121
1318
|
prepared.toolChoice as ToolChoice<TBaseTools>;
|
|
1122
|
-
if (prepared.
|
|
1123
|
-
effectiveTelemetryFromPrepare = prepared.
|
|
1319
|
+
if (prepared.telemetry !== undefined)
|
|
1320
|
+
effectiveTelemetryFromPrepare = prepared.telemetry;
|
|
1124
1321
|
if (prepared.maxOutputTokens !== undefined)
|
|
1125
1322
|
effectiveGenerationSettings.maxOutputTokens = prepared.maxOutputTokens;
|
|
1126
1323
|
if (prepared.temperature !== undefined)
|
|
@@ -1144,61 +1341,257 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1144
1341
|
effectiveGenerationSettings.providerOptions = prepared.providerOptions;
|
|
1145
1342
|
}
|
|
1146
1343
|
|
|
1344
|
+
const effectiveTelemetry = effectiveTelemetryFromPrepare;
|
|
1345
|
+
const telemetryDispatcher = createRestrictedTelemetryDispatcher<
|
|
1346
|
+
any,
|
|
1347
|
+
any,
|
|
1348
|
+
any
|
|
1349
|
+
>({
|
|
1350
|
+
telemetry: effectiveTelemetry as any,
|
|
1351
|
+
includeRuntimeContext: effectiveTelemetry?.includeRuntimeContext,
|
|
1352
|
+
includeToolsContext: effectiveTelemetry?.includeToolsContext,
|
|
1353
|
+
}) as any;
|
|
1354
|
+
|
|
1147
1355
|
const prompt = await standardizePrompt({
|
|
1148
1356
|
system: effectiveInstructions,
|
|
1357
|
+
allowSystemInMessages: this.allowSystemInMessages,
|
|
1149
1358
|
...(effectivePrompt != null
|
|
1150
1359
|
? { prompt: effectivePrompt }
|
|
1151
1360
|
: { messages: effectiveMessages! }),
|
|
1152
|
-
});
|
|
1361
|
+
} as Prompt);
|
|
1362
|
+
const download = options.experimental_download ?? this.experimentalDownload;
|
|
1153
1363
|
|
|
1154
1364
|
// Process tool approval responses before starting the agent loop.
|
|
1155
1365
|
// This mirrors how stream-text.ts handles tool-approval-response parts:
|
|
1156
1366
|
// approved tools are executed, denied tools get denial results, and
|
|
1157
1367
|
// approval parts are stripped from the messages.
|
|
1158
|
-
|
|
1159
|
-
|
|
1368
|
+
// Use the AI SDK core collector so this path cannot drift from the
|
|
1369
|
+
// hardened generateText/streamText implementation. The collected approvals
|
|
1370
|
+
// are mapped to the flat shape used below; the original (nested) approval
|
|
1371
|
+
// is carried on `collected` for re-validation.
|
|
1372
|
+
const collectedApprovals = collectToolApprovals<ToolSet>({
|
|
1373
|
+
messages: prompt.messages,
|
|
1374
|
+
});
|
|
1375
|
+
const approvedToolApprovals = collectedApprovals.approvedToolApprovals.map(
|
|
1376
|
+
collected => ({
|
|
1377
|
+
toolCallId: collected.toolCall.toolCallId,
|
|
1378
|
+
toolName: collected.toolCall.toolName,
|
|
1379
|
+
input: collected.toolCall.input,
|
|
1380
|
+
reason: collected.approvalResponse.reason,
|
|
1381
|
+
providerExecuted: collected.toolCall.providerExecuted === true,
|
|
1382
|
+
collected,
|
|
1383
|
+
}),
|
|
1384
|
+
);
|
|
1385
|
+
const deniedToolApprovals = collectedApprovals.deniedToolApprovals.map(
|
|
1386
|
+
collected => ({
|
|
1387
|
+
toolCallId: collected.toolCall.toolCallId,
|
|
1388
|
+
toolName: collected.toolCall.toolName,
|
|
1389
|
+
input: collected.toolCall.input,
|
|
1390
|
+
reason: collected.approvalResponse.reason,
|
|
1391
|
+
providerExecuted: collected.toolCall.providerExecuted === true,
|
|
1392
|
+
}),
|
|
1393
|
+
);
|
|
1394
|
+
|
|
1395
|
+
// Approval ids of provider-executed tool calls. Provider-executed tools
|
|
1396
|
+
// (e.g. MCP via the Responses API) cannot be resolved locally — the
|
|
1397
|
+
// provider owns execution. We therefore skip them from local execution
|
|
1398
|
+
// and preserve their approval responses in the messages so the provider
|
|
1399
|
+
// receives the approval on the next call. The discriminator is sourced
|
|
1400
|
+
// from the original `tool-call` part (matching how core's stream-text.ts
|
|
1401
|
+
// decides), not from the response part which may be missing the flag.
|
|
1402
|
+
const providerExecutedApprovalIds = new Set<string>(
|
|
1403
|
+
[
|
|
1404
|
+
...collectedApprovals.approvedToolApprovals,
|
|
1405
|
+
...collectedApprovals.deniedToolApprovals,
|
|
1406
|
+
]
|
|
1407
|
+
.filter(collected => collected.toolCall.providerExecuted === true)
|
|
1408
|
+
.map(collected => collected.approvalResponse.approvalId),
|
|
1409
|
+
);
|
|
1160
1410
|
|
|
1161
1411
|
if (approvedToolApprovals.length > 0 || deniedToolApprovals.length > 0) {
|
|
1162
1412
|
const _toolResultMessages: ModelMessage[] = [];
|
|
1163
|
-
const toolResultContent:
|
|
1164
|
-
|
|
1413
|
+
const toolResultContent: LanguageModelV4ToolResultPart[] = [];
|
|
1414
|
+
const approvedRawResults: Array<{
|
|
1165
1415
|
toolCallId: string;
|
|
1166
1416
|
toolName: string;
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
| { type: 'json'; value: JSONValue }
|
|
1170
|
-
| { type: 'execution-denied'; reason: string | undefined };
|
|
1417
|
+
input: unknown;
|
|
1418
|
+
output: unknown;
|
|
1171
1419
|
}> = [];
|
|
1172
1420
|
|
|
1173
1421
|
// Execute approved tools
|
|
1174
1422
|
for (const approval of approvedToolApprovals) {
|
|
1423
|
+
// Provider-executed approvals are forwarded to the provider via the
|
|
1424
|
+
// preserved approval response below, not executed locally.
|
|
1425
|
+
if (approval.providerExecuted) {
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1175
1428
|
const tool = (this.tools as ToolSet)[approval.toolName];
|
|
1176
1429
|
if (tool && typeof tool.execute === 'function') {
|
|
1430
|
+
if (!tool.needsApproval) {
|
|
1431
|
+
const reason = `Tool "${approval.toolName}" does not require approval`;
|
|
1432
|
+
toolResultContent.push({
|
|
1433
|
+
type: 'tool-result' as const,
|
|
1434
|
+
toolCallId: approval.toolCallId,
|
|
1435
|
+
toolName: approval.toolName,
|
|
1436
|
+
output: await createLanguageModelToolResultOutput({
|
|
1437
|
+
toolCallId: approval.toolCallId,
|
|
1438
|
+
toolName: approval.toolName,
|
|
1439
|
+
input: approval.input,
|
|
1440
|
+
output: reason,
|
|
1441
|
+
tool,
|
|
1442
|
+
errorMode: 'text',
|
|
1443
|
+
supportedUrls: {},
|
|
1444
|
+
download,
|
|
1445
|
+
}),
|
|
1446
|
+
});
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// Re-validate through the shared core implementation: input schema,
|
|
1451
|
+
// HMAC signature (when configured), and approval policy. It throws on
|
|
1452
|
+
// invalid input/signature; convert that to a denial result so the
|
|
1453
|
+
// agent loop can continue gracefully.
|
|
1454
|
+
let revalidationReason: string | undefined;
|
|
1455
|
+
try {
|
|
1456
|
+
const { deniedToolApprovals: policyDenied } =
|
|
1457
|
+
await validateApprovedToolApprovals({
|
|
1458
|
+
approvedToolApprovals: [approval.collected],
|
|
1459
|
+
tools: this.tools as ToolSet,
|
|
1460
|
+
toolApproval: undefined,
|
|
1461
|
+
messages: prompt.messages,
|
|
1462
|
+
toolsContext:
|
|
1463
|
+
effectiveToolsContext as InferToolSetContext<ToolSet>,
|
|
1464
|
+
runtimeContext: effectiveRuntimeContext,
|
|
1465
|
+
});
|
|
1466
|
+
if (policyDenied.length > 0) {
|
|
1467
|
+
revalidationReason =
|
|
1468
|
+
policyDenied[0].approvalResponse.reason ??
|
|
1469
|
+
'Tool approval denied';
|
|
1470
|
+
}
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
revalidationReason = getErrorMessage(error);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
if (revalidationReason != null) {
|
|
1476
|
+
toolResultContent.push({
|
|
1477
|
+
type: 'tool-result' as const,
|
|
1478
|
+
toolCallId: approval.toolCallId,
|
|
1479
|
+
toolName: approval.toolName,
|
|
1480
|
+
output: await createLanguageModelToolResultOutput({
|
|
1481
|
+
toolCallId: approval.toolCallId,
|
|
1482
|
+
toolName: approval.toolName,
|
|
1483
|
+
input: approval.input,
|
|
1484
|
+
output: revalidationReason,
|
|
1485
|
+
tool,
|
|
1486
|
+
errorMode: 'text',
|
|
1487
|
+
supportedUrls: {},
|
|
1488
|
+
download,
|
|
1489
|
+
}),
|
|
1490
|
+
});
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1177
1494
|
try {
|
|
1178
1495
|
const { execute } = tool;
|
|
1179
|
-
const
|
|
1496
|
+
const resolvedContext = await resolveToolContext({
|
|
1497
|
+
toolName: approval.toolName,
|
|
1498
|
+
tool,
|
|
1499
|
+
toolsContext: effectiveToolsContext,
|
|
1500
|
+
});
|
|
1501
|
+
const toolCallEvent: ToolCall = {
|
|
1502
|
+
type: 'tool-call',
|
|
1180
1503
|
toolCallId: approval.toolCallId,
|
|
1181
|
-
|
|
1182
|
-
|
|
1504
|
+
toolName: approval.toolName,
|
|
1505
|
+
input: approval.input,
|
|
1506
|
+
};
|
|
1507
|
+
const messages = prompt.messages as unknown as ModelMessage[];
|
|
1508
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1509
|
+
toolCall: toolCallEvent,
|
|
1510
|
+
stepNumber: 0,
|
|
1511
|
+
messages,
|
|
1512
|
+
toolContext: resolvedContext,
|
|
1513
|
+
});
|
|
1514
|
+
const startTime = Date.now();
|
|
1515
|
+
const executeApprovedTool = () =>
|
|
1516
|
+
execute(approval.input, {
|
|
1517
|
+
toolCallId: approval.toolCallId,
|
|
1518
|
+
messages: [],
|
|
1519
|
+
context: resolvedContext,
|
|
1520
|
+
});
|
|
1521
|
+
const toolResult =
|
|
1522
|
+
telemetryDispatcher.executeTool != null
|
|
1523
|
+
? await telemetryDispatcher.executeTool({
|
|
1524
|
+
callId: 'workflow-agent',
|
|
1525
|
+
toolCallId: approval.toolCallId,
|
|
1526
|
+
execute: executeApprovedTool,
|
|
1527
|
+
})
|
|
1528
|
+
: await executeApprovedTool();
|
|
1529
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1530
|
+
toolCall: toolCallEvent,
|
|
1531
|
+
stepNumber: 0,
|
|
1532
|
+
durationMs: Date.now() - startTime,
|
|
1533
|
+
messages,
|
|
1534
|
+
toolContext: resolvedContext,
|
|
1535
|
+
success: true,
|
|
1536
|
+
output: toolResult,
|
|
1183
1537
|
});
|
|
1184
1538
|
toolResultContent.push({
|
|
1185
1539
|
type: 'tool-result' as const,
|
|
1186
1540
|
toolCallId: approval.toolCallId,
|
|
1187
1541
|
toolName: approval.toolName,
|
|
1188
|
-
output:
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1542
|
+
output: await createLanguageModelToolResultOutput({
|
|
1543
|
+
toolCallId: approval.toolCallId,
|
|
1544
|
+
toolName: approval.toolName,
|
|
1545
|
+
input: approval.input,
|
|
1546
|
+
output: toolResult,
|
|
1547
|
+
tool,
|
|
1548
|
+
errorMode: 'none',
|
|
1549
|
+
supportedUrls: {},
|
|
1550
|
+
download,
|
|
1551
|
+
}),
|
|
1552
|
+
});
|
|
1553
|
+
approvedRawResults.push({
|
|
1554
|
+
toolCallId: approval.toolCallId,
|
|
1555
|
+
toolName: approval.toolName,
|
|
1556
|
+
input: approval.input,
|
|
1557
|
+
output: toolResult,
|
|
1192
1558
|
});
|
|
1193
1559
|
} catch (error) {
|
|
1560
|
+
const errorMessage = getErrorMessage(error);
|
|
1561
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1562
|
+
toolCall: {
|
|
1563
|
+
type: 'tool-call',
|
|
1564
|
+
toolCallId: approval.toolCallId,
|
|
1565
|
+
toolName: approval.toolName,
|
|
1566
|
+
input: approval.input,
|
|
1567
|
+
},
|
|
1568
|
+
stepNumber: 0,
|
|
1569
|
+
durationMs: 0,
|
|
1570
|
+
messages: prompt.messages as unknown as ModelMessage[],
|
|
1571
|
+
toolContext: undefined,
|
|
1572
|
+
success: false,
|
|
1573
|
+
error,
|
|
1574
|
+
});
|
|
1194
1575
|
toolResultContent.push({
|
|
1195
1576
|
type: 'tool-result' as const,
|
|
1196
1577
|
toolCallId: approval.toolCallId,
|
|
1197
1578
|
toolName: approval.toolName,
|
|
1198
|
-
output: {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1579
|
+
output: await createLanguageModelToolResultOutput({
|
|
1580
|
+
toolCallId: approval.toolCallId,
|
|
1581
|
+
toolName: approval.toolName,
|
|
1582
|
+
input: approval.input,
|
|
1583
|
+
output: errorMessage,
|
|
1584
|
+
tool,
|
|
1585
|
+
errorMode: 'text',
|
|
1586
|
+
supportedUrls: {},
|
|
1587
|
+
download,
|
|
1588
|
+
}),
|
|
1589
|
+
});
|
|
1590
|
+
approvedRawResults.push({
|
|
1591
|
+
toolCallId: approval.toolCallId,
|
|
1592
|
+
toolName: approval.toolName,
|
|
1593
|
+
input: approval.input,
|
|
1594
|
+
output: errorMessage,
|
|
1202
1595
|
});
|
|
1203
1596
|
}
|
|
1204
1597
|
}
|
|
@@ -1206,6 +1599,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1206
1599
|
|
|
1207
1600
|
// Create denial results for denied tools
|
|
1208
1601
|
for (const denial of deniedToolApprovals) {
|
|
1602
|
+
// Provider-executed denials are forwarded to the provider via the
|
|
1603
|
+
// preserved approval response below, not turned into a local result.
|
|
1604
|
+
if (denial.providerExecuted) {
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1209
1607
|
toolResultContent.push({
|
|
1210
1608
|
type: 'tool-result' as const,
|
|
1211
1609
|
toolCallId: denial.toolCallId,
|
|
@@ -1217,20 +1615,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1217
1615
|
});
|
|
1218
1616
|
}
|
|
1219
1617
|
|
|
1220
|
-
// Strip approval parts
|
|
1618
|
+
// Strip approval parts that we resolved locally and inject tool results.
|
|
1619
|
+
// Provider-executed approval parts are preserved so the next call to
|
|
1620
|
+
// `convertToLanguageModelPrompt` forwards the approval response to the
|
|
1621
|
+
// provider (it only forwards responses flagged `providerExecuted`).
|
|
1221
1622
|
const cleanedMessages: ModelMessage[] = [];
|
|
1222
1623
|
for (const msg of prompt.messages) {
|
|
1223
1624
|
if (msg.role === 'assistant' && Array.isArray(msg.content)) {
|
|
1224
1625
|
const filtered = (msg.content as any[]).filter(
|
|
1225
|
-
(p: any) =>
|
|
1626
|
+
(p: any) =>
|
|
1627
|
+
p.type !== 'tool-approval-request' ||
|
|
1628
|
+
providerExecutedApprovalIds.has(p.approvalId),
|
|
1226
1629
|
);
|
|
1227
1630
|
if (filtered.length > 0) {
|
|
1228
1631
|
cleanedMessages.push({ ...msg, content: filtered });
|
|
1229
1632
|
}
|
|
1230
1633
|
} else if (msg.role === 'tool') {
|
|
1231
|
-
const filtered = (msg.content as any[]).
|
|
1232
|
-
(p
|
|
1233
|
-
|
|
1634
|
+
const filtered = (msg.content as any[]).flatMap((p: any) => {
|
|
1635
|
+
if (p.type !== 'tool-approval-response') {
|
|
1636
|
+
return [p];
|
|
1637
|
+
}
|
|
1638
|
+
if (!providerExecutedApprovalIds.has(p.approvalId)) {
|
|
1639
|
+
return [];
|
|
1640
|
+
}
|
|
1641
|
+
// Re-stamp `providerExecuted` so the conversion layer forwards the
|
|
1642
|
+
// response even if the client omitted the flag on the response part.
|
|
1643
|
+
return [{ ...p, providerExecuted: true }];
|
|
1644
|
+
});
|
|
1234
1645
|
if (filtered.length > 0) {
|
|
1235
1646
|
cleanedMessages.push({ ...msg, content: filtered });
|
|
1236
1647
|
}
|
|
@@ -1253,22 +1664,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1253
1664
|
// can transition approved/denied tool parts to the correct state
|
|
1254
1665
|
// and properly separate them from the subsequent model step.
|
|
1255
1666
|
if (options.writable && toolResultContent.length > 0) {
|
|
1256
|
-
const approvedResults = toolResultContent
|
|
1257
|
-
.filter(r => r.output.type !== 'execution-denied')
|
|
1258
|
-
.map(r => ({
|
|
1259
|
-
toolCallId: r.toolCallId,
|
|
1260
|
-
toolName: r.toolName,
|
|
1261
|
-
input: approvedToolApprovals.find(
|
|
1262
|
-
a => a.toolCallId === r.toolCallId,
|
|
1263
|
-
)?.input,
|
|
1264
|
-
output: 'value' in r.output ? r.output.value : undefined,
|
|
1265
|
-
}));
|
|
1266
1667
|
const deniedResults = toolResultContent
|
|
1267
1668
|
.filter(r => r.output.type === 'execution-denied')
|
|
1268
1669
|
.map(r => ({ toolCallId: r.toolCallId }));
|
|
1269
1670
|
await writeApprovalToolResults(
|
|
1270
1671
|
options.writable,
|
|
1271
|
-
|
|
1672
|
+
approvedRawResults,
|
|
1272
1673
|
deniedResults,
|
|
1273
1674
|
);
|
|
1274
1675
|
}
|
|
@@ -1277,14 +1678,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1277
1678
|
const modelPrompt = await convertToLanguageModelPrompt({
|
|
1278
1679
|
prompt,
|
|
1279
1680
|
supportedUrls: {},
|
|
1280
|
-
download
|
|
1681
|
+
download,
|
|
1281
1682
|
});
|
|
1282
1683
|
|
|
1283
1684
|
const effectiveAbortSignal = mergeAbortSignals(
|
|
1284
1685
|
options.abortSignal ?? effectiveGenerationSettings.abortSignal,
|
|
1285
|
-
options.timeout
|
|
1286
|
-
? AbortSignal.timeout(options.timeout)
|
|
1287
|
-
: undefined,
|
|
1686
|
+
options.timeout,
|
|
1288
1687
|
);
|
|
1289
1688
|
|
|
1290
1689
|
// Merge generation settings: constructor defaults < prepareCall < stream options
|
|
@@ -1321,52 +1720,59 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1321
1720
|
};
|
|
1322
1721
|
|
|
1323
1722
|
// Merge constructor + stream callbacks (constructor first, then stream)
|
|
1324
|
-
const
|
|
1325
|
-
this.
|
|
1326
|
-
|
|
|
1723
|
+
const mergedOnStepEnd = mergeCallbacks(
|
|
1724
|
+
this.constructorOnStepEnd as
|
|
1725
|
+
| WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>
|
|
1327
1726
|
| undefined,
|
|
1328
|
-
options.onStepFinish,
|
|
1727
|
+
options.onStepEnd ?? options.onStepFinish,
|
|
1329
1728
|
);
|
|
1330
|
-
const
|
|
1331
|
-
this.
|
|
1332
|
-
|
|
|
1729
|
+
const mergedOnEnd = mergeCallbacks(
|
|
1730
|
+
this.constructorOnEnd as
|
|
1731
|
+
| WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>
|
|
1333
1732
|
| undefined,
|
|
1334
|
-
|
|
1733
|
+
onEnd,
|
|
1335
1734
|
);
|
|
1336
1735
|
const mergedOnStart = mergeCallbacks(
|
|
1337
|
-
this.constructorOnStart
|
|
1736
|
+
this.constructorOnStart as
|
|
1737
|
+
| WorkflowAgentOnStartCallback<TTools, TRuntimeContext>
|
|
1738
|
+
| undefined,
|
|
1338
1739
|
options.experimental_onStart,
|
|
1339
1740
|
);
|
|
1340
1741
|
const mergedOnStepStart = mergeCallbacks(
|
|
1341
|
-
this.constructorOnStepStart
|
|
1742
|
+
this.constructorOnStepStart as
|
|
1743
|
+
| WorkflowAgentOnStepStartCallback<TTools, TRuntimeContext>
|
|
1744
|
+
| undefined,
|
|
1342
1745
|
options.experimental_onStepStart,
|
|
1343
1746
|
);
|
|
1344
|
-
const
|
|
1345
|
-
this.
|
|
1346
|
-
options.
|
|
1747
|
+
const mergedOnToolExecutionStart = mergeCallbacks(
|
|
1748
|
+
this.constructorOnToolExecutionStart,
|
|
1749
|
+
options.onToolExecutionStart,
|
|
1347
1750
|
);
|
|
1348
|
-
const
|
|
1349
|
-
this.
|
|
1350
|
-
options.
|
|
1751
|
+
const mergedOnToolExecutionEnd = mergeCallbacks(
|
|
1752
|
+
this.constructorOnToolExecutionEnd,
|
|
1753
|
+
options.onToolExecutionEnd,
|
|
1351
1754
|
);
|
|
1352
1755
|
|
|
1353
1756
|
// Determine effective tool choice
|
|
1354
1757
|
const effectiveToolChoice = effectiveToolChoiceFromPrepare;
|
|
1355
1758
|
|
|
1356
|
-
// Merge telemetry settings
|
|
1357
|
-
const effectiveTelemetry = effectiveTelemetryFromPrepare;
|
|
1358
|
-
|
|
1359
1759
|
// Filter tools if activeTools is specified (stream-level overrides constructor default)
|
|
1360
1760
|
const effectiveActiveTools = options.activeTools ?? this.activeTools;
|
|
1361
1761
|
const effectiveTools =
|
|
1362
1762
|
effectiveActiveTools && effectiveActiveTools.length > 0
|
|
1363
|
-
?
|
|
1763
|
+
? (filterActiveTools({
|
|
1764
|
+
tools: this.tools,
|
|
1765
|
+
activeTools: effectiveActiveTools,
|
|
1766
|
+
}) ?? this.tools)
|
|
1364
1767
|
: this.tools;
|
|
1768
|
+
const effectiveModelInfo = getModelInfo(effectiveModel);
|
|
1365
1769
|
|
|
1366
1770
|
// Initialize context
|
|
1367
|
-
let
|
|
1771
|
+
let runtimeContext: TRuntimeContext = effectiveRuntimeContext;
|
|
1772
|
+
let toolsContext: Record<string, Context | undefined> =
|
|
1773
|
+
effectiveToolsContext;
|
|
1368
1774
|
|
|
1369
|
-
const steps: StepResult<TTools,
|
|
1775
|
+
const steps: StepResult<TTools, TRuntimeContext>[] = [];
|
|
1370
1776
|
|
|
1371
1777
|
// Track tool calls and results from the last step for the result
|
|
1372
1778
|
let lastStepToolCalls: ToolCall[] = [];
|
|
@@ -1377,17 +1783,45 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1377
1783
|
await mergedOnStart({
|
|
1378
1784
|
model: effectiveModel,
|
|
1379
1785
|
messages: prompt.messages,
|
|
1786
|
+
runtimeContext,
|
|
1787
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1380
1788
|
});
|
|
1381
1789
|
}
|
|
1790
|
+
await telemetryDispatcher.onStart?.({
|
|
1791
|
+
callId: 'workflow-agent',
|
|
1792
|
+
operationId: 'ai.workflowAgent.stream',
|
|
1793
|
+
provider: effectiveModelInfo.provider,
|
|
1794
|
+
modelId: effectiveModelInfo.modelId,
|
|
1795
|
+
system: undefined,
|
|
1796
|
+
messages: prompt.messages,
|
|
1797
|
+
tools: effectiveTools,
|
|
1798
|
+
toolChoice: effectiveToolChoice,
|
|
1799
|
+
activeTools: effectiveActiveTools as never,
|
|
1800
|
+
maxOutputTokens: mergedGenerationSettings.maxOutputTokens,
|
|
1801
|
+
temperature: mergedGenerationSettings.temperature,
|
|
1802
|
+
topP: mergedGenerationSettings.topP,
|
|
1803
|
+
topK: mergedGenerationSettings.topK,
|
|
1804
|
+
presencePenalty: mergedGenerationSettings.presencePenalty,
|
|
1805
|
+
frequencyPenalty: mergedGenerationSettings.frequencyPenalty,
|
|
1806
|
+
stopSequences: mergedGenerationSettings.stopSequences,
|
|
1807
|
+
seed: mergedGenerationSettings.seed,
|
|
1808
|
+
maxRetries: mergedGenerationSettings.maxRetries ?? 2,
|
|
1809
|
+
timeout: undefined,
|
|
1810
|
+
headers: mergedGenerationSettings.headers,
|
|
1811
|
+
providerOptions: mergedGenerationSettings.providerOptions,
|
|
1812
|
+
output: (options.output ?? this.output) as never,
|
|
1813
|
+
runtimeContext,
|
|
1814
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1815
|
+
});
|
|
1382
1816
|
|
|
1383
|
-
// Helper to wrap executeTool with
|
|
1817
|
+
// Helper to wrap executeTool with onToolExecutionStart/onToolExecutionEnd callbacks
|
|
1384
1818
|
const executeToolWithCallbacks = async (
|
|
1385
1819
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1386
1820
|
tools: ToolSet,
|
|
1387
1821
|
messages: LanguageModelV4Prompt,
|
|
1388
|
-
|
|
1822
|
+
perToolContexts: Record<string, Context | undefined>,
|
|
1389
1823
|
currentStepNumber: number = 0,
|
|
1390
|
-
): Promise<
|
|
1824
|
+
): Promise<WorkflowToolExecutionResult> => {
|
|
1391
1825
|
const toolCallEvent: ToolCall = {
|
|
1392
1826
|
type: 'tool-call',
|
|
1393
1827
|
toolCallId: toolCall.toolCallId,
|
|
@@ -1395,62 +1829,172 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1395
1829
|
input: toolCall.input,
|
|
1396
1830
|
};
|
|
1397
1831
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1832
|
+
const tool = tools[toolCall.toolName];
|
|
1833
|
+
const resolvedContext = tool
|
|
1834
|
+
? await resolveToolContext({
|
|
1835
|
+
toolName: toolCall.toolName,
|
|
1836
|
+
tool,
|
|
1837
|
+
toolsContext: perToolContexts,
|
|
1838
|
+
})
|
|
1839
|
+
: undefined;
|
|
1840
|
+
const modelMessages = getToolCallbackMessages(messages);
|
|
1841
|
+
|
|
1842
|
+
if (mergedOnToolExecutionStart) {
|
|
1843
|
+
await mergedOnToolExecutionStart({
|
|
1400
1844
|
toolCall: toolCallEvent,
|
|
1401
1845
|
stepNumber: currentStepNumber,
|
|
1846
|
+
messages: modelMessages,
|
|
1847
|
+
toolContext: resolvedContext as
|
|
1848
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1849
|
+
| undefined,
|
|
1402
1850
|
});
|
|
1403
1851
|
}
|
|
1852
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1853
|
+
toolCall: toolCallEvent,
|
|
1854
|
+
stepNumber: currentStepNumber,
|
|
1855
|
+
messages: modelMessages,
|
|
1856
|
+
toolContext: resolvedContext as
|
|
1857
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1858
|
+
| undefined,
|
|
1859
|
+
});
|
|
1404
1860
|
|
|
1405
1861
|
const startTime = Date.now();
|
|
1406
|
-
let result:
|
|
1862
|
+
let result: WorkflowToolExecutionResult;
|
|
1407
1863
|
try {
|
|
1408
|
-
|
|
1864
|
+
const execute = () =>
|
|
1865
|
+
executeTool(toolCall, tools, messages, resolvedContext, download);
|
|
1866
|
+
result =
|
|
1867
|
+
telemetryDispatcher.executeTool != null
|
|
1868
|
+
? await telemetryDispatcher.executeTool({
|
|
1869
|
+
callId: 'workflow-agent',
|
|
1870
|
+
toolCallId: toolCall.toolCallId,
|
|
1871
|
+
execute,
|
|
1872
|
+
})
|
|
1873
|
+
: await execute();
|
|
1409
1874
|
} catch (err) {
|
|
1410
1875
|
const durationMs = Date.now() - startTime;
|
|
1411
|
-
if (
|
|
1412
|
-
await
|
|
1876
|
+
if (mergedOnToolExecutionEnd) {
|
|
1877
|
+
await mergedOnToolExecutionEnd({
|
|
1413
1878
|
toolCall: toolCallEvent,
|
|
1414
1879
|
stepNumber: currentStepNumber,
|
|
1415
1880
|
durationMs,
|
|
1881
|
+
messages: modelMessages,
|
|
1882
|
+
toolContext: resolvedContext as
|
|
1883
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1884
|
+
| undefined,
|
|
1416
1885
|
success: false,
|
|
1417
1886
|
error: err,
|
|
1418
1887
|
});
|
|
1419
1888
|
}
|
|
1889
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1890
|
+
toolCall: toolCallEvent,
|
|
1891
|
+
stepNumber: currentStepNumber,
|
|
1892
|
+
durationMs,
|
|
1893
|
+
messages: modelMessages,
|
|
1894
|
+
toolContext: resolvedContext as
|
|
1895
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1896
|
+
| undefined,
|
|
1897
|
+
success: false,
|
|
1898
|
+
error: err,
|
|
1899
|
+
});
|
|
1420
1900
|
throw err;
|
|
1421
1901
|
}
|
|
1422
1902
|
|
|
1423
1903
|
const durationMs = Date.now() - startTime;
|
|
1424
|
-
if (
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
'type' in result.output &&
|
|
1428
|
-
(result.output.type === 'error-text' ||
|
|
1429
|
-
result.output.type === 'error-json');
|
|
1430
|
-
if (isError) {
|
|
1431
|
-
await mergedOnToolCallFinish({
|
|
1904
|
+
if (mergedOnToolExecutionEnd) {
|
|
1905
|
+
if (result.isError) {
|
|
1906
|
+
await mergedOnToolExecutionEnd({
|
|
1432
1907
|
toolCall: toolCallEvent,
|
|
1433
1908
|
stepNumber: currentStepNumber,
|
|
1434
1909
|
durationMs,
|
|
1910
|
+
messages: modelMessages,
|
|
1911
|
+
toolContext: resolvedContext as
|
|
1912
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1913
|
+
| undefined,
|
|
1435
1914
|
success: false,
|
|
1436
|
-
error:
|
|
1915
|
+
error: result.rawOutput,
|
|
1437
1916
|
});
|
|
1438
1917
|
} else {
|
|
1439
|
-
await
|
|
1918
|
+
await mergedOnToolExecutionEnd({
|
|
1440
1919
|
toolCall: toolCallEvent,
|
|
1441
1920
|
stepNumber: currentStepNumber,
|
|
1442
1921
|
durationMs,
|
|
1922
|
+
messages: modelMessages,
|
|
1923
|
+
toolContext: resolvedContext as
|
|
1924
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1925
|
+
| undefined,
|
|
1443
1926
|
success: true,
|
|
1444
|
-
output:
|
|
1445
|
-
result.output && 'value' in result.output
|
|
1446
|
-
? result.output.value
|
|
1447
|
-
: undefined,
|
|
1927
|
+
output: result.rawOutput,
|
|
1448
1928
|
});
|
|
1449
1929
|
}
|
|
1450
1930
|
}
|
|
1931
|
+
if (result.isError) {
|
|
1932
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1933
|
+
toolCall: toolCallEvent,
|
|
1934
|
+
stepNumber: currentStepNumber,
|
|
1935
|
+
durationMs,
|
|
1936
|
+
messages: modelMessages,
|
|
1937
|
+
toolContext: resolvedContext as
|
|
1938
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1939
|
+
| undefined,
|
|
1940
|
+
success: false,
|
|
1941
|
+
error: result.rawOutput,
|
|
1942
|
+
});
|
|
1943
|
+
} else {
|
|
1944
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1945
|
+
toolCall: toolCallEvent,
|
|
1946
|
+
stepNumber: currentStepNumber,
|
|
1947
|
+
durationMs,
|
|
1948
|
+
messages: modelMessages,
|
|
1949
|
+
toolContext: resolvedContext as
|
|
1950
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1951
|
+
| undefined,
|
|
1952
|
+
success: true,
|
|
1953
|
+
output: result.rawOutput,
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1451
1956
|
return result;
|
|
1452
1957
|
};
|
|
1453
1958
|
|
|
1959
|
+
const recordProviderExecutedToolTelemetry = async (
|
|
1960
|
+
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1961
|
+
result: WorkflowToolExecutionResult,
|
|
1962
|
+
messages: LanguageModelV4Prompt,
|
|
1963
|
+
currentStepNumber: number,
|
|
1964
|
+
) => {
|
|
1965
|
+
const toolCallEvent: ToolCall = {
|
|
1966
|
+
type: 'tool-call',
|
|
1967
|
+
toolCallId: toolCall.toolCallId,
|
|
1968
|
+
toolName: toolCall.toolName,
|
|
1969
|
+
input: toolCall.input,
|
|
1970
|
+
};
|
|
1971
|
+
const modelMessages = getToolCallbackMessages(messages);
|
|
1972
|
+
|
|
1973
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1974
|
+
toolCall: toolCallEvent,
|
|
1975
|
+
stepNumber: currentStepNumber,
|
|
1976
|
+
messages: modelMessages,
|
|
1977
|
+
toolContext: undefined,
|
|
1978
|
+
});
|
|
1979
|
+
|
|
1980
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1981
|
+
toolCall: toolCallEvent,
|
|
1982
|
+
stepNumber: currentStepNumber,
|
|
1983
|
+
durationMs: 0,
|
|
1984
|
+
messages: modelMessages,
|
|
1985
|
+
toolContext: undefined,
|
|
1986
|
+
...(result.isError
|
|
1987
|
+
? {
|
|
1988
|
+
success: false as const,
|
|
1989
|
+
error: result.rawOutput,
|
|
1990
|
+
}
|
|
1991
|
+
: {
|
|
1992
|
+
success: true as const,
|
|
1993
|
+
output: result.rawOutput,
|
|
1994
|
+
}),
|
|
1995
|
+
});
|
|
1996
|
+
};
|
|
1997
|
+
|
|
1454
1998
|
// Check for abort before starting
|
|
1455
1999
|
if (mergedGenerationSettings.abortSignal?.aborted) {
|
|
1456
2000
|
if (options.onAbort) {
|
|
@@ -1471,17 +2015,19 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1471
2015
|
writable: options.writable,
|
|
1472
2016
|
prompt: modelPrompt,
|
|
1473
2017
|
stopConditions: options.stopWhen ?? this.stopWhen,
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
onStepStart: mergedOnStepStart,
|
|
2018
|
+
|
|
2019
|
+
onStepEnd: mergedOnStepEnd as any,
|
|
2020
|
+
onStepStart: mergedOnStepStart as any,
|
|
1477
2021
|
onError: options.onError,
|
|
1478
|
-
prepareStep:
|
|
1479
|
-
|
|
1480
|
-
|
|
2022
|
+
prepareStep: (options.prepareStep ??
|
|
2023
|
+
(this.prepareStep as
|
|
2024
|
+
| PrepareStepCallback<ToolSet, TRuntimeContext>
|
|
2025
|
+
| undefined)) as any,
|
|
1481
2026
|
generationSettings: mergedGenerationSettings,
|
|
1482
2027
|
toolChoice: effectiveToolChoice as ToolChoice<ToolSet>,
|
|
1483
|
-
|
|
1484
|
-
|
|
2028
|
+
runtimeContext,
|
|
2029
|
+
toolsContext,
|
|
2030
|
+
telemetry: effectiveTelemetry,
|
|
1485
2031
|
includeRawChunks: options.includeRawChunks ?? false,
|
|
1486
2032
|
repairToolCall: (options.experimental_repairToolCall ??
|
|
1487
2033
|
this.experimentalRepairToolCall) as
|
|
@@ -1511,25 +2057,34 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1511
2057
|
toolCalls,
|
|
1512
2058
|
messages: iterMessages,
|
|
1513
2059
|
step,
|
|
1514
|
-
|
|
2060
|
+
runtimeContext: yieldedRuntimeContext,
|
|
2061
|
+
toolsContext: yieldedToolsContext,
|
|
1515
2062
|
providerExecutedToolResults,
|
|
1516
2063
|
} = result.value;
|
|
1517
2064
|
// Capture current step number before pushing (0-based)
|
|
1518
2065
|
const currentStepNumber = steps.length;
|
|
1519
2066
|
if (step) {
|
|
1520
|
-
steps.push(step as unknown as StepResult<TTools,
|
|
2067
|
+
steps.push(step as unknown as StepResult<TTools, TRuntimeContext>);
|
|
1521
2068
|
}
|
|
1522
|
-
if (
|
|
1523
|
-
|
|
2069
|
+
if (yieldedRuntimeContext !== undefined) {
|
|
2070
|
+
runtimeContext = yieldedRuntimeContext as TRuntimeContext;
|
|
2071
|
+
}
|
|
2072
|
+
if (yieldedToolsContext !== undefined) {
|
|
2073
|
+
toolsContext = yieldedToolsContext;
|
|
1524
2074
|
}
|
|
1525
2075
|
|
|
1526
2076
|
// Only execute tools if there are tool calls
|
|
1527
2077
|
if (toolCalls.length > 0) {
|
|
2078
|
+
const invalidToolCalls = toolCalls.filter(tc => tc.invalid === true);
|
|
2079
|
+
const validToolCalls = toolCalls.filter(tc => tc.invalid !== true);
|
|
2080
|
+
|
|
1528
2081
|
// Separate provider-executed tool calls from client-executed ones
|
|
1529
|
-
const nonProviderToolCalls =
|
|
2082
|
+
const nonProviderToolCalls = validToolCalls.filter(
|
|
1530
2083
|
tc => !tc.providerExecuted,
|
|
1531
2084
|
);
|
|
1532
|
-
const providerToolCalls =
|
|
2085
|
+
const providerToolCalls = validToolCalls.filter(
|
|
2086
|
+
tc => tc.providerExecuted,
|
|
2087
|
+
);
|
|
1533
2088
|
|
|
1534
2089
|
// Check which tools need approval (can be async)
|
|
1535
2090
|
const approvalNeeded = await Promise.all(
|
|
@@ -1539,10 +2094,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1539
2094
|
if (tool.needsApproval == null) return false;
|
|
1540
2095
|
if (typeof tool.needsApproval === 'boolean')
|
|
1541
2096
|
return tool.needsApproval;
|
|
2097
|
+
const resolvedContext = await resolveToolContext({
|
|
2098
|
+
toolName: tc.toolName,
|
|
2099
|
+
tool,
|
|
2100
|
+
toolsContext:
|
|
2101
|
+
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
2102
|
+
});
|
|
1542
2103
|
return tool.needsApproval(tc.input, {
|
|
1543
2104
|
toolCallId: tc.toolCallId,
|
|
1544
2105
|
messages: iterMessages as unknown as ModelMessage[],
|
|
1545
|
-
context:
|
|
2106
|
+
context: resolvedContext,
|
|
1546
2107
|
});
|
|
1547
2108
|
}),
|
|
1548
2109
|
);
|
|
@@ -1573,27 +2134,49 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1573
2134
|
// Execute any executable tools that were also called in this step
|
|
1574
2135
|
const executableResults = await Promise.all(
|
|
1575
2136
|
executableToolCalls.map(
|
|
1576
|
-
(toolCall): Promise<
|
|
2137
|
+
(toolCall): Promise<WorkflowToolExecutionResult> =>
|
|
1577
2138
|
executeToolWithCallbacks(
|
|
1578
2139
|
toolCall,
|
|
1579
2140
|
effectiveTools as ToolSet,
|
|
1580
2141
|
iterMessages,
|
|
1581
|
-
|
|
2142
|
+
toolsContext,
|
|
1582
2143
|
currentStepNumber,
|
|
1583
2144
|
),
|
|
1584
2145
|
),
|
|
1585
2146
|
);
|
|
1586
2147
|
|
|
1587
2148
|
// Collect provider tool results
|
|
1588
|
-
const providerResults:
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
2149
|
+
const providerResults: WorkflowToolExecutionResult[] =
|
|
2150
|
+
await Promise.all(
|
|
2151
|
+
providerToolCalls.map(toolCall =>
|
|
2152
|
+
resolveProviderToolResult(
|
|
2153
|
+
toolCall,
|
|
2154
|
+
providerExecutedToolResults,
|
|
2155
|
+
effectiveTools as ToolSet,
|
|
2156
|
+
download,
|
|
2157
|
+
),
|
|
1593
2158
|
),
|
|
1594
2159
|
);
|
|
2160
|
+
await Promise.all(
|
|
2161
|
+
providerToolCalls.map((toolCall, index) =>
|
|
2162
|
+
recordProviderExecutedToolTelemetry(
|
|
2163
|
+
toolCall,
|
|
2164
|
+
providerResults[index],
|
|
2165
|
+
iterMessages,
|
|
2166
|
+
currentStepNumber,
|
|
2167
|
+
),
|
|
2168
|
+
),
|
|
2169
|
+
);
|
|
1595
2170
|
|
|
1596
|
-
const
|
|
2171
|
+
const continuationInvalidResults = invalidToolCalls.map(
|
|
2172
|
+
createInvalidToolResult,
|
|
2173
|
+
);
|
|
2174
|
+
const resolvedResults: LanguageModelV4ToolResultPart[] = [
|
|
2175
|
+
...executableResults.map(result => result.modelResult),
|
|
2176
|
+
...providerResults.map(result => result.modelResult),
|
|
2177
|
+
...continuationInvalidResults,
|
|
2178
|
+
];
|
|
2179
|
+
const executedResults = [...executableResults, ...providerResults];
|
|
1597
2180
|
|
|
1598
2181
|
const allToolCalls: ToolCall[] = toolCalls.map(tc => ({
|
|
1599
2182
|
type: 'tool-call' as const,
|
|
@@ -1602,13 +2185,14 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1602
2185
|
input: tc.input,
|
|
1603
2186
|
}));
|
|
1604
2187
|
|
|
1605
|
-
const allToolResults: ToolResult[] =
|
|
2188
|
+
const allToolResults: ToolResult[] = executedResults.map(r => ({
|
|
1606
2189
|
type: 'tool-result' as const,
|
|
1607
|
-
toolCallId: r.toolCallId,
|
|
1608
|
-
toolName: r.toolName,
|
|
1609
|
-
input: toolCalls.find(
|
|
1610
|
-
|
|
1611
|
-
|
|
2190
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2191
|
+
toolName: r.modelResult.toolName,
|
|
2192
|
+
input: toolCalls.find(
|
|
2193
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2194
|
+
)?.input,
|
|
2195
|
+
output: r.rawOutput,
|
|
1612
2196
|
}));
|
|
1613
2197
|
|
|
1614
2198
|
if (resolvedResults.length > 0) {
|
|
@@ -1620,18 +2204,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1620
2204
|
|
|
1621
2205
|
const messages = iterMessages as unknown as ModelMessage[];
|
|
1622
2206
|
|
|
1623
|
-
if (
|
|
2207
|
+
if (mergedOnEnd && !wasAborted) {
|
|
1624
2208
|
const lastStep = steps[steps.length - 1];
|
|
1625
|
-
|
|
2209
|
+
const totalUsage = aggregateUsage(steps);
|
|
2210
|
+
await mergedOnEnd({
|
|
1626
2211
|
steps,
|
|
1627
2212
|
messages,
|
|
1628
2213
|
text: lastStep?.text ?? '',
|
|
1629
2214
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
1630
|
-
|
|
1631
|
-
|
|
2215
|
+
usage: totalUsage,
|
|
2216
|
+
totalUsage,
|
|
2217
|
+
runtimeContext,
|
|
2218
|
+
toolsContext:
|
|
2219
|
+
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1632
2220
|
output: undefined as OUTPUT,
|
|
1633
2221
|
});
|
|
1634
2222
|
}
|
|
2223
|
+
if (!wasAborted && steps.length > 0) {
|
|
2224
|
+
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2225
|
+
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2226
|
+
const totalUsage = aggregateUsage(steps);
|
|
2227
|
+
await telemetryDispatcher.onEnd?.({
|
|
2228
|
+
...lastStep,
|
|
2229
|
+
steps: telemetrySteps,
|
|
2230
|
+
usage: totalUsage,
|
|
2231
|
+
totalUsage,
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
1635
2234
|
|
|
1636
2235
|
// Emit tool-approval-request chunks for tools that need approval
|
|
1637
2236
|
// so useChat can show the approval UI
|
|
@@ -1674,40 +2273,67 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1674
2273
|
// Execute client tools (all have execute functions at this point)
|
|
1675
2274
|
const clientToolResults = await Promise.all(
|
|
1676
2275
|
nonProviderToolCalls.map(
|
|
1677
|
-
(toolCall): Promise<
|
|
2276
|
+
(toolCall): Promise<WorkflowToolExecutionResult> =>
|
|
1678
2277
|
executeToolWithCallbacks(
|
|
1679
2278
|
toolCall,
|
|
1680
2279
|
effectiveTools as ToolSet,
|
|
1681
2280
|
iterMessages,
|
|
1682
|
-
|
|
2281
|
+
toolsContext,
|
|
1683
2282
|
currentStepNumber,
|
|
1684
2283
|
),
|
|
1685
2284
|
),
|
|
1686
2285
|
);
|
|
1687
2286
|
|
|
1688
2287
|
// For provider-executed tools, use the results from the stream
|
|
1689
|
-
const providerToolResults:
|
|
1690
|
-
|
|
1691
|
-
|
|
2288
|
+
const providerToolResults: WorkflowToolExecutionResult[] =
|
|
2289
|
+
await Promise.all(
|
|
2290
|
+
providerToolCalls.map(toolCall =>
|
|
2291
|
+
resolveProviderToolResult(
|
|
2292
|
+
toolCall,
|
|
2293
|
+
providerExecutedToolResults,
|
|
2294
|
+
effectiveTools as ToolSet,
|
|
2295
|
+
download,
|
|
2296
|
+
),
|
|
2297
|
+
),
|
|
1692
2298
|
);
|
|
2299
|
+
await Promise.all(
|
|
2300
|
+
providerToolCalls.map((toolCall, index) =>
|
|
2301
|
+
recordProviderExecutedToolTelemetry(
|
|
2302
|
+
toolCall,
|
|
2303
|
+
providerToolResults[index],
|
|
2304
|
+
iterMessages,
|
|
2305
|
+
currentStepNumber,
|
|
2306
|
+
),
|
|
2307
|
+
),
|
|
2308
|
+
);
|
|
2309
|
+
const continuationInvalidToolResults = invalidToolCalls.map(
|
|
2310
|
+
createInvalidToolResult,
|
|
2311
|
+
);
|
|
1693
2312
|
|
|
1694
|
-
// Combine results in the original order
|
|
1695
|
-
|
|
2313
|
+
// Combine executable/provider results in the original order,
|
|
2314
|
+
// while preserving invalid tool calls as error results for the
|
|
2315
|
+
// next model step without emitting them as synthetic UI success.
|
|
2316
|
+
const executedToolResults = toolCalls.flatMap(tc => {
|
|
1696
2317
|
const clientResult = clientToolResults.find(
|
|
1697
|
-
r => r.toolCallId === tc.toolCallId,
|
|
2318
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
1698
2319
|
);
|
|
1699
|
-
if (clientResult) return clientResult;
|
|
2320
|
+
if (clientResult) return [clientResult];
|
|
1700
2321
|
const providerResult = providerToolResults.find(
|
|
2322
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
2323
|
+
);
|
|
2324
|
+
if (providerResult) return [providerResult];
|
|
2325
|
+
return [];
|
|
2326
|
+
});
|
|
2327
|
+
const continuationToolResults = toolCalls.flatMap(tc => {
|
|
2328
|
+
const invalidResult = continuationInvalidToolResults.find(
|
|
1701
2329
|
r => r.toolCallId === tc.toolCallId,
|
|
1702
2330
|
);
|
|
1703
|
-
if (
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
output: { type: 'text' as const, value: '' },
|
|
1710
|
-
};
|
|
2331
|
+
if (invalidResult) return [invalidResult];
|
|
2332
|
+
const executedResult = executedToolResults.find(
|
|
2333
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
2334
|
+
);
|
|
2335
|
+
if (executedResult) return [executedResult.modelResult];
|
|
2336
|
+
return [];
|
|
1711
2337
|
});
|
|
1712
2338
|
|
|
1713
2339
|
// Write tool results and step boundaries to the stream so the
|
|
@@ -1716,12 +2342,13 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1716
2342
|
if (options.writable) {
|
|
1717
2343
|
await writeToolResultsWithStepBoundary(
|
|
1718
2344
|
options.writable,
|
|
1719
|
-
|
|
1720
|
-
toolCallId: r.toolCallId,
|
|
1721
|
-
toolName: r.toolName,
|
|
1722
|
-
input: toolCalls.find(
|
|
1723
|
-
|
|
1724
|
-
|
|
2345
|
+
executedToolResults.map(r => ({
|
|
2346
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2347
|
+
toolName: r.modelResult.toolName,
|
|
2348
|
+
input: toolCalls.find(
|
|
2349
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2350
|
+
)?.input,
|
|
2351
|
+
output: r.rawOutput,
|
|
1725
2352
|
})),
|
|
1726
2353
|
);
|
|
1727
2354
|
}
|
|
@@ -1733,15 +2360,17 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1733
2360
|
toolName: tc.toolName,
|
|
1734
2361
|
input: tc.input,
|
|
1735
2362
|
}));
|
|
1736
|
-
lastStepToolResults =
|
|
2363
|
+
lastStepToolResults = executedToolResults.map(r => ({
|
|
1737
2364
|
type: 'tool-result' as const,
|
|
1738
|
-
toolCallId: r.toolCallId,
|
|
1739
|
-
toolName: r.toolName,
|
|
1740
|
-
input: toolCalls.find(
|
|
1741
|
-
|
|
2365
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2366
|
+
toolName: r.modelResult.toolName,
|
|
2367
|
+
input: toolCalls.find(
|
|
2368
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2369
|
+
)?.input,
|
|
2370
|
+
output: r.rawOutput,
|
|
1742
2371
|
}));
|
|
1743
2372
|
|
|
1744
|
-
result = await iterator.next(
|
|
2373
|
+
result = await iterator.next(continuationToolResults);
|
|
1745
2374
|
} else {
|
|
1746
2375
|
// Final step with no tool calls - reset tracking
|
|
1747
2376
|
lastStepToolCalls = [];
|
|
@@ -1766,7 +2395,8 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1766
2395
|
// Call onError for non-abort errors (including tool execution errors)
|
|
1767
2396
|
await options.onError({ error });
|
|
1768
2397
|
}
|
|
1769
|
-
|
|
2398
|
+
await telemetryDispatcher.onError?.(error);
|
|
2399
|
+
// Don't throw yet - we want to call onEnd first
|
|
1770
2400
|
}
|
|
1771
2401
|
|
|
1772
2402
|
// Use the final messages from the iterator, or fall back to standardized messages
|
|
@@ -1799,19 +2429,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1799
2429
|
}
|
|
1800
2430
|
}
|
|
1801
2431
|
|
|
1802
|
-
// Call
|
|
1803
|
-
if (
|
|
2432
|
+
// Call onEnd callback if provided (always call, even on errors, but not on abort)
|
|
2433
|
+
if (mergedOnEnd && !wasAborted) {
|
|
1804
2434
|
const lastStep = steps[steps.length - 1];
|
|
1805
|
-
|
|
2435
|
+
const totalUsage = aggregateUsage(steps);
|
|
2436
|
+
await mergedOnEnd({
|
|
1806
2437
|
steps,
|
|
1807
2438
|
messages: messages as ModelMessage[],
|
|
1808
2439
|
text: lastStep?.text ?? '',
|
|
1809
2440
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
1810
|
-
|
|
1811
|
-
|
|
2441
|
+
usage: totalUsage,
|
|
2442
|
+
totalUsage,
|
|
2443
|
+
runtimeContext,
|
|
2444
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1812
2445
|
output: experimentalOutput,
|
|
1813
2446
|
});
|
|
1814
2447
|
}
|
|
2448
|
+
if (!wasAborted && steps.length > 0) {
|
|
2449
|
+
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2450
|
+
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2451
|
+
const totalUsage = aggregateUsage(steps);
|
|
2452
|
+
await telemetryDispatcher.onEnd?.({
|
|
2453
|
+
...lastStep,
|
|
2454
|
+
steps: telemetrySteps,
|
|
2455
|
+
usage: totalUsage,
|
|
2456
|
+
totalUsage,
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
1815
2459
|
|
|
1816
2460
|
// Re-throw any error that occurred
|
|
1817
2461
|
if (encounteredError) {
|
|
@@ -1845,6 +2489,25 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1845
2489
|
}
|
|
1846
2490
|
}
|
|
1847
2491
|
|
|
2492
|
+
function getModelInfo(model: LanguageModel): {
|
|
2493
|
+
provider: string;
|
|
2494
|
+
modelId: string;
|
|
2495
|
+
} {
|
|
2496
|
+
return typeof model === 'string'
|
|
2497
|
+
? { provider: model.split('/')[0] ?? 'gateway', modelId: model }
|
|
2498
|
+
: { provider: model.provider, modelId: model.modelId };
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
function normalizeStepForTelemetry<
|
|
2502
|
+
TOOLS extends ToolSet,
|
|
2503
|
+
RUNTIME_CONTEXT extends Context,
|
|
2504
|
+
>(step: StepResult<TOOLS, RUNTIME_CONTEXT>) {
|
|
2505
|
+
return {
|
|
2506
|
+
...step,
|
|
2507
|
+
model: step.model ?? { provider: 'unknown', modelId: 'unknown' },
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
|
|
1848
2511
|
/**
|
|
1849
2512
|
* Filter tools to only include the specified active tools.
|
|
1850
2513
|
*/
|
|
@@ -1965,6 +2628,33 @@ async function writeApprovalToolResults(
|
|
|
1965
2628
|
}
|
|
1966
2629
|
}
|
|
1967
2630
|
|
|
2631
|
+
/**
|
|
2632
|
+
* Resolve the per-tool context that gets passed into a tool's `execute`
|
|
2633
|
+
* (and `needsApproval`) function. When the tool declares a `contextSchema`,
|
|
2634
|
+
* the entry is validated against it.
|
|
2635
|
+
*/
|
|
2636
|
+
async function resolveToolContext({
|
|
2637
|
+
toolName,
|
|
2638
|
+
tool,
|
|
2639
|
+
toolsContext,
|
|
2640
|
+
}: {
|
|
2641
|
+
toolName: string;
|
|
2642
|
+
tool: ToolSet[string];
|
|
2643
|
+
toolsContext: Record<string, Context | undefined> | undefined;
|
|
2644
|
+
}): Promise<unknown> {
|
|
2645
|
+
const contextSchema = (tool as { contextSchema?: unknown }).contextSchema;
|
|
2646
|
+
const entry = toolsContext?.[toolName];
|
|
2647
|
+
if (contextSchema == null) {
|
|
2648
|
+
return entry;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
return await validateTypes({
|
|
2652
|
+
value: entry,
|
|
2653
|
+
schema: contextSchema as Parameters<typeof validateTypes>[0]['schema'],
|
|
2654
|
+
context: { field: 'tool context', entityName: toolName },
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
|
|
1968
2658
|
function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
1969
2659
|
let inputTokens = 0;
|
|
1970
2660
|
let outputTokens = 0;
|
|
@@ -1979,43 +2669,15 @@ function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
|
1979
2669
|
} as LanguageModelUsage;
|
|
1980
2670
|
}
|
|
1981
2671
|
|
|
1982
|
-
function
|
|
1983
|
-
|
|
1984
|
-
activeTools: string[],
|
|
1985
|
-
): ToolSet {
|
|
1986
|
-
const filtered: ToolSet = {};
|
|
1987
|
-
for (const toolName of activeTools) {
|
|
1988
|
-
if (toolName in tools) {
|
|
1989
|
-
filtered[toolName] = tools[toolName];
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
return filtered;
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
// Matches AI SDK's getErrorMessage from @ai-sdk/provider-utils
|
|
1996
|
-
function getErrorMessage(error: unknown): string {
|
|
1997
|
-
if (error == null) {
|
|
1998
|
-
return 'unknown error';
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
if (typeof error === 'string') {
|
|
2002
|
-
return error;
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
if (error instanceof Error) {
|
|
2006
|
-
return error.message;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
return JSON.stringify(error);
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
function resolveProviderToolResult(
|
|
2013
|
-
toolCall: { toolCallId: string; toolName: string },
|
|
2672
|
+
async function resolveProviderToolResult(
|
|
2673
|
+
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
2014
2674
|
providerExecutedToolResults?: Map<
|
|
2015
2675
|
string,
|
|
2016
2676
|
{ toolCallId: string; toolName: string; result: unknown; isError?: boolean }
|
|
2017
2677
|
>,
|
|
2018
|
-
|
|
2678
|
+
tools?: ToolSet,
|
|
2679
|
+
download?: DownloadFunction,
|
|
2680
|
+
): Promise<WorkflowToolExecutionResult> {
|
|
2019
2681
|
const streamResult = providerExecutedToolResults?.get(toolCall.toolCallId);
|
|
2020
2682
|
if (!streamResult) {
|
|
2021
2683
|
console.warn(
|
|
@@ -2023,45 +2685,79 @@ function resolveProviderToolResult(
|
|
|
2023
2685
|
`did not receive a result from the stream. This may indicate a provider issue.`,
|
|
2024
2686
|
);
|
|
2025
2687
|
return {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2688
|
+
modelResult: {
|
|
2689
|
+
type: 'tool-result' as const,
|
|
2690
|
+
toolCallId: toolCall.toolCallId,
|
|
2691
|
+
toolName: toolCall.toolName,
|
|
2692
|
+
output: {
|
|
2693
|
+
type: 'text' as const,
|
|
2694
|
+
value: '',
|
|
2695
|
+
},
|
|
2032
2696
|
},
|
|
2697
|
+
rawOutput: '',
|
|
2698
|
+
isError: false,
|
|
2033
2699
|
};
|
|
2034
2700
|
}
|
|
2035
2701
|
|
|
2036
2702
|
const result = streamResult.result;
|
|
2037
|
-
const
|
|
2703
|
+
const errorMode = streamResult.isError
|
|
2704
|
+
? typeof result === 'string'
|
|
2705
|
+
? 'text'
|
|
2706
|
+
: 'json'
|
|
2707
|
+
: 'none';
|
|
2038
2708
|
|
|
2709
|
+
return {
|
|
2710
|
+
modelResult: {
|
|
2711
|
+
type: 'tool-result' as const,
|
|
2712
|
+
toolCallId: toolCall.toolCallId,
|
|
2713
|
+
toolName: toolCall.toolName,
|
|
2714
|
+
output: await createLanguageModelToolResultOutput({
|
|
2715
|
+
toolCallId: toolCall.toolCallId,
|
|
2716
|
+
toolName: toolCall.toolName,
|
|
2717
|
+
input: toolCall.input,
|
|
2718
|
+
output: result,
|
|
2719
|
+
tool: tools?.[toolCall.toolName],
|
|
2720
|
+
errorMode,
|
|
2721
|
+
supportedUrls: {},
|
|
2722
|
+
download,
|
|
2723
|
+
}),
|
|
2724
|
+
},
|
|
2725
|
+
rawOutput: result,
|
|
2726
|
+
isError: streamResult.isError === true,
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
function createInvalidToolResult(toolCall: {
|
|
2731
|
+
toolCallId: string;
|
|
2732
|
+
toolName: string;
|
|
2733
|
+
error?: unknown;
|
|
2734
|
+
}): LanguageModelV4ToolResultPart {
|
|
2039
2735
|
return {
|
|
2040
2736
|
type: 'tool-result' as const,
|
|
2041
2737
|
toolCallId: toolCall.toolCallId,
|
|
2042
2738
|
toolName: toolCall.toolName,
|
|
2043
|
-
output:
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
: streamResult.isError
|
|
2048
|
-
? {
|
|
2049
|
-
type: 'error-json' as const,
|
|
2050
|
-
value: result as JSONValue,
|
|
2051
|
-
}
|
|
2052
|
-
: {
|
|
2053
|
-
type: 'json' as const,
|
|
2054
|
-
value: result as JSONValue,
|
|
2055
|
-
},
|
|
2739
|
+
output: {
|
|
2740
|
+
type: 'error-text' as const,
|
|
2741
|
+
value: getErrorMessage(toolCall.error),
|
|
2742
|
+
},
|
|
2056
2743
|
};
|
|
2057
2744
|
}
|
|
2058
2745
|
|
|
2746
|
+
function getToolCallbackMessages(
|
|
2747
|
+
messages: LanguageModelV4Prompt,
|
|
2748
|
+
): ModelMessage[] {
|
|
2749
|
+
const withoutAssistantToolCall =
|
|
2750
|
+
messages.at(-1)?.role === 'assistant' ? messages.slice(0, -1) : messages;
|
|
2751
|
+
return withoutAssistantToolCall as unknown as ModelMessage[];
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2059
2754
|
async function executeTool(
|
|
2060
2755
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
2061
2756
|
tools: ToolSet,
|
|
2062
2757
|
messages: LanguageModelV4Prompt,
|
|
2063
|
-
|
|
2064
|
-
|
|
2758
|
+
context?: unknown,
|
|
2759
|
+
download?: DownloadFunction,
|
|
2760
|
+
): Promise<WorkflowToolExecutionResult> {
|
|
2065
2761
|
const tool = tools[toolCall.toolName];
|
|
2066
2762
|
if (!tool) throw new Error(`Tool "${toolCall.toolName}" not found`);
|
|
2067
2763
|
if (typeof tool.execute !== 'function') {
|
|
@@ -2072,6 +2768,7 @@ async function executeTool(
|
|
|
2072
2768
|
}
|
|
2073
2769
|
// Input is already parsed and validated by streamModelCall's parseToolCall
|
|
2074
2770
|
const parsedInput = toolCall.input;
|
|
2771
|
+
let toolResult: unknown;
|
|
2075
2772
|
|
|
2076
2773
|
try {
|
|
2077
2774
|
// Extract execute function to avoid binding `this` to the tool object.
|
|
@@ -2080,149 +2777,56 @@ async function executeTool(
|
|
|
2080
2777
|
// When the execute function is a workflow step (marked with 'use step'),
|
|
2081
2778
|
// the step system captures `this` for serialization, causing failures.
|
|
2082
2779
|
const { execute } = tool;
|
|
2083
|
-
|
|
2780
|
+
toolResult = await execute(parsedInput, {
|
|
2084
2781
|
toolCallId: toolCall.toolCallId,
|
|
2085
2782
|
// Pass the conversation messages to the tool so it has context about the conversation
|
|
2086
2783
|
messages,
|
|
2087
|
-
// Pass context to the tool
|
|
2088
|
-
context
|
|
2784
|
+
// Pass per-tool context to the tool (resolved from `toolsContext`)
|
|
2785
|
+
context,
|
|
2089
2786
|
});
|
|
2090
|
-
|
|
2091
|
-
// Use the appropriate output type based on the result
|
|
2092
|
-
// AI SDK supports 'text' for strings and 'json' for objects
|
|
2093
|
-
const output =
|
|
2094
|
-
typeof toolResult === 'string'
|
|
2095
|
-
? { type: 'text' as const, value: toolResult }
|
|
2096
|
-
: { type: 'json' as const, value: toolResult };
|
|
2097
|
-
|
|
2098
|
-
return {
|
|
2099
|
-
type: 'tool-result' as const,
|
|
2100
|
-
toolCallId: toolCall.toolCallId,
|
|
2101
|
-
toolName: toolCall.toolName,
|
|
2102
|
-
output,
|
|
2103
|
-
};
|
|
2104
2787
|
} catch (error) {
|
|
2105
2788
|
// Convert tool errors to error-text results sent back to the model,
|
|
2106
2789
|
// allowing the agent to recover rather than killing the entire stream.
|
|
2107
2790
|
// This aligns with AI SDK's streamText behavior for individual tool failures.
|
|
2791
|
+
const errorMessage = getErrorMessage(error);
|
|
2108
2792
|
return {
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2793
|
+
modelResult: {
|
|
2794
|
+
type: 'tool-result',
|
|
2795
|
+
toolCallId: toolCall.toolCallId,
|
|
2796
|
+
toolName: toolCall.toolName,
|
|
2797
|
+
output: await createLanguageModelToolResultOutput({
|
|
2798
|
+
toolCallId: toolCall.toolCallId,
|
|
2799
|
+
toolName: toolCall.toolName,
|
|
2800
|
+
input: parsedInput,
|
|
2801
|
+
output: errorMessage,
|
|
2802
|
+
tool,
|
|
2803
|
+
errorMode: 'text',
|
|
2804
|
+
supportedUrls: {},
|
|
2805
|
+
download,
|
|
2806
|
+
}),
|
|
2115
2807
|
},
|
|
2808
|
+
rawOutput: errorMessage,
|
|
2809
|
+
isError: true,
|
|
2116
2810
|
};
|
|
2117
2811
|
}
|
|
2118
|
-
}
|
|
2119
|
-
|
|
2120
|
-
/**
|
|
2121
|
-
* Collected tool approval information for a single tool call.
|
|
2122
|
-
*/
|
|
2123
|
-
interface CollectedApproval {
|
|
2124
|
-
toolCallId: string;
|
|
2125
|
-
toolName: string;
|
|
2126
|
-
input: unknown;
|
|
2127
|
-
approvalId: string;
|
|
2128
|
-
reason?: string;
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
/**
|
|
2132
|
-
* Collect tool approval responses from model messages.
|
|
2133
|
-
* Mirrors the logic from `collectToolApprovals` in the AI SDK core
|
|
2134
|
-
* (`packages/ai/src/generate-text/collect-tool-approvals.ts`).
|
|
2135
|
-
*
|
|
2136
|
-
* Scans the last tool message for `tool-approval-response` parts,
|
|
2137
|
-
* matches them with `tool-approval-request` parts in assistant messages
|
|
2138
|
-
* and the corresponding `tool-call` parts.
|
|
2139
|
-
*/
|
|
2140
|
-
function collectToolApprovalsFromMessages(messages: ModelMessage[]): {
|
|
2141
|
-
approvedToolApprovals: CollectedApproval[];
|
|
2142
|
-
deniedToolApprovals: CollectedApproval[];
|
|
2143
|
-
} {
|
|
2144
|
-
const lastMessage = messages.at(-1);
|
|
2145
|
-
|
|
2146
|
-
if (lastMessage?.role !== 'tool') {
|
|
2147
|
-
return { approvedToolApprovals: [], deniedToolApprovals: [] };
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
// Gather tool calls from assistant messages
|
|
2151
|
-
const toolCallsByToolCallId: Record<
|
|
2152
|
-
string,
|
|
2153
|
-
{ toolName: string; input: unknown }
|
|
2154
|
-
> = {};
|
|
2155
|
-
for (const message of messages) {
|
|
2156
|
-
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
2157
|
-
for (const part of message.content as any[]) {
|
|
2158
|
-
if (part.type === 'tool-call') {
|
|
2159
|
-
toolCallsByToolCallId[part.toolCallId] = {
|
|
2160
|
-
toolName: part.toolName,
|
|
2161
|
-
input: part.input ?? part.args,
|
|
2162
|
-
};
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
// Gather approval requests from assistant messages
|
|
2169
|
-
const approvalRequestsByApprovalId: Record<
|
|
2170
|
-
string,
|
|
2171
|
-
{ approvalId: string; toolCallId: string }
|
|
2172
|
-
> = {};
|
|
2173
|
-
for (const message of messages) {
|
|
2174
|
-
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
2175
|
-
for (const part of message.content as any[]) {
|
|
2176
|
-
if (part.type === 'tool-approval-request') {
|
|
2177
|
-
approvalRequestsByApprovalId[part.approvalId] = {
|
|
2178
|
-
approvalId: part.approvalId,
|
|
2179
|
-
toolCallId: part.toolCallId,
|
|
2180
|
-
};
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
|
|
2186
|
-
// Gather existing tool results to avoid re-executing
|
|
2187
|
-
const existingToolResults = new Set<string>();
|
|
2188
|
-
for (const part of lastMessage.content as any[]) {
|
|
2189
|
-
if (part.type === 'tool-result') {
|
|
2190
|
-
existingToolResults.add(part.toolCallId);
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
const approvedToolApprovals: CollectedApproval[] = [];
|
|
2195
|
-
const deniedToolApprovals: CollectedApproval[] = [];
|
|
2196
|
-
|
|
2197
|
-
// Collect approval responses from the last tool message
|
|
2198
|
-
const approvalResponses = (lastMessage.content as any[]).filter(
|
|
2199
|
-
(part: any) => part.type === 'tool-approval-response',
|
|
2200
|
-
);
|
|
2201
|
-
|
|
2202
|
-
for (const response of approvalResponses) {
|
|
2203
|
-
const approvalRequest = approvalRequestsByApprovalId[response.approvalId];
|
|
2204
|
-
if (approvalRequest == null) continue;
|
|
2205
2812
|
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
if (toolCall == null) continue;
|
|
2211
|
-
|
|
2212
|
-
const approval: CollectedApproval = {
|
|
2213
|
-
toolCallId: approvalRequest.toolCallId,
|
|
2813
|
+
return {
|
|
2814
|
+
modelResult: {
|
|
2815
|
+
type: 'tool-result' as const,
|
|
2816
|
+
toolCallId: toolCall.toolCallId,
|
|
2214
2817
|
toolName: toolCall.toolName,
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2818
|
+
output: await createLanguageModelToolResultOutput({
|
|
2819
|
+
toolCallId: toolCall.toolCallId,
|
|
2820
|
+
toolName: toolCall.toolName,
|
|
2821
|
+
input: parsedInput,
|
|
2822
|
+
output: toolResult,
|
|
2823
|
+
tool,
|
|
2824
|
+
errorMode: 'none',
|
|
2825
|
+
supportedUrls: {},
|
|
2826
|
+
download,
|
|
2827
|
+
}),
|
|
2828
|
+
},
|
|
2829
|
+
rawOutput: toolResult,
|
|
2830
|
+
isError: false,
|
|
2831
|
+
};
|
|
2228
2832
|
}
|