avantgate 2.3.0 → 2.4.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.
@@ -47,4 +47,18 @@ export declare class DtoValidationError extends Error {
47
47
  readonly issues: unknown[];
48
48
  constructor(toolName: string, message: string, issues?: unknown[]);
49
49
  }
50
+ export interface AmbiguousToolExecutionDetails {
51
+ toolCallId: string;
52
+ toolName?: string;
53
+ impact?: string;
54
+ message?: string;
55
+ originalError?: unknown;
56
+ }
57
+ export declare class AmbiguousToolExecutionError extends Error {
58
+ readonly toolCallId: string;
59
+ readonly toolName?: string;
60
+ readonly impact?: string;
61
+ readonly originalError?: unknown;
62
+ constructor(details: AmbiguousToolExecutionDetails | string, toolName?: string, impact?: string);
63
+ }
50
64
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/agent/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEvC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAOnF;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,WAAW,EAAE,MAAM,CAAC;gBAExB,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAMlD;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,aAAa,CAAC,EAAE,OAAO,CAAC;gBAE5B,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO;CAOzF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtC,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEpB,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;CAapD;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,KAAK,EAAE,MAAM,EAAE,CAAC;gBAEpB,KAAK,EAAE,MAAM,EAAE;CAK5B;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAErB,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAM5C;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAErB,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAMjD;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAEnB,MAAM,EAAE,MAAM;CAK3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,MAAM,EAAE,OAAO,EAAE,CAAC;gBAEtB,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO;CAMtE"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/agent/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEvC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAOnF;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,WAAW,EAAE,MAAM,CAAC;gBAExB,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAMlD;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,aAAa,CAAC,EAAE,OAAO,CAAC;gBAE5B,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO;CAOzF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtC,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEpB,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;CAapD;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,KAAK,EAAE,MAAM,EAAE,CAAC;gBAEpB,KAAK,EAAE,MAAM,EAAE;CAK5B;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAErB,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAM5C;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAErB,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAMjD;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAEnB,MAAM,EAAE,MAAM;CAK3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,MAAM,EAAE,OAAO,EAAE,CAAC;gBAEtB,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO;CAMtE;AAED,MAAM,WAAW,6BAA6B;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChC,SAAgB,aAAa,CAAC,EAAE,OAAO,CAAC;gBAE5B,OAAO,EAAE,6BAA6B,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAahG"}
@@ -16,4 +16,5 @@ export * from "./tool-state";
16
16
  export * from "./adapters";
17
17
  export * from "./telemetry";
18
18
  export * from "./idempotency-guard";
