@node-llm/core 1.16.2 → 1.17.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 (42) hide show
  1. package/dist/agent/Agent.d.ts +3 -0
  2. package/dist/agent/Agent.d.ts.map +1 -1
  3. package/dist/agent/Agent.js +3 -0
  4. package/dist/aliases.d.ts +33 -9
  5. package/dist/aliases.d.ts.map +1 -1
  6. package/dist/aliases.js +34 -10
  7. package/dist/chat/Chat.d.ts +16 -5
  8. package/dist/chat/Chat.d.ts.map +1 -1
  9. package/dist/chat/Chat.js +113 -78
  10. package/dist/chat/ChatOptions.d.ts +19 -0
  11. package/dist/chat/ChatOptions.d.ts.map +1 -1
  12. package/dist/chat/ChatStream.d.ts.map +1 -1
  13. package/dist/chat/ChatStream.js +69 -58
  14. package/dist/chat/ToolCallOutcome.d.ts +30 -0
  15. package/dist/chat/ToolCallOutcome.d.ts.map +1 -0
  16. package/dist/chat/ToolCallOutcome.js +43 -0
  17. package/dist/config.d.ts +11 -0
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +9 -1
  20. package/dist/constants.d.ts +1 -0
  21. package/dist/constants.d.ts.map +1 -1
  22. package/dist/constants.js +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/llm.d.ts +1 -1
  26. package/dist/llm.d.ts.map +1 -1
  27. package/dist/models/models.json +1464 -1014
  28. package/dist/providers/bedrock/BedrockProvider.js +1 -1
  29. package/dist/providers/bedrock/Chat.js +1 -1
  30. package/dist/providers/bedrock/Embeddings.js +1 -1
  31. package/dist/providers/bedrock/Image.js +1 -1
  32. package/dist/providers/bedrock/Moderation.js +1 -1
  33. package/dist/providers/bedrock/Streaming.js +1 -1
  34. package/dist/providers/bedrock/config.d.ts +9 -1
  35. package/dist/providers/bedrock/config.d.ts.map +1 -1
  36. package/dist/providers/bedrock/config.js +4 -2
  37. package/dist/providers/bedrock/index.d.ts.map +1 -1
  38. package/dist/providers/bedrock/index.js +1 -0
  39. package/dist/utils/handler-stacking.d.ts +31 -0
  40. package/dist/utils/handler-stacking.d.ts.map +1 -0
  41. package/dist/utils/handler-stacking.js +66 -0
  42. package/package.json +13 -10
package/dist/chat/Chat.js CHANGED
@@ -12,8 +12,10 @@ import { ToolExecutionMode } from "../constants.js";
12
12
  import { ConfigurationError } from "../errors/index.js";
13
13
  import { ChatValidator } from "./Validation.js";
14
14
  import { ToolHandler } from "./ToolHandler.js";
15
+ import { shouldRunToolCallsConcurrently, executeToolCallOutcomes } from "./ToolCallOutcome.js";
15
16
  import { logger } from "../utils/logger.js";
16
17
  import { runMiddleware } from "../utils/middleware-runner.js";
18
+ import { collectHandlers, runAllHandlers, runDirectiveHandlers, runConfirmHandlers, runChainHandlers } from "../utils/handler-stacking.js";
17
19
  import { ChatResponseString } from "./ChatResponse.js";
