@n8n/agents 0.10.1 → 0.12.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 (140) hide show
  1. package/dist/build.tsbuildinfo +1 -1
  2. package/dist/index.d.ts +10 -2
  3. package/dist/index.js +36 -5
  4. package/dist/index.js.map +1 -1
  5. package/dist/runtime/agent-runtime.d.ts +14 -6
  6. package/dist/runtime/agent-runtime.js +359 -267
  7. package/dist/runtime/agent-runtime.js.map +1 -1
  8. package/dist/runtime/delegate-sub-agent-tool.d.ts +87 -0
  9. package/dist/runtime/delegate-sub-agent-tool.js +362 -0
  10. package/dist/runtime/delegate-sub-agent-tool.js.map +1 -0
  11. package/dist/runtime/event-bus.d.ts +8 -0
  12. package/dist/runtime/event-bus.js +48 -0
  13. package/dist/runtime/event-bus.js.map +1 -1
  14. package/dist/runtime/mcp-connection.d.ts +2 -3
  15. package/dist/runtime/mcp-connection.js +3 -6
  16. package/dist/runtime/mcp-connection.js.map +1 -1
  17. package/dist/runtime/message-list.d.ts +9 -3
  18. package/dist/runtime/message-list.js +16 -5
  19. package/dist/runtime/message-list.js.map +1 -1
  20. package/dist/runtime/messages.js +10 -2
  21. package/dist/runtime/messages.js.map +1 -1
  22. package/dist/runtime/model-factory.js +12 -0
  23. package/dist/runtime/model-factory.js.map +1 -1
  24. package/dist/runtime/provider-credentials.d.ts +13 -0
  25. package/dist/runtime/provider-credentials.js +1 -0
  26. package/dist/runtime/provider-credentials.js.map +1 -1
  27. package/dist/runtime/runtime-helpers.d.ts +1 -2
  28. package/dist/runtime/runtime-helpers.js +0 -8
  29. package/dist/runtime/runtime-helpers.js.map +1 -1
  30. package/dist/runtime/sdk-owned-tool.d.ts +4 -0
  31. package/dist/runtime/sdk-owned-tool.js +19 -0
  32. package/dist/runtime/sdk-owned-tool.js.map +1 -0
  33. package/dist/runtime/stream.js +8 -0
  34. package/dist/runtime/stream.js.map +1 -1
  35. package/dist/runtime/sub-agent-task-path.d.ts +10 -0
  36. package/dist/runtime/sub-agent-task-path.js +39 -0
  37. package/dist/runtime/sub-agent-task-path.js.map +1 -0
  38. package/dist/runtime/title-generation.js +8 -5
  39. package/dist/runtime/title-generation.js.map +1 -1
  40. package/dist/runtime/tool-adapter.d.ts +0 -9
  41. package/dist/runtime/tool-adapter.js +12 -21
  42. package/dist/runtime/tool-adapter.js.map +1 -1
  43. package/dist/runtime/write-todos-tool.d.ts +3 -0
  44. package/dist/runtime/write-todos-tool.js +95 -0
  45. package/dist/runtime/write-todos-tool.js.map +1 -0
  46. package/dist/sdk/agent.d.ts +19 -12
  47. package/dist/sdk/agent.js +318 -91
  48. package/dist/sdk/agent.js.map +1 -1
  49. package/dist/sdk/cancellation.d.ts +7 -0
  50. package/dist/sdk/cancellation.js +16 -0
  51. package/dist/sdk/cancellation.js.map +1 -0
  52. package/dist/sdk/catalog.js +40 -4
  53. package/dist/sdk/catalog.js.map +1 -1
  54. package/dist/sdk/guardrails/index.d.ts +5 -0
  55. package/dist/sdk/guardrails/index.js +15 -0
  56. package/dist/sdk/guardrails/index.js.map +1 -0
  57. package/dist/sdk/guardrails/patterns.d.ts +13 -0
  58. package/dist/sdk/guardrails/patterns.js +61 -0
  59. package/dist/sdk/guardrails/patterns.js.map +1 -0
  60. package/dist/sdk/guardrails/redactor.d.ts +24 -0
  61. package/dist/sdk/guardrails/redactor.js +86 -0
  62. package/dist/sdk/guardrails/redactor.js.map +1 -0
  63. package/dist/sdk/guardrails/streaming-redactor.d.ts +9 -0
  64. package/dist/sdk/guardrails/streaming-redactor.js +43 -0
  65. package/dist/sdk/guardrails/streaming-redactor.js.map +1 -0
  66. package/dist/sdk/mcp-client.d.ts +1 -1
  67. package/dist/sdk/mcp-client.js +2 -2
  68. package/dist/sdk/mcp-client.js.map +1 -1
  69. package/dist/sdk/memory.d.ts +1 -3
  70. package/dist/sdk/memory.js +0 -14
  71. package/dist/sdk/memory.js.map +1 -1
  72. package/dist/sdk/provider-capabilities.js +1 -0
  73. package/dist/sdk/provider-capabilities.js.map +1 -1
  74. package/dist/sdk/tool.d.ts +3 -0
  75. package/dist/sdk/tool.js +42 -10
  76. package/dist/sdk/tool.js.map +1 -1
  77. package/dist/storage/base-memory.d.ts +0 -31
  78. package/dist/storage/base-memory.js +0 -9
  79. package/dist/storage/base-memory.js.map +1 -1
  80. package/dist/types/index.d.ts +2 -2
  81. package/dist/types/index.js.map +1 -1
  82. package/dist/types/runtime/event.d.ts +31 -1
  83. package/dist/types/runtime/event.js +2 -0
  84. package/dist/types/runtime/event.js.map +1 -1
  85. package/dist/types/sdk/agent-builder.d.ts +0 -1
  86. package/dist/types/sdk/agent.d.ts +20 -15
  87. package/dist/types/sdk/guardrail.d.ts +1 -1
  88. package/dist/types/sdk/memory.d.ts +0 -42
  89. package/dist/types/sdk/message.d.ts +1 -0
  90. package/dist/types/sdk/tool.d.ts +19 -1
  91. package/dist/utils/json-schema.d.ts +3 -0
  92. package/dist/utils/json-schema.js +72 -0
  93. package/dist/utils/json-schema.js.map +1 -0
  94. package/dist/workspace/filesystem/daytona-filesystem.d.ts +25 -0
  95. package/dist/workspace/filesystem/daytona-filesystem.js +130 -0
  96. package/dist/workspace/filesystem/daytona-filesystem.js.map +1 -0
  97. package/dist/workspace/filesystem/n8n-sandbox-filesystem.d.ts +25 -0
  98. package/dist/workspace/filesystem/n8n-sandbox-filesystem.js +126 -0
  99. package/dist/workspace/filesystem/n8n-sandbox-filesystem.js.map +1 -0
  100. package/dist/workspace/sandbox/create-workspace.d.ts +4 -0
  101. package/dist/workspace/sandbox/create-workspace.js +68 -0
  102. package/dist/workspace/sandbox/create-workspace.js.map +1 -0
  103. package/dist/workspace/sandbox/daytona-auth-manager.d.ts +26 -0
  104. package/dist/workspace/sandbox/daytona-auth-manager.js +84 -0
  105. package/dist/workspace/sandbox/daytona-auth-manager.js.map +1 -0
  106. package/dist/workspace/sandbox/daytona-sandbox.d.ts +72 -0
  107. package/dist/workspace/sandbox/daytona-sandbox.js +357 -0
  108. package/dist/workspace/sandbox/daytona-sandbox.js.map +1 -0
  109. package/dist/workspace/sandbox/index.d.ts +7 -0
  110. package/dist/workspace/sandbox/index.js +22 -0
  111. package/dist/workspace/sandbox/index.js.map +1 -0
  112. package/dist/workspace/sandbox/lazy-daytona.d.ts +2 -0
  113. package/dist/workspace/sandbox/lazy-daytona.js +12 -0
  114. package/dist/workspace/sandbox/lazy-daytona.js.map +1 -0
  115. package/dist/workspace/sandbox/logger.d.ts +12 -0
  116. package/dist/workspace/sandbox/logger.js +3 -0
  117. package/dist/workspace/sandbox/logger.js.map +1 -0
  118. package/dist/workspace/sandbox/n8n-sandbox-sandbox.d.ts +35 -0
  119. package/dist/workspace/sandbox/n8n-sandbox-sandbox.js +138 -0
  120. package/dist/workspace/sandbox/n8n-sandbox-sandbox.js.map +1 -0
  121. package/dist/workspace/sandbox/provider.d.ts +4 -0
  122. package/dist/workspace/sandbox/provider.js +14 -0
  123. package/dist/workspace/sandbox/provider.js.map +1 -0
  124. package/dist/workspace/sandbox/run-in-sandbox.d.ts +26 -0
  125. package/dist/workspace/sandbox/run-in-sandbox.js +19 -0
  126. package/dist/workspace/sandbox/run-in-sandbox.js.map +1 -0
  127. package/dist/workspace/sandbox/types.d.ts +45 -0
  128. package/dist/workspace/sandbox/types.js +3 -0
  129. package/dist/workspace/sandbox/types.js.map +1 -0
  130. package/dist/workspace/sandbox/workspace-root.d.ts +16 -0
  131. package/dist/workspace/sandbox/workspace-root.js +69 -0
  132. package/dist/workspace/sandbox/workspace-root.js.map +1 -0
  133. package/dist/workspace/tools/batch-str-replace-file.js +2 -2
  134. package/dist/workspace/tools/batch-str-replace-file.js.map +1 -1
  135. package/dist/workspace/tools/str-replace-file.js +2 -2
  136. package/dist/workspace/tools/str-replace-file.js.map +1 -1
  137. package/package.json +29 -17
  138. package/dist/sdk/network.d.ts +0 -20
  139. package/dist/sdk/network.js +0 -46
  140. package/dist/sdk/network.js.map +0 -1
