avantgate 2.2.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.
- package/README.md +3 -1
- package/dist/agent/errors.d.ts +14 -0
- package/dist/agent/errors.d.ts.map +1 -1
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +317 -49
- package/dist/agent/index.mjs +51 -44
- package/dist/agent/isolated-tool.d.ts.map +1 -1
- package/dist/agent/registry.d.ts +2 -15
- package/dist/agent/registry.d.ts.map +1 -1
- package/dist/agent/strategy.d.ts.map +1 -1
- package/dist/agent/tool-resilience.d.ts +5 -0
- package/dist/agent/tool-resilience.d.ts.map +1 -0
- package/dist/agent/types.d.ts +66 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/{chunk-W27A25DK.mjs → chunk-B22KKIQQ.mjs} +271 -6
- package/dist/{chunk-AH5XXSS4.mjs → chunk-KEJ4VNSN.mjs} +1 -1
- package/dist/control-layer.d.ts +30 -3
- package/dist/control-layer.d.ts.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +954 -83
- package/dist/index.mjs +675 -79
- package/dist/middleware.d.ts +15 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/presets.d.ts +13 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/providers/http-client.d.ts +3 -0
- package/dist/providers/http-client.d.ts.map +1 -1
- package/dist/types.d.ts +87 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/immutability.d.ts +5 -0
- package/dist/utils/immutability.d.ts.map +1 -0
- package/dist/workflow/index.js +264 -6
- package/dist/workflow/index.mjs +2 -2
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ Contact our team at [contact@gatewall.fr](mailto:contact@gatewall.fr) for privat
|
|
|
94
94
|
| 📊 **Observability & Cockpit** | [GateWall Cockpit Console](docs/observability/gatewall-cockpit.md) • [Telemetry & Browser SDK](docs/observability/telemetry-and-browser-sdk.md) • [GateWall vs. Temporal](docs/observability/temporal-and-gatewall.md) |
|
|
95
95
|
| 💶 **Accounting** | [Financial Normalizer](docs/finance/normalizer.md) |
|
|
96
96
|
| 🎨 **Presentation Plane** | [Headless UI Canvas & React Bindings](docs/ui/headless-canvas.md) |
|
|
97
|
-
| 🚀 **Release Notes** | [v2.1.0 Release Notes](docs/release/release-2.1.0.md) • [v2.0.0 Release Notes](docs/release/release-2.0.0.md) • [v1.9.0 Release Notes](docs/release/release-1.9.0.md) |
|
|
97
|
+
| 🚀 **Release Notes** | [v2.3.0 Release Notes](docs/release/release-2.3.0.md) • [v2.2.0 Release Notes](docs/release/release-2.2.0.md) • [v2.1.0 Release Notes](docs/release/release-2.1.0.md) • [v2.0.0 Release Notes](docs/release/release-2.0.0.md) • [v1.9.0 Release Notes](docs/release/release-1.9.0.md) |
|
|
98
98
|
|
|
99
99
|
---
|
|
100
100
|
|
|
@@ -127,6 +127,8 @@ pnpm add avantgate zod
|
|
|
127
127
|
yarn add avantgate zod
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
> 🚀 **Quickstart Guide:** See [**Getting Started with AvantGate**](docs/getting-started.md) for 3-minute quickstart recipes with launch-safe presets and compile-time anti-IDOR tools.
|
|
131
|
+
|
|
130
132
|
### 🧩 Subpath Exports
|
|
131
133
|
|
|
132
134
|
| Import Path | Description | Documentation |
|
package/dist/agent/errors.d.ts
CHANGED
|
@@ -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"}
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/agent/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
];
|
|
275
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
949
|
+
if (!roles || roles.length === 0 || roles.some((role) => roleSet.has(role))) {
|
|
950
|
+
result.push(item);
|
|
681
951
|
}
|
|
682
|
-
|
|
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
|
-
|
|
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
|
|
960
|
+
let list;
|
|
696
961
|
if (filter?.domain) {
|
|
697
|
-
list =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2441
|
+
getToolExecutionRecord,
|
|
2442
|
+
withServerIdempotency,
|
|
2443
|
+
withToolResilience
|
|
2176
2444
|
});
|