19
+ export * from "./tool-resilience";
19
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,OAAO,CAAC;AACtB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,OAAO,CAAC;AACtB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -22,6 +22,7 @@ var agent_exports = {};
22
22
  __export(agent_exports, {
23
23
  AccessControlToolStrategy: () => AccessControlToolStrategy,
24
24
  AgentToolFactory: () => AgentToolFactory,
25
+ AmbiguousToolExecutionError: () => AmbiguousToolExecutionError,
25
26
  CircularToolCallError: () => CircularToolCallError,
26
27
  CompositeToolStrategy: () => CompositeToolStrategy,
27
28
  DtoValidationError: () => DtoValidationError,
@@ -45,6 +46,7 @@ __export(agent_exports, {
45
46
  ToolSubCallQuotaError: () => ToolSubCallQuotaError,
46
47
  applyToolStrategy: () => applyToolStrategy,
47
48
  auditToolResult: () => auditToolResult,
49
+ clearResilienceFallbackStore: () => clearResilienceFallbackStore,
48
50
  createAdapterIdempotencyStore: () => createAdapterIdempotencyStore,
49
51
  createCustomStorageAdapter: () => createCustomStorageAdapter,
50
52
  createIsolatedTool: () => createIsolatedTool,
@@ -54,7 +56,9 @@ __export(agent_exports, {
54
56
  createToolInvoker: () => createToolInvoker,
55
57
  createToolSharedState: () => createToolSharedState,
56
58
  dto: () => dto,
57
- withServerIdempotency: () => withServerIdempotency
59
+ getToolExecutionRecord: () => getToolExecutionRecord,
60
+ withServerIdempotency: () => withServerIdempotency,
61
+ withToolResilience: () => withToolResilience
58
62
  });
59
63
  module.exports = __toCommonJS(agent_exports);
60
64
 
@@ -157,6 +161,25 @@ var DtoValidationError = class extends Error {
157
161
  this.issues = issues;
158
162
  }
159
163
  };
164
+ var AmbiguousToolExecutionError = class extends Error {
165
+ toolCallId;
166
+ toolName;
167
+ impact;
168
+ originalError;
169
+ constructor(details, toolName, impact) {
170
+ const isStr = typeof details === "string";
171
+ const callId = isStr ? details : details.toolCallId;
172
+ const name = isStr ? toolName : details.toolName;
173
+ const imp = isStr ? impact ?? "MUTATIVE" : details.impact ?? "MUTATIVE";
174
+ const msg = isStr ? "" : details.message ? `: ${details.message}` : "";
175
+ super(`Ambiguous tool execution detected for [${name || "tool"}::${callId}] (${imp})${msg}. Re-execution blocked without reconciliation.`);
176
+ this.name = "AmbiguousToolExecutionError";
177
+ this.toolCallId = callId;
178
+ this.toolName = name;
179
+ this.impact = imp;
180
+ this.originalError = isStr ? void 0 : details.originalError;
181
+ }
182
+ };
160
183
 
161
184
  // src/sanitizer.ts
162
185
  var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
@@ -263,16 +286,223 @@ function auditToolResult(data, options) {
263
286
  };
264
287
  }
265
288
 
289
+ // src/agent/tool-resilience.ts
290
+ var MAX_FALLBACK_CAP = 1e3;
291
+ var inMemoryFallbackStore = /* @__PURE__ */ new Map();
292
+ var clearResilienceFallbackStore = () => {
293
+ inMemoryFallbackStore.clear();
294
+ };
295
+ var setFallbackRecord = (toolCallId, record) => {
296
+ if (inMemoryFallbackStore.size >= MAX_FALLBACK_CAP) {
297
+ const firstKey = inMemoryFallbackStore.keys().next().value;
298
+ if (firstKey) inMemoryFallbackStore.delete(firstKey);
299
+ }
300
+ inMemoryFallbackStore.set(toolCallId, record);
301
+ };
302
+ var isDeterministicClientError = (error) => {
303
+ if (!error || typeof error !== "object") {
304
+ return false;
305
+ }
306
+ const err = error;
307
+ const status = Number(err.status ?? err.statusCode);
308
+ if (status >= 400 && status < 500 && status !== 408 && status !== 429) {
309
+ return true;
310
+ }
311
+ return err.name === "DtoValidationError" || err.name === "ToolAccessDeniedError";
312
+ };
313
+ var isAmbiguousTimeoutError = (error, isTimedOut) => {
314
+ if (isTimedOut) return true;
315
+ if (!error || typeof error !== "object") return false;
316
+ const err = error;
317
+ const name = String(err.name ?? "");
318
+ const status = Number(err.status ?? err.statusCode);
319
+ if (name === "AbortError" || name === "TimeoutError") return true;
320
+ if (status === 408 || status === 502 || status === 503 || status === 504) return true;
321
+ const msg = String(err.message ?? "").toLowerCase();
322
+ const code = String(err.code ?? "").toLowerCase();
323
+ return ["timeout", "etimedout", "econnreset", "socket hang up", "network error", "fetch failed"].some((key) => msg.includes(key) || code.includes(key));
324
+ };
325
+ var classifyErrorStatus = (error, isTimedOut) => {
326
+ if (isDeterministicClientError(error)) {
327
+ return "FAILED";
328
+ }
329
+ if (isAmbiguousTimeoutError(error, isTimedOut)) {
330
+ return "UNCONFIRMED_TIMEOUT";
331
+ }
332
+ return "FAILED";
333
+ };
334
+ var getToolExecutionRecord = async (storage, toolCallId) => {
335
+ if (!storage) {
336
+ return inMemoryFallbackStore.get(toolCallId) ?? null;
337
+ }
338
+ if (storage.getStateValue) {
339
+ const val = await storage.getStateValue(`tool_resilience:${toolCallId}`);
340
+ if (val) return val;
341
+ const directVal = await storage.getStateValue(toolCallId);
342
+ if (directVal) return directVal;
343
+ }
344
+ const step = await storage.getStep("_tool_resilience", toolCallId);
345
+ return step?.metadata?.resilienceRecord ?? null;
346
+ };
347
+ var saveToStateStorage = async (storage, toolCallId, record) => {
348
+ if (!storage.setStateValue) return;
349
+ await storage.setStateValue(`tool_resilience:${toolCallId}`, record);
350
+ await storage.setStateValue(toolCallId, record);
351
+ };
352
+ var saveResilienceRecord = async (storage, record) => {
353
+ if (!storage) {
354
+ setFallbackRecord(record.toolCallId, record);
355
+ return;
356
+ }
357
+ await saveToStateStorage(storage, record.toolCallId, record);
358
+ const status = record.status === "COMPLETED" ? "COMPLETED" : record.status === "FAILED" ? "FAILED" : "RUNNING";
359
+ await storage.saveStep({
360
+ workflowId: "_tool_resilience",
361
+ stepId: record.toolCallId,
362
+ status,
363
+ result: record.result,
364
+ error: record.error,
365
+ metadata: { resilienceRecord: record },
366
+ createdAt: new Date(record.startedAt).toISOString(),
367
+ updatedAt: new Date(record.updatedAt).toISOString()
368
+ });
369
+ };
370
+ var handleAmbiguousFailure = (options, originalError) => {
371
+ const workflowId = options.workflowId ?? "_tool_resilience";
372
+ if (options.suspendOnAmbiguous) {
373
+ throw new StepSuspendedError(options.toolCallId, workflowId, {
374
+ toolCallId: options.toolCallId,
375
+ impact: options.impact,
376
+ reason: "Ambiguous mutation requires human intervention",
377
+ originalError
378
+ });
379
+ }
380
+ throw new AmbiguousToolExecutionError({
381
+ toolCallId: options.toolCallId,
382
+ toolName: options.toolName,
383
+ impact: options.impact,
384
+ message: "Ambiguous execution state (UNCONFIRMED_TIMEOUT). Re-execution blocked without reconciliation.",
385
+ originalError
386
+ });
387
+ };
388
+ var saveReconciledRecord = async (options, result) => {
389
+ await saveResilienceRecord(options.storage, {
390
+ toolCallId: options.toolCallId,
391
+ status: "COMPLETED",
392
+ result,
393
+ impact: options.impact,
394
+ startedAt: Date.now(),
395
+ updatedAt: Date.now()
396
+ });
397
+ return { result, isCached: true, status: "COMPLETED" };
398
+ };
399
+ var tryReconciliation = async (options) => {
400
+ if (!options.onAmbiguousRetry) {
401
+ return handleAmbiguousFailure(options);
402
+ }
403
+ try {
404
+ const outcome = await options.onAmbiguousRetry({ toolCallId: options.toolCallId, args: options.args });
405
+ if (!outcome.reconciled) {
406
+ return handleAmbiguousFailure(options);
407
+ }
408
+ return await saveReconciledRecord(options, outcome.result);
409
+ } catch (err) {
410
+ if (err instanceof StepSuspendedError || err instanceof AmbiguousToolExecutionError) throw err;
411
+ return handleAmbiguousFailure(options, err);
412
+ }
413
+ };
414
+ var checkExistingState = async (options) => {
415
+ const existing = await getToolExecutionRecord(options.storage, options.toolCallId);
416
+ if (!existing) {
417
+ return null;
418
+ }
419
+ if (existing.status === "COMPLETED") {
420
+ return { result: existing.result, isCached: true, status: "COMPLETED" };
421
+ }
422
+ if (existing.status === "UNCONFIRMED_TIMEOUT") {
423
+ if (options.impact === "READ_ONLY") {
424
+ return null;
425
+ }
426
+ return await tryReconciliation(options);
427
+ }
428
+ return null;
429
+ };
430
+ var executeActionWithTimeout = async (action, timeoutMs, idempotencyKey) => {
431
+ const controller = new AbortController();
432
+ let isTimedOut = false;
433
+ const timer = setTimeout(() => {
434
+ isTimedOut = true;
435
+ controller.abort(new Error(`Tool execution timed out after ${timeoutMs}ms`));
436
+ }, timeoutMs);
437
+ try {
438
+ const result = await action(controller.signal, idempotencyKey);
439
+ return { result, isTimedOut: false };
440
+ } catch (error) {
441
+ return { error, isTimedOut };
442
+ } finally {
443
+ clearTimeout(timer);
444
+ }
445
+ };
446
+ var saveFailureRecord = async (options, startedAt, error, isTimedOut) => {
447
+ const status = classifyErrorStatus(error, isTimedOut);
448
+ const errorMsg = error instanceof Error ? error.message : String(error);
449
+ await saveResilienceRecord(options.storage, {
450
+ toolCallId: options.toolCallId,
451
+ status,
452
+ error: errorMsg,
453
+ impact: options.impact,
454
+ startedAt,
455
+ updatedAt: Date.now()
456
+ });
457
+ throw error;
458
+ };
459
+ var saveSuccessRecord = async (options, startedAt, result) => {
460
+ await saveResilienceRecord(options.storage, {
461
+ toolCallId: options.toolCallId,
462
+ status: "COMPLETED",
463
+ result,
464
+ impact: options.impact,
465
+ startedAt,
466
+ updatedAt: Date.now()
467
+ });
468
+ return { result, isCached: false, status: "COMPLETED" };
469
+ };
470
+ var claimExecutionIntent = async (options, startedAt) => {
471
+ await saveResilienceRecord(options.storage, {
472
+ toolCallId: options.toolCallId,
473
+ status: "CLAIMED",
474
+ impact: options.impact,
475
+ startedAt,
476
+ updatedAt: startedAt
477
+ });
478
+ };
479
+ var withToolResilience = async (options) => {
480
+ const existingResult = await checkExistingState(options);
481
+ if (existingResult) {
482
+ return existingResult;
483
+ }
484
+ const startedAt = Date.now();
485
+ const idempotencyKey = options.idempotencyKey || options.toolCallId;
486
+ await claimExecutionIntent(options, startedAt);
487
+ const timeoutMs = options.timeoutMs ?? 3e4;
488
+ const outcome = await executeActionWithTimeout(options.action, timeoutMs, idempotencyKey);
489
+ if (outcome.error) {
490
+ return await saveFailureRecord(options, startedAt, outcome.error, outcome.isTimedOut);
491
+ }
492
+ return await saveSuccessRecord(options, startedAt, outcome.result);
493
+ };
494
+
266
495
  // src/agent/isolated-tool.ts
267
496
  var verifyRoles = (configuredRoles, context, toolIdentifier) => {
268
497
  if (!configuredRoles || configuredRoles.length === 0) {
269
498
  return;
270
499
  }
271
- const userRoles = [
272
- ...context.roles ?? [],
273
- ...context.role ? [context.role] : []
274
- ];
275
- const hasAllowedRole = configuredRoles.some((role) => userRoles.includes(role));
500
+ if (!context.role && (!context.roles || context.roles.length === 0)) {
501
+ throw new ToolAccessDeniedError(toolIdentifier, "Access denied: insufficient role permissions");
502
+ }
503
+ const roleSet = new Set(context.roles ?? []);
504
+ if (context.role) roleSet.add(context.role);
505
+ const hasAllowedRole = configuredRoles.some((role) => roleSet.has(role));
276
506
  if (!hasAllowedRole) {
277
507
  throw new ToolAccessDeniedError(
278
508
  toolIdentifier,
@@ -389,6 +619,35 @@ var processLlmPayload = async (rawResult, args, context, config, toolIdentifier)
389
619
  config.throwOnPii === true
390
620
  );
391
621
  };
622
+ var buildResilientContext = (config, args, context) => {
623
+ const toolCallId = context.toolCallId || `call_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
624
+ const genKey = config.resilience?.idempotencyKeyGenerator?.(args, context);
625
+ const idempotencyKey = context.idempotencyKey || genKey || toolCallId;
626
+ const resilientContext = { ...context, toolCallId, idempotencyKey };
627
+ return { toolCallId, idempotencyKey, resilientContext };
628
+ };
629
+ var executeWithResilienceGuard = async (config, args, context, toolIdentifier) => {
630
+ if (!config.resilience) {
631
+ return await config.execute(args, context);
632
+ }
633
+ const { toolCallId, idempotencyKey, resilientContext } = buildResilientContext(config, args, context);
634
+ const outcome = await withToolResilience({
635
+ toolCallId,
636
+ toolName: toolIdentifier,
637
+ storage: context.storage,
638
+ impact: config.impact ?? "READ_ONLY",
639
+ idempotencyKey,
640
+ timeoutMs: config.resilience.timeoutMs,
641
+ suspendOnAmbiguous: config.resilience.suspendOnAmbiguous,
642
+ workflowId: context.workflowId,
643
+ args,
644
+ action: async (signal, idKey) => {
645
+ return await config.execute(args, { ...resilientContext, abortSignal: signal, idempotencyKey: idKey });
646
+ },
647
+ onAmbiguousRetry: config.resilience.onAmbiguousRetry
648
+ });
649
+ return outcome.result;
650
+ };
392
651
  var createIsolatedTool = (config) => {
393
652
  const toolId = config.id || config.name;
394
653
  const toolAlias = config.alias;
@@ -410,6 +669,7 @@ var createIsolatedTool = (config) => {
410
669
  _roles: config.roles ? Object.freeze([...config.roles]) : void 0,
411
670
  _impact: toolImpact,
412
671
  _requireApproval: requireApproval,
672
+ _resilience: config.resilience,
413
673
  _lastPiiFilteredCount: 0,
414
674
  _lastInvalidationTags: void 0,
415
675
  execute: async (args, context) => {
@@ -424,7 +684,12 @@ var createIsolatedTool = (config) => {
424
684
  updatedContext,
425
685
  toolIdentifier
426
686
  );
427
- const rawResult = await config.execute(args, updatedContext);
687
+ const rawResult = await executeWithResilienceGuard(
688
+ config,
689
+ args,
690
+ updatedContext,
691
+ toolIdentifier
692
+ );
428
693
  verifyTenantIsolation(config.assertTenant, rawResult, updatedContext, toolIdentifier);
429
694
  await verifyResourceOwnership(
430
695
  config.assertOwnership,
@@ -603,14 +868,22 @@ function toDescriptor(tool) {
603
868
  var ToolRegistry = class _ToolRegistry {
604
869
  toolsById = /* @__PURE__ */ new Map();
605
870
  toolsByPublicName = /* @__PURE__ */ new Map();
606
- /**
607
- * Registers a new tool in the registry.
608
- */
871
+ toolsByDomain = /* @__PURE__ */ new Map();
872
+ toolsByImpact = /* @__PURE__ */ new Map();
609
873
  register(toolDef) {
610
874
  const normalizedDef = normalizeToolDefinition(toolDef);
611
875
  const publicName = normalizedDef.alias || normalizedDef.name;
612
876
  this.toolsById.set(normalizedDef.id, normalizedDef);
613
877
  this.toolsByPublicName.set(publicName, normalizedDef);
878
+ if (normalizedDef.domain) {
879
+ const domainList = this.toolsByDomain.get(normalizedDef.domain) ?? [];
880
+ domainList.push(normalizedDef);
881
+ this.toolsByDomain.set(normalizedDef.domain, domainList);
882
+ }
883
+ const impact = normalizedDef.impact ?? "READ_ONLY";
884
+ const impactList = this.toolsByImpact.get(impact) ?? [];
885
+ impactList.push(normalizedDef);
886
+ this.toolsByImpact.set(impact, impactList);
614
887
  return this;
615
888
  }
616
889
  /**
@@ -656,52 +929,48 @@ var ToolRegistry = class _ToolRegistry {
656
929
  * Filters tools belonging to a specific business domain.
657
930
  */
658
931
  getByDomain(domain) {
659
- return this.getAll().filter((item) => item.domain === domain);
932
+ const tools = this.toolsByDomain.get(domain);
933
+ return tools ? [...tools] : [];
660
934
  }
661
- /**
662
- * Filters tools matching a specific workflow phase.
663
- */
664
935
  getByPhase(phase) {
665
- return this.getAll().filter((item) => {
666
- if (!item.phases || item.phases.length === 0) {
667
- return true;
936
+ const result = [];
937
+ for (const item of this.toolsById.values()) {
938
+ if (!item.phases || item.phases.length === 0 || item.phases.includes(phase)) {
939
+ result.push(item);
668
940
  }
669
- return item.phases.includes(phase);
670
- });
941
+ }
942
+ return result;
671
943
  }
672
- /**
673
- * Filters tools matching the user's roles (RBAC).
674
- */
675
944
  filterByRoles(userRoles) {
676
945
  const roleSet = new Set(userRoles);
677
- return this.getAll().filter((item) => {
946
+ const result = [];
947
+ for (const item of this.toolsById.values()) {
678
948
  const roles = item.roles ?? item.requiredRoles;
679
- if (!roles || roles.length === 0) {
680
- return true;
949
+ if (!roles || roles.length === 0 || roles.some((role) => roleSet.has(role))) {
950
+ result.push(item);
681
951
  }
682
- return roles.some((role) => roleSet.has(role));
683
- });
952
+ }
953
+ return result;
684
954
  }
685
- /**
686
- * Filters tools matching a specific physical impact profile (READ_ONLY, MUTATIVE, DESTRUCTIVE).
687
- */
688
955
  getByImpact(impact) {
689
- return this.getAll().filter((item) => (item.impact ?? "READ_ONLY") === impact);
956
+ const tools = this.toolsByImpact.get(impact);
957
+ return tools ? [...tools] : [];
690
958
  }
691
- /**
692
- * Exports headless technical descriptors without UI rendering coupling.
693
- */
694
959
  getDescriptors(filter) {
695
- let list = this.getAll();
960
+ let list;
696
961
  if (filter?.domain) {
697
- list = list.filter((item) => item.domain === filter.domain);
962
+ list = this.getByDomain(filter.domain);
963
+ } else if (filter?.impact) {
964
+ list = this.getByImpact(filter.impact);
965
+ } else {
966
+ list = this.getAll();
967
+ }
968
+ if (filter?.domain && filter?.impact) {
969
+ list = list.filter((item) => (item.impact ?? "READ_ONLY") === filter.impact);
698
970
  }
699
971
  if (filter?.role) {
700
972
  list = list.filter((item) => matchesRoleFilter(item, filter.role));
701
973
  }
702
- if (filter?.impact) {
703
- list = list.filter((item) => (item.impact ?? "READ_ONLY") === filter.impact);
704
- }
705
974
  return list.map(toDescriptor);
706
975
  }
707
976
  /**
@@ -802,17 +1071,12 @@ function matchesRoles(tool, context) {
802
1071
  if (!toolRoles || toolRoles.length === 0) {
803
1072
  return true;
804
1073
  }
805
- const userRoles = [];
806
- if (context.role) {
807
- userRoles.push(context.role);
808
- }
809
- if (context.roles) {
810
- userRoles.push(...context.roles);
811
- }
812
- if (userRoles.length === 0) {
1074
+ if (!context.role && (!context.roles || context.roles.length === 0)) {
813
1075
  return false;
814
1076
  }
815
- return toolRoles.some((roleName) => userRoles.includes(roleName));
1077
+ const userRoleSet = new Set(context.roles ?? []);
1078
+ if (context.role) userRoleSet.add(context.role);
1079
+ return toolRoles.some((roleName) => userRoleSet.has(roleName));
816
1080
  }
817
1081
  var AccessControlToolStrategy = class {
818
1082
  allowedDomains;
@@ -2140,6 +2404,7 @@ var createAdapterIdempotencyStore = (adapter, namespace = "idempotency") => {
2140
2404
  0 && (module.exports = {
2141
2405
  AccessControlToolStrategy,
2142
2406
  AgentToolFactory,
2407
+ AmbiguousToolExecutionError,
2143
2408
  CircularToolCallError,
2144
2409
  CompositeToolStrategy,
2145
2410
  DtoValidationError,
@@ -2163,6 +2428,7 @@ var createAdapterIdempotencyStore = (adapter, namespace = "idempotency") => {
2163
2428
  ToolSubCallQuotaError,
2164
2429
  applyToolStrategy,
2165
2430
  auditToolResult,
2431
+ clearResilienceFallbackStore,
2166
2432
  createAdapterIdempotencyStore,
2167
2433
  createCustomStorageAdapter,
2168
2434
  createIsolatedTool,
@@ -2172,5 +2438,7 @@ var createAdapterIdempotencyStore = (adapter, namespace = "idempotency") => {
2172
2438
  createToolInvoker,
2173
2439
  createToolSharedState,
2174
2440
  dto,
2175
- withServerIdempotency
2441
+ getToolExecutionRecord,
2442
+ withServerIdempotency,
2443
+ withToolResilience
2176
2444
  });