@@ -6,6 +6,7 @@ const catalog_1 = require("../sdk/catalog");
6
6
  const message_1 = require("../sdk/message");
7
7
  const background_task_tracker_1 = require("./background-task-tracker");
8
8
  const deferred_tool_manager_1 = require("./deferred-tool-manager");
9
+ const delegate_sub_agent_tool_1 = require("./delegate-sub-agent-tool");
9
10
  const episodic_memory_1 = require("./episodic-memory");
10
11
  const event_bus_1 = require("./event-bus");
11
12
  const execution_counter_1 = require("./execution-counter");
@@ -26,9 +27,12 @@ const runtime_helpers_1 = require("./runtime-helpers");
26
27
  const scoped_memory_task_runner_1 = require("./scoped-memory-task-runner");
27
28
  const stream_1 = require("./stream");
28
29
  const strip_orphaned_tool_messages_1 = require("./strip-orphaned-tool-messages");
30
+ const sub_agent_task_path_1 = require("./sub-agent-task-path");
29
31
  const title_generation_1 = require("./title-generation");
30
32
  const tool_adapter_1 = require("./tool-adapter");
33
+ const cancellation_1 = require("../sdk/cancellation");
31
34
  const telemetry_1 = require("../sdk/telemetry");
35
+ const json_schema_1 = require("../utils/json-schema");
32
36
  const parse_1 = require("../utils/parse");
33
37
  const zod_1 = require("../utils/zod");
34
38
  function isActiveSpanTracer(value) {
@@ -56,9 +60,7 @@ function summarizeToolForTelemetry(tool) {
56
60
  description: tool.description,
57
61
  type: tool.mcpTool ? 'mcp' : 'local',
58
62
  ...(tool.mcpServerName ? { mcp_server: tool.mcpServerName } : {}),
59
- ...(tool.suspendSchema || tool.resumeSchema || tool.withDefaultApproval
60
- ? { approval: true }
61
- : {}),
63
+ ...(tool.suspendSchema || tool.resumeSchema || tool.approval ? { approval: true } : {}),
62
64
  ...(tool.inputSchema ? { input_schema: getToolInputSchema(tool) } : {}),
63
65
  };
64
66
  }
@@ -72,6 +74,23 @@ function summarizeProviderToolForTelemetry(tool) {
72
74
  ...(tool.inputSchema ? { input_schema: getToolInputSchema(tool) } : {}),
73
75
  };
74
76
  }
