@agentionai/agents 1.6.0 → 1.8.0-beta-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 (41) hide show
  1. package/README.md +4 -2
  2. package/dist/agents/Agent.d.ts +9 -2
  3. package/dist/agents/Agent.js +4 -0
  4. package/dist/agents/AgentConfig.d.ts +76 -2
  5. package/dist/agents/BaseAgent.d.ts +24 -2
  6. package/dist/agents/BaseAgent.js +17 -0
  7. package/dist/agents/anthropic/ClaudeAgent.d.ts +4 -3
  8. package/dist/agents/anthropic/ClaudeAgent.js +47 -17
  9. package/dist/agents/cancellation.d.ts +55 -0
  10. package/dist/agents/cancellation.js +72 -0
  11. package/dist/agents/errors/AgentError.d.ts +50 -2
  12. package/dist/agents/errors/AgentError.js +57 -1
  13. package/dist/agents/google/GeminiAgent.d.ts +3 -2
  14. package/dist/agents/google/GeminiAgent.js +34 -11
  15. package/dist/agents/mistral/MistralAgent.d.ts +3 -2
  16. package/dist/agents/mistral/MistralAgent.js +33 -13
  17. package/dist/agents/ollama/OllamaAgent.d.ts +17 -3
  18. package/dist/agents/ollama/OllamaAgent.js +69 -19
  19. package/dist/agents/openai/OpenAiAgent.d.ts +4 -3
  20. package/dist/agents/openai/OpenAiAgent.js +52 -17
  21. package/dist/agents/openai-compatible/OpenAICompatibleAgent.d.ts +4 -3
  22. package/dist/agents/openai-compatible/OpenAICompatibleAgent.js +48 -19
  23. package/dist/agents/openrouter/OpenRouterAgent.d.ts +234 -0
  24. package/dist/agents/openrouter/OpenRouterAgent.js +711 -0
  25. package/dist/agents/openrouter/types.d.ts +164 -0
  26. package/dist/agents/openrouter/types.js +15 -0
  27. package/dist/core.d.ts +1 -0
  28. package/dist/core.js +1 -0
  29. package/dist/history/transformers.d.ts +80 -0
  30. package/dist/history/transformers.js +156 -1
  31. package/dist/history/types.d.ts +22 -2
  32. package/dist/history/types.js +13 -2
  33. package/dist/index.d.ts +5 -1
  34. package/dist/index.js +5 -1
  35. package/dist/mcp/MCPClient.js +4 -2
  36. package/dist/openrouter.d.ts +6 -0
  37. package/dist/openrouter.js +24 -0
  38. package/dist/tools/Tool.d.ts +13 -3
  39. package/dist/tools/Tool.js +18 -4
  40. package/dist/viz/types.d.ts +1 -1
  41. package/package.json +10 -1
@@ -1,4 +1,5 @@
1
1
  import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
2
+ import { ExecuteOptions } from "../cancellation";
2
3
  import { History, MessageContent } from "../../history/History";
3
4
  import { Tool, Response, ResponseUsage } from "openai/resources/responses/responses";
4
5
  import type { Model as OpenAIModelCard } from "openai/resources/models";
@@ -95,8 +96,8 @@ export declare class OpenAiAgent<M extends OpenAIModel = OpenAIModel> extends Ba
95
96
  */
96
97
  private buildReasoningParams;
97
98
  protected process(_input: string): Promise<string>;
98
- execute(input: string | MessageContent[]): Promise<string>;
99
- protected handleResponse(response: Response): Promise<string>;
99
+ execute(input: string | MessageContent[], options?: ExecuteOptions): Promise<string>;
100
+ protected handleResponse(response: Response, options?: ExecuteOptions): Promise<string>;
100
101
  private handleToolUse;
101
102
  /**
102
103
  * Stream a response as an async generator of `StreamChunk` objects.
@@ -110,7 +111,7 @@ export declare class OpenAiAgent<M extends OpenAIModel = OpenAIModel> extends Ba
110
111
  * }
111
112
  * ```
112
113
  */