18
20
  export class Chat {
19
21
  provider;
@@ -45,6 +47,9 @@ export class Chat {
45
47
  if (!this.options.toolExecution) {
46
48
  this.options.toolExecution = config.toolExecution || ToolExecutionMode.AUTO;
47
49
  }
50
+ if (this.options.toolConcurrency === undefined) {
51
+ this.options.toolConcurrency = config.toolConcurrency;
52
+ }
48
53
  if (options.tools) {
49
54
  const toolList = options.tools;
50
55
  this.options.tools = []; // Clear and re-add via normalized method
@@ -285,11 +290,11 @@ export class Chat {
285
290
  }
286
291
  // --- Event Handlers ---
287
292
  onNewMessage(handler) {
288
- this.options.onNewMessage = handler;
293
+ this.options.onNewMessageHandlers = [...(this.options.onNewMessageHandlers ?? []), handler];
289
294
  return this;
290
295
  }
291
296
  onEndMessage(handler) {
292
- this.options.onEndMessage = handler;
297
+ this.options.onEndMessageHandlers = [...(this.options.onEndMessageHandlers ?? []), handler];
293
298
  return this;
294
299
  }
295
300
  onToolCall(handler) {
@@ -299,21 +304,29 @@ export class Chat {
299
304
  return this.onToolCallEnd((_call, result) => handler(result));
300
305
  }
301
306
  /**
302
- * Called when a tool call starts.
307
+ * Called when a tool call starts. Calling this more than once registers
308
+ * additional handlers rather than replacing the previous one.
303
309
  */
304
310
  onToolCallStart(handler) {
305
- this.options.onToolCallStart = handler;
311
+ this.options.onToolCallStartHandlers = [
312
+ ...(this.options.onToolCallStartHandlers ?? []),
313
+ handler
314
+ ];
306
315
  return this;
307
316
  }
308
317
  /**
309
- * Called when a tool call ends successfully.
318
+ * Called when a tool call ends successfully. Calling this more than once
319
+ * registers additional handlers rather than replacing the previous one.
310
320
  */
311
321
  onToolCallEnd(handler) {
312
- this.options.onToolCallEnd = handler;
322
+ this.options.onToolCallEndHandlers = [...(this.options.onToolCallEndHandlers ?? []), handler];
313
323
  return this;
314
324
  }
315
325
  onToolCallError(handler) {
316
- this.options.onToolCallError = handler;
326
+ this.options.onToolCallErrorHandlers = [
327
+ ...(this.options.onToolCallErrorHandlers ?? []),
328
+ handler
329
+ ];
317
330
  return this;
318
331
  }
319
332
  /**
@@ -326,28 +339,49 @@ export class Chat {
326
339
  this.options.toolExecution = mode;
327
340
  return this;
328
341
  }
342
+ /**
343
+ * Enable or disable concurrent execution of independent tool calls
344
+ * returned in the same turn. Disabled ("confirm" mode always runs
345
+ * sequentially since approval is interactive).
346
+ */
347
+ withToolConcurrency(enabled) {
348
+ this.options.toolConcurrency = enabled;
349
+ return this;
350
+ }
329
351
  /**
330
352
  * Hook for confirming tool execution in "confirm" mode.
331
- * Return true to proceed, false to cancel the specific call.
353
+ * Return true to proceed, false to cancel the specific call. When multiple
354
+ * handlers are registered, every one of them must approve.
332
355
  */
333
356
  onConfirmToolCall(handler) {
334
- this.options.onConfirmToolCall = handler;
357
+ this.options.onConfirmToolCallHandlers = [
358
+ ...(this.options.onConfirmToolCallHandlers ?? []),
359
+ handler
360
+ ];
335
361
  return this;
336
362
  }
337
363
  /**
338
364
  * Add a hook to process messages before sending to the LLM.
339
- * Useful for PII detection, redaction, and input moderation.
365
+ * Useful for PII detection, redaction, and input moderation. Multiple
366
+ * hooks chain: each one's output feeds the next.
340
367
  */
341
368
  beforeRequest(handler) {
342
- this.options.onBeforeRequest = handler;
369
+ this.options.onBeforeRequestHandlers = [
370
+ ...(this.options.onBeforeRequestHandlers ?? []),
371
+ handler
372
+ ];
343
373
  return this;
344
374
  }
345
375
  /**
346
376
  * Add a hook to process the response before returning it.
347
- * Useful for output redaction, compliance, and post-moderation.
377
+ * Useful for output redaction, compliance, and post-moderation. Multiple
378
+ * hooks chain: each one's output feeds the next.
348
379
  */
349
380
  afterResponse(handler) {
350
- this.options.onAfterResponse = handler;
381
+ this.options.onAfterResponseHandlers = [
382
+ ...(this.options.onAfterResponseHandlers ?? []),
383
+ handler
384
+ ];
351
385
  return this;
352
386
  }
353
387
  /**
@@ -398,6 +432,23 @@ export class Chat {
398
432
  options: this.options,
399
433
  state
400
434
  };
435
+ const newMessageHandlers = collectHandlers(this.options.onNewMessage, this.options.onNewMessageHandlers);
436
+ const endMessageHandlers = collectHandlers(this.options.onEndMessage, this.options.onEndMessageHandlers);
437
+ const beforeRequestHandlers = collectHandlers(this.options.onBeforeRequest, this.options.onBeforeRequestHandlers);
438
+ const afterResponseHandlers = collectHandlers(this.options.onAfterResponse, this.options.onAfterResponseHandlers);
439
+ const toolCallStartHandlers = collectHandlers(this.options.onToolCallStart, this.options.onToolCallStartHandlers);
440
+ const toolCallEndHandlers = collectHandlers(this.options.onToolCallEnd, this.options.onToolCallEndHandlers);
441
+ const toolCallErrorHandlers = collectHandlers(this.options.onToolCallError, this.options.onToolCallErrorHandlers);
442
+ const confirmToolCallHandlers = collectHandlers(this.options.onConfirmToolCall, this.options.onConfirmToolCallHandlers);
443
+ const combinedToolCallStart = toolCallStartHandlers.length
444
+ ? (call) => toolCallStartHandlers.forEach((handler) => handler(call))
445
+ : undefined;
446
+ const combinedToolCallEnd = toolCallEndHandlers.length
447
+ ? (call, result) => toolCallEndHandlers.forEach((handler) => handler(call, result))
448
+ : undefined;
449
+ const combinedConfirmToolCall = confirmToolCallHandlers.length
450
+ ? (call) => runConfirmHandlers(confirmToolCallHandlers, call)
451
+ : undefined;
401
452
  try {
402
453
  // 1. onRequest Hook
403
454
  await runMiddleware(this.middlewares, "onRequest", context);
@@ -426,12 +477,7 @@ export class Chat {
426
477
  ...this.options.params
427
478
  };
428
479
  // --- Content Policy Hooks (Input) ---
429
- if (this.options.onBeforeRequest) {
430
- const result = await this.options.onBeforeRequest(executeOptions.messages);
431
- if (result) {
432
- executeOptions.messages = result;
433
- }
434
- }
480
+ executeOptions.messages = await runChainHandlers(beforeRequestHandlers, executeOptions.messages);
435
481
  const trackUsage = (u) => {
436
482
  if (u) {
437
483
  if (u.cost === undefined) {
@@ -458,18 +504,12 @@ export class Chat {
458
504
  }
459
505
  };
460
506
  // Start generation
461
- if (this.options.onNewMessage)
462
- this.options.onNewMessage();
507
+ await runAllHandlers(newMessageHandlers);
463
508
  let response = await this.executor.executeChat(executeOptions);
464
509
  trackUsage(response.usage);
465
510
  assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.thinking, response.reasoning, response.tool_calls, response.finish_reason, this.options.schema, response.metadata, response.attachments);
466
511
  // --- Content Policy Hooks (Output - Turn 1) ---
467
- if (this.options.onAfterResponse) {
468
- const result = await this.options.onAfterResponse(assistantMessage);
469
- if (result) {
470
- assistantMessage = result;
471
- }
472
- }
512
+ assistantMessage = await runChainHandlers(afterResponseHandlers, assistantMessage);
473
513
  // Add to history (temporary, might be rolled back on retry)
474
514
  this.messages.push({
475
515
  role: "assistant",
@@ -477,9 +517,8 @@ export class Chat {
477
517
  tool_calls: response.tool_calls,
478
518
  usage: response.usage
479
519
  });
480
- if (this.options.onEndMessage &&
481
- (!response.tool_calls || response.tool_calls.length === 0)) {
482
- this.options.onEndMessage(assistantMessage);
520
+ if (!response.tool_calls || response.tool_calls.length === 0) {
521
+ await runAllHandlers(endMessageHandlers, assistantMessage);
483
522
  }
484
523
  const maxToolCalls = options?.maxToolCalls ?? this.options.maxToolCalls ?? 5;
485
524
  let stepCount = 0;
@@ -493,55 +532,56 @@ export class Chat {
493
532
  if (stepCount > maxToolCalls) {
494
533
  throw new Error(`[NodeLLM] Maximum tool execution calls (${maxToolCalls}) exceeded.`);
495
534
  }
496
- for (const toolCall of response.tool_calls) {
497
- if (this.options.toolExecution === ToolExecutionMode.CONFIRM) {
498
- const approved = await ToolHandler.requestToolConfirmation(toolCall, this.options.onConfirmToolCall);
499
- if (!approved) {
500
- this.messages.push(this.provider.formatToolResultMessage(toolCall.id, "Action cancelled by user."));
501
- continue;
502
- }
535
+ const runConcurrently = shouldRunToolCallsConcurrently(this.options.toolConcurrency, this.options.toolExecution, response.tool_calls.length);
536
+ const outcomes = await executeToolCallOutcomes(response.tool_calls, {
537
+ ...this.options,
538
+ tools: this.options.tools,
539
+ onToolCallStart: combinedToolCallStart,
540
+ onToolCallEnd: combinedToolCallEnd,
541
+ onConfirmToolCall: combinedConfirmToolCall
542
+ }, this.middlewares, context, runConcurrently);
543
+ for (const outcome of outcomes) {
544
+ const toolCall = outcome.toolCall;
545
+ if (outcome.kind === "cancelled") {
546
+ this.messages.push(this.provider.formatToolResultMessage(toolCall.id, "Action cancelled by user."));
547
+ continue;
503
548
  }
504
- await runMiddleware(this.middlewares, "onToolCallStart", context, toolCall);
505
- try {
506
- const toolResult = await ToolHandler.execute(toolCall, this.options.tools, this.options.onToolCallStart, this.options.onToolCallEnd);
507
- await runMiddleware(this.middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
549
+ if (outcome.kind === "success") {
550
+ const { toolResult } = outcome;
508
551
  this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
509
552
  if (toolResult.halted) {
510
553
  haltTriggered = true;
511
554
  assistantMessage = new ChatResponseString(toolResult.content, totalUsage, this.model, this.provider.id, undefined, undefined, undefined, "tool_halt");
512
- if (this.options.onEndMessage) {
513
- this.options.onEndMessage(assistantMessage);
514
- }
555
+ await runAllHandlers(endMessageHandlers, assistantMessage);
515
556
  break;
516
557
  }
558
+ continue;
517
559
  }
518
- catch (error) {
519
- let currentError = error;
520
- const middlewareDirective = await runMiddleware(this.middlewares, "onToolCallError", context, toolCall, currentError);
521
- const directive = middlewareDirective ||
522
- (await this.options.onToolCallError?.(toolCall, currentError));
523
- if (directive === "STOP")
524
- throw currentError;
525
- if (directive === "RETRY") {
526
- try {
527
- const toolResult = await ToolHandler.execute(toolCall, this.options.tools, this.options.onToolCallStart, this.options.onToolCallEnd);
528
- await runMiddleware(this.middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
529
- this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
530
- continue;
531
- }
532
- catch (retryError) {
533
- currentError = retryError;
534
- await runMiddleware(this.middlewares, "onToolCallError", context, toolCall, currentError);
535
- }
536
- }
537
- this.messages.push(this.provider.formatToolResultMessage(toolCall.id, `Fatal error executing tool '${toolCall.function.name}': ${currentError.message}`, { isError: true }));
538
- if (directive === "CONTINUE")
560
+ let currentError = outcome.error;
561
+ const middlewareDirective = await runMiddleware(this.middlewares, "onToolCallError", context, toolCall, currentError);
562
+ const directive = middlewareDirective ||
563
+ (await runDirectiveHandlers(toolCallErrorHandlers, toolCall, currentError));
564
+ if (directive === "STOP")
565
+ throw currentError;
566
+ if (directive === "RETRY") {
567
+ try {
568
+ const toolResult = await ToolHandler.execute(toolCall, this.options.tools, combinedToolCallStart, combinedToolCallEnd);
569
+ await runMiddleware(this.middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
570
+ this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
539
571
  continue;
540
- const errorObj = currentError;
541
- if (errorObj.fatal || errorObj.status === 401 || errorObj.status === 403)
542
- throw currentError;
543
- logger.error(`Tool execution failed:`, currentError);
572
+ }
573
+ catch (retryError) {
574
+ currentError = retryError;
575
+ await runMiddleware(this.middlewares, "onToolCallError", context, toolCall, currentError);
576
+ }
544
577
  }
578
+ this.messages.push(this.provider.formatToolResultMessage(toolCall.id, `Fatal error executing tool '${toolCall.function.name}': ${currentError.message}`, { isError: true }));
579
+ if (directive === "CONTINUE")
580
+ continue;
581
+ const errorObj = currentError;
582
+ if (errorObj.fatal || errorObj.status === 401 || errorObj.status === 403)
583
+ throw currentError;
584
+ logger.error(`Tool execution failed:`, currentError);
545
585
  }
546
586
  if (haltTriggered)
547
587
  break;
@@ -562,23 +602,18 @@ export class Chat {
562
602
  });
563
603
  trackUsage(response.usage);
564
604
  assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.thinking, response.reasoning, response.tool_calls, response.finish_reason, this.options.schema, response.metadata, response.attachments);
565
- if (this.options.onAfterResponse) {
566
- const result = await this.options.onAfterResponse(assistantMessage);
567
- if (result)
568
- assistantMessage = result;
569
- }
605
+ assistantMessage = await runChainHandlers(afterResponseHandlers, assistantMessage);
570
606
  this.messages.push({
571
607
  role: "assistant",
572
608
  content: assistantMessage?.toString() || null,
573
609
  tool_calls: response.tool_calls,
574
610
  usage: response.usage
575
611
  });
576
- if (this.options.onEndMessage &&
577
- (!response.tool_calls || response.tool_calls.length === 0)) {
578
- this.options.onEndMessage(assistantMessage);
612
+ if (!response.tool_calls || response.tool_calls.length === 0) {
613
+ await runAllHandlers(endMessageHandlers, assistantMessage);
579
614
  }
580
615
  }
581
- finalResponse = new ChatResponseString(assistantMessage.toString() || "", totalUsage, this.model, this.provider.id, assistantMessage.thinking, assistantMessage.reasoning, response.tool_calls, assistantMessage.finish_reason, this.options.schema);
616
+ finalResponse = new ChatResponseString(assistantMessage.toString() || "", totalUsage, this.model, this.provider.id, assistantMessage.thinking, assistantMessage.reasoning, response.tool_calls, assistantMessage.finish_reason, this.options.schema, assistantMessage.metadata, assistantMessage.attachments);
582
617
  // 5. onResponse Hook
583
618
  const requestDirective = await runMiddleware(this.middlewares, "onResponse", context, finalResponse);
584
619
  if (requestDirective && typeof requestDirective === "object") {
@@ -18,6 +18,17 @@ export interface ChatOptions {
18
18
  onToolCallStart?: (toolCall: unknown) => void;
19
19
  onToolCallEnd?: (toolCall: unknown, result: unknown) => void;
20
20
  onToolCallError?: (toolCall: unknown, error: Error) => "STOP" | "CONTINUE" | "RETRY" | void | Promise<"STOP" | "CONTINUE" | "RETRY" | void>;
21
+ /**
22
+ * Additional handlers registered via the fluent on*() methods. Each on*()
23
+ * call appends here instead of overwriting the single onX field above, so
24
+ * e.g. calling chat.onToolCallStart() twice runs both handlers instead of
25
+ * silently dropping the first.
26
+ */
27
+ onNewMessageHandlers?: Array<() => void>;
28
+ onEndMessageHandlers?: Array<(message: ChatResponseString) => void>;
29
+ onToolCallStartHandlers?: Array<(toolCall: unknown) => void>;
30
+ onToolCallEndHandlers?: Array<(toolCall: unknown, result: unknown) => void>;
31
+ onToolCallErrorHandlers?: Array<(toolCall: unknown, error: Error) => "STOP" | "CONTINUE" | "RETRY" | void | Promise<"STOP" | "CONTINUE" | "RETRY" | void>>;
21
32
  headers?: Record<string, string>;
22
33
  responseFormat?: ResponseFormat;
23
34
  thinking?: ThinkingConfig;
@@ -29,10 +40,18 @@ export interface ChatOptions {
29
40
  maxToolCalls?: number;
30
41
  requestTimeout?: number;
31
42
  toolExecution?: ToolExecutionMode;
43
+ /**
44
+ * When true, independent tool calls returned in the same turn are executed
45
+ * concurrently instead of one at a time. Defaults to `config.toolConcurrency`.
46
+ */
47
+ toolConcurrency?: boolean;
32
48
  toolChoice?: ToolChoice;
33
49
  toolCalls?: "one" | "many" | number;
34
50
  onConfirmToolCall?: (toolCall: unknown) => Promise<boolean> | boolean;
35
51
  onBeforeRequest?: (messages: Message[]) => Promise<Message[] | void>;
36
52
  onAfterResponse?: (response: ChatResponseString) => Promise<ChatResponseString | void>;
53
+ onConfirmToolCallHandlers?: Array<(toolCall: unknown) => Promise<boolean> | boolean>;
54
+ onBeforeRequestHandlers?: Array<(messages: Message[]) => Promise<Message[] | void>>;
55
+ onAfterResponseHandlers?: Array<(response: ChatResponseString) => Promise<ChatResponseString | void>>;
37
56
  }
38
57
  //# sourceMappingURL=ChatOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatOptions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtF,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7D,eAAe,CAAC,EAAE,CAChB,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,KAAK,KACT,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACtE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CACxF"}
1
+ {"version":3,"file":"ChatOptions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatOptions.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChatStream.d.ts","sourceRoot":"","sources":["../../src/chat/ChatStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,WAAW,EAIZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAyB,MAAM,0BAA0B,CAAC;AAEtF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAavC;;;GAGG;AACH,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,cAAc,CAAY;gBAGf,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EAC1C,QAAQ,CAAC,EAAE,OAAO,EAAE,EACpB,cAAc,CAAC,EAAE,OAAO,EAAE;IA6B5B,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAAC,SAAS,CAAC;CAwXrF"}
1
+ {"version":3,"file":"ChatStream.d.ts","sourceRoot":"","sources":["../../src/chat/ChatStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,WAAW,EAIZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAyB,MAAM,0BAA0B,CAAC;AAEtF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAqBvC;;;GAGG;AACH,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,cAAc,CAAY;gBAGf,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EAC1C,QAAQ,CAAC,EAAE,OAAO,EAAE,EACpB,cAAc,CAAC,EAAE,OAAO,EAAE;IAiC5B,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAAC,SAAS,CAAC;CAuZrF"}
@@ -11,6 +11,8 @@ import { logger } from "../utils/logger.js";
11
11
  import { ModelRegistry } from "../models/ModelRegistry.js";
12
12
  import { runMiddleware } from "../utils/middleware-runner.js";
13
13
  import { randomUUID } from "node:crypto";
14
+ import { shouldRunToolCallsConcurrently, executeToolCallOutcomes } from "./ToolCallOutcome.js";
15
+ import { collectHandlers, runAllHandlers, runDirectiveHandlers, runConfirmHandlers, runChainHandlers } from "../utils/handler-stacking.js";
14
16
  /**
15
17
  * Internal handler for chat streaming logic.
16
18
  * Wraps the provider's stream with side effects like history updates and events.
@@ -48,6 +50,9 @@ export class ChatStream {
48
50
  if (!this.options.toolExecution) {
49
51
  this.options.toolExecution = config.toolExecution || ToolExecutionMode.AUTO;
50
52
  }
53
+ if (this.options.toolConcurrency === undefined) {
54
+ this.options.toolConcurrency = config.toolConcurrency;
55
+ }
51
56
  }
52
57
  get history() {
53
58
  return [...this.systemMessages, ...this.messages];
@@ -63,6 +68,23 @@ export class ChatStream {
63
68
  const requestId = randomUUID();
64
69
  const state = {};
65
70
  const middlewares = options.middlewares || [];
71
+ const newMessageHandlers = collectHandlers(options.onNewMessage, options.onNewMessageHandlers);
72
+ const endMessageHandlers = collectHandlers(options.onEndMessage, options.onEndMessageHandlers);
73
+ const beforeRequestHandlers = collectHandlers(options.onBeforeRequest, options.onBeforeRequestHandlers);
74
+ const afterResponseHandlers = collectHandlers(options.onAfterResponse, options.onAfterResponseHandlers);
75
+ const toolCallStartHandlers = collectHandlers(options.onToolCallStart, options.onToolCallStartHandlers);
76
+ const toolCallEndHandlers = collectHandlers(options.onToolCallEnd, options.onToolCallEndHandlers);
77
+ const toolCallErrorHandlers = collectHandlers(options.onToolCallError, options.onToolCallErrorHandlers);
78
+ const confirmToolCallHandlers = collectHandlers(options.onConfirmToolCall, options.onConfirmToolCallHandlers);
79
+ const combinedToolCallStart = toolCallStartHandlers.length
80
+ ? (call) => toolCallStartHandlers.forEach((handler) => handler(call))
81
+ : undefined;
82
+ const combinedToolCallEnd = toolCallEndHandlers.length
83
+ ? (call, result) => toolCallEndHandlers.forEach((handler) => handler(call, result))
84
+ : undefined;
85
+ const combinedConfirmToolCall = confirmToolCallHandlers.length
86
+ ? (call) => runConfirmHandlers(confirmToolCallHandlers, call)
87
+ : undefined;
66
88
  // Process Multimodal Content
67
89
  let messageContent = content;
68
90
  const files = [...(requestOptions.images ?? []), ...(requestOptions.files ?? [])];
@@ -143,12 +165,7 @@ export class ChatStream {
143
165
  let currentTurnUsage;
144
166
  context.messages = [...systemMessages, ...messages];
145
167
  let requestMessages = context.messages; // Use up-to-date messages from context
146
- if (options.onBeforeRequest) {
147
- const result = await options.onBeforeRequest(requestMessages);
148
- if (result) {
149
- requestMessages = result;
150
- }
151
- }
168
+ requestMessages = await runChainHandlers(beforeRequestHandlers, requestMessages);
152
169
  const streamRequest = {
153
170
  model,
154
171
  messages: requestMessages,
@@ -170,8 +187,7 @@ export class ChatStream {
170
187
  }
171
188
  for await (const chunk of provider.stream(streamRequest)) {
172
189
  if (isFirst) {
173
- if (options.onNewMessage)
174
- options.onNewMessage();
190
+ await runAllHandlers(newMessageHandlers);
175
191
  isFirst = false;
176
192
  }
177
193
  if (chunk.content) {
@@ -204,12 +220,7 @@ export class ChatStream {
204
220
  }
205
221
  assistantResponse = new ChatResponseString(fullContent || "", currentTurnUsage || { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, model, provider.id, thinking.text || thinking.signature ? thinking : undefined, fullReasoning || undefined, toolCalls, undefined, // finish_reason
206
222
  options.schema);
207
- if (options.onAfterResponse) {
208
- const result = await options.onAfterResponse(assistantResponse);
209
- if (result) {
210
- assistantResponse = result;
211
- }
212
- }
223
+ assistantResponse = await runChainHandlers(afterResponseHandlers, assistantResponse);
213
224
  messages.push({
214
225
  role: "assistant",
215
226
  content: assistantResponse || null,
@@ -218,62 +229,62 @@ export class ChatStream {
218
229
  usage: currentTurnUsage
219
230
  });
220
231
  if (!toolCalls || toolCalls.length === 0) {
221
- if (options.onEndMessage) {
222
- options.onEndMessage(assistantResponse);
223
- }
232
+ await runAllHandlers(endMessageHandlers, assistantResponse);
224
233
  break;
225
234
  }
226
235
  if (!ToolHandler.shouldExecuteTools(toolCalls, options.toolExecution)) {
227
236
  break;
228
237
  }
229
- for (const toolCall of toolCalls) {
230
- if (options.toolExecution === ToolExecutionMode.CONFIRM) {
231
- const approved = await ToolHandler.requestToolConfirmation(toolCall, options.onConfirmToolCall);
232
- if (!approved) {
233
- messages.push(provider.formatToolResultMessage(toolCall.id, "Action cancelled by user."));
234
- continue;
235
- }
238
+ const runConcurrently = shouldRunToolCallsConcurrently(options.toolConcurrency, options.toolExecution, toolCalls.length);
239
+ const outcomes = await executeToolCallOutcomes(toolCalls, {
240
+ ...options,
241
+ tools: options.tools,
242
+ onToolCallStart: combinedToolCallStart,
243
+ onToolCallEnd: combinedToolCallEnd,
244
+ onConfirmToolCall: combinedConfirmToolCall
245
+ }, middlewares, context, runConcurrently);
246
+ for (const outcome of outcomes) {
247
+ const toolCall = outcome.toolCall;
248
+ if (outcome.kind === "cancelled") {
249
+ messages.push(provider.formatToolResultMessage(toolCall.id, "Action cancelled by user."));
250
+ continue;
236
251
  }
237
- // 2. onToolCallStart Hook
238
- await runMiddleware(middlewares, "onToolCallStart", context, toolCall);
239
- try {
240
- const toolResult = await ToolHandler.execute(toolCall, options.tools, options.onToolCallStart, options.onToolCallEnd);
241
- // 3. onToolCallEnd Hook
242
- await runMiddleware(middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
252
+ if (outcome.kind === "success") {
253
+ const { toolResult } = outcome;
243
254
  messages.push(provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
255
+ continue;
244
256
  }
245
- catch (error) {
246
- let currentError = error;
247
- // 4. onToolCallError Hook
248
- const middlewareDirective = await runMiddleware(middlewares, "onToolCallError", context, toolCall, currentError);
249
- const directive = middlewareDirective || (await options.onToolCallError?.(toolCall, currentError));
250
- if (directive === "STOP") {
251
- throw error;
252
- }
253
- if (directive === "RETRY") {
254
- try {
255
- const toolResult = await ToolHandler.execute(toolCall, options.tools, options.onToolCallStart, options.onToolCallEnd);
256
- await runMiddleware(middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
257
- messages.push(provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
258
- continue;
259
- }
260
- catch (retryError) {
261
- currentError = retryError;
262
- await runMiddleware(middlewares, "onToolCallError", context, toolCall, currentError);
263
- }
264
- }
265
- messages.push(provider.formatToolResultMessage(toolCall.id, `Fatal error executing tool '${toolCall.function.name}': ${currentError.message}`, { isError: true }));
266
- if (directive === "CONTINUE") {
257
+ let currentError = outcome.error;
258
+ // 4. onToolCallError Hook
259
+ const middlewareDirective = await runMiddleware(middlewares, "onToolCallError", context, toolCall, currentError);
260
+ const directive = middlewareDirective ||
261
+ (await runDirectiveHandlers(toolCallErrorHandlers, toolCall, currentError));
262
+ if (directive === "STOP") {
263
+ throw currentError;
264
+ }
265
+ if (directive === "RETRY") {
266
+ try {
267
+ const toolResult = await ToolHandler.execute(toolCall, options.tools, combinedToolCallStart, combinedToolCallEnd);
268
+ await runMiddleware(middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
269
+ messages.push(provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
267
270
  continue;
268
271
  }
269
- const isFatal = currentError.fatal === true ||
270
- currentError.status === 401 ||
271
- currentError.status === 403;
272
- if (isFatal) {
273
- throw currentError;
272
+ catch (retryError) {
273
+ currentError = retryError;
274
+ await runMiddleware(middlewares, "onToolCallError", context, toolCall, currentError);
274
275
  }
275
- logger.error(`Tool execution failed for '${toolCall.function.name}':`, currentError);
276
276
  }
277
+ messages.push(provider.formatToolResultMessage(toolCall.id, `Fatal error executing tool '${toolCall.function.name}': ${currentError.message}`, { isError: true }));
278
+ if (directive === "CONTINUE") {
279
+ continue;
280
+ }
281
+ const isFatal = currentError.fatal === true ||
282
+ currentError.status === 401 ||
283
+ currentError.status === 403;
284
+ if (isFatal) {
285
+ throw currentError;
286
+ }
287
+ logger.error(`Tool execution failed for '${toolCall.function.name}':`, currentError);
277
288
  }
278
289
  // Loop continues -> streaming next chunk
279
290
  }
@@ -0,0 +1,30 @@
1
+ import { ToolCall, ToolDefinition } from "./Tool.js";
2
+ import { ToolExecutionMode } from "../constants.js";
3
+ import { ToolExecutionResult } from "./ToolHandler.js";
4
+ import { Middleware, MiddlewareContext } from "../types/Middleware.js";
5
+ export type ToolCallOutcome = {
6
+ kind: "cancelled";
7
+ toolCall: ToolCall;
8
+ } | {
9
+ kind: "success";
10
+ toolCall: ToolCall;
11
+ toolResult: ToolExecutionResult;
12
+ } | {
13
+ kind: "error";
14
+ toolCall: ToolCall;
15
+ error: unknown;
16
+ };
17
+ export interface ToolCallExecutionOptions {
18
+ tools?: ToolDefinition[];
19
+ toolExecution?: ToolExecutionMode;
20
+ onConfirmToolCall?: (toolCall: unknown) => Promise<boolean> | boolean;
21
+ onToolCallStart?: (toolCall: unknown) => void;
22
+ onToolCallEnd?: (toolCall: unknown, result: unknown) => void;
23
+ }
24
+ /**
25
+ * Confirm mode stays sequential since approval is interactive; concurrency
26
+ * only kicks in for AUTO/DRY_RUN turns with more than one independent call.
27
+ */
28
+ export declare function shouldRunToolCallsConcurrently(toolConcurrency: boolean | undefined, toolExecution: ToolExecutionMode | undefined, callCount: number): boolean;
29
+ export declare function executeToolCallOutcomes(toolCalls: ToolCall[], options: ToolCallExecutionOptions, middlewares: Middleware[], context: MiddlewareContext, concurrent: boolean): Promise<ToolCallOutcome[]>;
30
+ //# sourceMappingURL=ToolCallOutcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolCallOutcome.d.ts","sourceRoot":"","sources":["../../src/chat/ToolCallOutcome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAe,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGvE,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,mBAAmB,CAAA;CAAE,GACxE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACtE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9D;AAuCD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,OAAO,GAAG,SAAS,EACpC,aAAa,EAAE,iBAAiB,GAAG,SAAS,EAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAET;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,wBAAwB,EACjC,WAAW,EAAE,UAAU,EAAE,EACzB,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,eAAe,EAAE,CAAC,CAY5B"}