77
+ function isDeniedApprovalResumeData(value) {
78
+ return value !== null && typeof value === 'object' && Reflect.get(value, 'approved') === false;
79
+ }
80
+ function shouldEmitToolExecutionStart(tool, resumeData) {
81
+ if (!tool.approval)
82
+ return true;
83
+ if (!tool.approval.required && tool.approval.conditional !== true)
84
+ return true;
85
+ if (resumeData === undefined)
86
+ return false;
87
+ return !isDeniedApprovalResumeData(resumeData);
88
+ }
89
+ function getToolResumeJsonSchema(tool) {
90
+ if (!tool.resumeSchema)
91
+ return undefined;
92
+ return (0, zod_1.isZodSchema)(tool.resumeSchema) ? (0, zod_to_json_schema_1.zodToJsonSchema)(tool.resumeSchema) : tool.resumeSchema;
93
+ }
75
94
  function buildAgentRootInputAttributes(config) {
76
95
  const localTools = (config.tools ?? []).map(summarizeToolForTelemetry);
77
96
  const providerTools = (config.providerTools ?? []).map(summarizeProviderToolForTelemetry);
@@ -115,11 +134,13 @@ class AgentRuntime {
115
134
  this.backgroundTasks = new background_task_tracker_1.BackgroundTaskTracker();
116
135
  this.episodicMemoryTasksByResource = new Map();
117
136
  this.config = config;
137
+ this.runId = config.runId ?? (0, run_state_1.generateRunId)();
118
138
  if (config.deferredTools && config.deferredTools.length > 0) {
119
139
  this.deferredToolManager = new deferred_tool_manager_1.DeferredToolManager(config.deferredTools, config.toolSearch);
120
140
  }
121
- this.runState = new run_state_1.RunStateManager(config.checkpointStorage);
141
+ this.runState = config.runState ?? new run_state_1.RunStateManager(config.checkpointStorage);
122
142
  this.eventBus = config.eventBus ?? new event_bus_1.AgentEventBus();
143
+ this.modelCost = config.modelCost;
123
144
  this.currentState = {
124
145
  persistence: undefined,
125
146
  status: 'idle',
@@ -131,6 +152,7 @@ class AgentRuntime {
131
152
  this.config.telemetry = telemetry;
132
153
  }
133
154
  async dispose() {
155
+ this.eventBus.dispose();
134
156
  await this.backgroundTasks.flush();
135
157
  }
136
158
  getState() {
@@ -140,44 +162,59 @@ class AgentRuntime {
140
162
  this.eventBus.abort();
141
163
  }
142
164
  async generate(input, options) {
143
- const runId = (0, run_state_1.generateRunId)();
165
+ const abortScope = this.eventBus.createAbortScope(options?.abortSignal);
144
166
  let list = undefined;
145
167
  try {
146
168
  const initializedList = await this.initRun(input, options);
147
169
  list = initializedList;
148
- const rawResult = await this.withTelemetryRootSpan('generate', options, runId, async () => await this.runGenerateLoop({ list: initializedList, options, runId }));
149
- return this.finalizeGenerate(rawResult, list, runId);
170
+ const rawResult = await this.withTelemetryRootSpan('generate', options, this.runId, async () => await this.runGenerateLoop({ list: initializedList, options, abortScope }));
171
+ return this.finalizeGenerate(rawResult, list);
150
172
  }
151
173
  catch (error) {
152
174
  await this.flushTelemetry(options);
153
- const isAbort = this.eventBus.isAborted;
175
+ const isAbort = abortScope.isAborted;
154
176
  this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
155
177
  if (!isAbort) {
156
178
  this.eventBus.emit({ type: "error", message: String(error), error });
157
179
  }
158
- return { runId, messages: list?.responseDelta() ?? [], finishReason: 'error', error };
180
+ return {
181
+ runId: this.runId,
182
+ messages: list?.responseDelta() ?? [],
183
+ finishReason: 'error',
184
+ error,
185
+ getState: () => this.getState(),
186
+ };
187
+ }
188
+ finally {
189
+ abortScope.dispose();
159
190
  }
160
191
  }
161
192
  async stream(input, options) {
162
- const runId = (0, run_state_1.generateRunId)();
193
+ const abortScope = this.eventBus.createAbortScope(options?.abortSignal);
163
194
  let list;
164
195
  try {
165
196
  list = await this.initRun(input, options);
166
197
  }
167
198
  catch (error) {
168
- const isAbort = this.eventBus.isAborted;
199
+ const isAbort = abortScope.isAborted;
169
200
  this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
170
201
  if (!isAbort) {
171
202
  this.eventBus.emit({ type: "error", message: String(error), error });
172
203
  }
173
- return { runId, stream: (0, runtime_helpers_1.makeErrorStream)(error) };
204
+ abortScope.dispose();
205
+ return { runId: this.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error), getState: () => this.getState() };
174
206
  }
175
- return { runId, stream: this.startStreamLoop({ list, options, runId }) };
207
+ return {
208
+ runId: this.runId,
209
+ stream: this.startStreamLoop({ list, options, abortScope }),
210
+ getState: () => this.getState(),
211
+ };
176
212
  }
177
213
  async resume(method, data, options) {
178
- const state = await this.runState.resume(options.runId);
214
+ this.runId = options.runId;
215
+ const state = await this.runState.resume(this.runId);
179
216
  if (!state)
180
- throw new Error(`No suspended run found for runId: ${options.runId}`);
217
+ throw new Error(`No suspended run found for runId: ${this.runId}`);
181
218
  const toolCall = state.pendingToolCalls[options.toolCallId];
182
219
  if (!toolCall)
183
220
  throw new Error(`No tool call found for toolCallId: ${options.toolCallId}`);
@@ -187,7 +224,8 @@ class AgentRuntime {
187
224
  if (!toolForValidation)
188
225
  throw new Error(`Tool ${toolCall.toolName} not found`);
189
226
  let resumeData = data;
190
- if (toolForValidation.resumeSchema) {
227
+ let abortScope;
228
+ if (!(0, cancellation_1.isCancellation)(resumeData) && toolForValidation.resumeSchema) {
191
229
  const parseResult = await (0, parse_1.parseWithSchema)(toolForValidation.resumeSchema, data);
192
230
  if (!parseResult.success) {
193
231
  throw new Error(`Invalid resume payload: ${parseResult.error}`);
@@ -217,7 +255,8 @@ class AgentRuntime {
217
255
  persistence: state.persistence,
218
256
  ...mergedExecOptions,
219
257
  };
220
- this.eventBus.resetAbort(resumeOptions.abortSignal);
258
+ abortScope = this.eventBus.createAbortScope(resumeOptions.abortSignal);
259
+ const activeAbortScope = abortScope;
221
260
  const pendingResume = {
222
261
  pendingToolCalls: state.pendingToolCalls,
223
262
  resumeToolCallId: options.toolCallId,
@@ -227,42 +266,50 @@ class AgentRuntime {
227
266
  await this.ensureModelCost();
228
267
  await this.setListObservationLogMemory(list, state.persistence);
229
268
  if (method === 'generate') {
230
- const rawResult = await this.withTelemetryRootSpan('generate', resumeOptions, options.runId, async () => await this.runGenerateLoop({
269
+ const rawResult = await this.withTelemetryRootSpan('generate', resumeOptions, this.runId, async () => await this.runGenerateLoop({
231
270
  list,
232
271
  options: resumeOptions,
233
- runId: options.runId,
272
+ abortScope: activeAbortScope,
234
273
  pendingResume,
235
274
  }));
236
275
  if (!rawResult.pendingSuspend) {
237
- await this.cleanupRun(options.runId);
276
+ await this.cleanupRun();
277
+ }
278
+ try {
279
+ return this.finalizeGenerate(rawResult, list);
280
+ }
281
+ finally {
282
+ abortScope.dispose();
238
283
  }
239
- return this.finalizeGenerate(rawResult, list, options.runId);
240
284
  }
241
285
  return {
242
- runId: options.runId,
286
+ runId: this.runId,
243
287
  stream: this.startStreamLoop({
244
288
  list,
245
289
  options: resumeOptions,
246
- runId: options.runId,
290
+ abortScope: activeAbortScope,
247
291
  pendingResume,
248
292
  }),
293
+ getState: () => this.getState(),
249
294
  };
250
295
  }
251
296
  catch (error) {
252
- const isAbort = this.eventBus.isAborted;
297
+ const isAbort = abortScope?.isAborted ?? false;
298
+ abortScope?.dispose();
253
299
  this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
254
300
  if (!isAbort) {
255
301
  this.eventBus.emit({ type: "error", message: String(error), error });
256
302
  }
257
303
  if (method === 'generate') {
258
304
  return {
259
- runId: options.runId,
305
+ runId: this.runId,
260
306
  messages: [],
261
307
  finishReason: 'error',
262
308
  error,
309
+ getState: () => this.getState(),
263
310
  };
264
311
  }
265
- return { runId: options.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error) };
312
+ return { runId: this.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error), getState: () => this.getState() };
266
313
  }
267
314
  }
268
315
  async buildMessageList(input, options) {
@@ -273,9 +320,6 @@ class AgentRuntime {
273
320
  list.addHistory((0, strip_orphaned_tool_messages_1.stripOrphanedToolMessages)(memMessages));
274
321
  }
275
322
  }
276
- if (this.config.semanticRecall && options?.persistence?.threadId) {
277
- await this.performSemanticRecall(list, input, options.persistence, options.executionCounter);
278
- }
279
323
  await this.setListObservationLogMemory(list, options?.persistence);
280
324
  list.addInput(input);
281
325
  return list;
@@ -300,87 +344,7 @@ class AgentRuntime {
300
344
  resourceId,
301
345
  });
302
346
  }
303
- async performSemanticRecall(list, input, persistence, executionCounter) {
304
- if (!this.config.semanticRecall || !this.config.memory)
305
- return;
306
- const userText = input
307
- .filter((m) => (0, message_1.isLlmMessage)(m) && m.role === 'user')
308
- .flatMap((m) => ((0, message_1.isLlmMessage)(m) ? m.content : []))
309
- .filter((c) => c.type === 'text')
310
- .map((c) => c.text)
311
- .join(' ');
312
- if (!userText)
313
- return;
314
- let recalled = [];
315
- if (this.config.memory.queryEmbeddings && this.config.semanticRecall.embedder) {
316
- const { embed } = getAiSdk();
317
- const embeddingModel = (0, model_factory_1.createEmbeddingModel)(this.config.semanticRecall.embedder, this.config.semanticRecall.apiKey);
318
- const { embedding, usage } = await embed({ model: embeddingModel, value: userText });
319
- (0, execution_counter_1.incrementTokenCountFromUsage)(executionCounter, usage);
320
- const hits = await this.config.memory.queryEmbeddings({
321
- scope: this.config.semanticRecall.scope ?? 'resource',
322
- threadId: persistence.threadId,
323
- resourceId: persistence.resourceId,
324
- vector: embedding,
325
- topK: this.config.semanticRecall.topK,
326
- });
327
- if (hits.length > 0) {
328
- const hitIds = new Set(hits.map((h) => h.id));
329
- const allMsgs = await this.config.memory.getMessages(persistence.threadId);
330
- if (this.config.semanticRecall.messageRange) {
331
- recalled = this.expandMessageRange(allMsgs, hitIds, this.config.semanticRecall.messageRange);
332
- }
333
- else {
334
- recalled = allMsgs.filter((m) => {
335
- const id = m.id;
336
- return id !== undefined && hitIds.has(id);
337
- });
338
- }
339
- }
340
- }
341
- else if (this.config.memory.search) {
342
- recalled = await this.config.memory.search(userText, {
343
- threadId: persistence.threadId,
344
- resourceId: persistence.resourceId,
345
- topK: this.config.semanticRecall.topK,
346
- messageRange: this.config.semanticRecall.messageRange,
347
- });
348
- }
349
- if (recalled.length === 0)
350
- return;
351
- const { historyIds } = list.serialize();
352
- const historyIdSet = new Set(historyIds);
353
- const newRecalled = recalled.filter((m) => {
354
- const id = m.id;
355
- return !id || !historyIdSet.has(id);
356
- });
357
- if (newRecalled.length > 0) {
358
- list.addHistory(newRecalled);
359
- }
360
- }
361
- expandMessageRange(allMsgs, hitIds, range) {
362
- const expandedIds = new Set();
363
- for (const msg of allMsgs) {
364
- const id = 'id' in msg && typeof msg.id === 'string' ? msg.id : undefined;
365
- if (!id || !hitIds.has(id))
366
- continue;
367
- const idx = allMsgs.indexOf(msg);
368
- const start = Math.max(0, idx - (range.before ?? 0));
369
- const end = Math.min(allMsgs.length - 1, idx + (range.after ?? 0));
370
- for (let i = start; i <= end; i++) {
371
- const el = allMsgs[i];
372
- const mid = 'id' in el && typeof el.id === 'string' ? el.id : undefined;
373
- if (mid)
374
- expandedIds.add(mid);
375
- }
376
- }
377
- return allMsgs.filter((m) => {
378
- const mid = 'id' in m && typeof m.id === 'string' ? m.id : undefined;
379
- return mid && expandedIds.has(mid);
380
- });
381
- }
382
347
  async initRun(input, options) {
383
- this.eventBus.resetAbort(options?.abortSignal);
384
348
  this.updateState({
385
349
  status: 'running',
386
350
  persistence: options?.persistence,
@@ -391,14 +355,13 @@ class AgentRuntime {
391
355
  this.incrementMessageCount(options?.executionCounter);
392
356
  return await this.buildMessageList(normalizedInput, options);
393
357
  }
394
- finalizeGenerate(result, list, runId) {
395
- result.runId = runId;
358
+ finalizeGenerate(result, list) {
359
+ result.runId = this.runId;
396
360
  result.usage = this.applyCost(result.usage);
397
361
  result.model = this.modelIdString;
398
- const finalized = (0, runtime_helpers_1.applySubAgentUsage)(result);
399
362
  this.updateState({ status: 'success', messageList: list.serialize() });
400
- this.eventBus.emit({ type: "agent_end", messages: finalized.messages });
401
- return finalized;
363
+ this.eventBus.emit({ type: "agent_end", messages: result.messages });
364
+ return { ...result, getState: () => this.getState() };
402
365
  }
403
366
  resolveTelemetry(options) {
404
367
  if (this.config.telemetry)
@@ -422,6 +385,12 @@ class AgentRuntime {
422
385
  },
423
386
  };
424
387
  }
388
+ buildSmoothStreamTransformOptions(options) {
389
+ if (options?.smoothStream === false)
390
+ return {};
391
+ const { smoothStream } = (0, lazy_ai_1.loadAi)();
392
+ return { experimental_transform: smoothStream(options?.smoothStream ?? {}) };
393
+ }
425
394
  buildTelemetryOptions(options) {
426
395
  const t = this.resolveTelemetry(options);
427
396
  if (!t?.enabled)
@@ -537,13 +506,12 @@ class AgentRuntime {
537
506
  });
538
507
  }
539
508
  async runGenerateLoop(ctx) {
540
- const { list, options, runId, pendingResume } = ctx;
509
+ const { list, options, abortScope, pendingResume } = ctx;
541
510
  this.hydrateDeferredToolsFromList(list);
542
511
  let totalUsage;
543
512
  let lastFinishReason = 'stop';
544
513
  let structuredOutput;
545
514
  const toolCallSummary = [];
546
- const collectedSubAgentUsage = [];
547
515
  const runTelemetry = this.resolveTelemetry(options);
548
516
  const staticLoopContext = this.buildStaticLoopContext({
549
517
  ...options,
@@ -553,10 +521,12 @@ class AgentRuntime {
553
521
  const pendingToolCtx = {
554
522
  toolMap: pendingLoopContext.toolMap,
555
523
  list,
556
- runId,
524
+ runId: this.runId,
557
525
  persistence: options?.persistence,
558
526
  telemetry: runTelemetry,
559
527
  executionCounter: options?.executionCounter,
528
+ abortSignal: abortScope.signal,
529
+ isAborted: () => abortScope.isAborted,
560
530
  };
561
531
  const maxIterations = options?.maxIterations ?? MAX_LOOP_ITERATIONS;
562
532
  let iterationCount = options?.iterationCount ?? 0;
@@ -568,11 +538,9 @@ class AgentRuntime {
568
538
  });
569
539
  for (const r of batch.results) {
570
540
  toolCallSummary.push(r.toolEntry);
571
- if (r.subAgentUsage)
572
- collectedSubAgentUsage.push(...r.subAgentUsage);
573
541
  }
574
542
  if (Object.keys(batch.pending).length > 0) {
575
- const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId, maxIterations, iterationCount);
543
+ const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, maxIterations, iterationCount);
576
544
  return {
577
545
  runId: suspendRunId,
578
546
  messages: list.responseDelta(),
@@ -586,21 +554,24 @@ class AgentRuntime {
586
554
  suspendPayload: s.payload,
587
555
  resumeSchema: s.resumeSchema,
588
556
  })),
557
+ getState: () => this.getState(),
589
558
  };
590
559
  }
591
560
  }
592
561
  const { generateText } = (0, lazy_ai_1.loadAi)();
593
562
  for (; iterationCount < maxIterations; iterationCount++) {
594
- if (this.eventBus.isAborted) {
563
+ if (abortScope.isAborted) {
595
564
  this.updateState({ status: 'cancelled' });
596
565
  throw new Error('Agent run was aborted');
597
566
  }
598
567
  this.eventBus.emit({ type: "turn_start" });
599
568
  const { toolMap, aiTools, hasTools, effectiveInstructions } = this.buildToolLoopContext(staticLoopContext.aiProviderTools, options?.persistence, options?.executionCounter);
569
+ const { system, messages } = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
600
570
  const result = await generateText({
601
571
  model: staticLoopContext.model,
602
- messages: list.forLlm(effectiveInstructions, this.config.instructionProviderOptions),
603
- abortSignal: this.eventBus.signal,
572
+ system,
573
+ messages,
574
+ abortSignal: abortScope.signal,
604
575
  ...(hasTools ? { tools: aiTools } : {}),
605
576
  ...(staticLoopContext.providerOptions
606
577
  ? { providerOptions: staticLoopContext.providerOptions }
@@ -626,19 +597,19 @@ class AgentRuntime {
626
597
  const batch = await this.iterateToolCallsConcurrent({
627
598
  toolMap,
628
599
  list,
629
- runId,
600
+ runId: this.runId,
630
601
  persistence: options?.persistence,
631
602
  telemetry: runTelemetry,
632
603
  executionCounter: options?.executionCounter,
604
+ abortSignal: abortScope.signal,
605
+ isAborted: () => abortScope.isAborted,
633
606
  toolCalls: result.toolCalls,
634
607
  });
635
608
  for (const r of batch.results) {
636
609
  toolCallSummary.push(r.toolEntry);
637
- if (r.subAgentUsage)
638
- collectedSubAgentUsage.push(...r.subAgentUsage);
639
610
  }
640
611
  if (Object.keys(batch.pending).length > 0) {
641
- const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId, maxIterations, iterationCount + 1);
612
+ const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, maxIterations, iterationCount + 1);
642
613
  return {
643
614
  runId: suspendRunId,
644
615
  messages: list.responseDelta(),
@@ -652,6 +623,7 @@ class AgentRuntime {
652
623
  suspendPayload: s.payload,
653
624
  resumeSchema: s.resumeSchema,
654
625
  })),
626
+ getState: () => this.getState(),
655
627
  };
656
628
  }
657
629
  this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(list.responseDelta()));
@@ -677,35 +649,68 @@ class AgentRuntime {
677
649
  }
678
650
  }
679
651
  return {
680
- runId: runId ?? '',
652
+ runId: this.runId,
681
653
  messages: list.responseDelta(),
682
654
  finishReason: lastFinishReason,
683
655
  usage: totalUsage,
684
656
  ...(structuredOutput !== undefined && { structuredOutput }),
685
657
  ...(toolCallSummary.length > 0 && { toolCalls: toolCallSummary }),
686
- ...(collectedSubAgentUsage.length > 0 && { subAgentUsage: collectedSubAgentUsage }),
658
+ getState: () => this.getState(),
687
659
  };
688
660
  }
689
661
  startStreamLoop(ctx) {
690
- const { options, runId } = ctx;
662
+ const { options } = ctx;
691
663
  const { readable, writable } = new TransformStream();
692
664
  const writer = writable.getWriter();
665
+ const writeEventChunk = (chunk) => {
666
+ writer.write(chunk).catch(() => { });
667
+ };
693
668
  const onToolExecutionStart = (data) => {
694
669
  if (data.type !== "tool_execution_start")
695
670
  return;
696
- writer
697
- .write({
671
+ writeEventChunk({
698
672
  type: 'tool-execution-start',
699
673
  toolCallId: data.toolCallId,
700
674
  toolName: data.toolName,
701
- })
702
- .catch(() => { });
675
+ startTime: Date.now(),
676
+ });
677
+ };
678
+ const onToolExecutionEnd = (data) => {
679
+ if (data.type !== "tool_execution_end")
680
+ return;
681
+ writeEventChunk({
682
+ type: 'tool-execution-end',
683
+ toolCallId: data.toolCallId,
684
+ toolName: data.toolName,
685
+ isError: data.isError,
686
+ endTime: Date.now(),
687
+ });
688
+ };
689
+ const onSubAgentStarted = (data) => {
690
+ if (data.type !== "subagent_started")
691
+ return;
692
+ const { type: _type, ...payload } = data;
693
+ writeEventChunk({ type: 'subagent-started', ...payload });
694
+ };
695
+ const onSubAgentCompleted = (data) => {
696
+ if (data.type !== "subagent_completed")
697
+ return;
698
+ const { type: _type, ...payload } = data;
699
+ writeEventChunk({ type: 'subagent-completed', ...payload });
703
700
  };
704
701
  this.eventBus.on("tool_execution_start", onToolExecutionStart);
705
- this.withTelemetryRootSpan('stream', options, runId, async () => await this.runStreamLoop({ ...ctx, writer }))
702
+ this.eventBus.on("tool_execution_end", onToolExecutionEnd);
703
+ this.eventBus.on("subagent_started", onSubAgentStarted);
704
+ this.eventBus.on("subagent_completed", onSubAgentCompleted);
705
+ this.withTelemetryRootSpan('stream', options, this.runId, async () => await this.runStreamLoop({ ...ctx, writer }))
706
706
  .catch(async (error) => {
707
707
  await this.flushTelemetry(options);
708
- await this.cleanupRun(runId);
708
+ await this.cleanupRun();
709
+ const isAbort = ctx.abortScope.isAborted;
710
+ this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
711
+ if (!isAbort) {
712
+ this.eventBus.emit({ type: "error", message: String(error), error });
713
+ }
709
714
  try {
710
715
  await writer.write({ type: 'error', error });
711
716
  await writer.write({ type: 'finish', finishReason: 'error' });
@@ -715,13 +720,18 @@ class AgentRuntime {
715
720
  writer.abort(error).catch(() => { });
716
721
  }
717
722
  })
718
- .finally(() => {
723
+ .finally(async () => {
724
+ await writer.close().catch(() => { });
719
725
  this.eventBus.off("tool_execution_start", onToolExecutionStart);
726
+ this.eventBus.off("tool_execution_end", onToolExecutionEnd);
727
+ this.eventBus.off("subagent_started", onSubAgentStarted);
728
+ this.eventBus.off("subagent_completed", onSubAgentCompleted);
729
+ ctx.abortScope.dispose();
720
730
  });
721
731
  return readable;
722
732
  }
723
733
  async runStreamLoop(ctx) {
724
- const { list, options, runId, pendingResume, writer } = ctx;
734
+ const { list, options, abortScope, pendingResume, writer } = ctx;
725
735
  this.hydrateDeferredToolsFromList(list);
726
736
  const writeChunk = async (chunk) => {
727
737
  await writer.write(chunk);
@@ -729,20 +739,19 @@ class AgentRuntime {
729
739
  let totalUsage;
730
740
  let lastFinishReason = 'stop';
731
741
  let structuredOutput;
732
- const collectedSubAgentUsage = [];
733
742
  const maxIterations = options?.maxIterations ?? MAX_LOOP_ITERATIONS;
734
743
  let iterationCount = options?.iterationCount ?? 0;
735
744
  let reachedStopCondition = false;
736
745
  const { streamText } = (0, lazy_ai_1.loadAi)();
737
746
  const closeStreamWithError = async (error, status) => {
738
- await this.cleanupRun(runId);
747
+ await this.cleanupRun();
739
748
  this.updateState({ status });
740
749
  await writer.write({ type: 'error', error });
741
750
  await writer.write({ type: 'finish', finishReason: 'error' });
742
751
  await writer.close();
743
752
  };
744
753
  const handleAbort = async () => {
745
- if (!this.eventBus.isAborted)
754
+ if (!abortScope.isAborted)
746
755
  return false;
747
756
  await closeStreamWithError(new Error('Agent run was aborted'), 'cancelled');
748
757
  return true;
@@ -756,10 +765,12 @@ class AgentRuntime {
756
765
  const pendingToolCtx = {
757
766
  toolMap: pendingLoopContext.toolMap,
758
767
  list,
759
- runId,
768
+ runId: this.runId,
760
769
  persistence: options?.persistence,
761
770
  telemetry: runTelemetry,
762
771
  executionCounter: options?.executionCounter,
772
+ abortSignal: abortScope.signal,
773
+ isAborted: () => abortScope.isAborted,
763
774
  };
764
775
  if (pendingResume) {
765
776
  try {
@@ -768,13 +779,12 @@ class AgentRuntime {
768
779
  pendingResume,
769
780
  });
770
781
  for (const r of batch.results) {
771
- if (r.subAgentUsage)
772
- collectedSubAgentUsage.push(...r.subAgentUsage);
773
782
  await writer.write({
774
783
  type: 'tool-result',
775
784
  toolCallId: r.toolCallId,
776
785
  toolName: r.toolName,
777
786
  output: r.modelOutput,
787
+ ...(r.toolEntry.canceled ? { canceled: true } : {}),
778
788
  });
779
789
  if (r.customMessage) {
780
790
  await writer.write({ type: 'message', message: r.customMessage });
@@ -790,7 +800,7 @@ class AgentRuntime {
790
800
  });
791
801
  }
792
802
  if (Object.keys(batch.pending).length > 0) {
793
- const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId, maxIterations, iterationCount);
803
+ const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, maxIterations, iterationCount);
794
804
  for (const s of batch.suspensions) {
795
805
  await writer.write({
796
806
  type: 'tool-call-suspended',
@@ -818,25 +828,45 @@ class AgentRuntime {
818
828
  return;
819
829
  this.eventBus.emit({ type: "turn_start" });
820
830
  const { toolMap, aiTools, hasTools, effectiveInstructions } = this.buildToolLoopContext(staticLoopContext.aiProviderTools, options?.persistence, options?.executionCounter);
821
- const messages = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
831
+ const { system, messages } = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
822
832
  const result = streamText({
823
833
  model: staticLoopContext.model,
834
+ system,
824
835
  messages,
825
- abortSignal: this.eventBus.signal,
836
+ abortSignal: abortScope.signal,
826
837
  ...(hasTools ? { tools: aiTools } : {}),
827
838
  ...(staticLoopContext.providerOptions
828
839
  ? { providerOptions: staticLoopContext.providerOptions }
829
840
  : {}),
830
841
  ...(staticLoopContext.outputSpec ? { output: staticLoopContext.outputSpec } : {}),
831
842
  ...this.buildAiSdkOptions(toolMap, options),
843
+ ...this.buildSmoothStreamTransformOptions(options),
832
844
  });
833
845
  try {
834
846
  for await (const chunk of result.fullStream) {
835
847
  if (chunk.type === 'finish')
836
848
  continue;
849
+ if ((chunk.type === 'tool-result' || chunk.type === 'tool-error') &&
850
+ chunk.providerExecuted) {
851
+ await writeChunk({
852
+ type: 'tool-execution-end',
853
+ toolCallId: chunk.toolCallId,
854
+ toolName: chunk.toolName ?? '',
855
+ isError: chunk.type === 'tool-error',
856
+ endTime: Date.now(),
857
+ });
858
+ }
837
859
  const converted = (0, stream_1.convertChunk)(chunk);
838
860
  if (converted)
839
861
  await writeChunk(converted);
862
+ if (chunk.type === 'tool-call' && chunk.providerExecuted) {
863
+ await writeChunk({
864
+ type: 'tool-execution-start',
865
+ toolCallId: chunk.toolCallId,
866
+ toolName: chunk.toolName ?? '',
867
+ startTime: Date.now(),
868
+ });
869
+ }
840
870
  }
841
871
  }
842
872
  catch (streamError) {
@@ -874,22 +904,23 @@ class AgentRuntime {
874
904
  const batch = await this.iterateToolCallsConcurrent({
875
905
  toolMap,
876
906
  list,
877
- runId,
907
+ runId: this.runId,
878
908
  persistence: options?.persistence,
879
909
  telemetry: runTelemetry,
880
910
  executionCounter: options?.executionCounter,
911
+ abortSignal: abortScope.signal,
912
+ isAborted: () => abortScope.isAborted,
881
913
  toolCalls,
882
914
  });
883
915
  if (await handleAbort())
884
916
  return;
885
917
  for (const r of batch.results) {
886
- if (r.subAgentUsage)
887
- collectedSubAgentUsage.push(...r.subAgentUsage);
888
918
  await writer.write({
889
919
  type: 'tool-result',
890
920
  toolCallId: r.toolCallId,
891
921
  toolName: r.toolName,
892
922
  output: r.modelOutput,
923
+ ...(r.toolEntry.canceled ? { canceled: true } : {}),
893
924
  });
894
925
  if (r.customMessage) {
895
926
  await writer.write({ type: 'message', message: r.customMessage });
@@ -905,7 +936,7 @@ class AgentRuntime {
905
936
  });
906
937
  }
907
938
  if (Object.keys(batch.pending).length > 0) {
908
- const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId, maxIterations, iterationCount + 1);
939
+ const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, maxIterations, iterationCount + 1);
909
940
  for (const s of batch.suspensions) {
910
941
  await writer.write({
911
942
  type: 'tool-call-suspended',
@@ -932,45 +963,35 @@ class AgentRuntime {
932
963
  if (!reachedStopCondition && iterationCount >= maxIterations) {
933
964
  lastFinishReason = 'max-iterations';
934
965
  }
966
+ await this.saveToMemory(list, options);
967
+ if (this.config.titleGeneration && options?.persistence && this.config.memory) {
968
+ const titlePromise = (0, title_generation_1.generateThreadTitle)({
969
+ memory: this.config.memory,
970
+ threadId: options.persistence.threadId,
971
+ resourceId: options.persistence.resourceId,
972
+ titleConfig: this.config.titleGeneration,
973
+ agentModel: this.config.model,
974
+ turnDelta: list.turnDelta(),
975
+ executionCounter: options.executionCounter,
976
+ });
977
+ this.backgroundTasks.track(titlePromise);
978
+ if (this.config.titleGeneration.sync) {
979
+ await titlePromise;
980
+ }
981
+ }
982
+ await this.cleanupRun();
983
+ await this.flushTelemetry(options);
935
984
  const costUsage = this.applyCost(totalUsage);
936
- const parentCost = costUsage?.cost ?? 0;
937
- const subCost = collectedSubAgentUsage.reduce((sum, s) => sum + (s.usage.cost ?? 0), 0);
938
985
  await writer.write({
939
986
  type: 'finish',
940
987
  finishReason: lastFinishReason,
941
988
  ...(costUsage && { usage: costUsage }),
942
989
  model: this.modelIdString,
943
990
  ...(structuredOutput !== undefined && { structuredOutput }),
944
- ...(collectedSubAgentUsage.length > 0 && {
945
- subAgentUsage: collectedSubAgentUsage,
946
- totalCost: parentCost + subCost,
947
- }),
948
991
  });
949
- try {
950
- await this.saveToMemory(list, options);
951
- if (this.config.titleGeneration && options?.persistence && this.config.memory) {
952
- const titlePromise = (0, title_generation_1.generateThreadTitle)({
953
- memory: this.config.memory,
954
- threadId: options.persistence.threadId,
955
- resourceId: options.persistence.resourceId,
956
- titleConfig: this.config.titleGeneration,
957
- agentModel: this.config.model,
958
- turnDelta: list.turnDelta(),
959
- executionCounter: options.executionCounter,
960
- });
961
- this.backgroundTasks.track(titlePromise);
962
- if (this.config.titleGeneration.sync) {
963
- await titlePromise;
964
- }
965
- }
966
- await this.cleanupRun(runId);
967
- await this.flushTelemetry(options);
968
- this.updateState({ status: 'success', messageList: list.serialize() });
969
- this.eventBus.emit({ type: "agent_end", messages: list.responseDelta() });
970
- }
971
- finally {
972
- await writer.close();
973
- }
992
+ this.updateState({ status: 'success', messageList: list.serialize() });
993
+ this.eventBus.emit({ type: "agent_end", messages: list.responseDelta() });
994
+ await writer.close();
974
995
  }
975
996
  async saveToMemory(list, options) {
976
997
  if (!this.config.memory || !options?.persistence)
@@ -979,9 +1000,6 @@ class AgentRuntime {
979
1000
  if (delta.length === 0)
980
1001
  return;
981
1002
  await (0, memory_store_1.saveMessagesToThread)(this.config.memory, options.persistence.threadId, options.persistence.resourceId, delta);
982
- if (this.config.semanticRecall?.embedder && this.config.memory.saveEmbeddings) {
983
- await this.saveEmbeddingsForMessages(options.persistence.threadId, options.persistence.resourceId, delta, options.executionCounter);
984
- }
985
1003
  const observationTasks = this.scheduleObservationLogJobs(options.persistence, options.executionCounter);
986
1004
  this.scheduleEpisodicMemoryJob(options.persistence, observationTasks, options.executionCounter);
987
1005
  }
@@ -1119,43 +1137,6 @@ class AgentRuntime {
1119
1137
  observationScopeId: persistence.threadId,
1120
1138
  };
1121
1139
  }
1122
- async saveEmbeddingsForMessages(threadId, resourceId, messages, executionCounter) {
1123
- const embeddable = [];
1124
- for (const msg of messages) {
1125
- if (!(0, message_1.isLlmMessage)(msg) || (msg.role !== 'user' && msg.role !== 'assistant'))
1126
- continue;
1127
- const text = msg.content
1128
- .filter((c) => c.type === 'text')
1129
- .map((c) => c.text)
1130
- .join('\n');
1131
- if (!text)
1132
- continue;
1133
- embeddable.push({ id: msg.id, text });
1134
- }
1135
- if (embeddable.length === 0)
1136
- return;
1137
- const embedder = this.config.semanticRecall?.embedder;
1138
- if (!embedder)
1139
- return;
1140
- const { embedMany } = getAiSdk();
1141
- const embeddingModel = (0, model_factory_1.createEmbeddingModel)(embedder, this.config.semanticRecall?.apiKey);
1142
- const { embeddings, usage } = await embedMany({
1143
- model: embeddingModel,
1144
- values: embeddable.map((e) => e.text),
1145
- });
1146
- (0, execution_counter_1.incrementTokenCountFromUsage)(executionCounter, usage);
1147
- await this.config.memory.saveEmbeddings({
1148
- scope: this.config.semanticRecall?.scope ?? 'resource',
1149
- threadId,
1150
- resourceId,
1151
- entries: embeddable.map((e, i) => ({
1152
- id: e.id,
1153
- vector: embeddings[i],
1154
- text: e.text,
1155
- model: embedder,
1156
- })),
1157
- });
1158
- }
1159
1140
  buildThinkingProviderOptions() {
1160
1141
  if (!this.config.thinking)
1161
1142
  return undefined;
@@ -1215,8 +1196,39 @@ class AgentRuntime {
1215
1196
  }
1216
1197
  return merged;
1217
1198
  }
1199
+ isDelegateSubAgentCall(toolName) {
1200
+ return toolName === delegate_sub_agent_tool_1.DELEGATE_SUB_AGENT_TOOL_NAME;
1201
+ }
1202
+ getToolCallBatchSize(toolName, toolMap) {
1203
+ if (!this.isDelegateSubAgentCall(toolName))
1204
+ return this.concurrency;
1205
+ const tool = toolMap.get(toolName);
1206
+ const delegateOptions = tool ? (0, delegate_sub_agent_tool_1.getInlineDelegateSubAgentToolOptions)(tool) : undefined;
1207
+ return delegateOptions?.policy?.maxChildren ?? sub_agent_task_path_1.DEFAULT_SUB_AGENT_MAX_CHILDREN;
1208
+ }
1209
+ takeNextToolCallBatch(calls, start, toolMap) {
1210
+ const first = calls[start];
1211
+ if (!first) {
1212
+ throw new Error('Unable to build tool-call batch');
1213
+ }
1214
+ const isDelegateBatch = this.isDelegateSubAgentCall(first.toolName);
1215
+ const batchSize = this.getToolCallBatchSize(first.toolName, toolMap);
1216
+ if (batchSize < 1 ||
1217
+ Number.isNaN(batchSize) ||
1218
+ (isDelegateBatch && !Number.isFinite(batchSize))) {
1219
+ throw new Error(`Invalid tool-call batch size for ${first.toolName}: ${batchSize}`);
1220
+ }
1221
+ const batch = [];
1222
+ for (let i = start; i < calls.length && batch.length < batchSize; i++) {
1223
+ const candidate = calls[i];
1224
+ if (this.isDelegateSubAgentCall(candidate.toolName) !== isDelegateBatch)
1225
+ break;
1226
+ batch.push(candidate);
1227
+ }
1228
+ return batch;
1229
+ }
1218
1230
  async iterateToolCallsConcurrent(ctx) {
1219
- const { toolCalls, toolMap, list, runId, telemetry: resolvedTelemetry, executionCounter } = ctx;
1231
+ const { toolCalls, toolMap, list, runId, telemetry: resolvedTelemetry, executionCounter, abortSignal, } = ctx;
1220
1232
  const executableCalls = toolCalls.filter((tc) => !tc.providerExecuted);
1221
1233
  const providerExecutedCount = toolCalls.length - executableCalls.length;
1222
1234
  for (let i = 0; i < providerExecutedCount; i++) {
@@ -1224,18 +1236,18 @@ class AgentRuntime {
1224
1236
  }
1225
1237
  const executableCallsById = new Map(executableCalls.map((tc) => [tc.toolCallId, tc]));
1226
1238
  const unexecutedIds = new Set(executableCalls.map((tc) => tc.toolCallId));
1227
- const batchSize = this.concurrency;
1228
1239
  const results = [];
1229
1240
  const suspensions = [];
1230
1241
  const errors = [];
1231
1242
  const pending = {};
1232
- for (let batchStart = 0; batchStart < executableCalls.length; batchStart += batchSize) {
1233
- if (this.eventBus.isAborted) {
1243
+ for (let batchStart = 0; batchStart < executableCalls.length;) {
1244
+ if (ctx.isAborted()) {
1234
1245
  this.updateState({ status: 'cancelled' });
1235
1246
  throw new Error('Agent run was aborted');
1236
1247
  }
1237
- const batch = executableCalls.slice(batchStart, batchStart + batchSize);
1238
- const settledResults = await Promise.allSettled(batch.map(async (tc) => await this.processToolCall(tc.toolCallId, tc.toolName, tc.input, toolMap, list, runId, ctx.persistence, undefined, resolvedTelemetry, executionCounter, true)));
1248
+ const batch = this.takeNextToolCallBatch(executableCalls, batchStart, toolMap);
1249
+ batchStart += batch.length;
1250
+ const settledResults = await Promise.allSettled(batch.map(async (tc) => await this.processToolCall(tc.toolCallId, tc.toolName, tc.input, toolMap, list, runId, ctx.persistence, undefined, resolvedTelemetry, executionCounter, abortSignal, true)));
1239
1251
  for (const tc of batch) {
1240
1252
  unexecutedIds.delete(tc.toolCallId);
1241
1253
  }
@@ -1279,7 +1291,6 @@ class AgentRuntime {
1279
1291
  input: toolInput,
1280
1292
  toolEntry: result.value.toolEntry,
1281
1293
  modelOutput: result.value.modelOutput,
1282
- subAgentUsage: result.value.subAgentUsage,
1283
1294
  customMessage: result.value.customMessage,
1284
1295
  });
1285
1296
  }
@@ -1310,7 +1321,7 @@ class AgentRuntime {
1310
1321
  return { results, suspensions, errors, pending };
1311
1322
  }
1312
1323
  async iteratePendingToolCallsConcurrent(ctx) {
1313
- const { pendingResume, toolMap, list, runId, persistence, telemetry: resolvedTelemetry, executionCounter, } = ctx;
1324
+ const { pendingResume, toolMap, list, runId, persistence, telemetry: resolvedTelemetry, executionCounter, abortSignal, } = ctx;
1314
1325
  const resumedId = pendingResume.resumeToolCallId;
1315
1326
  const resumedEntry = pendingResume.pendingToolCalls[resumedId];
1316
1327
  if (!resumedEntry) {
@@ -1321,7 +1332,14 @@ class AgentRuntime {
1321
1332
  const suspensions = [];
1322
1333
  const errors = [];
1323
1334
  const pending = {};
1324
- const processResult = await this.processToolCall(resumedEntry.toolCallId, resumedToolName, resumedEntry.input, toolMap, list, runId, persistence, pendingResume.resumeData, resolvedTelemetry, executionCounter, false);
1335
+ let processResult;
1336
+ try {
1337
+ processResult = await this.processToolCall(resumedEntry.toolCallId, resumedToolName, resumedEntry.input, toolMap, list, runId, persistence, pendingResume.resumeData, resolvedTelemetry, executionCounter, abortSignal, false);
1338
+ }
1339
+ catch (error) {
1340
+ processResult = { outcome: 'error', error };
1341
+ list.setToolCallError(resumedEntry.toolCallId, error);
1342
+ }
1325
1343
  if (processResult.outcome === 'suspended') {
1326
1344
  pending[resumedId] = {
1327
1345
  ...resumedEntry,
@@ -1345,10 +1363,44 @@ class AgentRuntime {
1345
1363
  input: resumedEntry.input,
1346
1364
  toolEntry: processResult.toolEntry,
1347
1365
  modelOutput: processResult.modelOutput,
1348
- subAgentUsage: processResult.subAgentUsage,
1349
1366
  customMessage: processResult.customMessage,
1350
1367
  });
1351
1368
  }
1369
+ else if (processResult.outcome === 'cancelled') {
1370
+ results.push({
1371
+ toolCallId: resumedEntry.toolCallId,
1372
+ toolName: resumedToolName,
1373
+ input: resumedEntry.input,
1374
+ toolEntry: processResult.toolEntry,
1375
+ modelOutput: processResult.modelOutput,
1376
+ });
1377
+ list.addInput([
1378
+ { role: 'user', content: [{ type: 'text', text: processResult.userMessage }] },
1379
+ ]);
1380
+ for (const id of Object.keys(pendingResume.pendingToolCalls)) {
1381
+ if (id !== resumedId) {
1382
+ const siblingEntry = pendingResume.pendingToolCalls[id];
1383
+ const modelOutput = '[Skipped: a sibling tool call was cancelled]';
1384
+ list.setToolCallResult(id, modelOutput, {
1385
+ canceled: true,
1386
+ });
1387
+ results.push({
1388
+ toolCallId: siblingEntry.toolCallId,
1389
+ toolName: siblingEntry.toolName,
1390
+ input: siblingEntry.input,
1391
+ toolEntry: {
1392
+ tool: siblingEntry.toolName,
1393
+ input: siblingEntry.input,
1394
+ output: modelOutput,
1395
+ transformed: false,
1396
+ canceled: true,
1397
+ },
1398
+ modelOutput,
1399
+ });
1400
+ }
1401
+ }
1402
+ return { results, suspensions, errors, pending };
1403
+ }
1352
1404
  else if (processResult.outcome === 'error') {
1353
1405
  errors.push({
1354
1406
  toolCallId: resumedEntry.toolCallId,
@@ -1391,6 +1443,8 @@ class AgentRuntime {
1391
1443
  persistence,
1392
1444
  telemetry: resolvedTelemetry,
1393
1445
  executionCounter,
1446
+ abortSignal,
1447
+ isAborted: ctx.isAborted,
1394
1448
  });
1395
1449
  results.push(...batch.results);
1396
1450
  suspensions.push(...batch.suspensions);
@@ -1399,14 +1453,8 @@ class AgentRuntime {
1399
1453
  }
1400
1454
  return { results, suspensions, errors, pending };
1401
1455
  }
1402
- async processToolCall(toolCallId, toolName, toolInput, toolMap, list, runId, persistence, resumeData, resolvedTelemetry, executionCounter, countToolCall = true) {
1456
+ async processToolCall(toolCallId, toolName, toolInput, toolMap, list, runId, persistence, resumeData, resolvedTelemetry, executionCounter, abortSignal, countToolCall = true) {
1403
1457
  const builtTool = toolMap.get(toolName);
1404
- this.eventBus.emit({
1405
- type: "tool_execution_start",
1406
- toolCallId,
1407
- toolName,
1408
- args: toolInput,
1409
- });
1410
1458
  const makeToolError = (error) => {
1411
1459
  this.eventBus.emit({
1412
1460
  type: "tool_execution_end",
@@ -1442,6 +1490,30 @@ class AgentRuntime {
1442
1490
  });
1443
1491
  return { outcome: 'noop' };
1444
1492
  }
1493
+ if ((0, cancellation_1.isCancellation)(resumeData) && !builtTool.handleCancellation) {
1494
+ const modelOutput = `[Tool call cancelled. User said: "${resumeData.message}"]`;
1495
+ this.eventBus.emit({
1496
+ type: "tool_execution_end",
1497
+ toolCallId,
1498
+ toolName,
1499
+ result: modelOutput,
1500
+ isError: false,
1501
+ });
1502
+ list.setToolCallResult(toolCallId, modelOutput, { canceled: true });
1503
+ return {
1504
+ outcome: 'cancelled',
1505
+ toolEntry: {
1506
+ tool: toolName,
1507
+ input: toolInput,
1508
+ output: modelOutput,
1509
+ transformed: false,
1510
+ canceled: true,
1511
+ },
1512
+ modelOutput,
1513
+ userMessage: resumeData.message,
1514
+ canceled: true,
1515
+ };
1516
+ }
1445
1517
  if (countToolCall) {
1446
1518
  this.incrementToolCallCount(executionCounter);
1447
1519
  }
@@ -1452,11 +1524,22 @@ class AgentRuntime {
1452
1524
  }
1453
1525
  toolInput = result.data;
1454
1526
  }
1527
+ if (shouldEmitToolExecutionStart(builtTool, resumeData)) {
1528
+ this.eventBus.emit({
1529
+ type: "tool_execution_start",
1530
+ toolCallId,
1531
+ toolName,
1532
+ args: toolInput,
1533
+ });
1534
+ }
1455
1535
  let toolResult;
1456
1536
  try {
1457
1537
  toolResult = await this.withTelemetryToolSpan(toolCallId, toolName, toolInput, resolvedTelemetry, async () => await (0, tool_adapter_1.executeTool)(toolInput, builtTool, resumeData, resolvedTelemetry, toolCallId, {
1458
1538
  runId,
1459
1539
  persistence,
1540
+ emitEvent: (event) => this.eventBus.emit(event),
1541
+ abortSignal,
1542
+ executionCounter,
1460
1543
  }));
1461
1544
  }
1462
1545
  catch (error) {
@@ -1474,9 +1557,7 @@ class AgentRuntime {
1474
1557
  const error = new Error(`Tool ${toolName} has no resume schema`);
1475
1558
  return makeToolError(error);
1476
1559
  }
1477
- const resumeSchema = (0, zod_1.isZodSchema)(builtTool.resumeSchema)
1478
- ? (0, zod_to_json_schema_1.zodToJsonSchema)(builtTool.resumeSchema)
1479
- : builtTool.resumeSchema;
1560
+ const resumeSchema = getToolResumeJsonSchema(builtTool);
1480
1561
  if (!resumeSchema) {
1481
1562
  return makeToolError(new Error('Invalid resume schema'));
1482
1563
  }
@@ -1486,24 +1567,22 @@ class AgentRuntime {
1486
1567
  resumeSchema,
1487
1568
  };
1488
1569
  }
1489
- let actualResult = toolResult;
1490
- let extractedSubAgentUsage;
1491
- if ((0, tool_adapter_1.isAgentToolResult)(toolResult)) {
1492
- actualResult = toolResult.output;
1493
- extractedSubAgentUsage = toolResult.subAgentUsage;
1570
+ let modelResult;
1571
+ try {
1572
+ modelResult = builtTool.toModelOutput ? builtTool.toModelOutput(toolResult) : toolResult;
1573
+ }
1574
+ catch (error) {
1575
+ return makeToolError(error);
1494
1576
  }
1495
1577
  this.eventBus.emit({
1496
1578
  type: "tool_execution_end",
1497
1579
  toolCallId,
1498
1580
  toolName,
1499
- result: actualResult,
1581
+ result: toolResult,
1500
1582
  isError: false,
1501
1583
  });
1502
- const modelResult = builtTool.toModelOutput
1503
- ? builtTool.toModelOutput(actualResult)
1504
- : actualResult;
1505
1584
  list.setToolCallResult(toolCallId, (0, json_value_1.toJsonValue)(modelResult));
1506
- const customMessage = builtTool?.toMessage?.(actualResult);
1585
+ const customMessage = builtTool?.toMessage?.(toolResult);
1507
1586
  if (customMessage) {
1508
1587
  list.addResponse([customMessage]);
1509
1588
  }
@@ -1512,27 +1591,43 @@ class AgentRuntime {
1512
1591
  toolEntry: {
1513
1592
  tool: toolName,
1514
1593
  input: toolInput,
1515
- output: actualResult,
1594
+ output: toolResult,
1516
1595
  transformed: !!builtTool.toModelOutput,
1517
1596
  },
1518
1597
  modelOutput: modelResult,
1519
- subAgentUsage: extractedSubAgentUsage,
1520
1598
  customMessage,
1521
1599
  };
1522
1600
  }
1523
1601
  buildStaticLoopContext(execOptions) {
1524
- const { Output } = getAiSdk();
1602
+ const { Output, jsonSchema } = getAiSdk();
1525
1603
  const aiProviderTools = (0, tool_adapter_1.toAiSdkProviderTools)(this.config.providerTools);
1526
1604
  const model = (0, model_factory_1.createModel)(this.config.model);
1605
+ const outputSchema = this.config.structuredOutput;
1606
+ const isRawJsonSchemaOutput = outputSchema !== undefined && !(0, zod_1.isZodSchema)(outputSchema);
1607
+ const providerOptions = this.relaxStrictJsonSchemaIfNeeded(this.buildCallProviderOptions(execOptions?.providerOptions), isRawJsonSchemaOutput);
1608
+ const outputSpec = outputSchema
1609
+ ? Output.object({
1610
+ schema: (0, zod_1.isZodSchema)(outputSchema)
1611
+ ? outputSchema
1612
+ : jsonSchema((0, json_schema_1.lockAdditionalProperties)(outputSchema)),
1613
+ })
1614
+ : undefined;
1527
1615
  return {
1528
1616
  model,
1529
1617
  aiProviderTools,
1530
- providerOptions: this.buildCallProviderOptions(execOptions?.providerOptions),
1531
- outputSpec: this.config.structuredOutput
1532
- ? Output.object({ schema: this.config.structuredOutput })
1533
- : undefined,
1618
+ providerOptions,
1619
+ outputSpec,
1534
1620
  };
1535
1621
  }
1622
+ relaxStrictJsonSchemaIfNeeded(providerOptions, isRawJsonSchemaOutput) {
1623
+ if (!isRawJsonSchemaOutput)
1624
+ return providerOptions;
1625
+ const result = { ...providerOptions };
1626
+ for (const provider of ['openai', 'groq']) {
1627
+ result[provider] = { strictJsonSchema: false, ...result[provider] };
1628
+ }
1629
+ return result;
1630
+ }
1536
1631
  buildToolLoopContext(aiProviderTools, persistence, executionCounter) {
1537
1632
  const allUserTools = this.getCurrentTools(persistence, executionCounter);
1538
1633
  const aiTools = (0, tool_adapter_1.toAiSdkTools)(allUserTools);
@@ -1592,8 +1687,7 @@ class AgentRuntime {
1592
1687
  const block = `<built_in_rules>\n${fragments.map((f) => `- ${f}`).join('\n')}\n</built_in_rules>`;
1593
1688
  return userInstructions ? `${block}\n\n${userInstructions}` : block;
1594
1689
  }
1595
- async persistSuspension(pendingToolCalls, options, list, totalUsage, existingRunId, maxIterations, iterationCount) {
1596
- const runId = existingRunId ?? (0, run_state_1.generateRunId)();
1690
+ async persistSuspension(pendingToolCalls, options, list, totalUsage, maxIterations, iterationCount) {
1597
1691
  const resolvedMaxIterations = maxIterations ?? options?.maxIterations;
1598
1692
  const resolvedIterationCount = iterationCount ?? options?.iterationCount;
1599
1693
  const executionOptions = resolvedMaxIterations !== undefined ? { maxIterations: resolvedMaxIterations } : undefined;
@@ -1606,14 +1700,12 @@ class AgentRuntime {
1606
1700
  executionOptions,
1607
1701
  ...(resolvedIterationCount !== undefined ? { iterationCount: resolvedIterationCount } : {}),
1608
1702
  };
1609
- await this.runState.suspend(runId, state);
1703
+ await this.runState.suspend(this.runId, state);
1610
1704
  this.updateState({ status: 'suspended', pendingToolCalls, messageList: list.serialize() });
1611
- return runId;
1705
+ return this.runId;
1612
1706
  }
1613
- async cleanupRun(runId) {
1614
- if (runId) {
1615
- await this.runState.complete(runId);
1616
- }
1707
+ async cleanupRun() {
1708
+ await this.runState.complete(this.runId);
1617
1709
  }
1618
1710
  emitTurnEnd(newMessages, toolResults) {
1619
1711
  const assistantMsg = newMessages.find((m) => 'role' in m && m.role === 'assistant');