@n8n/agents 0.5.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/build.tsbuildinfo +1 -1
  2. package/dist/index.d.ts +12 -8
  3. package/dist/index.js +10 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/integrations/langsmith.d.ts +2 -0
  6. package/dist/integrations/langsmith.js +117 -2
  7. package/dist/integrations/langsmith.js.map +1 -1
  8. package/dist/runtime/agent-runtime.d.ts +6 -0
  9. package/dist/runtime/agent-runtime.js +291 -80
  10. package/dist/runtime/agent-runtime.js.map +1 -1
  11. package/dist/runtime/event-bus.d.ts +2 -0
  12. package/dist/runtime/event-bus.js +7 -0
  13. package/dist/runtime/event-bus.js.map +1 -1
  14. package/dist/runtime/json-value.d.ts +2 -0
  15. package/dist/runtime/json-value.js +43 -0
  16. package/dist/runtime/json-value.js.map +1 -0
  17. package/dist/runtime/memory-store.d.ts +40 -2
  18. package/dist/runtime/memory-store.js +138 -3
  19. package/dist/runtime/memory-store.js.map +1 -1
  20. package/dist/runtime/message-list.d.ts +5 -0
  21. package/dist/runtime/message-list.js +47 -0
  22. package/dist/runtime/message-list.js.map +1 -1
  23. package/dist/runtime/messages.d.ts +0 -2
  24. package/dist/runtime/messages.js +127 -101
  25. package/dist/runtime/messages.js.map +1 -1
  26. package/dist/runtime/model-factory.js +117 -34
  27. package/dist/runtime/model-factory.js.map +1 -1
  28. package/dist/runtime/provider-credentials.d.ts +167 -0
  29. package/dist/runtime/provider-credentials.js +34 -0
  30. package/dist/runtime/provider-credentials.js.map +1 -0
  31. package/dist/runtime/run-state.js.map +1 -1
  32. package/dist/runtime/runtime-helpers.d.ts +3 -4
  33. package/dist/runtime/runtime-helpers.js +6 -32
  34. package/dist/runtime/runtime-helpers.js.map +1 -1
  35. package/dist/runtime/stream.js +25 -43
  36. package/dist/runtime/stream.js.map +1 -1
  37. package/dist/runtime/strip-orphaned-tool-messages.js +1 -25
  38. package/dist/runtime/strip-orphaned-tool-messages.js.map +1 -1
  39. package/dist/runtime/title-generation.d.ts +12 -3
  40. package/dist/runtime/title-generation.js +72 -3
  41. package/dist/runtime/title-generation.js.map +1 -1
  42. package/dist/runtime/tool-adapter.d.ts +1 -1
  43. package/dist/runtime/tool-adapter.js +3 -2
  44. package/dist/runtime/tool-adapter.js.map +1 -1
  45. package/dist/runtime/working-memory.d.ts +1 -1
  46. package/dist/runtime/working-memory.js +1 -1
  47. package/dist/runtime/working-memory.js.map +1 -1
  48. package/dist/sdk/agent.d.ts +19 -15
  49. package/dist/sdk/agent.js +22 -201
  50. package/dist/sdk/agent.js.map +1 -1
  51. package/dist/sdk/eval.d.ts +0 -3
  52. package/dist/sdk/eval.js +4 -9
  53. package/dist/sdk/eval.js.map +1 -1
  54. package/dist/sdk/provider-capabilities.js +4 -2
  55. package/dist/sdk/provider-capabilities.js.map +1 -1
  56. package/dist/sdk/provider-tools.d.ts +17 -2
  57. package/dist/sdk/provider-tools.js +19 -0
  58. package/dist/sdk/provider-tools.js.map +1 -1
  59. package/dist/sdk/telemetry.d.ts +2 -0
  60. package/dist/sdk/telemetry.js +6 -0
  61. package/dist/sdk/telemetry.js.map +1 -1
  62. package/dist/sdk/tool.d.ts +17 -10
  63. package/dist/sdk/tool.js +34 -0
  64. package/dist/sdk/tool.js.map +1 -1
  65. package/dist/sdk/verify.js +1 -4
  66. package/dist/sdk/verify.js.map +1 -1
  67. package/dist/storage/base-memory.d.ts +64 -0
  68. package/dist/storage/base-memory.js +54 -0
  69. package/dist/storage/base-memory.js.map +1 -0
  70. package/dist/storage/postgres-memory.d.ts +33 -16
  71. package/dist/storage/postgres-memory.js +46 -25
  72. package/dist/storage/postgres-memory.js.map +1 -1
  73. package/dist/storage/sqlite-memory.d.ts +16 -6
  74. package/dist/storage/sqlite-memory.js +16 -10
  75. package/dist/storage/sqlite-memory.js.map +1 -1
  76. package/dist/types/index.d.ts +4 -2
  77. package/dist/types/index.js +3 -1
  78. package/dist/types/index.js.map +1 -1
  79. package/dist/types/runtime/event.d.ts +2 -2
  80. package/dist/types/sdk/agent-builder.d.ts +0 -3
  81. package/dist/types/sdk/agent.d.ts +63 -25
  82. package/dist/types/sdk/credential-provider.d.ts +2 -3
  83. package/dist/types/sdk/memory.d.ts +23 -2
  84. package/dist/types/sdk/message.d.ts +11 -10
  85. package/dist/types/sdk/observation.d.ts +100 -0
  86. package/dist/types/sdk/observation.js +5 -0
  87. package/dist/types/sdk/observation.js.map +1 -0
  88. package/dist/types/sdk/tool-descriptor.d.ts +13 -0
  89. package/dist/types/sdk/{schema.js → tool-descriptor.js} +1 -1
  90. package/dist/types/sdk/tool-descriptor.js.map +1 -0
  91. package/dist/types/sdk/tool.d.ts +8 -3
  92. package/dist/types/telemetry.d.ts +1 -0
  93. package/dist/utils/parse.d.ts +10 -0
  94. package/dist/utils/parse.js +26 -0
  95. package/dist/utils/parse.js.map +1 -0
  96. package/dist/utils/zod.d.ts +1 -1
  97. package/dist/utils/zod.js.map +1 -1
  98. package/dist/workspace/tools/batch-str-replace-file.d.ts +3 -0
  99. package/dist/workspace/tools/batch-str-replace-file.js +67 -0
  100. package/dist/workspace/tools/batch-str-replace-file.js.map +1 -0
  101. package/dist/workspace/tools/str-replace-file.d.ts +3 -0
  102. package/dist/workspace/tools/str-replace-file.js +51 -0
  103. package/dist/workspace/tools/str-replace-file.js.map +1 -0
  104. package/dist/workspace/tools/workspace-tools.js +4 -0
  105. package/dist/workspace/tools/workspace-tools.js.map +1 -1
  106. package/package.json +17 -8
  107. package/dist/codegen/generate-agent-code.d.ts +0 -2
  108. package/dist/codegen/generate-agent-code.js +0 -197
  109. package/dist/codegen/generate-agent-code.js.map +0 -1
  110. package/dist/sdk/from-schema.d.ts +0 -15
  111. package/dist/sdk/from-schema.js +0 -218
  112. package/dist/sdk/from-schema.js.map +0 -1
  113. package/dist/types/sdk/schema.d.ts +0 -99
  114. package/dist/types/sdk/schema.js.map +0 -1
