@agentionai/agents 1.6.0 → 1.7.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.
@@ -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,9 +165,9 @@ 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;
@@ -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,6 +405,10 @@ 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
413
  const error = new AgentError_1.MaxTokensExceededError("Response exceeded maximum token limit", this.config.maxTokens || 1024);
388
414
  this.emit(AgentEvent_1.AgentEvent.MAX_TOKENS_EXCEEDED, error);
@@ -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({
package/dist/core.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./agents/model-types";
3
3
  export * from "./agents/AgentConfig";
4
4
  export * from "./agents/AgentEvent";
5
5
  export * from "./agents/errors/AgentError";
6
+ export * from "./agents/cancellation";
6
7
  export * from "./history/History";
7
8
  export * from "./history/types";
8
9
  export * from "./graph/AgentGraph";
package/dist/core.js CHANGED
@@ -22,6 +22,7 @@ __exportStar(require("./agents/model-types"), exports);
22
22
  __exportStar(require("./agents/AgentConfig"), exports);
23
23
  __exportStar(require("./agents/AgentEvent"), exports);
24
24
  __exportStar(require("./agents/errors/AgentError"), exports);
25
+ __exportStar(require("./agents/cancellation"), exports);
25
26
  // History
26
27
  __exportStar(require("./history/History"), exports);
27
28
  __exportStar(require("./history/types"), exports);
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from "./agents/model-types";
15
15
  export * from "./agents/AgentConfig";
16
16
  export * from "./agents/AgentEvent";
17
17
  export * from "./agents/errors/AgentError";
18
+ export * from "./agents/cancellation";
18
19
  export * from "./history/History";
19
20
  export * from "./history/types";
20
21
  export { anthropicTransformer, openAiTransformer, mistralTransformer, geminiTransformer, ollamaTransformer, chatCompletionsTransformer, } from "./history/transformers";
package/dist/index.js CHANGED
@@ -43,6 +43,7 @@ __exportStar(require("./agents/model-types"), exports);
43
43
  __exportStar(require("./agents/AgentConfig"), exports);
44
44
  __exportStar(require("./agents/AgentEvent"), exports);
45
45
  __exportStar(require("./agents/errors/AgentError"), exports);
46
+ __exportStar(require("./agents/cancellation"), exports);
46
47
  // History
47
48
  __exportStar(require("./history/History"), exports);
48
49
  __exportStar(require("./history/types"), exports);
@@ -634,8 +634,10 @@ class MCPClient extends events_1.default {
634
634
  name: mcpTool.name,
635
635
  description: mcpTool.description ?? mcpTool.name,
636
636
  inputSchema,
637
- execute: async (input) => {
638
- const result = await this.invokeTool(mcpTool.name, input ?? {});
637
+ execute: async (input, _context, options) => {
638
+ // The agent run's signal, when it supplied one, overrides the client's
639
+ // default `callOptions.signal` for this call.
640
+ const result = await this.invokeTool(mcpTool.name, input ?? {}, options?.signal ? { signal: options.signal } : undefined);
639
641
  const context = { toolName: mcpTool.name, input: input ?? {} };
640
642
  if (result?.isError && this.throwOnToolError) {
641
643
  const rendered = (0, content_1.renderToolResult)(result);
@@ -1,5 +1,6 @@
1
1
  import EventEmitter from "events";
2
2
  import { BaseAgent, AgentVendor } from "../agents/BaseAgent";
3
+ import { ToolExecuteOptions } from "../agents/cancellation";
3
4
  export interface ToolInputSchema {
4
5
  type: "object";
5
6
  properties: Record<string, any>;
@@ -18,7 +19,11 @@ export interface ToolConfig<T> {
18
19
  name: string;
19
20
  description: string;
20
21
  inputSchema: ToolInputSchema;
21
- execute: (input: any, context?: Record<string, any> | null) => Promise<T>;
22
+ /**
23
+ * @param options Carries the `AbortSignal` of the agent run this call belongs
24
+ * to, so a long-running tool can be cancelled with the run.
25
+ */
26
+ execute: (input: any, context?: Record<string, any> | null, options?: ToolExecuteOptions) => Promise<T>;
22
27
  context?: Record<string, any>;
23
28
  }
24
29
  export declare class ToolEvent {
@@ -52,7 +57,7 @@ export declare class ToolResultEvent extends ToolEvent {
52
57
 
53
58
  */
54
59
  export declare class Tool<T> extends EventEmitter {
55
- protected executeFn: (input: unknown, context: Record<string, any> | null) => Promise<T>;
60
+ protected executeFn: (input: unknown, context: Record<string, any> | null, options?: ToolExecuteOptions) => Promise<T>;
56
61
  name: string;
57
62
  protected description: string;
58
63
  protected context: Record<string, any> | null;
@@ -65,7 +70,12 @@ export declare class Tool<T> extends EventEmitter {
65
70
  */
66
71
  static fromAgent(agent: BaseAgent, description: string): Tool<string>;
67
72
  constructor(config: ToolConfig<T>);
68
- execute(agentId: string, agentName: string, input: Record<string, any>, id: string, agentModel?: string, agentVendor?: AgentVendor): Promise<T>;
73
+ /**
74
+ * @param options Per-call options. `options.signal` is the agent run's
75
+ * `AbortSignal`, forwarded to the tool's own `execute` so it
76
+ * can cancel whatever work it started.
77
+ */
78
+ execute(agentId: string, agentName: string, input: Record<string, any>, id: string, agentModel?: string, agentVendor?: AgentVendor, options?: ToolExecuteOptions): Promise<T>;
69
79
  getPrompt(_vendor?: string): {
70
80
  name: string;
71
81
  description: string;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Tool = exports.ToolResultEvent = exports.ToolEvent = void 0;
7
7
  const events_1 = __importDefault(require("events"));
8
+ const cancellation_1 = require("../agents/cancellation");
8
9
  const VizReporter_1 = require("../viz/VizReporter");
9
10
  const VizConfig_1 = require("../viz/VizConfig");
10
11
  class ToolEvent {
@@ -67,11 +68,19 @@ class Tool extends events_1.default {
67
68
  },
68
69
  required: ["instructions"],
69
70
  },
70
- execute: async (input) => {
71
+ execute: async (input, _context, options) => {
71
72
  try {
72
- return (await agent.execute(input.instructions));
73
+ return (await agent.execute(input.instructions, {
74
+ signal: options?.signal,
75
+ }));
73
76
  }
74
77
  catch (error) {
78
+ // A cancelled run is not a tool failure: reporting it as a tool
79
+ // result would have the calling agent carry on with the very run
80
+ // that was just cancelled.
81
+ if ((0, cancellation_1.isAbortError)(error, options?.signal)) {
82
+ throw error;
83
+ }
75
84
  return JSON.stringify({
76
85
  error: "Failed to execute instructions: " + error.message,
77
86
  });
@@ -87,7 +96,12 @@ class Tool extends events_1.default {
87
96
  this.description = config.description;
88
97
  this.schema = config.inputSchema;
89
98
  }
90
- async execute(agentId, agentName, input, id, agentModel, agentVendor) {
99
+ /**
100
+ * @param options Per-call options. `options.signal` is the agent run's
101
+ * `AbortSignal`, forwarded to the tool's own `execute` so it
102
+ * can cancel whatever work it started.
103
+ */
104
+ async execute(agentId, agentName, input, id, agentModel, agentVendor, options) {
91
105
  const event = new ToolEvent(this, input, id, agentId, agentName);
92
106
  this.emit(ToolEvent.EXECUTE, event);
93
107
  if (event.isDefaultPrevented) {
@@ -105,7 +119,7 @@ class Tool extends events_1.default {
105
119
  vizEventId = VizReporter_1.vizReporter.toolStart(this.name, id, input, vizSource);
106
120
  }
107
121
  try {
108
- const result = await this.executeFn(input, this.context);
122
+ const result = await this.executeFn(input, this.context, options);
109
123
  const resultEvent = new ToolResultEvent(this, input, id, result, agentId, agentName);
110
124
  this.emit(ToolResultEvent.RESULT, resultEvent);
111
125
  if (resultEvent.isDefaultPrevented) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentionai/agents",
3
3
  "author": "Laurent Zuijdwijk",
4
- "version": "1.6.0",
4
+ "version": "1.7.0-beta.0",
5
5
  "description": "Agent Library",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",