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/dist/index.js
CHANGED
|
@@ -36,13 +36,22 @@ __export(index_exports, {
|
|
|
36
36
|
AvantGateSecretLeakBlockedError: () => SecretLeakBlockedError,
|
|
37
37
|
BudgetExceededError: () => BudgetExceededError,
|
|
38
38
|
CachedPricingAdapter: () => CachedPricingAdapter,
|
|
39
|
+
ChatMessageSchema: () => ChatMessageSchema,
|
|
40
|
+
ChatMessagesArraySchema: () => ChatMessagesArraySchema,
|
|
41
|
+
ChatRoleSchema: () => ChatRoleSchema,
|
|
39
42
|
ConfigurationError: () => ConfigurationError,
|
|
40
43
|
DEFAULT_MODEL_PRICES: () => DEFAULT_MODEL_PRICES,
|
|
41
44
|
HttpProviderClient: () => HttpProviderClient,
|
|
45
|
+
InvalidMessageSchemaError: () => InvalidMessageSchemaError,
|
|
46
|
+
MiddlewareTimeoutError: () => MiddlewareTimeoutError,
|
|
47
|
+
PRESETS: () => PRESETS,
|
|
42
48
|
PricingRegistry: () => PricingRegistry,
|
|
43
49
|
PromptBuilder: () => PromptBuilder,
|
|
50
|
+
PromptInjectionError: () => PromptInjectionError,
|
|
44
51
|
PromptRegistry: () => PromptRegistry,
|
|
45
52
|
PromptTemplate: () => PromptTemplate,
|
|
53
|
+
ProviderIdleTimeoutError: () => ProviderIdleTimeoutError,
|
|
54
|
+
ProviderTTFTTimeoutError: () => ProviderTTFTTimeoutError,
|
|
46
55
|
SEED_MODEL_PRICES: () => SEED_MODEL_PRICES,
|
|
47
56
|
SecretLeakBlockedError: () => SecretLeakBlockedError,
|
|
48
57
|
SqlitePricingAdapter: () => SqlitePricingAdapter,
|
|
@@ -51,11 +60,19 @@ __export(index_exports, {
|
|
|
51
60
|
WorkflowValidationError: () => WorkflowValidationError,
|
|
52
61
|
ZenLLMControlLayer: () => AvantGateControlLayer,
|
|
53
62
|
applyOutputGuards: () => applyOutputGuards,
|
|
63
|
+
applySingleAfterMiddleware: () => applySingleAfterMiddleware,
|
|
64
|
+
applySingleBeforeMiddleware: () => applySingleBeforeMiddleware,
|
|
54
65
|
calculateCostUSD: () => calculateCostUSD,
|
|
55
66
|
createAvantGate: () => createAvantGate,
|
|
67
|
+
createDevPermissiveConfig: () => createDevPermissiveConfig,
|
|
68
|
+
createEnterpriseStrictConfig: () => createEnterpriseStrictConfig,
|
|
56
69
|
createHttpProviderClient: () => createHttpProviderClient,
|
|
57
70
|
createLLMControlLayer: () => createLLMControlLayer,
|
|
71
|
+
createLaunchSafeConfig: () => createLaunchSafeConfig,
|
|
72
|
+
deepFreeze: () => deepFreeze,
|
|
58
73
|
defineWorkflow: () => defineWorkflow,
|
|
74
|
+
executeAfterResponseHooks: () => executeAfterResponseHooks,
|
|
75
|
+
executeBeforeRequestHooks: () => executeBeforeRequestHooks,
|
|
59
76
|
executeWorkflow: () => executeWorkflow,
|
|
60
77
|
extractAndCleanJSON: () => extractAndCleanJSON,
|
|
61
78
|
isWorkflowAbortSignal: () => isWorkflowAbortSignal,
|
|
@@ -70,6 +87,13 @@ __export(index_exports, {
|
|
|
70
87
|
module.exports = __toCommonJS(index_exports);
|
|
71
88
|
|
|
72
89
|
// src/types.ts
|
|
90
|
+
var import_zod = require("zod");
|
|
91
|
+
var ChatRoleSchema = import_zod.z.enum(["system", "user", "assistant", "tool"]);
|
|
92
|
+
var ChatMessageSchema = import_zod.z.object({
|
|
93
|
+
role: ChatRoleSchema,
|
|
94
|
+
content: import_zod.z.string().min(1, "Message content cannot be empty")
|
|
95
|
+
}).strict();
|
|
96
|
+
var ChatMessagesArraySchema = import_zod.z.array(ChatMessageSchema);
|
|
73
97
|
var ConfigurationError = class extends Error {
|
|
74
98
|
constructor(message) {
|
|
75
99
|
super(message);
|
|
@@ -82,6 +106,22 @@ var BudgetExceededError = class extends Error {
|
|
|
82
106
|
this.name = "BudgetExceededError";
|
|
83
107
|
}
|
|
84
108
|
};
|
|
109
|
+
var ProviderTTFTTimeoutError = class extends Error {
|
|
110
|
+
ttftTimeoutMs;
|
|
111
|
+
constructor(message, ttftTimeoutMs) {
|
|
112
|
+
super(message);
|
|
113
|
+
this.name = "ProviderTTFTTimeoutError";
|
|
114
|
+
this.ttftTimeoutMs = ttftTimeoutMs;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var ProviderIdleTimeoutError = class extends Error {
|
|
118
|
+
idleTimeoutMs;
|
|
119
|
+
constructor(message, idleTimeoutMs) {
|
|
120
|
+
super(message);
|
|
121
|
+
this.name = "ProviderIdleTimeoutError";
|
|
122
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
85
125
|
var SecretLeakBlockedError = class extends Error {
|
|
86
126
|
detections;
|
|
87
127
|
constructor(message, detections) {
|
|
@@ -1099,11 +1139,15 @@ var HttpProviderClient = class {
|
|
|
1099
1139
|
baseUrl;
|
|
1100
1140
|
apiKey;
|
|
1101
1141
|
providerType;
|
|
1142
|
+
ttftTimeoutMs;
|
|
1143
|
+
idleTimeoutMs;
|
|
1102
1144
|
constructor(config) {
|
|
1103
1145
|
this.providerType = config.provider;
|
|
1104
1146
|
this.name = `${config.provider}-http`;
|
|
1105
1147
|
this.apiKey = config.apiKey;
|
|
1106
1148
|
this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
|
|
1149
|
+
this.ttftTimeoutMs = config.ttftTimeoutMs ?? 15e3;
|
|
1150
|
+
this.idleTimeoutMs = config.idleTimeoutMs ?? 5e3;
|
|
1107
1151
|
}
|
|
1108
1152
|
resolveEndpoint = () => {
|
|
1109
1153
|
const trimmed = this.baseUrl.replace(/\/+$/, "");
|
|
@@ -1125,7 +1169,7 @@ var HttpProviderClient = class {
|
|
|
1125
1169
|
}
|
|
1126
1170
|
return headers;
|
|
1127
1171
|
};
|
|
1128
|
-
buildPayload = (options, model) => {
|
|
1172
|
+
buildPayload = (options, model, stream = false) => {
|
|
1129
1173
|
return {
|
|
1130
1174
|
model,
|
|
1131
1175
|
messages: options.messages.map((m) => ({
|
|
@@ -1133,6 +1177,7 @@ var HttpProviderClient = class {
|
|
|
1133
1177
|
content: m.content
|
|
1134
1178
|
})),
|
|
1135
1179
|
temperature: options.temperature ?? 0.2,
|
|
1180
|
+
stream,
|
|
1136
1181
|
...options.responseFormat ? { response_format: options.responseFormat } : {}
|
|
1137
1182
|
};
|
|
1138
1183
|
};
|
|
@@ -1147,22 +1192,101 @@ var HttpProviderClient = class {
|
|
|
1147
1192
|
promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
|
|
1148
1193
|
};
|
|
1149
1194
|
};
|
|
1195
|
+
readStreamWithHeartbeat = async (reader, controller) => {
|
|
1196
|
+
const decoder = new TextDecoder();
|
|
1197
|
+
let accumulatedText = "";
|
|
1198
|
+
let isFirstTokenReceived = false;
|
|
1199
|
+
let activeTimer = setTimeout(() => {
|
|
1200
|
+
controller.abort();
|
|
1201
|
+
}, this.ttftTimeoutMs);
|
|
1202
|
+
try {
|
|
1203
|
+
while (true) {
|
|
1204
|
+
const { done, value } = await reader.read();
|
|
1205
|
+
if (controller.signal.aborted) {
|
|
1206
|
+
throw new Error("Aborted");
|
|
1207
|
+
}
|
|
1208
|
+
if (done) {
|
|
1209
|
+
if (activeTimer) clearTimeout(activeTimer);
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1212
|
+
if (value && value.length > 0) {
|
|
1213
|
+
if (activeTimer) clearTimeout(activeTimer);
|
|
1214
|
+
if (!isFirstTokenReceived) {
|
|
1215
|
+
isFirstTokenReceived = true;
|
|
1216
|
+
}
|
|
1217
|
+
activeTimer = setTimeout(() => {
|
|
1218
|
+
controller.abort();
|
|
1219
|
+
reader.cancel().catch(() => {
|
|
1220
|
+
});
|
|
1221
|
+
}, this.idleTimeoutMs);
|
|
1222
|
+
accumulatedText += decoder.decode(value, { stream: true });
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
} catch (err) {
|
|
1226
|
+
if (controller.signal.aborted) {
|
|
1227
|
+
if (!isFirstTokenReceived) {
|
|
1228
|
+
throw new ProviderTTFTTimeoutError(
|
|
1229
|
+
`[AvantGate HTTP Provider ${this.providerType}] Time-to-First-Token (TTFT) timeout of ${this.ttftTimeoutMs}ms exceeded without receiving initial response bytes.`,
|
|
1230
|
+
this.ttftTimeoutMs
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
throw new ProviderIdleTimeoutError(
|
|
1234
|
+
`[AvantGate HTTP Provider ${this.providerType}] Idle read timeout of ${this.idleTimeoutMs}ms exceeded during stream generation.`,
|
|
1235
|
+
this.idleTimeoutMs
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
throw err;
|
|
1239
|
+
} finally {
|
|
1240
|
+
if (activeTimer) clearTimeout(activeTimer);
|
|
1241
|
+
}
|
|
1242
|
+
return accumulatedText;
|
|
1243
|
+
};
|
|
1150
1244
|
complete = async (options) => {
|
|
1151
1245
|
const endpoint = this.resolveEndpoint();
|
|
1152
1246
|
const model = options.model ?? "default";
|
|
1153
1247
|
const headers = this.buildHeaders();
|
|
1154
1248
|
const body = JSON.stringify(this.buildPayload(options, model));
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1249
|
+
const abortController = new AbortController();
|
|
1250
|
+
const ttftTimer = setTimeout(() => {
|
|
1251
|
+
abortController.abort();
|
|
1252
|
+
}, this.ttftTimeoutMs);
|
|
1253
|
+
let response;
|
|
1254
|
+
try {
|
|
1255
|
+
response = await fetch(endpoint, {
|
|
1256
|
+
method: "POST",
|
|
1257
|
+
headers,
|
|
1258
|
+
body,
|
|
1259
|
+
signal: abortController.signal
|
|
1260
|
+
});
|
|
1261
|
+
} catch (err) {
|
|
1262
|
+
if (abortController.signal.aborted) {
|
|
1263
|
+
throw new ProviderTTFTTimeoutError(
|
|
1264
|
+
`[AvantGate HTTP Provider ${this.providerType}] Time-to-First-Token (TTFT) timeout of ${this.ttftTimeoutMs}ms exceeded before receiving headers.`,
|
|
1265
|
+
this.ttftTimeoutMs
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
throw err;
|
|
1269
|
+
} finally {
|
|
1270
|
+
clearTimeout(ttftTimer);
|
|
1271
|
+
}
|
|
1160
1272
|
if (!response.ok) {
|
|
1161
1273
|
const errorText = await response.text();
|
|
1162
1274
|
throw new Error(
|
|
1163
1275
|
`[AvantGate HTTP Provider ${this.providerType}] HTTP ${response.status} ${response.statusText}: ${errorText}`
|
|
1164
1276
|
);
|
|
1165
1277
|
}
|
|
1278
|
+
if (response.body) {
|
|
1279
|
+
const reader = response.body.getReader();
|
|
1280
|
+
const rawText = await this.readStreamWithHeartbeat(reader, abortController);
|
|
1281
|
+
try {
|
|
1282
|
+
const data2 = JSON.parse(rawText);
|
|
1283
|
+
const text2 = data2.choices?.[0]?.message?.content ?? "";
|
|
1284
|
+
const usage2 = this.extractUsage(data2.usage);
|
|
1285
|
+
return { text: text2, usage: usage2 };
|
|
1286
|
+
} catch {
|
|
1287
|
+
return { text: rawText };
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1166
1290
|
const data = await response.json();
|
|
1167
1291
|
const text = data.choices?.[0]?.message?.content ?? "";
|
|
1168
1292
|
const usage = this.extractUsage(data.usage);
|
|
@@ -1347,10 +1471,168 @@ var applyOutputGuards = (rawOutput, options) => {
|
|
|
1347
1471
|
};
|
|
1348
1472
|
};
|
|
1349
1473
|
|
|
1474
|
+
// src/utils/immutability.ts
|
|
1475
|
+
var deepFreeze = (target, options, currentDepth = 0, visited = /* @__PURE__ */ new WeakSet()) => {
|
|
1476
|
+
if (target === null || typeof target !== "object") {
|
|
1477
|
+
return target;
|
|
1478
|
+
}
|
|
1479
|
+
if (visited.has(target)) {
|
|
1480
|
+
return target;
|
|
1481
|
+
}
|
|
1482
|
+
const maxDepth = options?.maxDepth ?? 10;
|
|
1483
|
+
if (Object.isFrozen(target) || currentDepth >= maxDepth) {
|
|
1484
|
+
return target;
|
|
1485
|
+
}
|
|
1486
|
+
if (target instanceof Date || target instanceof RegExp || target instanceof Promise) {
|
|
1487
|
+
return Object.freeze(target);
|
|
1488
|
+
}
|
|
1489
|
+
visited.add(target);
|
|
1490
|
+
const values = Array.isArray(target) ? target : Object.values(target);
|
|
1491
|
+
for (const value of values) {
|
|
1492
|
+
if (value && typeof value === "object") {
|
|
1493
|
+
deepFreeze(value, options, currentDepth + 1, visited);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return Object.freeze(target);
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
// src/errors.ts
|
|
1500
|
+
var MiddlewareTimeoutError = class extends Error {
|
|
1501
|
+
middlewareName;
|
|
1502
|
+
timeoutMs;
|
|
1503
|
+
phase;
|
|
1504
|
+
constructor(middlewareName, phase, timeoutMs) {
|
|
1505
|
+
super(`Middleware "${middlewareName}" execution timed out during ${phase} after ${timeoutMs}ms.`);
|
|
1506
|
+
this.name = "MiddlewareTimeoutError";
|
|
1507
|
+
this.middlewareName = middlewareName;
|
|
1508
|
+
this.phase = phase;
|
|
1509
|
+
this.timeoutMs = timeoutMs;
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
var InvalidMessageSchemaError = class extends Error {
|
|
1513
|
+
middlewareName;
|
|
1514
|
+
validationErrors;
|
|
1515
|
+
constructor(middlewareName, validationErrors) {
|
|
1516
|
+
super(`Middleware "${middlewareName}" returned an invalid messages payload failing ChatMessageSchema validation.`);
|
|
1517
|
+
this.name = "InvalidMessageSchemaError";
|
|
1518
|
+
this.middlewareName = middlewareName;
|
|
1519
|
+
this.validationErrors = validationErrors;
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
var PromptInjectionError = class extends Error {
|
|
1523
|
+
reason;
|
|
1524
|
+
constructor(reason) {
|
|
1525
|
+
super(`[AvantGate Security Guard] Request blocked: ${reason}`);
|
|
1526
|
+
this.name = "PromptInjectionError";
|
|
1527
|
+
this.reason = reason;
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
// src/middleware.ts
|
|
1532
|
+
var executeWithTimeout = async (action, timeoutMs, middlewareName, phase) => {
|
|
1533
|
+
let timer;
|
|
1534
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1535
|
+
timer = setTimeout(() => {
|
|
1536
|
+
reject(new MiddlewareTimeoutError(middlewareName, phase, timeoutMs));
|
|
1537
|
+
}, timeoutMs);
|
|
1538
|
+
});
|
|
1539
|
+
try {
|
|
1540
|
+
return await Promise.race([Promise.resolve(action()), timeoutPromise]);
|
|
1541
|
+
} finally {
|
|
1542
|
+
if (timer) {
|
|
1543
|
+
clearTimeout(timer);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
var validateReturnedMessages = (messages, middlewareName) => {
|
|
1548
|
+
if (!messages) return;
|
|
1549
|
+
const parseResult = ChatMessagesArraySchema.safeParse(messages);
|
|
1550
|
+
if (!parseResult.success) {
|
|
1551
|
+
throw new InvalidMessageSchemaError(middlewareName, parseResult.error.format());
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
var applySingleBeforeMiddleware = async (middleware, current, model, temperature, timeoutMs = 5e3) => {
|
|
1555
|
+
if (!middleware.beforeRequest) return current;
|
|
1556
|
+
const frozenMessages = deepFreeze([...current.messages]);
|
|
1557
|
+
const frozenMetadata = current.metadata ? deepFreeze({ ...current.metadata }) : void 0;
|
|
1558
|
+
const result = await executeWithTimeout(
|
|
1559
|
+
() => middleware.beforeRequest({
|
|
1560
|
+
messages: frozenMessages,
|
|
1561
|
+
model,
|
|
1562
|
+
temperature,
|
|
1563
|
+
metadata: frozenMetadata
|
|
1564
|
+
}),
|
|
1565
|
+
timeoutMs,
|
|
1566
|
+
middleware.name,
|
|
1567
|
+
"beforeRequest"
|
|
1568
|
+
);
|
|
1569
|
+
if (!result) return current;
|
|
1570
|
+
validateReturnedMessages(result.messages, middleware.name);
|
|
1571
|
+
return {
|
|
1572
|
+
messages: result.messages ?? current.messages,
|
|
1573
|
+
shortCircuit: result.shortCircuit,
|
|
1574
|
+
metadata: current.metadata
|
|
1575
|
+
};
|
|
1576
|
+
};
|
|
1577
|
+
var executeBeforeRequestHooks = async (middlewares, context, timeoutMs = 5e3) => {
|
|
1578
|
+
if (!middlewares || middlewares.length === 0) {
|
|
1579
|
+
return { messages: context.messages, metadata: context.metadata };
|
|
1580
|
+
}
|
|
1581
|
+
let current = {
|
|
1582
|
+
messages: context.messages,
|
|
1583
|
+
metadata: context.metadata
|
|
1584
|
+
};
|
|
1585
|
+
for (const middleware of middlewares) {
|
|
1586
|
+
current = await applySingleBeforeMiddleware(
|
|
1587
|
+
middleware,
|
|
1588
|
+
current,
|
|
1589
|
+
context.model,
|
|
1590
|
+
context.temperature,
|
|
1591
|
+
timeoutMs
|
|
1592
|
+
);
|
|
1593
|
+
if (current.shortCircuit) return current;
|
|
1594
|
+
}
|
|
1595
|
+
return current;
|
|
1596
|
+
};
|
|
1597
|
+
var applySingleAfterMiddleware = async (middleware, current, baseContext, timeoutMs = 5e3) => {
|
|
1598
|
+
if (!middleware.afterResponse) return current;
|
|
1599
|
+
const frozenMetadata = current.metadata ? deepFreeze({ ...current.metadata }) : void 0;
|
|
1600
|
+
const result = await executeWithTimeout(
|
|
1601
|
+
() => middleware.afterResponse({
|
|
1602
|
+
...baseContext,
|
|
1603
|
+
responseText: current.responseText,
|
|
1604
|
+
metadata: frozenMetadata
|
|
1605
|
+
}),
|
|
1606
|
+
timeoutMs,
|
|
1607
|
+
middleware.name,
|
|
1608
|
+
"afterResponse"
|
|
1609
|
+
);
|
|
1610
|
+
if (!result) return current;
|
|
1611
|
+
return {
|
|
1612
|
+
responseText: result.responseText ?? current.responseText,
|
|
1613
|
+
metadata: result.metadata ?? current.metadata
|
|
1614
|
+
};
|
|
1615
|
+
};
|
|
1616
|
+
var executeAfterResponseHooks = async (middlewares, context, timeoutMs = 5e3) => {
|
|
1617
|
+
if (!middlewares || middlewares.length === 0) {
|
|
1618
|
+
return { responseText: context.responseText, metadata: context.metadata };
|
|
1619
|
+
}
|
|
1620
|
+
let current = {
|
|
1621
|
+
responseText: context.responseText,
|
|
1622
|
+
metadata: context.metadata
|
|
1623
|
+
};
|
|
1624
|
+
for (const middleware of middlewares) {
|
|
1625
|
+
current = await applySingleAfterMiddleware(middleware, current, context, timeoutMs);
|
|
1626
|
+
}
|
|
1627
|
+
return current;
|
|
1628
|
+
};
|
|
1629
|
+
|
|
1350
1630
|
// src/control-layer.ts
|
|
1351
1631
|
var AvantGateControlLayer = class {
|
|
1352
1632
|
config;
|
|
1353
1633
|
cachedPricingAdapter;
|
|
1634
|
+
reservations = /* @__PURE__ */ new Map();
|
|
1635
|
+
cachedProviderChain;
|
|
1354
1636
|
constructor(config) {
|
|
1355
1637
|
this.config = {
|
|
1356
1638
|
...config,
|
|
@@ -1358,6 +1640,11 @@ var AvantGateControlLayer = class {
|
|
|
1358
1640
|
fallback: this.resolveProviderConfig(config.fallback),
|
|
1359
1641
|
emergencyFallback: this.resolveProviderConfig(config.emergencyFallback)
|
|
1360
1642
|
};
|
|
1643
|
+
this.cachedProviderChain = [
|
|
1644
|
+
this.config.primary,
|
|
1645
|
+
this.config.fallback,
|
|
1646
|
+
this.config.emergencyFallback
|
|
1647
|
+
].filter((provider) => Boolean(provider?.client));
|
|
1361
1648
|
if (config.pricingAdapter) {
|
|
1362
1649
|
this.cachedPricingAdapter = new CachedPricingAdapter(
|
|
1363
1650
|
config.pricingAdapter,
|
|
@@ -1365,6 +1652,12 @@ var AvantGateControlLayer = class {
|
|
|
1365
1652
|
);
|
|
1366
1653
|
}
|
|
1367
1654
|
}
|
|
1655
|
+
getReservation = (id) => {
|
|
1656
|
+
return this.reservations.get(id);
|
|
1657
|
+
};
|
|
1658
|
+
getAllReservations = () => {
|
|
1659
|
+
return Array.from(this.reservations.values());
|
|
1660
|
+
};
|
|
1368
1661
|
resolveProviderConfig = (provider) => {
|
|
1369
1662
|
if (!provider) return void 0;
|
|
1370
1663
|
if (provider.client) return provider;
|
|
@@ -1377,16 +1670,15 @@ var AvantGateControlLayer = class {
|
|
|
1377
1670
|
return provider;
|
|
1378
1671
|
};
|
|
1379
1672
|
findProviderConfig = (provider, model) => {
|
|
1380
|
-
const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
|
|
1381
|
-
(p) => Boolean(p)
|
|
1382
|
-
);
|
|
1383
1673
|
if (provider) {
|
|
1384
|
-
|
|
1385
|
-
if (
|
|
1674
|
+
if (this.config.primary.provider === provider) return this.config.primary;
|
|
1675
|
+
if (this.config.fallback?.provider === provider) return this.config.fallback;
|
|
1676
|
+
if (this.config.emergencyFallback?.provider === provider) return this.config.emergencyFallback;
|
|
1386
1677
|
}
|
|
1387
1678
|
if (model) {
|
|
1388
|
-
|
|
1389
|
-
if (
|
|
1679
|
+
if (this.config.primary.model === model) return this.config.primary;
|
|
1680
|
+
if (this.config.fallback?.model === model) return this.config.fallback;
|
|
1681
|
+
if (this.config.emergencyFallback?.model === model) return this.config.emergencyFallback;
|
|
1390
1682
|
}
|
|
1391
1683
|
return this.config.primary;
|
|
1392
1684
|
};
|
|
@@ -1405,7 +1697,7 @@ var AvantGateControlLayer = class {
|
|
|
1405
1697
|
maxLength: this.config.security?.maxInputLength
|
|
1406
1698
|
});
|
|
1407
1699
|
if (!guard.valid) {
|
|
1408
|
-
throw new
|
|
1700
|
+
throw new PromptInjectionError(guard.blockedReason ?? "Prompt injection detected");
|
|
1409
1701
|
}
|
|
1410
1702
|
let processed = userQuery;
|
|
1411
1703
|
if (this.config.security?.customRedactionTerms && this.config.security.customRedactionTerms.length > 0) {
|
|
@@ -1416,6 +1708,21 @@ var AvantGateControlLayer = class {
|
|
|
1416
1708
|
}
|
|
1417
1709
|
return processed;
|
|
1418
1710
|
};
|
|
1711
|
+
reScanMessagesAfterHooks = (originalMessages, updatedMessages) => {
|
|
1712
|
+
if (this.config.reScanIngressAfterHooks === false || originalMessages === updatedMessages) {
|
|
1713
|
+
return updatedMessages;
|
|
1714
|
+
}
|
|
1715
|
+
return updatedMessages.map((msg, idx) => {
|
|
1716
|
+
const orig = originalMessages[idx];
|
|
1717
|
+
if (orig && (orig === msg || orig.role === msg.role && orig.content === msg.content)) {
|
|
1718
|
+
return msg;
|
|
1719
|
+
}
|
|
1720
|
+
return {
|
|
1721
|
+
...msg,
|
|
1722
|
+
content: this.applySecurityGuards(msg.content)
|
|
1723
|
+
};
|
|
1724
|
+
});
|
|
1725
|
+
};
|
|
1419
1726
|
applyOutputSecurityGuards = (rawOutput) => {
|
|
1420
1727
|
const isDlpEnabled = this.config.security?.outputDLP ?? this.config.security?.maskPII ?? false;
|
|
1421
1728
|
const shouldCheckSecrets = this.config.security?.blockSecretLeaks ?? true;
|
|
@@ -1431,33 +1738,79 @@ var AvantGateControlLayer = class {
|
|
|
1431
1738
|
});
|
|
1432
1739
|
return guardResult.text;
|
|
1433
1740
|
};
|
|
1434
|
-
|
|
1435
|
-
if (this.config.maxTokenBudget !== void 0 &&
|
|
1741
|
+
validateTokenBudget = (estimatedTokens) => {
|
|
1742
|
+
if (this.config.maxTokenBudget !== void 0 && estimatedTokens > this.config.maxTokenBudget) {
|
|
1436
1743
|
throw new BudgetExceededError(
|
|
1437
|
-
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${
|
|
1744
|
+
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
1438
1745
|
);
|
|
1439
1746
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1747
|
+
};
|
|
1748
|
+
validatePricingConfigured = async (targetModel, provider) => {
|
|
1749
|
+
const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
|
|
1750
|
+
if (isLocalFree) return;
|
|
1751
|
+
const providerCfg = this.findProviderConfig(provider, targetModel);
|
|
1752
|
+
const price = await resolveModelPriceAsync(targetModel, {
|
|
1753
|
+
provider: provider ?? providerCfg?.provider,
|
|
1754
|
+
providerPricing: providerCfg?.pricing,
|
|
1755
|
+
customPricing: this.config.customPricing,
|
|
1756
|
+
adapter: this.cachedPricingAdapter
|
|
1757
|
+
});
|
|
1758
|
+
if (!price) {
|
|
1759
|
+
throw new ConfigurationError(
|
|
1760
|
+
`[AvantGate Configuration Error] 'maxCostUSD' was set to $${this.config.maxCostUSD}, but no pricing was configured for model '${targetModel}'. Please define pricing in ProviderConfig, customPricing, or via PricingAdapter.`
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
evaluatePessimisticSolvency = (estimatedPromptCost, parentRunId) => {
|
|
1765
|
+
if (!parentRunId || this.config.maxCostUSD === void 0) return;
|
|
1766
|
+
let hasUnconfirmed = false;
|
|
1767
|
+
let settledSpend = 0;
|
|
1768
|
+
for (const r of this.reservations.values()) {
|
|
1769
|
+
if (r.parentRunId !== parentRunId) continue;
|
|
1770
|
+
if (r.status === "UNCONFIRMED_TIMEOUT") hasUnconfirmed = true;
|
|
1771
|
+
if (r.status === "SETTLED") {
|
|
1772
|
+
settledSpend += r.settledCostUsd ?? r.estimatedCostUsd;
|
|
1459
1773
|
}
|
|
1460
1774
|
}
|
|
1775
|
+
if (!hasUnconfirmed) return;
|
|
1776
|
+
const totalWorstCaseCost = settledSpend + 2 * estimatedPromptCost;
|
|
1777
|
+
if (totalWorstCaseCost > this.config.maxCostUSD) {
|
|
1778
|
+
throw new BudgetExceededError(
|
|
1779
|
+
`[AvantGate FinOps Guard] Retry rejected: worst-case cumulative spend ($${totalWorstCaseCost.toFixed(4)}) exceeds max budget ($${this.config.maxCostUSD.toFixed(4)}).`
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
createBudgetReservation = (estimatedCostUsd, parentRunId) => {
|
|
1784
|
+
const reservationId = `res_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
1785
|
+
const reservation = {
|
|
1786
|
+
id: reservationId,
|
|
1787
|
+
parentRunId,
|
|
1788
|
+
estimatedCostUsd,
|
|
1789
|
+
status: "RESERVED",
|
|
1790
|
+
createdAt: Date.now(),
|
|
1791
|
+
updatedAt: Date.now()
|
|
1792
|
+
};
|
|
1793
|
+
if (this.reservations.size >= 1e4) {
|
|
1794
|
+
const oldestKey = this.reservations.keys().next().value;
|
|
1795
|
+
if (oldestKey) this.reservations.delete(oldestKey);
|
|
1796
|
+
}
|
|
1797
|
+
this.reservations.set(reservationId, reservation);
|
|
1798
|
+
return reservation;
|
|
1799
|
+
};
|
|
1800
|
+
checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider, parentRunId) => {
|
|
1801
|
+
this.validateTokenBudget(estimatedPromptTokens);
|
|
1802
|
+
if (this.config.maxCostUSD === void 0) {
|
|
1803
|
+
return void 0;
|
|
1804
|
+
}
|
|
1805
|
+
await this.validatePricingConfigured(targetModel, provider);
|
|
1806
|
+
const estimatedPromptCost = this.calculateCost(targetModel, estimatedPromptTokens, 0, 0, provider);
|
|
1807
|
+
this.evaluatePessimisticSolvency(estimatedPromptCost, parentRunId);
|
|
1808
|
+
if (estimatedPromptCost > this.config.maxCostUSD) {
|
|
1809
|
+
throw new BudgetExceededError(
|
|
1810
|
+
`[AvantGate Budget Guard] Pre-flight cost budget exceeded: estimated prompt cost ($${estimatedPromptCost.toFixed(6)}) exceeds maxCostUSD ($${this.config.maxCostUSD}).`
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1813
|
+
return this.createBudgetReservation(estimatedPromptCost, parentRunId);
|
|
1461
1814
|
};
|
|
1462
1815
|
checkPostExecutionBudget = (tokensTotal, costUSD) => {
|
|
1463
1816
|
if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
|
|
@@ -1491,11 +1844,7 @@ var AvantGateControlLayer = class {
|
|
|
1491
1844
|
};
|
|
1492
1845
|
};
|
|
1493
1846
|
getProviderChain = () => {
|
|
1494
|
-
return
|
|
1495
|
-
this.config.primary,
|
|
1496
|
-
this.config.fallback,
|
|
1497
|
-
this.config.emergencyFallback
|
|
1498
|
-
].filter((provider) => Boolean(provider?.client));
|
|
1847
|
+
return this.cachedProviderChain;
|
|
1499
1848
|
};
|
|
1500
1849
|
executeSimulation = (query, systemPrompt, modelOverride) => {
|
|
1501
1850
|
const targetModel = modelOverride ?? this.config.primary.model;
|
|
@@ -1590,28 +1939,140 @@ var AvantGateControlLayer = class {
|
|
|
1590
1939
|
attempts: result.attempts
|
|
1591
1940
|
});
|
|
1592
1941
|
};
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1942
|
+
prepareInputMessages = (options) => {
|
|
1943
|
+
if (options.messages && options.messages.length > 0) {
|
|
1944
|
+
return this.sanitizeIncomingMessages(options.messages);
|
|
1945
|
+
}
|
|
1946
|
+
if (options.userQuery !== void 0) {
|
|
1947
|
+
const sanitizedQuery = this.applySecurityGuards(options.userQuery);
|
|
1948
|
+
return this.buildMessages(options.systemPrompt, sanitizedQuery);
|
|
1949
|
+
}
|
|
1950
|
+
throw new ConfigurationError("[AvantGate Input Guard] Either 'userQuery' or 'messages' must be provided.");
|
|
1951
|
+
};
|
|
1952
|
+
handleShortCircuit = async (shortCircuit, targetModel) => {
|
|
1953
|
+
const sanitizedText = this.applyOutputSecurityGuards(shortCircuit.text);
|
|
1954
|
+
const result = {
|
|
1955
|
+
text: sanitizedText,
|
|
1956
|
+
tokens: {
|
|
1957
|
+
prompt: shortCircuit.tokens?.promptTokens ?? 0,
|
|
1958
|
+
completion: shortCircuit.tokens?.completionTokens ?? 0,
|
|
1959
|
+
total: shortCircuit.tokens?.totalTokens ?? 0
|
|
1960
|
+
},
|
|
1961
|
+
costUSD: shortCircuit.costUSD ?? 0,
|
|
1962
|
+
modelUsed: `${targetModel}:shortcircuit`,
|
|
1963
|
+
failoverOccurred: false,
|
|
1964
|
+
attempts: 0
|
|
1965
|
+
};
|
|
1966
|
+
await this.notifyAuditSink(result);
|
|
1967
|
+
return result;
|
|
1968
|
+
};
|
|
1969
|
+
dispatchPromptExecution = async (messages, temperature, providerOverride) => {
|
|
1970
|
+
const query = messages[messages.length - 1]?.content ?? "";
|
|
1971
|
+
if (providerOverride) {
|
|
1972
|
+
return this.executeOverrideProvider(providerOverride, messages, query, temperature);
|
|
1973
|
+
}
|
|
1974
|
+
if (this.getProviderChain().length === 0) {
|
|
1600
1975
|
if (this.config.mockSimulation) {
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1976
|
+
const sim = this.executeSimulation(query);
|
|
1977
|
+
return {
|
|
1978
|
+
responseText: sim.text,
|
|
1979
|
+
usage: { promptTokens: sim.tokens.prompt, completionTokens: sim.tokens.completion, totalTokens: sim.tokens.total },
|
|
1980
|
+
modelUsed: sim.modelUsed,
|
|
1981
|
+
failoverOccurred: false,
|
|
1982
|
+
attempts: 1
|
|
1983
|
+
};
|
|
1604
1984
|
}
|
|
1605
1985
|
throw new ConfigurationError(
|
|
1606
1986
|
"[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
|
|
1607
1987
|
);
|
|
1608
1988
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1989
|
+
return this.executeProviderPipeline(messages, query, temperature);
|
|
1990
|
+
};
|
|
1991
|
+
finalizeExecutionResult = async (output, responseText) => {
|
|
1992
|
+
const sanitizedText = this.applyOutputSecurityGuards(responseText);
|
|
1993
|
+
const hitTokens = output.usage.promptCacheHitTokens ?? 0;
|
|
1994
|
+
const costUSD = this.calculateCost(output.modelUsed, output.usage.promptTokens, output.usage.completionTokens, hitTokens);
|
|
1995
|
+
this.checkPostExecutionBudget(output.usage.totalTokens, costUSD);
|
|
1996
|
+
const tokens = { prompt: output.usage.promptTokens, completion: output.usage.completionTokens, total: output.usage.totalTokens };
|
|
1997
|
+
const result = {
|
|
1998
|
+
text: sanitizedText,
|
|
1999
|
+
tokens,
|
|
2000
|
+
costUSD,
|
|
2001
|
+
modelUsed: output.modelUsed,
|
|
2002
|
+
failoverOccurred: output.failoverOccurred,
|
|
2003
|
+
attempts: output.attempts
|
|
2004
|
+
};
|
|
1612
2005
|
await this.notifyAuditSink(result);
|
|
1613
2006
|
return result;
|
|
1614
2007
|
};
|
|
2008
|
+
runAfterHooks = async (beforeState, output) => {
|
|
2009
|
+
const tokens = { prompt: output.usage.promptTokens, completion: output.usage.completionTokens, total: output.usage.totalTokens };
|
|
2010
|
+
const costUSD = this.calculateCost(output.modelUsed, output.usage.promptTokens, output.usage.completionTokens);
|
|
2011
|
+
return executeAfterResponseHooks(
|
|
2012
|
+
this.config.middlewares,
|
|
2013
|
+
{
|
|
2014
|
+
messages: beforeState.messages,
|
|
2015
|
+
responseText: output.responseText,
|
|
2016
|
+
modelUsed: output.modelUsed,
|
|
2017
|
+
tokens,
|
|
2018
|
+
costUSD,
|
|
2019
|
+
attempts: output.attempts,
|
|
2020
|
+
failoverOccurred: output.failoverOccurred,
|
|
2021
|
+
metadata: beforeState.metadata
|
|
2022
|
+
},
|
|
2023
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
2024
|
+
);
|
|
2025
|
+
};
|
|
2026
|
+
verifyPromptBudget = async (messages, model, parentRunId) => {
|
|
2027
|
+
const promptLength = messages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
2028
|
+
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
2029
|
+
return this.checkPreflightBudget(estimatedPromptTokens, model, void 0, parentRunId);
|
|
2030
|
+
};
|
|
2031
|
+
execute = async (options) => {
|
|
2032
|
+
const inputMessages = this.prepareInputMessages(options);
|
|
2033
|
+
const beforeState = await executeBeforeRequestHooks(
|
|
2034
|
+
this.config.middlewares,
|
|
2035
|
+
{
|
|
2036
|
+
messages: inputMessages,
|
|
2037
|
+
model: this.config.primary.model,
|
|
2038
|
+
temperature: options.temperature,
|
|
2039
|
+
metadata: options.metadata
|
|
2040
|
+
},
|
|
2041
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
2042
|
+
);
|
|
2043
|
+
if (beforeState.shortCircuit) {
|
|
2044
|
+
return this.handleShortCircuit(beforeState.shortCircuit, this.config.primary.model);
|
|
2045
|
+
}
|
|
2046
|
+
beforeState.messages = this.reScanMessagesAfterHooks(inputMessages, beforeState.messages);
|
|
2047
|
+
const parentRunId = beforeState.metadata?.parentRunId ?? options.metadata?.parentRunId;
|
|
2048
|
+
const reservation = await this.verifyPromptBudget(beforeState.messages, this.config.primary.model, parentRunId);
|
|
2049
|
+
let output;
|
|
2050
|
+
try {
|
|
2051
|
+
output = await this.dispatchPromptExecution(beforeState.messages, options.temperature, options.providerOverride);
|
|
2052
|
+
} catch (err) {
|
|
2053
|
+
if (reservation) {
|
|
2054
|
+
const errorMsg = String(err);
|
|
2055
|
+
const isClientError = errorMsg.includes("HTTP 400") || errorMsg.includes("HTTP 401") || errorMsg.includes("HTTP 403") || errorMsg.includes("HTTP 422");
|
|
2056
|
+
if (isClientError) {
|
|
2057
|
+
reservation.status = "RELEASED";
|
|
2058
|
+
reservation.updatedAt = Date.now();
|
|
2059
|
+
} else {
|
|
2060
|
+
reservation.status = "UNCONFIRMED_TIMEOUT";
|
|
2061
|
+
reservation.updatedAt = Date.now();
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
throw err;
|
|
2065
|
+
}
|
|
2066
|
+
const afterState = await this.runAfterHooks(beforeState, output);
|
|
2067
|
+
const finalResult = await this.finalizeExecutionResult(output, afterState.responseText);
|
|
2068
|
+
if (reservation) {
|
|
2069
|
+
reservation.status = "SETTLED";
|
|
2070
|
+
reservation.settledCostUsd = finalResult.costUSD;
|
|
2071
|
+
reservation.updatedAt = Date.now();
|
|
2072
|
+
}
|
|
2073
|
+
return finalResult;
|
|
2074
|
+
};
|
|
2075
|
+
executePrompt = this.execute;
|
|
1615
2076
|
executeStructured = async (options) => {
|
|
1616
2077
|
const rawResult = await this.execute({
|
|
1617
2078
|
userQuery: options.userQuery,
|
|
@@ -1643,11 +2104,6 @@ var AvantGateControlLayer = class {
|
|
|
1643
2104
|
return msg;
|
|
1644
2105
|
});
|
|
1645
2106
|
};
|
|
1646
|
-
verifyPromptBudget = async (messages, model) => {
|
|
1647
|
-
const promptLength = messages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
1648
|
-
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
1649
|
-
await this.checkPreflightBudget(estimatedPromptTokens, model);
|
|
1650
|
-
};
|
|
1651
2107
|
dispatchOverrideAttempt = async (override, messages, model, temperature) => {
|
|
1652
2108
|
const res = await override.complete({ model, messages, temperature: temperature ?? 0.1 });
|
|
1653
2109
|
const usage = this.resolveTokens(res.usage, JSON.stringify(messages), res.text);
|
|
@@ -1746,28 +2202,60 @@ var AvantGateControlLayer = class {
|
|
|
1746
2202
|
const delay = initialDelay * Math.pow(factor, attempt);
|
|
1747
2203
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1748
2204
|
};
|
|
1749
|
-
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
2205
|
+
handleStructuredShortCircuit = async (shortCircuit, options, targetModel) => {
|
|
2206
|
+
const { parsedData, sanitizedResponse } = this.parseAndValidateStructuredResult(
|
|
2207
|
+
shortCircuit.text,
|
|
2208
|
+
options
|
|
2209
|
+
);
|
|
2210
|
+
const tokens = {
|
|
2211
|
+
prompt: shortCircuit.tokens?.promptTokens ?? 0,
|
|
2212
|
+
completion: shortCircuit.tokens?.completionTokens ?? 0,
|
|
2213
|
+
total: shortCircuit.tokens?.totalTokens ?? 0
|
|
2214
|
+
};
|
|
2215
|
+
const result = {
|
|
2216
|
+
data: parsedData,
|
|
2217
|
+
rawText: sanitizedResponse,
|
|
2218
|
+
tokens,
|
|
2219
|
+
costUSD: shortCircuit.costUSD ?? 0,
|
|
2220
|
+
modelUsed: `${targetModel}:shortcircuit`,
|
|
2221
|
+
failoverOccurred: false
|
|
2222
|
+
};
|
|
2223
|
+
await this.notifyAuditSinkSafe(sanitizedResponse, result, 0);
|
|
2224
|
+
return result;
|
|
2225
|
+
};
|
|
2226
|
+
runStructuredAfterHooks = async (messages, output, attempt) => {
|
|
2227
|
+
const tokens = { prompt: output.promptTokens, completion: output.completionTokens, total: output.promptTokens + output.completionTokens };
|
|
2228
|
+
const costUSD = this.calculateCost(output.modelUsed, output.promptTokens, output.completionTokens);
|
|
2229
|
+
return executeAfterResponseHooks(
|
|
2230
|
+
this.config.middlewares,
|
|
2231
|
+
{
|
|
2232
|
+
messages,
|
|
2233
|
+
responseText: output.responseText,
|
|
2234
|
+
modelUsed: output.modelUsed,
|
|
2235
|
+
tokens,
|
|
2236
|
+
costUSD,
|
|
2237
|
+
attempts: attempt,
|
|
2238
|
+
failoverOccurred: output.failoverOccurred
|
|
2239
|
+
},
|
|
2240
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
2241
|
+
);
|
|
2242
|
+
};
|
|
2243
|
+
executeStructuredLoop = async (options, messages, targetModel, maxRetries) => {
|
|
1754
2244
|
let lastError;
|
|
1755
|
-
let
|
|
1756
|
-
let
|
|
2245
|
+
let accPrompt = 0;
|
|
2246
|
+
let accComp = 0;
|
|
1757
2247
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1758
2248
|
try {
|
|
1759
|
-
const output = await this.dispatchProviderAttempt(options,
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1764
|
-
options
|
|
1765
|
-
);
|
|
2249
|
+
const output = await this.dispatchProviderAttempt(options, messages, targetModel);
|
|
2250
|
+
accPrompt += output.promptTokens;
|
|
2251
|
+
accComp += output.completionTokens;
|
|
2252
|
+
const afterState = await this.runStructuredAfterHooks(messages, output, attempt + 1);
|
|
2253
|
+
const { parsedData, sanitizedResponse } = this.parseAndValidateStructuredResult(afterState.responseText, options);
|
|
1766
2254
|
const result = this.buildStructuredResult({
|
|
1767
2255
|
data: parsedData,
|
|
1768
2256
|
rawText: sanitizedResponse,
|
|
1769
|
-
promptTokens:
|
|
1770
|
-
completionTokens:
|
|
2257
|
+
promptTokens: accPrompt,
|
|
2258
|
+
completionTokens: accComp,
|
|
1771
2259
|
modelUsed: output.modelUsed,
|
|
1772
2260
|
failoverOccurred: output.failoverOccurred
|
|
1773
2261
|
});
|
|
@@ -1780,6 +2268,47 @@ var AvantGateControlLayer = class {
|
|
|
1780
2268
|
}
|
|
1781
2269
|
throw lastError;
|
|
1782
2270
|
};
|
|
2271
|
+
generateStructuredOutput = async (options) => {
|
|
2272
|
+
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
2273
|
+
const targetModel = options.model ?? this.config.primary.model;
|
|
2274
|
+
const processedMessages = this.sanitizeIncomingMessages(options.messages);
|
|
2275
|
+
const beforeState = await executeBeforeRequestHooks(
|
|
2276
|
+
this.config.middlewares,
|
|
2277
|
+
{
|
|
2278
|
+
messages: processedMessages,
|
|
2279
|
+
model: targetModel,
|
|
2280
|
+
temperature: options.temperature
|
|
2281
|
+
},
|
|
2282
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
2283
|
+
);
|
|
2284
|
+
if (beforeState.shortCircuit) {
|
|
2285
|
+
return this.handleStructuredShortCircuit(beforeState.shortCircuit, options, targetModel);
|
|
2286
|
+
}
|
|
2287
|
+
beforeState.messages = this.reScanMessagesAfterHooks(processedMessages, beforeState.messages);
|
|
2288
|
+
const reservation = await this.verifyPromptBudget(beforeState.messages, targetModel);
|
|
2289
|
+
try {
|
|
2290
|
+
const result = await this.executeStructuredLoop(options, beforeState.messages, targetModel, maxRetries);
|
|
2291
|
+
if (reservation) {
|
|
2292
|
+
reservation.status = "SETTLED";
|
|
2293
|
+
reservation.settledCostUsd = result.costUSD;
|
|
2294
|
+
reservation.updatedAt = Date.now();
|
|
2295
|
+
}
|
|
2296
|
+
return result;
|
|
2297
|
+
} catch (err) {
|
|
2298
|
+
if (reservation) {
|
|
2299
|
+
const errorMsg = String(err);
|
|
2300
|
+
const isClientError = errorMsg.includes("HTTP 400") || errorMsg.includes("HTTP 401") || errorMsg.includes("HTTP 403") || errorMsg.includes("HTTP 422");
|
|
2301
|
+
if (isClientError) {
|
|
2302
|
+
reservation.status = "RELEASED";
|
|
2303
|
+
reservation.updatedAt = Date.now();
|
|
2304
|
+
} else {
|
|
2305
|
+
reservation.status = "UNCONFIRMED_TIMEOUT";
|
|
2306
|
+
reservation.updatedAt = Date.now();
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
throw err;
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
1783
2312
|
};
|
|
1784
2313
|
var createLLMControlLayer = (config) => {
|
|
1785
2314
|
return new AvantGateControlLayer(config);
|
|
@@ -1867,7 +2396,7 @@ var PromptTemplate = class {
|
|
|
1867
2396
|
};
|
|
1868
2397
|
|
|
1869
2398
|
// src/prompts/prompt-builder.ts
|
|
1870
|
-
var
|
|
2399
|
+
var import_zod2 = require("zod");
|
|
1871
2400
|
var PromptBuilder = class {
|
|
1872
2401
|
personaSlot = "";
|
|
1873
2402
|
rulesSlot = [];
|
|
@@ -1923,7 +2452,7 @@ var PromptBuilder = class {
|
|
|
1923
2452
|
}
|
|
1924
2453
|
schemaContract(schema, options) {
|
|
1925
2454
|
let shapeDescription = "JSON Object";
|
|
1926
|
-
if (schema instanceof
|
|
2455
|
+
if (schema instanceof import_zod2.z.ZodObject) {
|
|
1927
2456
|
shapeDescription = JSON.stringify(Object.keys(schema.shape));
|
|
1928
2457
|
}
|
|
1929
2458
|
this.schemaContractText = [
|
|
@@ -2152,6 +2681,25 @@ var DtoValidationError = class extends Error {
|
|
|
2152
2681
|
this.issues = issues;
|
|
2153
2682
|
}
|
|
2154
2683
|
};
|
|
2684
|
+
var AmbiguousToolExecutionError = class extends Error {
|
|
2685
|
+
toolCallId;
|
|
2686
|
+
toolName;
|
|
2687
|
+
impact;
|
|
2688
|
+
originalError;
|
|
2689
|
+
constructor(details, toolName, impact) {
|
|
2690
|
+
const isStr = typeof details === "string";
|
|
2691
|
+
const callId = isStr ? details : details.toolCallId;
|
|
2692
|
+
const name = isStr ? toolName : details.toolName;
|
|
2693
|
+
const imp = isStr ? impact ?? "MUTATIVE" : details.impact ?? "MUTATIVE";
|
|
2694
|
+
const msg = isStr ? "" : details.message ? `: ${details.message}` : "";
|
|
2695
|
+
super(`Ambiguous tool execution detected for [${name || "tool"}::${callId}] (${imp})${msg}. Re-execution blocked without reconciliation.`);
|
|
2696
|
+
this.name = "AmbiguousToolExecutionError";
|
|
2697
|
+
this.toolCallId = callId;
|
|
2698
|
+
this.toolName = name;
|
|
2699
|
+
this.impact = imp;
|
|
2700
|
+
this.originalError = isStr ? void 0 : details.originalError;
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2155
2703
|
|
|
2156
2704
|
// src/agent/guardrails.ts
|
|
2157
2705
|
function sanitizeString(value) {
|
|
@@ -2205,16 +2753,220 @@ function auditToolResult(data, options) {
|
|
|
2205
2753
|
};
|
|
2206
2754
|
}
|
|
2207
2755
|
|
|
2756
|
+
// src/agent/tool-resilience.ts
|
|
2757
|
+
var MAX_FALLBACK_CAP = 1e3;
|
|
2758
|
+
var inMemoryFallbackStore = /* @__PURE__ */ new Map();
|
|
2759
|
+
var setFallbackRecord = (toolCallId, record) => {
|
|
2760
|
+
if (inMemoryFallbackStore.size >= MAX_FALLBACK_CAP) {
|
|
2761
|
+
const firstKey = inMemoryFallbackStore.keys().next().value;
|
|
2762
|
+
if (firstKey) inMemoryFallbackStore.delete(firstKey);
|
|
2763
|
+
}
|
|
2764
|
+
inMemoryFallbackStore.set(toolCallId, record);
|
|
2765
|
+
};
|
|
2766
|
+
var isDeterministicClientError = (error) => {
|
|
2767
|
+
if (!error || typeof error !== "object") {
|
|
2768
|
+
return false;
|
|
2769
|
+
}
|
|
2770
|
+
const err = error;
|
|
2771
|
+
const status = Number(err.status ?? err.statusCode);
|
|
2772
|
+
if (status >= 400 && status < 500 && status !== 408 && status !== 429) {
|
|
2773
|
+
return true;
|
|
2774
|
+
}
|
|
2775
|
+
return err.name === "DtoValidationError" || err.name === "ToolAccessDeniedError";
|
|
2776
|
+
};
|
|
2777
|
+
var isAmbiguousTimeoutError = (error, isTimedOut) => {
|
|
2778
|
+
if (isTimedOut) return true;
|
|
2779
|
+
if (!error || typeof error !== "object") return false;
|
|
2780
|
+
const err = error;
|
|
2781
|
+
const name = String(err.name ?? "");
|
|
2782
|
+
const status = Number(err.status ?? err.statusCode);
|
|
2783
|
+
if (name === "AbortError" || name === "TimeoutError") return true;
|
|
2784
|
+
if (status === 408 || status === 502 || status === 503 || status === 504) return true;
|
|
2785
|
+
const msg = String(err.message ?? "").toLowerCase();
|
|
2786
|
+
const code = String(err.code ?? "").toLowerCase();
|
|
2787
|
+
return ["timeout", "etimedout", "econnreset", "socket hang up", "network error", "fetch failed"].some((key) => msg.includes(key) || code.includes(key));
|
|
2788
|
+
};
|
|
2789
|
+
var classifyErrorStatus = (error, isTimedOut) => {
|
|
2790
|
+
if (isDeterministicClientError(error)) {
|
|
2791
|
+
return "FAILED";
|
|
2792
|
+
}
|
|
2793
|
+
if (isAmbiguousTimeoutError(error, isTimedOut)) {
|
|
2794
|
+
return "UNCONFIRMED_TIMEOUT";
|
|
2795
|
+
}
|
|
2796
|
+
return "FAILED";
|
|
2797
|
+
};
|
|
2798
|
+
var getToolExecutionRecord = async (storage, toolCallId) => {
|
|
2799
|
+
if (!storage) {
|
|
2800
|
+
return inMemoryFallbackStore.get(toolCallId) ?? null;
|
|
2801
|
+
}
|
|
2802
|
+
if (storage.getStateValue) {
|
|
2803
|
+
const val = await storage.getStateValue(`tool_resilience:${toolCallId}`);
|
|
2804
|
+
if (val) return val;
|
|
2805
|
+
const directVal = await storage.getStateValue(toolCallId);
|
|
2806
|
+
if (directVal) return directVal;
|
|
2807
|
+
}
|
|
2808
|
+
const step = await storage.getStep("_tool_resilience", toolCallId);
|
|
2809
|
+
return step?.metadata?.resilienceRecord ?? null;
|
|
2810
|
+
};
|
|
2811
|
+
var saveToStateStorage = async (storage, toolCallId, record) => {
|
|
2812
|
+
if (!storage.setStateValue) return;
|
|
2813
|
+
await storage.setStateValue(`tool_resilience:${toolCallId}`, record);
|
|
2814
|
+
await storage.setStateValue(toolCallId, record);
|
|
2815
|
+
};
|
|
2816
|
+
var saveResilienceRecord = async (storage, record) => {
|
|
2817
|
+
if (!storage) {
|
|
2818
|
+
setFallbackRecord(record.toolCallId, record);
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
await saveToStateStorage(storage, record.toolCallId, record);
|
|
2822
|
+
const status = record.status === "COMPLETED" ? "COMPLETED" : record.status === "FAILED" ? "FAILED" : "RUNNING";
|
|
2823
|
+
await storage.saveStep({
|
|
2824
|
+
workflowId: "_tool_resilience",
|
|
2825
|
+
stepId: record.toolCallId,
|
|
2826
|
+
status,
|
|
2827
|
+
result: record.result,
|
|
2828
|
+
error: record.error,
|
|
2829
|
+
metadata: { resilienceRecord: record },
|
|
2830
|
+
createdAt: new Date(record.startedAt).toISOString(),
|
|
2831
|
+
updatedAt: new Date(record.updatedAt).toISOString()
|
|
2832
|
+
});
|
|
2833
|
+
};
|
|
2834
|
+
var handleAmbiguousFailure = (options, originalError) => {
|
|
2835
|
+
const workflowId = options.workflowId ?? "_tool_resilience";
|
|
2836
|
+
if (options.suspendOnAmbiguous) {
|
|
2837
|
+
throw new StepSuspendedError(options.toolCallId, workflowId, {
|
|
2838
|
+
toolCallId: options.toolCallId,
|
|
2839
|
+
impact: options.impact,
|
|
2840
|
+
reason: "Ambiguous mutation requires human intervention",
|
|
2841
|
+
originalError
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
throw new AmbiguousToolExecutionError({
|
|
2845
|
+
toolCallId: options.toolCallId,
|
|
2846
|
+
toolName: options.toolName,
|
|
2847
|
+
impact: options.impact,
|
|
2848
|
+
message: "Ambiguous execution state (UNCONFIRMED_TIMEOUT). Re-execution blocked without reconciliation.",
|
|
2849
|
+
originalError
|
|
2850
|
+
});
|
|
2851
|
+
};
|
|
2852
|
+
var saveReconciledRecord = async (options, result) => {
|
|
2853
|
+
await saveResilienceRecord(options.storage, {
|
|
2854
|
+
toolCallId: options.toolCallId,
|
|
2855
|
+
status: "COMPLETED",
|
|
2856
|
+
result,
|
|
2857
|
+
impact: options.impact,
|
|
2858
|
+
startedAt: Date.now(),
|
|
2859
|
+
updatedAt: Date.now()
|
|
2860
|
+
});
|
|
2861
|
+
return { result, isCached: true, status: "COMPLETED" };
|
|
2862
|
+
};
|
|
2863
|
+
var tryReconciliation = async (options) => {
|
|
2864
|
+
if (!options.onAmbiguousRetry) {
|
|
2865
|
+
return handleAmbiguousFailure(options);
|
|
2866
|
+
}
|
|
2867
|
+
try {
|
|
2868
|
+
const outcome = await options.onAmbiguousRetry({ toolCallId: options.toolCallId, args: options.args });
|
|
2869
|
+
if (!outcome.reconciled) {
|
|
2870
|
+
return handleAmbiguousFailure(options);
|
|
2871
|
+
}
|
|
2872
|
+
return await saveReconciledRecord(options, outcome.result);
|
|
2873
|
+
} catch (err) {
|
|
2874
|
+
if (err instanceof StepSuspendedError || err instanceof AmbiguousToolExecutionError) throw err;
|
|
2875
|
+
return handleAmbiguousFailure(options, err);
|
|
2876
|
+
}
|
|
2877
|
+
};
|
|
2878
|
+
var checkExistingState = async (options) => {
|
|
2879
|
+
const existing = await getToolExecutionRecord(options.storage, options.toolCallId);
|
|
2880
|
+
if (!existing) {
|
|
2881
|
+
return null;
|
|
2882
|
+
}
|
|
2883
|
+
if (existing.status === "COMPLETED") {
|
|
2884
|
+
return { result: existing.result, isCached: true, status: "COMPLETED" };
|
|
2885
|
+
}
|
|
2886
|
+
if (existing.status === "UNCONFIRMED_TIMEOUT") {
|
|
2887
|
+
if (options.impact === "READ_ONLY") {
|
|
2888
|
+
return null;
|
|
2889
|
+
}
|
|
2890
|
+
return await tryReconciliation(options);
|
|
2891
|
+
}
|
|
2892
|
+
return null;
|
|
2893
|
+
};
|
|
2894
|
+
var executeActionWithTimeout = async (action, timeoutMs, idempotencyKey) => {
|
|
2895
|
+
const controller = new AbortController();
|
|
2896
|
+
let isTimedOut = false;
|
|
2897
|
+
const timer = setTimeout(() => {
|
|
2898
|
+
isTimedOut = true;
|
|
2899
|
+
controller.abort(new Error(`Tool execution timed out after ${timeoutMs}ms`));
|
|
2900
|
+
}, timeoutMs);
|
|
2901
|
+
try {
|
|
2902
|
+
const result = await action(controller.signal, idempotencyKey);
|
|
2903
|
+
return { result, isTimedOut: false };
|
|
2904
|
+
} catch (error) {
|
|
2905
|
+
return { error, isTimedOut };
|
|
2906
|
+
} finally {
|
|
2907
|
+
clearTimeout(timer);
|
|
2908
|
+
}
|
|
2909
|
+
};
|
|
2910
|
+
var saveFailureRecord = async (options, startedAt, error, isTimedOut) => {
|
|
2911
|
+
const status = classifyErrorStatus(error, isTimedOut);
|
|
2912
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2913
|
+
await saveResilienceRecord(options.storage, {
|
|
2914
|
+
toolCallId: options.toolCallId,
|
|
2915
|
+
status,
|
|
2916
|
+
error: errorMsg,
|
|
2917
|
+
impact: options.impact,
|
|
2918
|
+
startedAt,
|
|
2919
|
+
updatedAt: Date.now()
|
|
2920
|
+
});
|
|
2921
|
+
throw error;
|
|
2922
|
+
};
|
|
2923
|
+
var saveSuccessRecord = async (options, startedAt, result) => {
|
|
2924
|
+
await saveResilienceRecord(options.storage, {
|
|
2925
|
+
toolCallId: options.toolCallId,
|
|
2926
|
+
status: "COMPLETED",
|
|
2927
|
+
result,
|
|
2928
|
+
impact: options.impact,
|
|
2929
|
+
startedAt,
|
|
2930
|
+
updatedAt: Date.now()
|
|
2931
|
+
});
|
|
2932
|
+
return { result, isCached: false, status: "COMPLETED" };
|
|
2933
|
+
};
|
|
2934
|
+
var claimExecutionIntent = async (options, startedAt) => {
|
|
2935
|
+
await saveResilienceRecord(options.storage, {
|
|
2936
|
+
toolCallId: options.toolCallId,
|
|
2937
|
+
status: "CLAIMED",
|
|
2938
|
+
impact: options.impact,
|
|
2939
|
+
startedAt,
|
|
2940
|
+
updatedAt: startedAt
|
|
2941
|
+
});
|
|
2942
|
+
};
|
|
2943
|
+
var withToolResilience = async (options) => {
|
|
2944
|
+
const existingResult = await checkExistingState(options);
|
|
2945
|
+
if (existingResult) {
|
|
2946
|
+
return existingResult;
|
|
2947
|
+
}
|
|
2948
|
+
const startedAt = Date.now();
|
|
2949
|
+
const idempotencyKey = options.idempotencyKey || options.toolCallId;
|
|
2950
|
+
await claimExecutionIntent(options, startedAt);
|
|
2951
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
2952
|
+
const outcome = await executeActionWithTimeout(options.action, timeoutMs, idempotencyKey);
|
|
2953
|
+
if (outcome.error) {
|
|
2954
|
+
return await saveFailureRecord(options, startedAt, outcome.error, outcome.isTimedOut);
|
|
2955
|
+
}
|
|
2956
|
+
return await saveSuccessRecord(options, startedAt, outcome.result);
|
|
2957
|
+
};
|
|
2958
|
+
|
|
2208
2959
|
// src/agent/isolated-tool.ts
|
|
2209
2960
|
var verifyRoles = (configuredRoles, context, toolIdentifier) => {
|
|
2210
2961
|
if (!configuredRoles || configuredRoles.length === 0) {
|
|
2211
2962
|
return;
|
|
2212
2963
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
];
|
|
2217
|
-
|
|
2964
|
+
if (!context.role && (!context.roles || context.roles.length === 0)) {
|
|
2965
|
+
throw new ToolAccessDeniedError(toolIdentifier, "Access denied: insufficient role permissions");
|
|
2966
|
+
}
|
|
2967
|
+
const roleSet = new Set(context.roles ?? []);
|
|
2968
|
+
if (context.role) roleSet.add(context.role);
|
|
2969
|
+
const hasAllowedRole = configuredRoles.some((role) => roleSet.has(role));
|
|
2218
2970
|
if (!hasAllowedRole) {
|
|
2219
2971
|
throw new ToolAccessDeniedError(
|
|
2220
2972
|
toolIdentifier,
|
|
@@ -2331,6 +3083,35 @@ var processLlmPayload = async (rawResult, args, context, config, toolIdentifier)
|
|
|
2331
3083
|
config.throwOnPii === true
|
|
2332
3084
|
);
|
|
2333
3085
|
};
|
|
3086
|
+
var buildResilientContext = (config, args, context) => {
|
|
3087
|
+
const toolCallId = context.toolCallId || `call_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
3088
|
+
const genKey = config.resilience?.idempotencyKeyGenerator?.(args, context);
|
|
3089
|
+
const idempotencyKey = context.idempotencyKey || genKey || toolCallId;
|
|
3090
|
+
const resilientContext = { ...context, toolCallId, idempotencyKey };
|
|
3091
|
+
return { toolCallId, idempotencyKey, resilientContext };
|
|
3092
|
+
};
|
|
3093
|
+
var executeWithResilienceGuard = async (config, args, context, toolIdentifier) => {
|
|
3094
|
+
if (!config.resilience) {
|
|
3095
|
+
return await config.execute(args, context);
|
|
3096
|
+
}
|
|
3097
|
+
const { toolCallId, idempotencyKey, resilientContext } = buildResilientContext(config, args, context);
|
|
3098
|
+
const outcome = await withToolResilience({
|
|
3099
|
+
toolCallId,
|
|
3100
|
+
toolName: toolIdentifier,
|
|
3101
|
+
storage: context.storage,
|
|
3102
|
+
impact: config.impact ?? "READ_ONLY",
|
|
3103
|
+
idempotencyKey,
|
|
3104
|
+
timeoutMs: config.resilience.timeoutMs,
|
|
3105
|
+
suspendOnAmbiguous: config.resilience.suspendOnAmbiguous,
|
|
3106
|
+
workflowId: context.workflowId,
|
|
3107
|
+
args,
|
|
3108
|
+
action: async (signal, idKey) => {
|
|
3109
|
+
return await config.execute(args, { ...resilientContext, abortSignal: signal, idempotencyKey: idKey });
|
|
3110
|
+
},
|
|
3111
|
+
onAmbiguousRetry: config.resilience.onAmbiguousRetry
|
|
3112
|
+
});
|
|
3113
|
+
return outcome.result;
|
|
3114
|
+
};
|
|
2334
3115
|
var createIsolatedTool = (config) => {
|
|
2335
3116
|
const toolId = config.id || config.name;
|
|
2336
3117
|
const toolAlias = config.alias;
|
|
@@ -2352,6 +3133,7 @@ var createIsolatedTool = (config) => {
|
|
|
2352
3133
|
_roles: config.roles ? Object.freeze([...config.roles]) : void 0,
|
|
2353
3134
|
_impact: toolImpact,
|
|
2354
3135
|
_requireApproval: requireApproval,
|
|
3136
|
+
_resilience: config.resilience,
|
|
2355
3137
|
_lastPiiFilteredCount: 0,
|
|
2356
3138
|
_lastInvalidationTags: void 0,
|
|
2357
3139
|
execute: async (args, context) => {
|
|
@@ -2366,7 +3148,12 @@ var createIsolatedTool = (config) => {
|
|
|
2366
3148
|
updatedContext,
|
|
2367
3149
|
toolIdentifier
|
|
2368
3150
|
);
|
|
2369
|
-
const rawResult = await
|
|
3151
|
+
const rawResult = await executeWithResilienceGuard(
|
|
3152
|
+
config,
|
|
3153
|
+
args,
|
|
3154
|
+
updatedContext,
|
|
3155
|
+
toolIdentifier
|
|
3156
|
+
);
|
|
2370
3157
|
verifyTenantIsolation(config.assertTenant, rawResult, updatedContext, toolIdentifier);
|
|
2371
3158
|
await verifyResourceOwnership(
|
|
2372
3159
|
config.assertOwnership,
|
|
@@ -2687,6 +3474,73 @@ var defineWorkflow = (config) => {
|
|
|
2687
3474
|
}
|
|
2688
3475
|
};
|
|
2689
3476
|
};
|
|
3477
|
+
|
|
3478
|
+
// src/presets.ts
|
|
3479
|
+
var createLaunchSafeConfig = (options) => {
|
|
3480
|
+
const defaultSecurity = {
|
|
3481
|
+
detectPromptInjection: true,
|
|
3482
|
+
maskPII: true,
|
|
3483
|
+
outputDLP: true,
|
|
3484
|
+
blockSecretLeaks: true,
|
|
3485
|
+
secretLeakAction: "REDACT"
|
|
3486
|
+
};
|
|
3487
|
+
const defaultRetries = { maxRetries: 2, initialDelayMs: 300, backoffFactor: 1.5 };
|
|
3488
|
+
return {
|
|
3489
|
+
maxTokenBudget: 8192,
|
|
3490
|
+
maxCostUSD: 0.5,
|
|
3491
|
+
middlewareTimeoutMs: 5e3,
|
|
3492
|
+
reScanIngressAfterHooks: true,
|
|
3493
|
+
...options,
|
|
3494
|
+
retryOptions: { ...defaultRetries, ...options.retryOptions },
|
|
3495
|
+
security: { ...defaultSecurity, ...options.security }
|
|
3496
|
+
};
|
|
3497
|
+
};
|
|
3498
|
+
var createEnterpriseStrictConfig = (options) => {
|
|
3499
|
+
const defaultSecurity = {
|
|
3500
|
+
detectPromptInjection: true,
|
|
3501
|
+
maskPII: true,
|
|
3502
|
+
outputDLP: true,
|
|
3503
|
+
blockSecretLeaks: true,
|
|
3504
|
+
secretLeakAction: "BLOCK"
|
|
3505
|
+
};
|
|
3506
|
+
const defaultRetries = {
|
|
3507
|
+
maxRetries: 3,
|
|
3508
|
+
initialDelayMs: 500,
|
|
3509
|
+
backoffFactor: 2
|
|
3510
|
+
};
|
|
3511
|
+
return {
|
|
3512
|
+
maxTokenBudget: 4096,
|
|
3513
|
+
maxCostUSD: 0.15,
|
|
3514
|
+
middlewareTimeoutMs: 3e3,
|
|
3515
|
+
reScanIngressAfterHooks: true,
|
|
3516
|
+
...options,
|
|
3517
|
+
retryOptions: { ...defaultRetries, ...options.retryOptions },
|
|
3518
|
+
security: { ...defaultSecurity, ...options.security }
|
|
3519
|
+
};
|
|
3520
|
+
};
|
|
3521
|
+
var createDevPermissiveConfig = (options) => {
|
|
3522
|
+
const defaultSecurity = {
|
|
3523
|
+
detectPromptInjection: false,
|
|
3524
|
+
maskPII: false,
|
|
3525
|
+
outputDLP: false,
|
|
3526
|
+
blockSecretLeaks: false
|
|
3527
|
+
};
|
|
3528
|
+
const defaultRetries = {
|
|
3529
|
+
maxRetries: 1,
|
|
3530
|
+
initialDelayMs: 200,
|
|
3531
|
+
backoffFactor: 1
|
|
3532
|
+
};
|
|
3533
|
+
return {
|
|
3534
|
+
...options,
|
|
3535
|
+
retryOptions: { ...defaultRetries, ...options.retryOptions },
|
|
3536
|
+
security: { ...defaultSecurity, ...options.security }
|
|
3537
|
+
};
|
|
3538
|
+
};
|
|
3539
|
+
var PRESETS = {
|
|
3540
|
+
LAUNCH_SAFE: createLaunchSafeConfig,
|
|
3541
|
+
ENTERPRISE_STRICT: createEnterpriseStrictConfig,
|
|
3542
|
+
DEV_PERMISSIVE: createDevPermissiveConfig
|
|
3543
|
+
};
|
|
2690
3544
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2691
3545
|
0 && (module.exports = {
|
|
2692
3546
|
AvantGateBudgetExceededError,
|
|
@@ -2695,13 +3549,22 @@ var defineWorkflow = (config) => {
|
|
|
2695
3549
|
AvantGateSecretLeakBlockedError,
|
|
2696
3550
|
BudgetExceededError,
|
|
2697
3551
|
CachedPricingAdapter,
|
|
3552
|
+
ChatMessageSchema,
|
|
3553
|
+
ChatMessagesArraySchema,
|
|
3554
|
+
ChatRoleSchema,
|
|
2698
3555
|
ConfigurationError,
|
|
2699
3556
|
DEFAULT_MODEL_PRICES,
|
|
2700
3557
|
HttpProviderClient,
|
|
3558
|
+
InvalidMessageSchemaError,
|
|
3559
|
+
MiddlewareTimeoutError,
|
|
3560
|
+
PRESETS,
|
|
2701
3561
|
PricingRegistry,
|
|
2702
3562
|
PromptBuilder,
|
|
3563
|
+
PromptInjectionError,
|
|
2703
3564
|
PromptRegistry,
|
|
2704
3565
|
PromptTemplate,
|
|
3566
|
+
ProviderIdleTimeoutError,
|
|
3567
|
+
ProviderTTFTTimeoutError,
|
|
2705
3568
|
SEED_MODEL_PRICES,
|
|
2706
3569
|
SecretLeakBlockedError,
|
|
2707
3570
|
SqlitePricingAdapter,
|
|
@@ -2710,11 +3573,19 @@ var defineWorkflow = (config) => {
|
|
|
2710
3573
|
WorkflowValidationError,
|
|
2711
3574
|
ZenLLMControlLayer,
|
|
2712
3575
|
applyOutputGuards,
|
|
3576
|
+
applySingleAfterMiddleware,
|
|
3577
|
+
applySingleBeforeMiddleware,
|
|
2713
3578
|
calculateCostUSD,
|
|
2714
3579
|
createAvantGate,
|
|
3580
|
+
createDevPermissiveConfig,
|
|
3581
|
+
createEnterpriseStrictConfig,
|
|
2715
3582
|
createHttpProviderClient,
|
|
2716
3583
|
createLLMControlLayer,
|
|
3584
|
+
createLaunchSafeConfig,
|
|
3585
|
+
deepFreeze,
|
|
2717
3586
|
defineWorkflow,
|
|
3587
|
+
executeAfterResponseHooks,
|
|
3588
|
+
executeBeforeRequestHooks,
|
|
2718
3589
|
executeWorkflow,
|
|
2719
3590
|
extractAndCleanJSON,
|
|
2720
3591
|
isWorkflowAbortSignal,
|