@@ -32,17 +32,14 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.AgentRuntime = void 0;
40
37
  const ai_1 = require("ai");
41
- const ajv_1 = __importDefault(require("ajv"));
42
38
  const zod_to_json_schema_1 = require("zod-to-json-schema");
43
39
  const catalog_1 = require("../sdk/catalog");
44
40
  const message_1 = require("../sdk/message");
45
41
  const event_bus_1 = require("./event-bus");
42
+ const json_value_1 = require("./json-value");
46
43
  const memory_store_1 = require("./memory-store");
47
44
  const message_list_1 = require("./message-list");
48
45
  const messages_1 = require("./messages");
@@ -54,9 +51,67 @@ const strip_orphaned_tool_messages_1 = require("./strip-orphaned-tool-messages")
54
51
  const title_generation_1 = require("./title-generation");
55
52
  const tool_adapter_1 = require("./tool-adapter");
56
53
  const working_memory_1 = require("./working-memory");
54
+ const parse_1 = require("../utils/parse");
57
55
  const zod_1 = require("../utils/zod");
56
+ function isActiveSpanTracer(value) {
57
+ return (value !== null &&
58
+ typeof value === 'object' &&
59
+ typeof Reflect.get(value, 'startActiveSpan') === 'function');
60
+ }
61
+ function stringifyTelemetryValue(value) {
62
+ try {
63
+ return JSON.stringify(value);
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ }
69
+ function getToolInputSchema(tool) {
70
+ if (!tool.inputSchema) {
71
+ return undefined;
72
+ }
73
+ return (0, zod_1.isZodSchema)(tool.inputSchema) ? (0, zod_to_json_schema_1.zodToJsonSchema)(tool.inputSchema) : tool.inputSchema;
74
+ }
75
+ function summarizeToolForTelemetry(tool) {
76
+ return {
77
+ name: tool.name,
78
+ description: tool.description,
79
+ type: tool.mcpTool ? 'mcp' : 'local',
80
+ ...(tool.mcpServerName ? { mcp_server: tool.mcpServerName } : {}),
81
+ ...(tool.suspendSchema || tool.resumeSchema || tool.withDefaultApproval
82
+ ? { approval: true }
83
+ : {}),
84
+ ...(tool.inputSchema ? { input_schema: getToolInputSchema(tool) } : {}),
85
+ };
86
+ }
87
+ function summarizeProviderToolForTelemetry(tool) {
88
+ const [provider] = tool.name.split('.');
89
+ return {
90
+ name: tool.name,
91
+ provider,
92
+ type: 'provider',
93
+ args: tool.args,
94
+ ...(tool.inputSchema ? { input_schema: getToolInputSchema(tool) } : {}),
95
+ };
96
+ }
97
+ function buildAgentRootInputAttributes(config) {
98
+ const localTools = (config.tools ?? []).map(summarizeToolForTelemetry);
99
+ const providerTools = (config.providerTools ?? []).map(summarizeProviderToolForTelemetry);
100
+ const tools = [...localTools, ...providerTools];
101
+ const toolNames = tools
102
+ .map((tool) => (typeof tool.name === 'string' ? tool.name : undefined))
103
+ .filter((name) => name !== undefined);
104
+ const serialized = stringifyTelemetryValue({
105
+ agent: config.name,
106
+ tool_count: tools.length,
107
+ tools,
108
+ });
109
+ return {
110
+ ...(toolNames.length > 0 ? { 'langsmith.metadata.available_tools': toolNames } : {}),
111
+ ...(serialized ? { 'gen_ai.prompt': serialized } : {}),
112
+ };
113
+ }
58
114
  const MAX_LOOP_ITERATIONS = 20;
59
- const ajv = new ajv_1.default({ strict: false });
60
115
  const EMPTY_MESSAGE_LIST = {
61
116
  messages: [],
62
117
  historyIds: [],
@@ -85,8 +140,9 @@ class AgentRuntime {
85
140
  const runId = (0, run_state_1.generateRunId)();
86
141
  let list = undefined;
87
142
  try {
88
- list = await this.initRun(input, options);
89
- const rawResult = await this.runGenerateLoop(list, options, undefined, runId);
143
+ const initializedList = await this.initRun(input, options);
144
+ list = initializedList;
145
+ const rawResult = await this.withTelemetryRootSpan('generate', options, runId, async () => await this.runGenerateLoop({ list: initializedList, options, runId }));
90
146
  return this.finalizeGenerate(rawResult, list, runId);
91
147
  }
92
148
  catch (error) {
@@ -113,7 +169,7 @@ class AgentRuntime {
113
169
  }
114
170
  return { runId, stream: (0, runtime_helpers_1.makeErrorStream)(error) };
115
171
  }
116
- return { runId, stream: this.startStreamLoop(list, options, undefined, runId) };
172
+ return { runId, stream: this.startStreamLoop({ list, options, runId }) };
117
173
  }
118
174
  async resume(method, data, options) {
119
175
  const state = await this.runState.resume(options.runId);
@@ -127,9 +183,9 @@ class AgentRuntime {
127
183
  throw new Error(`Tool ${toolCall.toolName} not found`);
128
184
  let resumeData = data;
129
185
  if (tool.resumeSchema) {
130
- const parseResult = await tool.resumeSchema.safeParseAsync(data);
186
+ const parseResult = await (0, parse_1.parseWithSchema)(tool.resumeSchema, data);
131
187
  if (!parseResult.success) {
132
- throw new Error(`Invalid resume payload: ${parseResult.error.message}`);
188
+ throw new Error(`Invalid resume payload: ${parseResult.error}`);
133
189
  }
134
190
  resumeData = parseResult.data;
135
191
  }
@@ -155,7 +211,12 @@ class AgentRuntime {
155
211
  await this.ensureModelCost();
156
212
  await this.setListWorkingMemoryConfig(list, state.persistence);
157
213
  if (method === 'generate') {
158
- const rawResult = await this.runGenerateLoop(list, resumeOptions, pendingResume, options.runId);
214
+ const rawResult = await this.withTelemetryRootSpan('generate', resumeOptions, options.runId, async () => await this.runGenerateLoop({
215
+ list,
216
+ options: resumeOptions,
217
+ runId: options.runId,
218
+ pendingResume,
219
+ }));
159
220
  if (!rawResult.pendingSuspend) {
160
221
  await this.cleanupRun(options.runId);
161
222
  }
@@ -163,7 +224,12 @@ class AgentRuntime {
163
224
  }
164
225
  return {
165
226
  runId: options.runId,
166
- stream: this.startStreamLoop(list, resumeOptions, pendingResume, options.runId),
227
+ stream: this.startStreamLoop({
228
+ list,
229
+ options: resumeOptions,
230
+ runId: options.runId,
231
+ pendingResume,
232
+ }),
167
233
  };
168
234
  }
169
235
  catch (error) {
@@ -332,16 +398,96 @@ class AgentRuntime {
332
398
  },
333
399
  };
334
400
  }
335
- async runGenerateLoop(list, options, pendingResume, runId) {
336
- const { model, toolMap, aiTools, providerOptions, hasTools, outputSpec } = this.buildLoopContext({ ...options, persistence: options?.persistence });
401
+ buildTelemetryRootAttributes(t, spanName, runId) {
402
+ const metadataAttributes = this.buildTelemetryMetadataAttributes(t, 'langsmith.metadata');
403
+ return {
404
+ 'langsmith.traceable': 'true',
405
+ 'langsmith.trace.name': spanName,
406
+ 'langsmith.span.kind': 'chain',
407
+ 'langsmith.metadata.agent_name': this.config.name,
408
+ 'langsmith.metadata.agent_run_id': runId,
409
+ ...metadataAttributes,
410
+ ...buildAgentRootInputAttributes(this.config),
411
+ };
412
+ }
413
+ buildTelemetryMetadataAttributes(t, prefix) {
414
+ return Object.fromEntries(Object.entries(t.metadata ?? {}).map(([key, value]) => [`${prefix}.${key}`, value]));
415
+ }
416
+ buildAiSdkOperationAttributes(operationId, t) {
417
+ const functionId = t.functionId ?? this.config.name;
418
+ return {
419
+ 'operation.name': `${operationId} ${functionId}`,
420
+ 'resource.name': functionId,
421
+ 'ai.operationId': operationId,
422
+ 'ai.telemetry.functionId': functionId,
423
+ ...this.buildTelemetryMetadataAttributes(t, 'ai.telemetry.metadata'),
424
+ };
425
+ }
426
+ async withTelemetryRootSpan(operation, options, runId, fn) {
427
+ const t = this.resolveTelemetry(options);
428
+ if (!t?.enabled || t.runtimeRootSpanEnabled === false || !isActiveSpanTracer(t.tracer)) {
429
+ return await fn();
430
+ }
431
+ const spanName = `${t.functionId ?? this.config.name}.${operation}`;
432
+ return await t.tracer.startActiveSpan(spanName, { attributes: this.buildTelemetryRootAttributes(t, spanName, runId) }, async (span) => {
433
+ try {
434
+ return await fn();
435
+ }
436
+ catch (error) {
437
+ span.recordException?.(error);
438
+ span.setStatus?.({ code: 2, message: String(error) });
439
+ throw error;
440
+ }
441
+ finally {
442
+ span.end();
443
+ }
444
+ });
445
+ }
446
+ async withTelemetryToolSpan(toolCallId, toolName, input, t, fn) {
447
+ if (!t?.enabled || !isActiveSpanTracer(t.tracer)) {
448
+ return await fn();
449
+ }
450
+ const shouldRecordInputs = t.recordInputs ?? true;
451
+ const inputValue = shouldRecordInputs ? stringifyTelemetryValue(input) : undefined;
452
+ return await t.tracer.startActiveSpan('ai.toolCall', {
453
+ attributes: {
454
+ ...this.buildAiSdkOperationAttributes('ai.toolCall', t),
455
+ 'ai.toolCall.name': toolName,
456
+ 'ai.toolCall.id': toolCallId,
457
+ ...(inputValue !== undefined ? { 'ai.toolCall.args': inputValue } : {}),
458
+ },
459
+ }, async (span) => {
460
+ try {
461
+ const result = await fn();
462
+ const shouldRecordOutputs = t.recordOutputs ?? true;
463
+ const outputValue = shouldRecordOutputs ? stringifyTelemetryValue(result) : undefined;
464
+ if (outputValue !== undefined) {
465
+ span.setAttributes?.({ 'ai.toolCall.result': outputValue });
466
+ }
467
+ return result;
468
+ }
469
+ catch (error) {
470
+ span.recordException?.(error);
471
+ span.setStatus?.({ code: 2, message: String(error) });
472
+ throw error;
473
+ }
474
+ finally {
475
+ span.end();
476
+ }
477
+ });
478
+ }
479
+ async runGenerateLoop(ctx) {
480
+ const { list, options, runId, pendingResume } = ctx;
481
+ const { model, toolMap, aiTools, providerOptions, hasTools, outputSpec, effectiveInstructions, } = this.buildLoopContext({ ...options, persistence: options?.persistence });
337
482
  let totalUsage;
338
483
  let lastFinishReason = 'stop';
339
484
  let structuredOutput;
340
485
  const toolCallSummary = [];
341
486
  const collectedSubAgentUsage = [];
342
487
  const runTelemetry = this.resolveTelemetry(options);
488
+ const toolCtx = { toolMap, list, runId, telemetry: runTelemetry };
343
489
  if (pendingResume) {
344
- const batch = await this.iteratePendingToolCallsConcurrent(pendingResume, toolMap, list, runTelemetry);
490
+ const batch = await this.iteratePendingToolCallsConcurrent({ ...toolCtx, pendingResume });
345
491
  for (const r of batch.results) {
346
492
  toolCallSummary.push(r.toolEntry);
347
493
  if (r.subAgentUsage)
@@ -374,7 +520,7 @@ class AgentRuntime {
374
520
  this.eventBus.emit({ type: "turn_start" });
375
521
  const result = await (0, ai_1.generateText)({
376
522
  model,
377
- messages: list.forLlm(this.config.instructions, this.config.instructionProviderOptions),
523
+ messages: list.forLlm(effectiveInstructions, this.config.instructionProviderOptions),
378
524
  abortSignal: this.eventBus.signal,
379
525
  ...(hasTools ? { tools: aiTools } : {}),
380
526
  ...(providerOptions
@@ -393,10 +539,13 @@ class AgentRuntime {
393
539
  if (outputSpec) {
394
540
  structuredOutput = result.output;
395
541
  }
396
- this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractToolResults)(newMessages));
542
+ this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(newMessages));
397
543
  break;
398
544
  }
399
- const batch = await this.iterateToolCallsConcurrent(result.toolCalls, toolMap, list, runTelemetry);
545
+ const batch = await this.iterateToolCallsConcurrent({
546
+ ...toolCtx,
547
+ toolCalls: result.toolCalls,
548
+ });
400
549
  for (const r of batch.results) {
401
550
  toolCallSummary.push(r.toolEntry);
402
551
  if (r.subAgentUsage)
@@ -419,7 +568,7 @@ class AgentRuntime {
419
568
  })),
420
569
  };
421
570
  }
422
- this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractToolResults)(list.responseDelta()));
571
+ this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(list.responseDelta()));
423
572
  }
424
573
  if (lastFinishReason === 'tool-calls') {
425
574
  throw new Error(`Agent loop exceeded ${maxIterations} iterations without reaching a stop condition`);
@@ -427,7 +576,7 @@ class AgentRuntime {
427
576
  await this.saveToMemory(list, options);
428
577
  await this.flushTelemetry(options);
429
578
  if (this.config.titleGeneration && options?.persistence?.threadId && this.config.memory) {
430
- void (0, title_generation_1.generateThreadTitle)({
579
+ const titlePromise = (0, title_generation_1.generateThreadTitle)({
431
580
  memory: this.config.memory,
432
581
  threadId: options.persistence.threadId,
433
582
  resourceId: options.persistence.resourceId,
@@ -435,6 +584,9 @@ class AgentRuntime {
435
584
  agentModel: this.config.model,
436
585
  turnDelta: list.turnDelta(),
437
586
  });
587
+ if (this.config.titleGeneration.sync) {
588
+ await titlePromise;
589
+ }
438
590
  }
439
591
  return {
440
592
  runId: runId ?? '',
@@ -446,10 +598,24 @@ class AgentRuntime {
446
598
  ...(collectedSubAgentUsage.length > 0 && { subAgentUsage: collectedSubAgentUsage }),
447
599
  };
448
600
  }
449
- startStreamLoop(list, options, pendingResume, runId) {
601
+ startStreamLoop(ctx) {
602
+ const { options, runId } = ctx;
450
603
  const { readable, writable } = new TransformStream();
451
604
  const writer = writable.getWriter();
452
- this.runStreamLoop(list, options, writer, pendingResume, runId).catch(async (error) => {
605
+ const onToolExecutionStart = (data) => {
606
+ if (data.type !== "tool_execution_start")
607
+ return;
608
+ writer
609
+ .write({
610
+ type: 'tool-execution-start',
611
+ toolCallId: data.toolCallId,
612
+ toolName: data.toolName,
613
+ })
614
+ .catch(() => { });
615
+ };
616
+ this.eventBus.on("tool_execution_start", onToolExecutionStart);
617
+ this.withTelemetryRootSpan('stream', options, runId, async () => await this.runStreamLoop({ ...ctx, writer }))
618
+ .catch(async (error) => {
453
619
  await this.flushTelemetry(options);
454
620
  await this.cleanupRun(runId);
455
621
  try {
@@ -460,11 +626,15 @@ class AgentRuntime {
460
626
  catch {
461
627
  writer.abort(error).catch(() => { });
462
628
  }
629
+ })
630
+ .finally(() => {
631
+ this.eventBus.off("tool_execution_start", onToolExecutionStart);
463
632
  });
464
633
  return readable;
465
634
  }
466
- async runStreamLoop(list, options, writer, pendingResume, runId) {
467
- const { model, toolMap, aiTools, providerOptions, hasTools, outputSpec } = this.buildLoopContext({ ...options, persistence: options?.persistence });
635
+ async runStreamLoop(ctx) {
636
+ const { list, options, runId, pendingResume, writer } = ctx;
637
+ const { model, toolMap, aiTools, providerOptions, hasTools, outputSpec, effectiveInstructions, } = this.buildLoopContext({ ...options, persistence: options?.persistence });
468
638
  const writeChunk = async (chunk) => {
469
639
  await writer.write(chunk);
470
640
  };
@@ -487,15 +657,21 @@ class AgentRuntime {
487
657
  return true;
488
658
  };
489
659
  const runTelemetry = this.resolveTelemetry(options);
660
+ const toolCtx = { toolMap, list, runId, telemetry: runTelemetry };
490
661
  if (pendingResume) {
491
662
  try {
492
- const batch = await this.iteratePendingToolCallsConcurrent(pendingResume, toolMap, list, runTelemetry);
663
+ const batch = await this.iteratePendingToolCallsConcurrent({
664
+ ...toolCtx,
665
+ pendingResume,
666
+ });
493
667
  for (const r of batch.results) {
494
668
  if (r.subAgentUsage)
495
669
  collectedSubAgentUsage.push(...r.subAgentUsage);
496
670
  await writer.write({
497
- type: 'message',
498
- message: r.message,
671
+ type: 'tool-result',
672
+ toolCallId: r.toolCallId,
673
+ toolName: r.toolName,
674
+ output: r.modelOutput,
499
675
  });
500
676
  if (r.customMessage) {
501
677
  await writer.write({ type: 'message', message: r.customMessage });
@@ -503,8 +679,11 @@ class AgentRuntime {
503
679
  }
504
680
  for (const e of batch.errors) {
505
681
  await writer.write({
506
- type: 'message',
507
- message: e.message,
682
+ type: 'tool-result',
683
+ toolCallId: e.toolCallId,
684
+ toolName: e.toolName,
685
+ output: e.error,
686
+ isError: true,
508
687
  });
509
688
  }
510
689
  if (Object.keys(batch.pending).length > 0) {
@@ -535,9 +714,10 @@ class AgentRuntime {
535
714
  if (await handleAbort())
536
715
  return;
537
716
  this.eventBus.emit({ type: "turn_start" });
717
+ const messages = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
538
718
  const result = (0, ai_1.streamText)({
539
719
  model,
540
- messages: list.forLlm(this.config.instructions, this.config.instructionProviderOptions),
720
+ messages,
541
721
  abortSignal: this.eventBus.signal,
542
722
  ...(hasTools ? { tools: aiTools } : {}),
543
723
  ...(providerOptions
@@ -548,7 +728,7 @@ class AgentRuntime {
548
728
  });
549
729
  try {
550
730
  for await (const chunk of result.fullStream) {
551
- if (chunk.type === 'finish' || chunk.type === 'finish-step')
731
+ if (chunk.type === 'finish')
552
732
  continue;
553
733
  const converted = (0, stream_1.convertChunk)(chunk);
554
734
  if (converted)
@@ -580,20 +760,22 @@ class AgentRuntime {
580
760
  if (outputSpec) {
581
761
  structuredOutput = await result.output;
582
762
  }
583
- this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractToolResults)(newMessages));
763
+ this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(newMessages));
584
764
  break;
585
765
  }
586
766
  const toolCalls = await result.toolCalls;
587
767
  try {
588
- const batch = await this.iterateToolCallsConcurrent(toolCalls, toolMap, list, runTelemetry);
768
+ const batch = await this.iterateToolCallsConcurrent({ ...toolCtx, toolCalls });
589
769
  if (await handleAbort())
590
770
  return;
591
771
  for (const r of batch.results) {
592
772
  if (r.subAgentUsage)
593
773
  collectedSubAgentUsage.push(...r.subAgentUsage);
594
774
  await writer.write({
595
- type: 'message',
596
- message: r.message,
775
+ type: 'tool-result',
776
+ toolCallId: r.toolCallId,
777
+ toolName: r.toolName,
778
+ output: r.modelOutput,
597
779
  });
598
780
  if (r.customMessage) {
599
781
  await writer.write({ type: 'message', message: r.customMessage });
@@ -601,8 +783,11 @@ class AgentRuntime {
601
783
  }
602
784
  for (const e of batch.errors) {
603
785
  await writer.write({
604
- type: 'message',
605
- message: e.message,
786
+ type: 'tool-result',
787
+ toolCallId: e.toolCallId,
788
+ toolName: e.toolName,
789
+ output: e.error,
790
+ isError: true,
606
791
  });
607
792
  }
608
793
  if (Object.keys(batch.pending).length > 0) {
@@ -628,7 +813,7 @@ class AgentRuntime {
628
813
  await closeStreamWithError(error, 'failed');
629
814
  return;
630
815
  }
631
- this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractToolResults)(list.responseDelta()));
816
+ this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(list.responseDelta()));
632
817
  }
633
818
  const costUsage = this.applyCost(totalUsage);
634
819
  const parentCost = costUsage?.cost ?? 0;
@@ -647,7 +832,7 @@ class AgentRuntime {
647
832
  try {
648
833
  await this.saveToMemory(list, options);
649
834
  if (this.config.titleGeneration && options?.persistence && this.config.memory) {
650
- void (0, title_generation_1.generateThreadTitle)({
835
+ const titlePromise = (0, title_generation_1.generateThreadTitle)({
651
836
  memory: this.config.memory,
652
837
  threadId: options.persistence.threadId,
653
838
  resourceId: options.persistence.resourceId,
@@ -655,6 +840,9 @@ class AgentRuntime {
655
840
  agentModel: this.config.model,
656
841
  turnDelta: list.turnDelta(),
657
842
  });
843
+ if (this.config.titleGeneration.sync) {
844
+ await titlePromise;
845
+ }
658
846
  }
659
847
  await this.cleanupRun(runId);
660
848
  await this.flushTelemetry(options);
@@ -771,7 +959,8 @@ class AgentRuntime {
771
959
  }
772
960
  return merged;
773
961
  }
774
- async iterateToolCallsConcurrent(toolCalls, toolMap, list, resolvedTelemetry) {
962
+ async iterateToolCallsConcurrent(ctx) {
963
+ const { toolCalls, toolMap, list, runId, telemetry: resolvedTelemetry } = ctx;
775
964
  const executableCalls = toolCalls.filter((tc) => !tc.providerExecuted);
776
965
  const executableCallsById = new Map(executableCalls.map((tc) => [tc.toolCallId, tc]));
777
966
  const unexecutedIds = new Set(executableCalls.map((tc) => tc.toolCallId));
@@ -796,12 +985,12 @@ class AgentRuntime {
796
985
  const tc = batch[i];
797
986
  const toolInput = tc.input;
798
987
  if (result.status === 'rejected') {
988
+ list.setToolCallError(tc.toolCallId, result.reason);
799
989
  errors.push({
800
990
  toolCallId: tc.toolCallId,
801
991
  toolName: tc.toolName,
802
992
  input: toolInput,
803
993
  error: result.reason,
804
- message: (0, runtime_helpers_1.makeErrorToolResultMessage)(tc.toolCallId, tc.toolName, result.reason),
805
994
  });
806
995
  }
807
996
  else if (result.value.outcome === 'suspended') {
@@ -820,6 +1009,7 @@ class AgentRuntime {
820
1009
  input: toolInput,
821
1010
  suspendPayload: result.value.payload,
822
1011
  resumeSchema: result.value.resumeSchema,
1012
+ runId,
823
1013
  };
824
1014
  }
825
1015
  else if (result.value.outcome === 'success') {
@@ -828,9 +1018,9 @@ class AgentRuntime {
828
1018
  toolName: tc.toolName,
829
1019
  input: toolInput,
830
1020
  toolEntry: result.value.toolEntry,
1021
+ modelOutput: result.value.modelOutput,
831
1022
  subAgentUsage: result.value.subAgentUsage,
832
1023
  customMessage: result.value.customMessage,
833
- message: result.value.message,
834
1024
  });
835
1025
  }
836
1026
  else if (result.value.outcome === 'error') {
@@ -839,7 +1029,6 @@ class AgentRuntime {
839
1029
  toolName: tc.toolName,
840
1030
  input: toolInput,
841
1031
  error: result.value.error,
842
- message: result.value.message,
843
1032
  });
844
1033
  }
845
1034
  else if (result.value.outcome === 'noop') {
@@ -860,7 +1049,8 @@ class AgentRuntime {
860
1049
  }
861
1050
  return { results, suspensions, errors, pending };
862
1051
  }
863
- async iteratePendingToolCallsConcurrent(pendingResume, toolMap, list, resolvedTelemetry) {
1052
+ async iteratePendingToolCallsConcurrent(ctx) {
1053
+ const { pendingResume, toolMap, list, runId, telemetry: resolvedTelemetry } = ctx;
864
1054
  const resumedId = pendingResume.resumeToolCallId;
865
1055
  const resumedEntry = pendingResume.pendingToolCalls[resumedId];
866
1056
  if (!resumedEntry) {
@@ -878,6 +1068,7 @@ class AgentRuntime {
878
1068
  suspended: true,
879
1069
  suspendPayload: processResult.payload,
880
1070
  resumeSchema: processResult.resumeSchema,
1071
+ runId,
881
1072
  };
882
1073
  suspensions.push({
883
1074
  toolCallId: resumedId,
@@ -893,9 +1084,9 @@ class AgentRuntime {
893
1084
  toolName: resumedToolName,
894
1085
  input: resumedEntry.input,
895
1086
  toolEntry: processResult.toolEntry,
1087
+ modelOutput: processResult.modelOutput,
896
1088
  subAgentUsage: processResult.subAgentUsage,
897
1089
  customMessage: processResult.customMessage,
898
- message: processResult.message,
899
1090
  });
900
1091
  }
901
1092
  else if (processResult.outcome === 'error') {
@@ -904,7 +1095,6 @@ class AgentRuntime {
904
1095
  toolName: resumedToolName,
905
1096
  input: resumedEntry.input,
906
1097
  error: processResult.error,
907
- message: processResult.message,
908
1098
  });
909
1099
  }
910
1100
  else if (processResult.outcome === 'noop') {
@@ -933,7 +1123,13 @@ class AgentRuntime {
933
1123
  }
934
1124
  }
935
1125
  if (unexecuted.length > 0) {
936
- const batch = await this.iterateToolCallsConcurrent(unexecuted, toolMap, list, resolvedTelemetry);
1126
+ const batch = await this.iterateToolCallsConcurrent({
1127
+ toolCalls: unexecuted,
1128
+ toolMap,
1129
+ list,
1130
+ runId,
1131
+ telemetry: resolvedTelemetry,
1132
+ });
937
1133
  results.push(...batch.results);
938
1134
  suspensions.push(...batch.suspensions);
939
1135
  errors.push(...batch.errors);
@@ -957,57 +1153,52 @@ class AgentRuntime {
957
1153
  result: error,
958
1154
  isError: true,
959
1155
  });
960
- const errorMsg = (0, runtime_helpers_1.makeErrorToolResultMessage)(toolCallId, toolName, error);
961
- list.addResponse([errorMsg]);
962
- return { outcome: 'error', error, message: errorMsg };
1156
+ list.setToolCallError(toolCallId, error);
1157
+ return { outcome: 'error', error };
963
1158
  };
964
1159
  if (!builtTool) {
965
1160
  return makeToolError(new Error(`Tool ${toolName} not found`));
966
1161
  }
967
- const existingToolResults = list
1162
+ const settledBlock = list
968
1163
  .responseDelta()
969
- .filter((m) => (0, message_1.isLlmMessage)(m) && m.role === 'tool')
970
- .flatMap((m) => m.content.filter((content) => content.type === 'tool-result'));
971
- const existingToolResult = existingToolResults.find((r) => r.toolCallId === toolCallId);
972
- if (existingToolResult) {
1164
+ .flatMap((m) => ((0, message_1.isLlmMessage)(m) && 'content' in m ? m.content : []))
1165
+ .find((c) => c.type === 'tool-call' && c.toolCallId === toolCallId && c.state !== 'pending');
1166
+ if (settledBlock) {
1167
+ let settledResult;
1168
+ if (settledBlock.state === 'resolved') {
1169
+ settledResult = settledBlock.output;
1170
+ }
1171
+ else {
1172
+ settledResult = settledBlock.error;
1173
+ }
973
1174
  this.eventBus.emit({
974
1175
  type: "tool_execution_end",
975
1176
  toolCallId,
976
1177
  toolName,
977
- result: existingToolResult.result,
978
- isError: !!existingToolResult.isError,
1178
+ result: settledResult,
1179
+ isError: settledBlock.state === 'rejected',
979
1180
  });
980
1181
  return { outcome: 'noop' };
981
1182
  }
982
1183
  if (builtTool.inputSchema) {
983
- if ((0, zod_1.isZodSchema)(builtTool.inputSchema)) {
984
- const result = await builtTool.inputSchema.safeParseAsync(toolInput);
985
- if (!result.success) {
986
- return makeToolError(new Error(`Invalid tool input: ${result.error.message}`));
987
- }
988
- toolInput = result.data;
989
- }
990
- else {
991
- const validate = ajv.compile(builtTool.inputSchema);
992
- const valid = validate(toolInput);
993
- if (!valid) {
994
- const message = ajv.errorsText(validate.errors);
995
- return makeToolError(new Error(`Invalid tool input: ${message}`));
996
- }
1184
+ const result = await (0, parse_1.parseWithSchema)(builtTool.inputSchema, toolInput);
1185
+ if (!result.success) {
1186
+ return makeToolError(new Error(`Invalid tool input: ${result.error}`));
997
1187
  }
1188
+ toolInput = result.data;
998
1189
  }
999
1190
  let toolResult;
1000
1191
  try {
1001
- toolResult = await (0, tool_adapter_1.executeTool)(toolInput, builtTool, resumeData, resolvedTelemetry);
1192
+ toolResult = await this.withTelemetryToolSpan(toolCallId, toolName, toolInput, resolvedTelemetry, async () => await (0, tool_adapter_1.executeTool)(toolInput, builtTool, resumeData, resolvedTelemetry, toolCallId));
1002
1193
  }
1003
1194
  catch (error) {
1004
1195
  return makeToolError(error);
1005
1196
  }
1006
1197
  if ((0, tool_adapter_1.isSuspendedToolResult)(toolResult)) {
1007
1198
  if (builtTool?.suspendSchema) {
1008
- const parseResult = await builtTool.suspendSchema.safeParseAsync(toolResult.payload);
1199
+ const parseResult = await (0, parse_1.parseWithSchema)(builtTool.suspendSchema, toolResult.payload);
1009
1200
  if (!parseResult.success) {
1010
- return makeToolError(new Error(`Invalid suspend payload: ${parseResult.error.message}`));
1201
+ return makeToolError(new Error(`Invalid suspend payload: ${parseResult.error}`));
1011
1202
  }
1012
1203
  toolResult.payload = parseResult.data;
1013
1204
  }
@@ -1015,8 +1206,17 @@ class AgentRuntime {
1015
1206
  const error = new Error(`Tool ${toolName} has no resume schema`);
1016
1207
  return makeToolError(error);
1017
1208
  }
1018
- const resumeSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(builtTool.resumeSchema);
1019
- return { outcome: 'suspended', payload: toolResult.payload, resumeSchema };
1209
+ const resumeSchema = (0, zod_1.isZodSchema)(builtTool.resumeSchema)
1210
+ ? (0, zod_to_json_schema_1.zodToJsonSchema)(builtTool.resumeSchema)
1211
+ : builtTool.resumeSchema;
1212
+ if (!resumeSchema) {
1213
+ return makeToolError(new Error('Invalid resume schema'));
1214
+ }
1215
+ return {
1216
+ outcome: 'suspended',
1217
+ payload: toolResult.payload,
1218
+ resumeSchema,
1219
+ };
1020
1220
  }
1021
1221
  let actualResult = toolResult;
1022
1222
  let extractedSubAgentUsage;
@@ -1034,8 +1234,7 @@ class AgentRuntime {
1034
1234
  const modelResult = builtTool.toModelOutput
1035
1235
  ? builtTool.toModelOutput(actualResult)
1036
1236
  : actualResult;
1037
- const toolResultMsg = (0, runtime_helpers_1.makeToolResultMessage)(toolCallId, toolName, modelResult);
1038
- list.addResponse([toolResultMsg]);
1237
+ list.setToolCallResult(toolCallId, (0, json_value_1.toJsonValue)(modelResult));
1039
1238
  const customMessage = builtTool?.toMessage?.(actualResult);
1040
1239
  if (customMessage) {
1041
1240
  list.addResponse([customMessage]);
@@ -1048,9 +1247,9 @@ class AgentRuntime {
1048
1247
  output: actualResult,
1049
1248
  transformed: !!builtTool.toModelOutput,
1050
1249
  },
1250
+ modelOutput: modelResult,
1051
1251
  subAgentUsage: extractedSubAgentUsage,
1052
1252
  customMessage,
1053
- message: toolResultMsg,
1054
1253
  };
1055
1254
  }
1056
1255
  buildLoopContext(execOptions) {
@@ -1061,8 +1260,9 @@ class AgentRuntime {
1061
1260
  const aiTools = (0, tool_adapter_1.toAiSdkTools)(allUserTools);
1062
1261
  const aiProviderTools = (0, tool_adapter_1.toAiSdkProviderTools)(this.config.providerTools);
1063
1262
  const allTools = { ...aiTools, ...aiProviderTools };
1263
+ const model = (0, model_factory_1.createModel)(this.config.model);
1064
1264
  return {
1065
- model: (0, model_factory_1.createModel)(this.config.model),
1265
+ model,
1066
1266
  toolMap: (0, tool_adapter_1.buildToolMap)(allUserTools),
1067
1267
  aiTools: allTools,
1068
1268
  providerOptions: this.buildCallProviderOptions(execOptions?.providerOptions),
@@ -1070,8 +1270,19 @@ class AgentRuntime {
1070
1270
  outputSpec: this.config.structuredOutput
1071
1271
  ? ai_1.Output.object({ schema: this.config.structuredOutput })
1072
1272
  : undefined,
1273
+ effectiveInstructions: this.composeEffectiveInstructions(allUserTools),
1073
1274
  };
1074
1275
  }
1276
+ composeEffectiveInstructions(tools) {
1277
+ const fragments = tools
1278
+ .map((t) => t.systemInstruction)
1279
+ .filter((s) => typeof s === 'string' && s.trim().length > 0);
1280
+ const userInstructions = this.config.instructions;
1281
+ if (fragments.length === 0)
1282
+ return userInstructions;
1283
+ const block = `<built_in_rules>\n${fragments.map((f) => `- ${f}`).join('\n')}\n</built_in_rules>`;
1284
+ return userInstructions ? `${block}\n\n${userInstructions}` : block;
1285
+ }
1075
1286
  buildWorkingMemoryToolForRun(persistence) {
1076
1287
  const wmParams = this.resolveWorkingMemoryParams(persistence);
1077
1288
  if (!wmParams)