113
- executeStream(input: string | MessageContent[]): AsyncGenerator<StreamChunk>;
114
+ executeStream(input: string | MessageContent[], options?: ExecuteOptions): AsyncGenerator<StreamChunk>;
114
115
  private streamTurn;
115
116
  protected parseUsage(input: ResponseUsage): TokenUsage;
116
117
  }
@@ -8,6 +8,7 @@ exports.lowestReasoningEffort = lowestReasoningEffort;
8
8
  const openai_1 = __importDefault(require("openai"));
9
9
  const BaseAgent_1 = require("../BaseAgent");
10
10
  const AgentEvent_1 = require("../AgentEvent");
11
+ const cancellation_1 = require("../cancellation");
11
12
  const AgentError_1 = require("../errors/AgentError");
12
13
  const transformers_1 = require("../../history/transformers");
13
14
  const VizReporter_1 = require("../../viz/VizReporter");
@@ -72,7 +73,13 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
72
73
  const user = config.user ?? vendorConfig.user;
73
74
  this.config = {
74
75
  model: config.model || "gpt-4.1-mini",
75
- maxTokens: config.maxTokens || 1024,
76
+ // No default. `max_output_tokens` is optional on the Responses API, and
77
+ // omitting it lets the model use its full output budget. A default here
78
+ // silently truncated every response — and on reasoning models it was
79
+ // worse than a truncation, since reasoning tokens count against the same
80
+ // budget: a small cap could be spent entirely on thinking, returning
81
+ // `status: "incomplete"` with no text at all.
82
+ maxTokens: config.maxTokens,
76
83
  disableParallelToolUse,
77
84
  disableReasoning,
78
85
  reasoningEffort,
@@ -163,7 +170,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
163
170
  async process(_input) {
164
171
  return "";
165
172
  }
166
- async execute(input) {
173
+ async execute(input, options) {
167
174
  this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
168
175
  // Reset token usage for this execution
169
176
  this.resetTokenUsage();
@@ -204,11 +211,19 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
204
211
  // Note: Responses API doesn't support seed, presence_penalty, frequency_penalty, stop
205
212
  user: this.config.user,
206
213
  ...this.buildReasoningParams(),
207
- });
214
+ }, { signal: options?.signal });
208
215
  this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
209
- return await this.handleResponse(response);
216
+ return await this.handleResponse(response, options);
210
217
  }
211
218
  catch (error) {
219
+ if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
220
+ const abortError = this.abortError(error, options?.signal);
221
+ if (this.vizEventId) {
222
+ VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
223
+ this.vizEventId = undefined;
224
+ }
225
+ throw abortError;
226
+ }
212
227
  if (error && typeof error === "object" && "error" in error) {
213
228
  const openAIError = error;
214
229
  const apiError = new AgentError_1.ApiError(`OpenAI API error: ${openAIError.error.message || "Unknown error"}`, openAIError.status, openAIError.error);
@@ -239,7 +254,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
239
254
  this.history.endExecution();
240
255
  }
241
256
  }
242
- async handleResponse(response) {
257
+ async handleResponse(response, options) {
243
258
  if (!response.output || !response.output.length) {
244
259
  const error = new AgentError_1.ExecutionError("Invalid response format: missing output");
245
260
  this.emit(AgentEvent_1.AgentEvent.ERROR, error);
@@ -288,6 +303,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
288
303
  }
289
304
  else if (toolCalls.length) {
290
305
  try {
306
+ // Stop before the assistant turn is written: nothing else would notice
307
+ // a cancellation until the next provider call, and bailing out here
308
+ // avoids both running the tools' side effects and leaving a function
309
+ // call in history with no output to answer it.
310
+ (0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
291
311
  // Add assistant message with tool calls to history (normalized)
292
312
  const functionCalls = toolCalls.map((tc) => ({
293
313
  id: tc.id || tc.call_id,
@@ -297,7 +317,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
297
317
  }));
298
318
  const assistantEntry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text || "", functionCalls);
299
319
  this.addToHistory(assistantEntry);
300
- const toolResponses = await this.handleToolUse(toolCalls);
320
+ const toolResponses = await this.handleToolUse(toolCalls, options);
301
321
  // Add tool results to history (normalized)
302
322
  for (const result of toolResponses) {
303
323
  const resultEntry = transformers_1.openAiTransformer.toolResultEntry(result.call_id, result.output, false);
@@ -318,9 +338,9 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
318
338
  // Note: Responses API doesn't support seed, presence_penalty, frequency_penalty, stop
319
339
  user: this.config.user,
320
340
  ...this.buildReasoningParams(),
321
- });
341
+ }, { signal: options?.signal });
322
342
  this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
323
- return this.handleResponse(newResponse);
343
+ return this.handleResponse(newResponse, options);
324
344
  }
