@n8n/agents 0.10.1 → 0.11.1

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 (127) hide show
  1. package/dist/build.tsbuildinfo +1 -1
  2. package/dist/index.d.ts +8 -2
  3. package/dist/index.js +27 -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 +320 -243
  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 +1 -2
  15. package/dist/runtime/mcp-connection.js +1 -4
  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 +3 -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 +20 -4
  53. package/dist/sdk/catalog.js.map +1 -1
  54. package/dist/sdk/mcp-client.d.ts +1 -1
  55. package/dist/sdk/mcp-client.js +2 -2
  56. package/dist/sdk/mcp-client.js.map +1 -1
  57. package/dist/sdk/memory.d.ts +1 -3
  58. package/dist/sdk/memory.js +0 -14
  59. package/dist/sdk/memory.js.map +1 -1
  60. package/dist/sdk/provider-capabilities.js +1 -0
  61. package/dist/sdk/provider-capabilities.js.map +1 -1
  62. package/dist/sdk/tool.d.ts +3 -0
  63. package/dist/sdk/tool.js +42 -10
  64. package/dist/sdk/tool.js.map +1 -1
  65. package/dist/storage/base-memory.d.ts +0 -31
  66. package/dist/storage/base-memory.js +0 -9
  67. package/dist/storage/base-memory.js.map +1 -1
  68. package/dist/types/index.d.ts +2 -2
  69. package/dist/types/index.js.map +1 -1
  70. package/dist/types/runtime/event.d.ts +31 -1
  71. package/dist/types/runtime/event.js +2 -0
  72. package/dist/types/runtime/event.js.map +1 -1
  73. package/dist/types/sdk/agent-builder.d.ts +0 -1
  74. package/dist/types/sdk/agent.d.ts +20 -15
  75. package/dist/types/sdk/memory.d.ts +0 -42
  76. package/dist/types/sdk/message.d.ts +1 -0
  77. package/dist/types/sdk/tool.d.ts +19 -1
  78. package/dist/utils/json-schema.d.ts +3 -0
  79. package/dist/utils/json-schema.js +72 -0
  80. package/dist/utils/json-schema.js.map +1 -0
  81. package/dist/workspace/filesystem/daytona-filesystem.d.ts +25 -0
  82. package/dist/workspace/filesystem/daytona-filesystem.js +130 -0
  83. package/dist/workspace/filesystem/daytona-filesystem.js.map +1 -0
  84. package/dist/workspace/filesystem/n8n-sandbox-filesystem.d.ts +25 -0
  85. package/dist/workspace/filesystem/n8n-sandbox-filesystem.js +126 -0
  86. package/dist/workspace/filesystem/n8n-sandbox-filesystem.js.map +1 -0
  87. package/dist/workspace/sandbox/create-workspace.d.ts +4 -0
  88. package/dist/workspace/sandbox/create-workspace.js +58 -0
  89. package/dist/workspace/sandbox/create-workspace.js.map +1 -0
  90. package/dist/workspace/sandbox/daytona-auth-manager.d.ts +26 -0
  91. package/dist/workspace/sandbox/daytona-auth-manager.js +84 -0
  92. package/dist/workspace/sandbox/daytona-auth-manager.js.map +1 -0
  93. package/dist/workspace/sandbox/daytona-sandbox.d.ts +72 -0
  94. package/dist/workspace/sandbox/daytona-sandbox.js +357 -0
  95. package/dist/workspace/sandbox/daytona-sandbox.js.map +1 -0
  96. package/dist/workspace/sandbox/index.d.ts +7 -0
  97. package/dist/workspace/sandbox/index.js +22 -0
  98. package/dist/workspace/sandbox/index.js.map +1 -0
  99. package/dist/workspace/sandbox/lazy-daytona.d.ts +2 -0
  100. package/dist/workspace/sandbox/lazy-daytona.js +12 -0
  101. package/dist/workspace/sandbox/lazy-daytona.js.map +1 -0
  102. package/dist/workspace/sandbox/logger.d.ts +12 -0
  103. package/dist/workspace/sandbox/logger.js +3 -0
  104. package/dist/workspace/sandbox/logger.js.map +1 -0
  105. package/dist/workspace/sandbox/n8n-sandbox-sandbox.d.ts +35 -0
  106. package/dist/workspace/sandbox/n8n-sandbox-sandbox.js +138 -0
  107. package/dist/workspace/sandbox/n8n-sandbox-sandbox.js.map +1 -0
  108. package/dist/workspace/sandbox/provider.d.ts +4 -0
  109. package/dist/workspace/sandbox/provider.js +14 -0
  110. package/dist/workspace/sandbox/provider.js.map +1 -0
  111. package/dist/workspace/sandbox/run-in-sandbox.d.ts +26 -0
  112. package/dist/workspace/sandbox/run-in-sandbox.js +19 -0
  113. package/dist/workspace/sandbox/run-in-sandbox.js.map +1 -0
  114. package/dist/workspace/sandbox/types.d.ts +41 -0
  115. package/dist/workspace/sandbox/types.js +3 -0
  116. package/dist/workspace/sandbox/types.js.map +1 -0
  117. package/dist/workspace/sandbox/workspace-root.d.ts +16 -0
  118. package/dist/workspace/sandbox/workspace-root.js +69 -0
  119. package/dist/workspace/sandbox/workspace-root.js.map +1 -0
  120. package/dist/workspace/tools/batch-str-replace-file.js +2 -2
  121. package/dist/workspace/tools/batch-str-replace-file.js.map +1 -1
  122. package/dist/workspace/tools/str-replace-file.js +2 -2
  123. package/dist/workspace/tools/str-replace-file.js.map +1 -1
  124. package/package.json +29 -17
  125. package/dist/sdk/network.d.ts +0 -20
  126. package/dist/sdk/network.js +0 -46
  127. 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,63 @@ 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