325
345
  catch (error) {
326
346
  if (error && typeof error === "object" && "error" in error) {
@@ -358,7 +378,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
358
378
  throw error;
359
379
  }
360
380
  }
361
- async handleToolUse(content) {
381
+ async handleToolUse(content, options) {
362
382
  if (!content || !content.length) {
363
383
  throw new AgentError_1.ExecutionError("Invalid tool calls content");
364
384
  }
@@ -389,7 +409,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
389
409
  catch (parseError) {
390
410
  throw new AgentError_1.ToolExecutionError(`Invalid tool arguments: ${parseError instanceof Error ? parseError.message : "Parse error"}`, toolName, toolCall.arguments);
391
411
  }
392
- const result = await tool.execute(this.getId(), this.getName(), toolArgs, toolCall.id || "", this.config.model, "openai");
412
+ const result = await tool.execute(this.getId(), this.getName(), toolArgs, toolCall.id || "", this.config.model, "openai", { signal: options?.signal });
393
413
  return {
394
414
  call_id: toolCall.call_id,
395
415
  output: JSON.stringify(result),
@@ -422,7 +442,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
422
442
  * }
423
443
  * ```
424
444
  */
425
- async *executeStream(input) {
445
+ async *executeStream(input, options) {
426
446
  this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
427
447
  this.resetTokenUsage();
428
448
  this.currentToolCallCount = 0;
@@ -443,9 +463,17 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
443
463
  this.history.setSessionAnchor();
444
464
  this.history.beginExecution();
445
465
  try {
446
- yield* this.streamTurn();
466
+ yield* this.streamTurn(options);
447
467
  }
448
468
  catch (error) {
469
+ if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
470
+ const abortError = this.abortError(error, options?.signal);
471
+ if (this.vizEventId) {
472
+ VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
473
+ this.vizEventId = undefined;
474
+ }
475
+ throw abortError;
476
+ }
449
477
  if (error instanceof AgentError_1.AgentError) {
450
478
  this.emit(AgentEvent_1.AgentEvent.ERROR, error);
451
479
  if (this.vizEventId) {
@@ -476,7 +504,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
476
504
  this.history.endExecution();
477
505
  }
478
506
  }
479
- async *streamTurn() {
507
+ async *streamTurn(options) {
480
508
  const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries());
481
509
  this.startTurnTimer();
482
510
  const stream = await this.client.responses.create({
@@ -490,7 +518,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
490
518
  top_p: this.config.topP,
491
519
  user: this.config.user,
492
520
  ...this.buildReasoningParams("auto"),
493
- });
521
+ }, { signal: options?.signal });
494
522
  let completedEvent = null;
495
523
  for await (const event of stream) {
496
524
  if (event.type === "response.output_text.delta") {
@@ -510,15 +538,22 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
510
538
  }
511
539
  }
512
540
  if (event.type === "response.incomplete") {
513
- throw new AgentError_1.MaxTokensExceededError("Response incomplete: max tokens reached", this.config.maxTokens || 1024);
541
+ throw new AgentError_1.MaxTokensExceededError("Response incomplete: max tokens reached", this.config.maxTokens);
514
542
  }
515
543
  }
544
+ // The SDK's stream iterator swallows the abort and simply stops yielding.
545
+ // Without this the turn would fail as a malformed stream instead of a
546
+ // cancellation — checked here so the tokens already spent are reported.
547
+ (0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
516
548
  if (!completedEvent) {
517
549
  throw new AgentError_1.ExecutionError("OpenAI stream ended without a completed event");
518
550
  }
519
551
  const response = completedEvent.response;
520
552
  const toolCalls = response.output.filter((o) => o.type === "function_call");
521
553
  if (toolCalls.length > 0) {
554
+ // As in handleResponse(): bail out before the assistant turn is written,
555
+ // so a cancelled run leaves no unanswered function call in history.
556
+ (0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
522
557
  this.emit(AgentEvent_1.AgentEvent.TOOL_USE, toolCalls);
523
558
  this.currentToolCallCount += toolCalls.length;
524
559
  const functionCalls = toolCalls.map((tc) => ({
@@ -529,11 +564,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
529
564
  }));
530
565
  const assistantEntry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text || "", functionCalls);
531
566
  this.addToHistory(assistantEntry);
532
- const toolResults = await this.handleToolUse(toolCalls);
567
+ const toolResults = await this.handleToolUse(toolCalls, options);
533
568
  for (const result of toolResults) {
534
569
  this.addToHistory(transformers_1.openAiTransformer.toolResultEntry(result.call_id, result.output, false));
535
570
  }
536
- yield* this.streamTurn();
571
+ yield* this.streamTurn(options);
537
572
  }
538
573
  else {
539
574
  const textContent = response.output_text || "";
@@ -3,6 +3,7 @@ import { ChatCompletion, ChatCompletionTool } from "openai/resources/chat/comple
3
3
  import { Model } from "openai/resources/models";
4
4
  import { BaseAgent, BaseAgentConfig, ModelInfo, TokenUsage } from "../BaseAgent";
5
5
  import { AgentVendor } from "../AgentConfig";
6
+ import { ExecuteOptions } from "../cancellation";
6
7
  import { History, MessageContent } from "../../history/History";
7
8
  /**
8
9
  * A single chunk yielded by `executeStream()`.
@@ -50,9 +51,9 @@ export declare abstract class OpenAICompatibleAgent extends BaseAgent {
50
51
  listModels(): Promise<ModelInfo<Model>[]>;
51
52
  protected getToolDefinitions(): ChatCompletionTool[];
52
53
  protected process(_input: string): Promise<string>;
53
- execute(input: string | MessageContent[]): Promise<string>;
54
+ execute(input: string | MessageContent[], options?: ExecuteOptions): Promise<string>;
54
55
  private callProvider;
55
- protected handleResponse(response: ChatCompletion): Promise<string>;
56
+ protected handleResponse(response: ChatCompletion, options?: ExecuteOptions): Promise<string>;
56
57
  private handleToolCalls;
57
58
  /**
58
59
  * Stream a response as an async generator of `StreamChunk` objects.
@@ -69,7 +70,7 @@ export declare abstract class OpenAICompatibleAgent extends BaseAgent {
69
70
  * }
70
71
  * ```
71
72
  */
72
- executeStream(input: string | MessageContent[]): AsyncGenerator<StreamChunk>;
73
+ executeStream(input: string | MessageContent[], options?: ExecuteOptions): AsyncGenerator<StreamChunk>;
73
74
  private streamTurn;
74
75
  private accumulateStreamUsage;
75
76
  protected parseUsage(response: ChatCompletion): TokenUsage;
@@ -7,6 +7,7 @@ exports.OpenAICompatibleAgent = void 0;
7
7
  const openai_1 = __importDefault(require("openai"));
8
8
  const BaseAgent_1 = require("../BaseAgent");
9
9
  const AgentEvent_1 = require("../AgentEvent");
10
+ const cancellation_1 = require("../cancellation");
10
11
  const AgentError_1 = require("../errors/AgentError");
11
12
  const transformers_1 = require("../../history/transformers");
12
13
  const VizReporter_1 = require("../../viz/VizReporter");
@@ -84,7 +85,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
84
85
  async process(_input) {
85
86
  return "";
86
87
  }
87
- async execute(input) {
88
+ async execute(input, options) {
88
89
  this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
89
90
  this.resetTokenUsage();
90
91
  this.currentToolCallCount = 0;
@@ -105,11 +106,19 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
105
106
  this.history.setSessionAnchor();
106
107
  this.history.beginExecution();
107
108
  try {
108
- const response = await this.callProvider();
109
+ const response = await this.callProvider(options);
109
110
  this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
110
- return await this.handleResponse(response);
111
+ return await this.handleResponse(response, options);
111
112
  }
112
113
  catch (error) {
114
+ if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
115
+ const abortError = this.abortError(error, options?.signal);
116
+ if (this.vizEventId) {
117
+ VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
118
+ this.vizEventId = undefined;
119
+ }
120
+ throw abortError;
121
+ }
113
122
  if (error instanceof openai_1.default.APIError) {
114
123
  const apiError = new AgentError_1.ApiError(`${this.getVendorName()} API error: ${error.message}`, error.status, error);
115
124
  this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
@@ -139,7 +148,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
139
148
  this.history.endExecution();
140
149
  }
141
150
  }
142
- async callProvider() {
151
+ async callProvider(options) {
143
152
  const messages = transformers_1.chatCompletionsTransformer.toProvider(this.history.getEntries());
144
153
  const tools = this.tools.size > 0 ? this.getToolDefinitions() : undefined;
145
154
  this.startTurnTimer();
@@ -156,14 +165,14 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
156
165
  presence_penalty: this.config.presencePenalty,
157
166
  frequency_penalty: this.config.frequencyPenalty,
158
167
  ...this.buildExtraRequestParams(),
159
- });
168
+ }, { signal: options?.signal });
160
169
  }