709
  try {
710
710
  await writer.write({ type: 'error', error });
711
711
  await writer.write({ type: 'finish', finishReason: 'error' });
@@ -717,11 +717,15 @@ class AgentRuntime {
717
717
  })
718
718
  .finally(() => {
719
719
  this.eventBus.off("tool_execution_start", onToolExecutionStart);
720
+ this.eventBus.off("tool_execution_end", onToolExecutionEnd);
721
+ this.eventBus.off("subagent_started", onSubAgentStarted);
722
+ this.eventBus.off("subagent_completed", onSubAgentCompleted);
723
+ ctx.abortScope.dispose();
720
724
  });
721
725
  return readable;
722
726
  }
723
727
  async runStreamLoop(ctx) {
724
- const { list, options, runId, pendingResume, writer } = ctx;
728
+ const { list, options, abortScope, pendingResume, writer } = ctx;
725
729
  this.hydrateDeferredToolsFromList(list);
726
730
  const writeChunk = async (chunk) => {
727
731
  await writer.write(chunk);
@@ -729,20 +733,19 @@ class AgentRuntime {
729
733
  let totalUsage;
730
734
  let lastFinishReason = 'stop';
731
735
  let structuredOutput;
732
- const collectedSubAgentUsage = [];
733
736
  const maxIterations = options?.maxIterations ?? MAX_LOOP_ITERATIONS;
734
737
  let iterationCount = options?.iterationCount ?? 0;
735
738
  let reachedStopCondition = false;
736
739
  const { streamText } = (0, lazy_ai_1.loadAi)();
737
740
  const closeStreamWithError = async (error, status) => {
738
- await this.cleanupRun(runId);
741
+ await this.cleanupRun();
739
742
  this.updateState({ status });
740
743
  await writer.write({ type: 'error', error });
741
744
  await writer.write({ type: 'finish', finishReason: 'error' });
742
745
  await writer.close();
743
746
  };
744
747
  const handleAbort = async () => {
745
- if (!this.eventBus.isAborted)
748
+ if (!abortScope.isAborted)
746
749
  return false;
747
750
  await closeStreamWithError(new Error('Agent run was aborted'), 'cancelled');
748
751
  return true;
@@ -756,10 +759,12 @@ class AgentRuntime {
756
759
  const pendingToolCtx = {
757
760
  toolMap: pendingLoopContext.toolMap,
758
761
  list,
759
- runId,
762
+ runId: this.runId,
760
763
  persistence: options?.persistence,
761
764
  telemetry: runTelemetry,
762
765
  executionCounter: options?.executionCounter,
766
+ abortSignal: abortScope.signal,
767
+ isAborted: () => abortScope.isAborted,
763
768
  };
764
769
  if (pendingResume) {
765
770
  try {
@@ -768,13 +773,12 @@ class AgentRuntime {
768
773
  pendingResume,
769
774
  });
770
775
  for (const r of batch.results) {
771
- if (r.subAgentUsage)
772
- collectedSubAgentUsage.push(...r.subAgentUsage);
773
776
  await writer.write({
774
777
  type: 'tool-result',
775
778
  toolCallId: r.toolCallId,
776
779
  toolName: r.toolName,
777
780
  output: r.modelOutput,
781
+ ...(r.toolEntry.canceled ? { canceled: true } : {}),
778
782
  });
779
783
  if (r.customMessage) {
780
784
  await writer.write({ type: 'message', message: r.customMessage });
@@ -790,7 +794,7 @@ class AgentRuntime {
790
794
  });
791
795
  }
792
796
  if (Object.keys(batch.pending).length > 0) {
793
- const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId, maxIterations, iterationCount);
797
+ const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, maxIterations, iterationCount);
794
798
  for (const s of batch.suspensions) {
795
799
  await writer.write({
796
800
  type: 'tool-call-suspended',
@@ -818,25 +822,45 @@ class AgentRuntime {
818
822
  return;
819
823
  this.eventBus.emit({ type: "turn_start" });
820
824
  const { toolMap, aiTools, hasTools, effectiveInstructions } = this.buildToolLoopContext(staticLoopContext.aiProviderTools, options?.persistence, options?.executionCounter);
821
- const messages = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
825
+ const { system, messages } = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
822
826
  const result = streamText({
823
827
  model: staticLoopContext.model,
828
+ system,
824
829
  messages,
825
- abortSignal: this.eventBus.signal,
830
+ abortSignal: abortScope.signal,
826
831
  ...(hasTools ? { tools: aiTools } : {}),
827
832
  ...(staticLoopContext.providerOptions
828
833
  ? { providerOptions: staticLoopContext.providerOptions }
829
834
  : {}),
830
835
  ...(staticLoopContext.outputSpec ? { output: staticLoopContext.outputSpec } : {}),
831
836
  ...this.buildAiSdkOptions(toolMap, options),
837
+ ...this.buildSmoothStreamTransformOptions(options),
832
838
  });
833
839
  try {
834
840
  for await (const chunk of result.fullStream) {
835
841
  if (chunk.type === 'finish')
836
842
  continue;
843
+ if ((chunk.type === 'tool-result' || chunk.type === 'tool-error') &&
844
+ chunk.providerExecuted) {
845
+ await writeChunk({
846
+ type: 'tool-execution-end',
847
+ toolCallId: chunk.toolCallId,
848
+ toolName: chunk.toolName ?? '',
849
+ isError: chunk.type === 'tool-error',
850
+ endTime: Date.now(),
851
+ });
852
+ }
837
853
  const converted = (0, stream_1.convertChunk)(chunk);
838
854
  if (converted)
839
855
  await writeChunk(converted);
856
+ if (chunk.type === 'tool-call' && chunk.providerExecuted) {
857
+ await writeChunk({
858
+ type: 'tool-execution-start',
859
+ toolCallId: chunk.toolCallId,
860
+ toolName: chunk.toolName ?? '',
861
+ startTime: Date.now(),
862
+ });
863
+ }
840
864
  }
841
865
  }
842
866
  catch (streamError) {
@@ -874,22 +898,23 @@ class AgentRuntime {
874
898
  const batch = await this.iterateToolCallsConcurrent({
875
899
  toolMap,
876
900
  list,
877
- runId,
901
+ runId: this.runId,
878
902
  persistence: options?.persistence,
879
903
  telemetry: runTelemetry,
880
904
  executionCounter: options?.executionCounter,
905
+ abortSignal: abortScope.signal,
906
+ isAborted: () => abortScope.isAborted,
881
907
  toolCalls,
882
908
  });
883
909
  if (await handleAbort())
884
910
  return;
885
911
  for (const r of batch.results) {
886
- if (r.subAgentUsage)
887
- collectedSubAgentUsage.push(...r.subAgentUsage);
888
912
  await writer.write({
889
913
  type: 'tool-result',
890
914
  toolCallId: r.toolCallId,
891
915
  toolName: r.toolName,
892
916
  output: r.modelOutput,
917
+ ...(r.toolEntry.canceled ? { canceled: true } : {}),
893
918
  });
894
919
  if (r.customMessage) {
895
920
  await writer.write({ type: 'message', message: r.customMessage });
@@ -905,7 +930,7 @@ class AgentRuntime {
905
930
  });
906
931
  }
907
932
  if (Object.keys(batch.pending).length > 0) {
908
- const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId, maxIterations, iterationCount + 1);
933
+ const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, maxIterations, iterationCount + 1);
909
934
  for (const s of batch.suspensions) {
910
935
  await writer.write({
911
936
  type: 'tool-call-suspended',
@@ -933,18 +958,12 @@ class AgentRuntime {
933
958
  lastFinishReason = 'max-iterations';
934
959
  }
935
960
  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
961
  await writer.write({
939
962
  type: 'finish',
940
963
  finishReason: lastFinishReason,
941
964
  ...(costUsage && { usage: costUsage }),
942
965
  model: this.modelIdString,
943
966
  ...(structuredOutput !== undefined && { structuredOutput }),
944
- ...(collectedSubAgentUsage.length > 0 && {
945
- subAgentUsage: collectedSubAgentUsage,
946
- totalCost: parentCost + subCost,
947
- }),
948
967
  });
949
968
  try {
950
969
  await this.saveToMemory(list, options);
@@ -963,7 +982,7 @@ class AgentRuntime {
963
982
  await titlePromise;
964
983
  }
965
984
  }
966
- await this.cleanupRun(runId);
985
+ await this.cleanupRun();
967
986
  await this.flushTelemetry(options);
968
987
  this.updateState({ status: 'success', messageList: list.serialize() });
969
988
  this.eventBus.emit({ type: "agent_end", messages: list.responseDelta() });
@@ -979,9 +998,6 @@ class AgentRuntime {
979
998
  if (delta.length === 0)
980
999
  return;
981
1000
  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
1001
  const observationTasks = this.scheduleObservationLogJobs(options.persistence, options.executionCounter);
986
1002
  this.scheduleEpisodicMemoryJob(options.persistence, observationTasks, options.executionCounter);
987
1003
  }
@@ -1119,43 +1135,6 @@ class AgentRuntime {
1119
1135
  observationScopeId: persistence.threadId,
1120
1136
  };
1121
1137
  }
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
1138
  buildThinkingProviderOptions() {
1160
1139
  if (!this.config.thinking)
1161
1140
  return undefined;
@@ -1215,8 +1194,39 @@ class AgentRuntime {
1215
1194
  }
1216
1195
  return merged;
1217
1196
  }
1197
+ isDelegateSubAgentCall(toolName) {
1198
+ return toolName === delegate_sub_agent_tool_1.DELEGATE_SUB_AGENT_TOOL_NAME;
1199
+ }
1200
+ getToolCallBatchSize(toolName, toolMap) {
1201
+ if (!this.isDelegateSubAgentCall(toolName))
1202
+ return this.concurrency;
1203
+ const tool = toolMap.get(toolName);
1204
+ const delegateOptions = tool ? (0, delegate_sub_agent_tool_1.getInlineDelegateSubAgentToolOptions)(tool) : undefined;
1205
+ return delegateOptions?.policy?.maxChildren ?? sub_agent_task_path_1.DEFAULT_SUB_AGENT_MAX_CHILDREN;
1206
+ }
1207
+ takeNextToolCallBatch(calls, start, toolMap) {
1208
+ const first = calls[start];
1209
+ if (!first) {
1210
+ throw new Error('Unable to build tool-call batch');
1211
+ }
1212
+ const isDelegateBatch = this.isDelegateSubAgentCall(first.toolName);
1213
+ const batchSize = this.getToolCallBatchSize(first.toolName, toolMap);
1214
+ if (batchSize < 1 ||
1215
+ Number.isNaN(batchSize) ||
1216
+ (isDelegateBatch && !Number.isFinite(batchSize))) {
1217
+ throw new Error(`Invalid tool-call batch size for ${first.toolName}: ${batchSize}`);
1218
+ }
1219
+ const batch = [];
1220
+ for (let i = start; i < calls.length && batch.length < batchSize; i++) {
1221
+ const candidate = calls[i];
1222
+ if (this.isDelegateSubAgentCall(candidate.toolName) !== isDelegateBatch)
1223
+ break;
1224
+ batch.push(candidate);
1225
+ }
1226
+ return batch;
1227
+ }
1218
1228
  async iterateToolCallsConcurrent(ctx) {
1219
- const { toolCalls, toolMap, list, runId, telemetry: resolvedTelemetry, executionCounter } = ctx;
1229
+ const { toolCalls, toolMap, list, runId, telemetry: resolvedTelemetry, executionCounter, abortSignal, } = ctx;
1220
1230
  const executableCalls = toolCalls.filter((tc) => !tc.providerExecuted);
1221
1231
  const providerExecutedCount = toolCalls.length - executableCalls.length;
1222
1232
  for (let i = 0; i < providerExecutedCount; i++) {
@@ -1224,18 +1234,18 @@ class AgentRuntime {
1224
1234
  }
1225
1235
  const executableCallsById = new Map(executableCalls.map((tc) => [tc.toolCallId, tc]));
1226
1236
  const unexecutedIds = new Set(executableCalls.map((tc) => tc.toolCallId));
1227
- const batchSize = this.concurrency;
1228
1237
  const results = [];
1229
1238
  const suspensions = [];
1230
1239
  const errors = [];
1231
1240
  const pending = {};
1232
- for (let batchStart = 0; batchStart < executableCalls.length; batchStart += batchSize) {
1233
- if (this.eventBus.isAborted) {
1241
+ for (let batchStart = 0; batchStart < executableCalls.length;) {
1242
+ if (ctx.isAborted()) {
1234
1243
  this.updateState({ status: 'cancelled' });
1235
1244
  throw new Error('Agent run was aborted');
1236
1245
  }
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)));
1246
+ const batch = this.takeNextToolCallBatch(executableCalls, batchStart, toolMap);
1247
+ batchStart += batch.length;
1248
+ 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
1249
  for (const tc of batch) {
1240
1250
  unexecutedIds.delete(tc.toolCallId);
1241
1251
  }
@@ -1279,7 +1289,6 @@ class AgentRuntime {
1279
1289
  input: toolInput,
1280
1290
  toolEntry: result.value.toolEntry,
1281
1291
  modelOutput: result.value.modelOutput,
1282
- subAgentUsage: result.value.subAgentUsage,
1283
1292
  customMessage: result.value.customMessage,
1284
1293
  });
1285
1294
  }
@@ -1310,7 +1319,7 @@ class AgentRuntime {
1310
1319
  return { results, suspensions, errors, pending };
1311
1320
  }
1312
1321
  async iteratePendingToolCallsConcurrent(ctx) {
1313
- const { pendingResume, toolMap, list, runId, persistence, telemetry: resolvedTelemetry, executionCounter, } = ctx;
1322
+ const { pendingResume, toolMap, list, runId, persistence, telemetry: resolvedTelemetry, executionCounter, abortSignal, } = ctx;
1314
1323
  const resumedId = pendingResume.resumeToolCallId;
1315
1324
  const resumedEntry = pendingResume.pendingToolCalls[resumedId];
1316
1325
  if (!resumedEntry) {
@@ -1321,7 +1330,7 @@ class AgentRuntime {
1321
1330
  const suspensions = [];
1322
1331
  const errors = [];
1323
1332
  const pending = {};
1324
- const processResult = await this.processToolCall(resumedEntry.toolCallId, resumedToolName, resumedEntry.input, toolMap, list, runId, persistence, pendingResume.resumeData, resolvedTelemetry, executionCounter, false);
1333
+ const processResult = await this.processToolCall(resumedEntry.toolCallId, resumedToolName, resumedEntry.input, toolMap, list, runId, persistence, pendingResume.resumeData, resolvedTelemetry, executionCounter, abortSignal, false);
1325
1334
  if (processResult.outcome === 'suspended') {
1326
1335
  pending[resumedId] = {
1327
1336
  ...resumedEntry,
@@ -1345,10 +1354,44 @@ class AgentRuntime {
1345
1354
  input: resumedEntry.input,
1346
1355
  toolEntry: processResult.toolEntry,
1347
1356
  modelOutput: processResult.modelOutput,
1348
- subAgentUsage: processResult.subAgentUsage,
1349
1357
  customMessage: processResult.customMessage,
1350
1358
  });
1351
1359
  }
1360
+ else if (processResult.outcome === 'cancelled') {
1361
+ results.push({
1362
+ toolCallId: resumedEntry.toolCallId,
1363
+ toolName: resumedToolName,
1364
+ input: resumedEntry.input,
1365
+ toolEntry: processResult.toolEntry,
1366
+ modelOutput: processResult.modelOutput,
1367
+ });
1368
+ list.addInput([
1369
+ { role: 'user', content: [{ type: 'text', text: processResult.userMessage }] },
1370
+ ]);
1371
+ for (const id of Object.keys(pendingResume.pendingToolCalls)) {
1372
+ if (id !== resumedId) {
1373
+ const siblingEntry = pendingResume.pendingToolCalls[id];
1374
+ const modelOutput = '[Skipped: a sibling tool call was cancelled]';
1375
+ list.setToolCallResult(id, modelOutput, {
1376
+ canceled: true,
1377
+ });
1378
+ results.push({
1379
+ toolCallId: siblingEntry.toolCallId,
1380
+ toolName: siblingEntry.toolName,
1381
+ input: siblingEntry.input,
1382
+ toolEntry: {
1383
+ tool: siblingEntry.toolName,
1384
+ input: siblingEntry.input,
1385
+ output: modelOutput,
1386
+ transformed: false,
1387
+ canceled: true,
1388
+ },
1389
+ modelOutput,
1390
+ });
1391
+ }
1392
+ }
1393
+ return { results, suspensions, errors, pending };
1394
+ }
1352
1395
  else if (processResult.outcome === 'error') {
1353
1396
  errors.push({
1354
1397
  toolCallId: resumedEntry.toolCallId,
@@ -1391,6 +1434,8 @@ class AgentRuntime {
1391
1434
  persistence,
1392
1435
  telemetry: resolvedTelemetry,
1393
1436
  executionCounter,
1437
+ abortSignal,
1438
+ isAborted: ctx.isAborted,
1394
1439
  });
1395
1440
  results.push(...batch.results);
1396
1441
  suspensions.push(...batch.suspensions);
@@ -1399,14 +1444,8 @@ class AgentRuntime {
1399
1444
  }
1400
1445
  return { results, suspensions, errors, pending };
1401
1446
  }
1402
- async processToolCall(toolCallId, toolName, toolInput, toolMap, list, runId, persistence, resumeData, resolvedTelemetry, executionCounter, countToolCall = true) {
1447
+ async processToolCall(toolCallId, toolName, toolInput, toolMap, list, runId, persistence, resumeData, resolvedTelemetry, executionCounter, abortSignal, countToolCall = true) {
1403
1448
  const builtTool = toolMap.get(toolName);
1404
- this.eventBus.emit({
1405
- type: "tool_execution_start",
1406
- toolCallId,
1407
- toolName,
1408
- args: toolInput,
1409
- });
1410
1449
  const makeToolError = (error) => {
1411
1450
  this.eventBus.emit({
1412
1451
  type: "tool_execution_end",
@@ -1442,6 +1481,30 @@ class AgentRuntime {
1442
1481
  });
1443
1482
  return { outcome: 'noop' };
1444
1483
  }
1484
+ if ((0, cancellation_1.isCancellation)(resumeData) && !builtTool.handleCancellation) {
1485
+ const modelOutput = `[Tool call cancelled. User said: "${resumeData.message}"]`;
1486
+ this.eventBus.emit({
1487
+ type: "tool_execution_end",
1488
+ toolCallId,
1489
+ toolName,
1490
+ result: modelOutput,
1491
+ isError: false,
1492
+ });
1493
+ list.setToolCallResult(toolCallId, modelOutput, { canceled: true });
1494
+ return {
1495
+ outcome: 'cancelled',
1496
+ toolEntry: {
1497
+ tool: toolName,
1498
+ input: toolInput,
1499
+ output: modelOutput,
1500
+ transformed: false,
1501
+ canceled: true,
1502
+ },
1503
+ modelOutput,
1504
+ userMessage: resumeData.message,
1505
+ canceled: true,
1506
+ };
1507
+ }
1445
1508
  if (countToolCall) {
1446
1509
  this.incrementToolCallCount(executionCounter);
1447
1510
  }
@@ -1452,11 +1515,22 @@ class AgentRuntime {
1452
1515
  }
1453
1516
  toolInput = result.data;
1454
1517
  }
1518
+ if (shouldEmitToolExecutionStart(builtTool, resumeData)) {
1519
+ this.eventBus.emit({
1520
+ type: "tool_execution_start",
1521
+ toolCallId,
1522
+ toolName,
1523
+ args: toolInput,
1524
+ });
1525
+ }
1455
1526
  let toolResult;
1456
1527
  try {
1457
1528
  toolResult = await this.withTelemetryToolSpan(toolCallId, toolName, toolInput, resolvedTelemetry, async () => await (0, tool_adapter_1.executeTool)(toolInput, builtTool, resumeData, resolvedTelemetry, toolCallId, {
1458
1529
  runId,
1459
1530
  persistence,
1531
+ emitEvent: (event) => this.eventBus.emit(event),
1532
+ abortSignal,
1533
+ executionCounter,
1460
1534
  }));
1461
1535
  }
1462
1536
  catch (error) {
@@ -1474,9 +1548,7 @@ class AgentRuntime {
1474
1548
  const error = new Error(`Tool ${toolName} has no resume schema`);
1475
1549
  return makeToolError(error);
1476
1550
  }
1477
- const resumeSchema = (0, zod_1.isZodSchema)(builtTool.resumeSchema)
1478
- ? (0, zod_to_json_schema_1.zodToJsonSchema)(builtTool.resumeSchema)
1479
- : builtTool.resumeSchema;
1551
+ const resumeSchema = getToolResumeJsonSchema(builtTool);
1480
1552
  if (!resumeSchema) {
1481
1553
  return makeToolError(new Error('Invalid resume schema'));
1482
1554
  }
@@ -1486,24 +1558,16 @@ class AgentRuntime {
1486
1558
  resumeSchema,
1487
1559
  };
1488
1560
  }
1489
- let actualResult = toolResult;
1490
- let extractedSubAgentUsage;
1491
- if ((0, tool_adapter_1.isAgentToolResult)(toolResult)) {
1492
- actualResult = toolResult.output;
1493
- extractedSubAgentUsage = toolResult.subAgentUsage;
1494
- }
1495
1561
  this.eventBus.emit({
1496
1562
  type: "tool_execution_end",
1497
1563
  toolCallId,
1498
1564
  toolName,
1499
- result: actualResult,
1565
+ result: toolResult,
1500
1566
  isError: false,
1501
1567
  });
1502
- const modelResult = builtTool.toModelOutput
1503
- ? builtTool.toModelOutput(actualResult)
1504
- : actualResult;
1568
+ const modelResult = builtTool.toModelOutput ? builtTool.toModelOutput(toolResult) : toolResult;
1505
1569
  list.setToolCallResult(toolCallId, (0, json_value_1.toJsonValue)(modelResult));
1506
- const customMessage = builtTool?.toMessage?.(actualResult);
1570
+ const customMessage = builtTool?.toMessage?.(toolResult);
1507
1571
  if (customMessage) {
1508
1572
  list.addResponse([customMessage]);
1509
1573
  }
@@ -1512,27 +1576,43 @@ class AgentRuntime {
1512
1576
  toolEntry: {
1513
1577
  tool: toolName,
1514
1578
  input: toolInput,
1515
- output: actualResult,
1579
+ output: toolResult,
1516
1580
  transformed: !!builtTool.toModelOutput,
1517
1581
  },
1518
1582
  modelOutput: modelResult,
1519
- subAgentUsage: extractedSubAgentUsage,
1520
1583
  customMessage,
1521
1584
  };
1522
1585
  }
1523
1586
  buildStaticLoopContext(execOptions) {
1524
- const { Output } = getAiSdk();
1587
+ const { Output, jsonSchema } = getAiSdk();
1525
1588
  const aiProviderTools = (0, tool_adapter_1.toAiSdkProviderTools)(this.config.providerTools);
1526
1589
  const model = (0, model_factory_1.createModel)(this.config.model);
1590
+ const outputSchema = this.config.structuredOutput;
1591
+ const isRawJsonSchemaOutput = outputSchema !== undefined && !(0, zod_1.isZodSchema)(outputSchema);
1592
+ const providerOptions = this.relaxStrictJsonSchemaIfNeeded(this.buildCallProviderOptions(execOptions?.providerOptions), isRawJsonSchemaOutput);
1593
+ const outputSpec = outputSchema
1594
+ ? Output.object({
1595
+ schema: (0, zod_1.isZodSchema)(outputSchema)
1596
+ ? outputSchema
1597
+ : jsonSchema((0, json_schema_1.lockAdditionalProperties)(outputSchema)),
1598
+ })
1599
+ : undefined;
1527
1600
  return {
1528
1601
  model,
1529
1602
  aiProviderTools,
1530
- providerOptions: this.buildCallProviderOptions(execOptions?.providerOptions),
1531
- outputSpec: this.config.structuredOutput
1532
- ? Output.object({ schema: this.config.structuredOutput })
1533
- : undefined,
1603
+ providerOptions,
1604
+ outputSpec,
1534
1605
  };
1535
1606
  }
1607
+ relaxStrictJsonSchemaIfNeeded(providerOptions, isRawJsonSchemaOutput) {
1608
+ if (!isRawJsonSchemaOutput)
1609
+ return providerOptions;
1610
+ const result = { ...providerOptions };
1611
+ for (const provider of ['openai', 'groq']) {
1612
+ result[provider] = { strictJsonSchema: false, ...result[provider] };
1613
+ }
1614
+ return result;
1615
+ }
1536
1616
  buildToolLoopContext(aiProviderTools, persistence, executionCounter) {
1537
1617
  const allUserTools = this.getCurrentTools(persistence, executionCounter);
1538
1618
  const aiTools = (0, tool_adapter_1.toAiSdkTools)(allUserTools);
@@ -1592,8 +1672,7 @@ class AgentRuntime {
1592
1672
  const block = `<built_in_rules>\n${fragments.map((f) => `- ${f}`).join('\n')}\n</built_in_rules>`;
1593
1673
  return userInstructions ? `${block}\n\n${userInstructions}` : block;
1594
1674
  }
1595
- async persistSuspension(pendingToolCalls, options, list, totalUsage, existingRunId, maxIterations, iterationCount) {
1596
- const runId = existingRunId ?? (0, run_state_1.generateRunId)();
1675
+ async persistSuspension(pendingToolCalls, options, list, totalUsage, maxIterations, iterationCount) {
1597
1676
  const resolvedMaxIterations = maxIterations ?? options?.maxIterations;
1598
1677
  const resolvedIterationCount = iterationCount ?? options?.iterationCount;
1599
1678
  const executionOptions = resolvedMaxIterations !== undefined ? { maxIterations: resolvedMaxIterations } : undefined;
@@ -1606,14 +1685,12 @@ class AgentRuntime {
1606
1685
  executionOptions,
1607
1686
  ...(resolvedIterationCount !== undefined ? { iterationCount: resolvedIterationCount } : {}),
1608
1687
  };
1609
- await this.runState.suspend(runId, state);
1688
+ await this.runState.suspend(this.runId, state);
1610
1689
  this.updateState({ status: 'suspended', pendingToolCalls, messageList: list.serialize() });
1611
- return runId;
1690
+ return this.runId;
1612
1691
  }
1613
- async cleanupRun(runId) {
1614
- if (runId) {
1615
- await this.runState.complete(runId);
1616
- }
1692
+ async cleanupRun() {
1693
+ await this.runState.complete(this.runId);
1617
1694
  }
1618
1695
  emitTurnEnd(newMessages, toolResults) {
1619
1696
  const assistantMsg = newMessages.find((m) => 'role' in m && m.role === 'assistant');