161
- async handleResponse(response) {
170
+ async handleResponse(response, options) {
162
171
  const usage = this.accumulateUsage(this.parseUsage(response));
163
172
  const choice = response.choices[0];
164
173
  const message = choice.message;
165
174
  if (choice.finish_reason === "length") {
166
- const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens || 1024);
175
+ const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens);
167
176
  this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
168
177
  this.emit(AgentEvent_1.AgentEvent.ERROR, error);
169
178
  if (this.vizEventId) {
@@ -189,19 +198,24 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
189
198
  return textContent;
190
199
  }
191
200
  const toolCalls = message.tool_calls;
201
+ // Stop before the assistant turn is written: nothing else would notice a
202
+ // cancellation until the next provider call, and bailing out here avoids
203
+ // both running the tools' side effects and leaving a tool call in history
204
+ // with no tool message to answer it.
205
+ (0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
192
206
  this.emit(AgentEvent_1.AgentEvent.TOOL_USE, toolCalls);
193
207
  this.currentToolCallCount += toolCalls.length;
194
208
  const assistantEntry = transformers_1.chatCompletionsTransformer.fromProviderMessage(message);
195
209
  this.addToHistory(assistantEntry);
196
- const toolResults = await this.handleToolCalls(toolCalls);
210
+ const toolResults = await this.handleToolCalls(toolCalls, options);
197
211
  for (const result of toolResults) {
198
212
  const resultEntry = transformers_1.chatCompletionsTransformer.toolResultEntry(result.toolCallId, result.content);
199
213
  this.addToHistory(resultEntry);
200
214
  }
201
215
  try {
202
- const newResponse = await this.callProvider();
216
+ const newResponse = await this.callProvider(options);
203
217
  this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
204
- return this.handleResponse(newResponse);
218
+ return this.handleResponse(newResponse, options);
205
219
  }
206
220
  catch (error) {
207
221
  const executionError = new AgentError_1.ExecutionError(`${this.getVendorName()} error during tool response: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -209,7 +223,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
209
223
  throw executionError;
210
224
  }
211
225
  }
212
- async handleToolCalls(toolCalls) {
226
+ async handleToolCalls(toolCalls, options) {
213
227
  return Promise.all(toolCalls.map(async (toolCall) => {
214
228
  const toolName = toolCall.type === "function" ? toolCall.function.name : "";
215
229
  const tool = this.tools.get(toolName);
@@ -224,7 +238,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
224
238
  }
225
239
  try {
226
240
  const args = JSON.parse(toolCall.function.arguments || "{}");
227
- const result = await tool.execute(this.getId(), this.getName(), args, toolCallId, this.config.model, this.vendor);
241
+ const result = await tool.execute(this.getId(), this.getName(), args, toolCallId, this.config.model, this.vendor, { signal: options?.signal });
228
242
  return { toolCallId, content: JSON.stringify(result) };
229
243
  }
230
244
  catch (error) {
@@ -253,7 +267,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
253
267
  * }
254
268
  * ```
255
269
  */
256
- async *executeStream(input) {
270
+ async *executeStream(input, options) {
257
271
  this.emit(AgentEvent_1.AgentEvent.BEFORE_EXECUTE, input);
258
272
  this.resetTokenUsage();
259
273
  this.currentToolCallCount = 0;
@@ -274,9 +288,17 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
274
288
  this.history.setSessionAnchor();
275
289
  this.history.beginExecution();
276
290
  try {
277
- yield* this.streamTurn();
291
+ yield* this.streamTurn(options);
278
292
  }
279
293
  catch (error) {
294
+ if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
295
+ const abortError = this.abortError(error, options?.signal);
296
+ if (this.vizEventId) {
297
+ VizReporter_1.vizReporter.agentError(this.vizEventId, "AbortError", abortError.message, false);
298
+ this.vizEventId = undefined;
299
+ }
300
+ throw abortError;
301
+ }
280
302
  if (error instanceof openai_1.default.APIError) {
281
303
  const apiError = new AgentError_1.ApiError(`${this.getVendorName()} API error: ${error.message}`, error.status, error);
282
304
  this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
@@ -306,7 +328,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
306
328
  this.history.endExecution();
307
329
  }
308
330
  }
309
- async *streamTurn() {
331
+ async *streamTurn(options) {
310
332
  const messages = transformers_1.chatCompletionsTransformer.toProvider(this.history.getEntries());
311
333
  const tools = this.tools.size > 0 ? this.getToolDefinitions() : undefined;
312
334
  this.startTurnTimer();
@@ -324,7 +346,7 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
324
346
  presence_penalty: this.config.presencePenalty,
325
347
  frequency_penalty: this.config.frequencyPenalty,
326
348
  ...this.buildExtraRequestParams(),
327
- });
349
+ }, { signal: options?.signal });
328
350
  let textContent = "";
329
351
  let reasoningContent = "";
330
352
  const toolCallAcc = new Map();
@@ -383,8 +405,12 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
383
405
  // continues into a tool call still reports what it spent.
384
406
  if (streamUsage)
385
407
  this.accumulateStreamUsage(streamUsage);
408
+ // The SDK's stream iterator swallows the abort and simply stops yielding,
409
+ // so without this an interrupted stream would look like a short but
410
+ // complete turn — writing partial text to history and emitting DONE.
411
+ (0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
386
412
  if (finishReason === "length") {
387
- const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens || 1024);
413
+ const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens);
388
414
  this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
389
415
  this.emit(AgentEvent_1.AgentEvent.ERROR, error);
390
416
  if (this.vizEventId) {
@@ -394,6 +420,9 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
394
420
  throw error;
395
421
  }
396
422
  if (finishReason === "tool_calls" && toolCallAcc.size > 0) {
423
+ // As in handleResponse(): bail out before the assistant turn is written,
424
+ // so a cancelled run leaves no unanswered tool call in history.
425
+ (0, cancellation_1.throwIfAborted)(options?.signal, `Execution of agent ${this.getName()}`);
397
426
  const toolCalls = Array.from(toolCallAcc.entries())
398
427
  .sort(([a], [b]) => a - b)
399
428
  .map(([, tc]) => ({
@@ -410,11 +439,11 @@ class OpenAICompatibleAgent extends BaseAgent_1.BaseAgent {
410
439
  reasoning_content: reasoningContent || null,
411
440
  });
412
441
  this.addToHistory(assistantEntry);
413
- const toolResults = await this.handleToolCalls(toolCalls);
442
+ const toolResults = await this.handleToolCalls(toolCalls, options);
414
443
  for (const result of toolResults) {
415
444
  this.addToHistory(transformers_1.chatCompletionsTransformer.toolResultEntry(result.toolCallId, result.content));
416
445
  }
417
- yield* this.streamTurn();
446
+ yield* this.streamTurn(options);
418
447
  }
419
448
  else {
420
449
  const assistantEntry = transformers_1.chatCompletionsTransformer.fromProviderMessage({