avantgate 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +21 -59
  2. package/dist/agent/index.js +97 -24
  3. package/dist/agent/index.mjs +5 -2
  4. package/dist/agent/isolated-tool.d.ts +8 -3
  5. package/dist/agent/isolated-tool.d.ts.map +1 -1
  6. package/dist/agent/types.d.ts +16 -0
  7. package/dist/agent/types.d.ts.map +1 -1
  8. package/dist/{chunk-CO26LNFD.mjs → chunk-36ULFTM2.mjs} +8 -8
  9. package/dist/{chunk-4XWO3NZW.mjs → chunk-AH5XXSS4.mjs} +1 -1
  10. package/dist/{chunk-5VA3EKPJ.mjs → chunk-W27A25DK.mjs} +97 -25
  11. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  12. package/dist/client/index.mjs +2 -0
  13. package/dist/control-layer.d.ts +8 -7
  14. package/dist/control-layer.d.ts.map +1 -1
  15. package/dist/finance/index.js +6 -6
  16. package/dist/finance/index.mjs +2 -1
  17. package/dist/finance/normalizer.d.ts +2 -2
  18. package/dist/finance/normalizer.d.ts.map +1 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +558 -133
  22. package/dist/index.mjs +451 -106
  23. package/dist/input-guard.d.ts +2 -2
  24. package/dist/input-guard.d.ts.map +1 -1
  25. package/dist/pricing.d.ts +17 -16
  26. package/dist/pricing.d.ts.map +1 -1
  27. package/dist/providers/http-client.d.ts +2 -2
  28. package/dist/providers/http-client.d.ts.map +1 -1
  29. package/dist/response-validator.d.ts +2 -2
  30. package/dist/response-validator.d.ts.map +1 -1
  31. package/dist/sanitizer.d.ts +1 -1
  32. package/dist/sanitizer.d.ts.map +1 -1
  33. package/dist/secret-guard.d.ts +29 -0
  34. package/dist/secret-guard.d.ts.map +1 -0
  35. package/dist/sqlite-pricing-adapter.d.ts +87 -0
  36. package/dist/sqlite-pricing-adapter.d.ts.map +1 -0
  37. package/dist/types.d.ts +14 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/workflow/index.js +92 -24
  40. package/dist/workflow/index.mjs +3 -2
  41. package/package.json +5 -2
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -23,6 +33,7 @@ __export(index_exports, {
23
33
  AvantGateBudgetExceededError: () => BudgetExceededError,
24
34
  AvantGateConfigurationError: () => ConfigurationError,
25
35
  AvantGateControlLayer: () => AvantGateControlLayer,
36
+ AvantGateSecretLeakBlockedError: () => SecretLeakBlockedError,
26
37
  BudgetExceededError: () => BudgetExceededError,
27
38
  CachedPricingAdapter: () => CachedPricingAdapter,
28
39
  ConfigurationError: () => ConfigurationError,
@@ -33,10 +44,13 @@ __export(index_exports, {
33
44
  PromptRegistry: () => PromptRegistry,
34
45
  PromptTemplate: () => PromptTemplate,
35
46
  SEED_MODEL_PRICES: () => SEED_MODEL_PRICES,
47
+ SecretLeakBlockedError: () => SecretLeakBlockedError,
48
+ SqlitePricingAdapter: () => SqlitePricingAdapter,
36
49
  WorkflowAbortSignal: () => WorkflowAbortSignal,
37
50
  WorkflowSagaRollbackError: () => WorkflowSagaRollbackError,
38
51
  WorkflowValidationError: () => WorkflowValidationError,
39
52
  ZenLLMControlLayer: () => AvantGateControlLayer,
53
+ applyOutputGuards: () => applyOutputGuards,
40
54
  calculateCostUSD: () => calculateCostUSD,
41
55
  createAvantGate: () => createAvantGate,
42
56
  createHttpProviderClient: () => createHttpProviderClient,
@@ -48,6 +62,7 @@ __export(index_exports, {
48
62
  resolveModelPrice: () => resolveModelPrice,
49
63
  resolveModelPriceAsync: () => resolveModelPriceAsync,
50
64
  sanitizePII: () => sanitizePII,
65
+ sanitizeSecrets: () => sanitizeSecrets,
51
66
  validateUserInput: () => validateUserInput,
52
67
  validateWithZod: () => validateWithZod
53
68
  });
@@ -66,6 +81,221 @@ var BudgetExceededError = class extends Error {
66
81
  this.name = "BudgetExceededError";
67
82
  }
68
83
  };
84
+ var SecretLeakBlockedError = class extends Error {
85
+ detections;
86
+ constructor(message, detections) {
87
+ super(message);
88
+ this.name = "SecretLeakBlockedError";
89
+ this.detections = detections;
90
+ }
91
+ };
92
+
93
+ // src/sqlite-pricing-adapter.ts
94
+ var import_fs = __toESM(require("fs"));
95
+ var import_path = __toESM(require("path"));
96
+ var normalizeIdentifier = (val) => {
97
+ return val.trim().toLowerCase().replace(":", "/");
98
+ };
99
+ var SqlitePricingAdapter = class {
100
+ db;
101
+ tableName;
102
+ constructor(options = {}) {
103
+ this.tableName = options.tableName ?? "model_pricing";
104
+ this.db = options.db ?? this.initDatabase(options.dbPath ?? ":memory:");
105
+ this.initSchema();
106
+ if (options.autoSeed) {
107
+ this.seedDefaultPrices(false);
108
+ }
109
+ }
110
+ initDatabase = (dbPath) => {
111
+ if (dbPath !== ":memory:") {
112
+ const dir = import_path.default.dirname(import_path.default.resolve(dbPath));
113
+ if (!import_fs.default.existsSync(dir)) {
114
+ import_fs.default.mkdirSync(dir, { recursive: true });
115
+ }
116
+ }
117
+ try {
118
+ const { DatabaseSync } = require("sqlite");
119
+ return new DatabaseSync(dbPath);
120
+ } catch (err) {
121
+ throw new ConfigurationError(
122
+ `[avantGate] Failed to initialize native SQLite at '${dbPath}'. Ensure you are running on Node.js 22+ or provide a custom 'db' instance in options. Error: ${String(err)}`
123
+ );
124
+ }
125
+ };
126
+ initSchema = () => {
127
+ this.db.exec(`
128
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
129
+ provider TEXT NOT NULL,
130
+ model TEXT NOT NULL,
131
+ prompt_usd_per_million REAL NOT NULL,
132
+ completion_usd_per_million REAL NOT NULL,
133
+ cache_hit_usd_per_million REAL,
134
+ is_active INTEGER DEFAULT 1,
135
+ updated_at TEXT NOT NULL,
136
+ PRIMARY KEY (provider, model)
137
+ );
138
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_lookup
139
+ ON ${this.tableName}(provider, model, is_active);
140
+ `);
141
+ };
142
+ /**
143
+ * Implémentation du port avantGate PricingAdapter.
144
+ * Résolution hiérarchique : (provider, model) -> (provider/model) -> (model).
145
+ */
146
+ fetchPrice = (model, provider) => {
147
+ const normModel = normalizeIdentifier(model);
148
+ const normProvider = provider ? normalizeIdentifier(provider) : void 0;
149
+ if (normProvider) {
150
+ const row = this.queryActiveRow(normProvider, normModel);
151
+ if (row) return this.mapRowToPrice(row);
152
+ }
153
+ if (normModel.includes("/")) {
154
+ const firstSlashIdx = normModel.indexOf("/");
155
+ const extractedProvider = normModel.slice(0, firstSlashIdx);
156
+ const extractedModel = normModel.slice(firstSlashIdx + 1);
157
+ const splitRow = this.queryActiveRow(extractedProvider, extractedModel);
158
+ if (splitRow) return this.mapRowToPrice(splitRow);
159
+ }
160
+ const directRow = this.queryByModelOnly(normModel);
161
+ if (directRow) return this.mapRowToPrice(directRow);
162
+ return void 0;
163
+ };
164
+ queryActiveRow = (provider, model) => {
165
+ const stmt = this.db.prepare(`
166
+ SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
167
+ FROM ${this.tableName}
168
+ WHERE provider = ? AND model = ? AND is_active = 1
169
+ LIMIT 1
170
+ `);
171
+ return stmt.get(provider, model);
172
+ };
173
+ queryByModelOnly = (model) => {
174
+ const stmt = this.db.prepare(`
175
+ SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
176
+ FROM ${this.tableName}
177
+ WHERE model = ? AND is_active = 1
178
+ LIMIT 1
179
+ `);
180
+ return stmt.get(model);
181
+ };
182
+ mapRowToPrice = (row) => {
183
+ return {
184
+ promptUSDPerMillion: Number(row.prompt_usd_per_million),
185
+ completionUSDPerMillion: Number(row.completion_usd_per_million),
186
+ cacheHitUSDPerMillion: row.cache_hit_usd_per_million != null ? Number(row.cache_hit_usd_per_million) : void 0
187
+ };
188
+ };
189
+ /**
190
+ * Enregistre ou met à jour la tarification d'un modèle.
191
+ */
192
+ upsertPrice = (provider, model, price, isActive = true) => {
193
+ const normProvider = normalizeIdentifier(provider);
194
+ const normModel = normalizeIdentifier(model);
195
+ const now = (/* @__PURE__ */ new Date()).toISOString();
196
+ const stmt = this.db.prepare(`
197
+ INSERT OR REPLACE INTO ${this.tableName}
198
+ (provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
199
+ VALUES (?, ?, ?, ?, ?, ?, ?)
200
+ `);
201
+ stmt.run(
202
+ normProvider,
203
+ normModel,
204
+ price.promptUSDPerMillion,
205
+ price.completionUSDPerMillion,
206
+ price.cacheHitUSDPerMillion ?? null,
207
+ isActive ? 1 : 0,
208
+ now
209
+ );
210
+ };
211
+ /**
212
+ * Supprime une règle tarifaire de la base de données.
213
+ */
214
+ deletePrice = (provider, model) => {
215
+ const normProvider = normalizeIdentifier(provider);
216
+ const normModel = normalizeIdentifier(model);
217
+ const stmt = this.db.prepare(`
218
+ DELETE FROM ${this.tableName}
219
+ WHERE provider = ? AND model = ?
220
+ `);
221
+ const result = stmt.run(normProvider, normModel);
222
+ return Boolean(result && typeof result.changes === "number" && result.changes > 0);
223
+ };
224
+ /**
225
+ * Retourne l'ensemble des règles tarifaires enregistrées.
226
+ */
227
+ getAllPrices = () => {
228
+ const stmt = this.db.prepare(`
229
+ SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
230
+ FROM ${this.tableName}
231
+ ORDER BY provider ASC, model ASC
232
+ `);
233
+ const rows = stmt.all();
234
+ return rows.map((row) => ({
235
+ provider: row.provider,
236
+ model: row.model,
237
+ price: this.mapRowToPrice(row),
238
+ isActive: Boolean(row.is_active),
239
+ updatedAt: row.updated_at
240
+ }));
241
+ };
242
+ /**
243
+ * Remplit la base avec le catalogue de départ SEED_MODEL_PRICES.
244
+ * @param force Si true, écrase les tarifs existants. Si false, insère uniquement les modèles manquants.
245
+ * @returns Le nombre d'enregistrements insérés.
246
+ */
247
+ seedDefaultPrices = (force = false) => {
248
+ const conflictClause = force ? "INSERT OR REPLACE" : "INSERT OR IGNORE";
249
+ const stmt = this.db.prepare(`
250
+ ${conflictClause} INTO ${this.tableName}
251
+ (provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
252
+ VALUES (?, ?, ?, ?, ?, ?, ?)
253
+ `);
254
+ let inserted = 0;
255
+ const now = (/* @__PURE__ */ new Date()).toISOString();
256
+ for (const [key, price] of Object.entries(SEED_MODEL_PRICES)) {
257
+ const normKey = normalizeIdentifier(key);
258
+ let provider = "direct";
259
+ let model = normKey;
260
+ if (normKey.includes("/")) {
261
+ const slashIdx = normKey.indexOf("/");
262
+ provider = normKey.slice(0, slashIdx);
263
+ model = normKey.slice(slashIdx + 1);
264
+ }
265
+ const res1 = stmt.run(
266
+ provider,
267
+ model,
268
+ price.promptUSDPerMillion,
269
+ price.completionUSDPerMillion,
270
+ price.cacheHitUSDPerMillion ?? null,
271
+ 1,
272
+ now
273
+ );
274
+ if (res1?.changes && res1.changes > 0) inserted++;
275
+ if (provider !== "direct") {
276
+ const res2 = stmt.run(
277
+ "direct",
278
+ normKey,
279
+ price.promptUSDPerMillion,
280
+ price.completionUSDPerMillion,
281
+ price.cacheHitUSDPerMillion ?? null,
282
+ 1,
283
+ now
284
+ );
285
+ if (res2?.changes && res2.changes > 0) inserted++;
286
+ }
287
+ }
288
+ return inserted;
289
+ };
290
+ /**
291
+ * Ferme la connexion SQLite sous-jacente si supporté.
292
+ */
293
+ close = () => {
294
+ if (typeof this.db.close === "function") {
295
+ this.db.close();
296
+ }
297
+ };
298
+ };
69
299
 
70
300
  // src/pricing.ts
71
301
  var SEED_MODEL_PRICES = {
@@ -91,9 +321,9 @@ var SEED_MODEL_PRICES = {
91
321
  // Local Ollama (Toujours $0)
92
322
  "ollama": { promptUSDPerMillion: 0, completionUSDPerMillion: 0 }
93
323
  };
94
- function normalizeKey(identifier) {
324
+ var normalizeKey = (identifier) => {
95
325
  return identifier.trim().toLowerCase().replace(":", "/");
96
- }
326
+ };
97
327
  var CachedPricingAdapter = class {
98
328
  cache = /* @__PURE__ */ new Map();
99
329
  ttlMs;
@@ -102,7 +332,7 @@ var CachedPricingAdapter = class {
102
332
  this.delegate = delegate;
103
333
  this.ttlMs = ttlMs;
104
334
  }
105
- async fetchPrice(model, provider) {
335
+ fetchPrice = async (model, provider) => {
106
336
  const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
107
337
  const cached = this.cache.get(key);
108
338
  if (cached && Date.now() < cached.expiresAt) {
@@ -120,72 +350,76 @@ var CachedPricingAdapter = class {
120
350
  }
121
351
  }
122
352
  return void 0;
123
- }
124
- peek(model, provider) {
353
+ };
354
+ peek = (model, provider) => {
125
355
  const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
126
- return this.cache.get(key)?.price;
127
- }
128
- clearCache() {
356
+ const cached = this.cache.get(key);
357
+ if (cached && Date.now() < cached.expiresAt) {
358
+ return cached.price;
359
+ }
360
+ return void 0;
361
+ };
362
+ clearCache = () => {
129
363
  this.cache.clear();
130
- }
131
- invalidate(model, provider) {
364
+ };
365
+ invalidate = (model, provider) => {
132
366
  if (!model) {
133
367
  this.clearCache();
134
368
  return;
135
369
  }
136
370
  const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
137
371
  this.cache.delete(key);
138
- }
372
+ };
139
373
  };
140
- var PricingRegistry = class {
374
+ var PricingRegistry = class _PricingRegistry {
141
375
  static prices = /* @__PURE__ */ new Map();
142
376
  static adapter;
143
- static registerPrice(identifier, price) {
144
- this.prices.set(normalizeKey(identifier), price);
145
- }
146
- static registerDistributorPrices(distributor, priceMap) {
377
+ static registerPrice = (identifier, price) => {
378
+ _PricingRegistry.prices.set(normalizeKey(identifier), price);
379
+ };
380
+ static registerDistributorPrices = (distributor, priceMap) => {
147
381
  for (const [model, price] of Object.entries(priceMap)) {
148
- this.prices.set(normalizeKey(`${distributor}/${model}`), price);
382
+ _PricingRegistry.prices.set(normalizeKey(`${distributor}/${model}`), price);
149
383
  }
150
- }
151
- static registerPrices(prices) {
384
+ };
385
+ static registerPrices = (prices) => {
152
386
  for (const [key, price] of Object.entries(prices)) {
153
- this.prices.set(normalizeKey(key), price);
387
+ _PricingRegistry.prices.set(normalizeKey(key), price);
154
388
  }
155
- }
156
- static getPrice(model, provider) {
389
+ };
390
+ static getPrice = (model, provider) => {
157
391
  if (provider) {
158
392
  const distributorKey = normalizeKey(`${provider}/${model}`);
159
- const directDistributorPrice = this.prices.get(distributorKey);
393
+ const directDistributorPrice = _PricingRegistry.prices.get(distributorKey);
160
394
  if (directDistributorPrice) {
161
395
  return directDistributorPrice;
162
396
  }
163
397
  }
164
- return this.prices.get(normalizeKey(model));
165
- }
166
- static setAdapter(adapter) {
167
- this.adapter = adapter;
168
- }
169
- static getAdapter() {
170
- return this.adapter;
171
- }
172
- static clear() {
173
- this.prices.clear();
174
- this.adapter = void 0;
175
- }
398
+ return _PricingRegistry.prices.get(normalizeKey(model));
399
+ };
400
+ static setAdapter = (adapter) => {
401
+ _PricingRegistry.adapter = adapter;
402
+ };
403
+ static getAdapter = () => {
404
+ return _PricingRegistry.adapter;
405
+ };
406
+ static clear = () => {
407
+ _PricingRegistry.prices.clear();
408
+ _PricingRegistry.adapter = void 0;
409
+ };
176
410
  /**
177
411
  * Réinitialise le registre en chargeant le catalogue d'exemple SEED_MODEL_PRICES.
178
412
  */
179
- static loadSeedPrices() {
180
- this.registerPrices(SEED_MODEL_PRICES);
181
- }
182
- static getAllPrices() {
413
+ static loadSeedPrices = () => {
414
+ _PricingRegistry.registerPrices(SEED_MODEL_PRICES);
415
+ };
416
+ static getAllPrices = () => {
183
417
  const out = {};
184
- for (const [k, v] of this.prices.entries()) {
418
+ for (const [k, v] of _PricingRegistry.prices.entries()) {
185
419
  out[k] = v;
186
420
  }
187
421
  return out;
188
- }
422
+ };
189
423
  };
190
424
  var DEFAULT_MODEL_PRICES = new Proxy({}, {
191
425
  get(_target, prop) {
@@ -205,7 +439,7 @@ var DEFAULT_MODEL_PRICES = new Proxy({}, {
205
439
  return void 0;
206
440
  }
207
441
  });
208
- function resolveModelPrice(model, options) {
442
+ var resolveModelPrice = (model, options) => {
209
443
  const opts = typeof options === "string" ? { provider: options } : options ?? {};
210
444
  if (opts.providerPricing) {
211
445
  return opts.providerPricing;
@@ -236,8 +470,8 @@ function resolveModelPrice(model, options) {
236
470
  return { promptUSDPerMillion: 0, completionUSDPerMillion: 0 };
237
471
  }
238
472
  return void 0;
239
- }
240
- async function resolveModelPriceAsync(model, options) {
473
+ };
474
+ var resolveModelPriceAsync = async (model, options) => {
241
475
  const opts = typeof options === "string" ? { provider: options } : options ?? {};
242
476
  const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
243
477
  if (activeAdapter) {
@@ -250,8 +484,8 @@ async function resolveModelPriceAsync(model, options) {
250
484
  }
251
485
  }
252
486
  return resolveModelPrice(model, options);
253
- }
254
- function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens = 0, options) {
487
+ };
488
+ var calculateCostUSD = (model, promptTokens, completionTokens, cacheHitTokens = 0, options) => {
255
489
  const pricing = resolveModelPrice(model, options);
256
490
  if (!pricing) {
257
491
  return 0;
@@ -260,7 +494,7 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
260
494
  const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
261
495
  const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
262
496
  return Math.max(0, promptCost + completionCost - cacheDiscount);
263
- }
497
+ };
264
498
 
265
499
  // src/sanitizer.ts
266
500
  var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
@@ -270,7 +504,7 @@ var SPI_LABELLED_REGEX = /(?:(?:num[ée]ro\s+fiscal|spi|n[°o]\s*fiscal|d[ée]cl
270
504
  var SPI_FORMATTED_REGEX = /\b[0-3]\d(?:\s+\d{2}){5}\s+\d\b/g;
271
505
  var IBAN_REGEX = /\b[A-Z]{2}\s*[0-9]{2}(?:[\s\r\n.-]*[A-Z0-9]){11,30}\b/g;
272
506
  var BIC_LABELLED_REGEX = /(?:(?:bic|swift)\s*[:=]?\s*)\b([A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?)\b/gi;
273
- function sanitizePII(input) {
507
+ var sanitizePII = (input) => {
274
508
  let count = 0;
275
509
  let result = input;
276
510
  result = result.replace(EMAIL_REGEX, () => {
@@ -313,7 +547,7 @@ function sanitizePII(input) {
313
547
  return match;
314
548
  });
315
549
  return { text: result, maskedCount: count };
316
- }
550
+ };
317
551
 
318
552
  // src/input-guard.ts
319
553
  var INJECTION_PATTERNS = [
@@ -324,7 +558,7 @@ var INJECTION_PATTERNS = [
324
558
  /output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
325
559
  /repeat\s+(?:the\s+words\s+above|everything\s+above)/i
326
560
  ];
327
- function validateUserInput(input, options) {
561
+ var validateUserInput = (input, options) => {
328
562
  const maxLength = options?.maxLength ?? 5e4;
329
563
  if (input.length > maxLength) {
330
564
  return {
@@ -343,7 +577,7 @@ function validateUserInput(input, options) {
343
577
  }
344
578
  }
345
579
  return { valid: true };
346
- }
580
+ };
347
581
 
348
582
  // src/finance/strategies/french-pcg.strategy.ts
349
583
  var FrenchPCGStrategy = class {
@@ -799,17 +1033,17 @@ var AccountingFactory = class {
799
1033
  };
800
1034
 
801
1035
  // src/finance/normalizer.ts
802
- function cleanFinancialJSON(rawText, options) {
1036
+ var cleanFinancialJSON = (rawText, options) => {
803
1037
  if (!rawText) {
804
1038
  return "";
805
1039
  }
806
1040
  const jsonBlock = extractAndCleanJSON(rawText);
807
1041
  const strategy = options?.jurisdiction ? AccountingFactory.getStrategy(options.jurisdiction) : options?.autoDetect !== false ? AccountingFactory.detectStrategy(rawText) : AccountingFactory.getStrategy("FR");
808
1042
  return strategy.cleanJSON(jsonBlock);
809
- }
1043
+ };
810
1044
 
811
1045
  // src/response-validator.ts
812
- function extractAndCleanJSON(rawText) {
1046
+ var extractAndCleanJSON = (rawText) => {
813
1047
  let cleaned = rawText.trim();
814
1048
  cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
815
1049
  const codeBlockMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
@@ -833,8 +1067,8 @@ function extractAndCleanJSON(rawText) {
833
1067
  cleaned = cleaned.slice(startIndex, endIndex + 1);
834
1068
  }
835
1069
  return cleaned;
836
- }
837
- function validateWithZod(rawText, schema, options) {
1070
+ };
1071
+ var validateWithZod = (rawText, schema, options) => {
838
1072
  let jsonString = extractAndCleanJSON(rawText);
839
1073
  if (options?.normalizer) {
840
1074
  jsonString = options.normalizer(jsonString);
@@ -849,7 +1083,7 @@ function validateWithZod(rawText, schema, options) {
849
1083
  parsed = JSON.parse(sanitized);
850
1084
  }
851
1085
  return schema.parse(parsed);
852
- }
1086
+ };
853
1087
 
854
1088
  // src/providers/http-client.ts
855
1089
  var DEFAULT_BASE_URLS = {
@@ -870,14 +1104,14 @@ var HttpProviderClient = class {
870
1104
  this.apiKey = config.apiKey;
871
1105
  this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
872
1106
  }
873
- resolveEndpoint() {
1107
+ resolveEndpoint = () => {
874
1108
  const trimmed = this.baseUrl.replace(/\/+$/, "");
875
1109
  if (trimmed.endsWith("/chat/completions")) {
876
1110
  return trimmed;
877
1111
  }
878
1112
  return `${trimmed}/chat/completions`;
879
- }
880
- buildHeaders() {
1113
+ };
1114
+ buildHeaders = () => {
881
1115
  const headers = {
882
1116
  "Content-Type": "application/json"
883
1117
  };
@@ -889,8 +1123,8 @@ var HttpProviderClient = class {
889
1123
  headers["X-Title"] = "AvantGate";
890
1124
  }
891
1125
  return headers;
892
- }
893
- buildPayload(options, model) {
1126
+ };
1127
+ buildPayload = (options, model) => {
894
1128
  return {
895
1129
  model,
896
1130
  messages: options.messages.map((m) => ({
@@ -900,8 +1134,8 @@ var HttpProviderClient = class {
900
1134
  temperature: options.temperature ?? 0.2,
901
1135
  ...options.responseFormat ? { response_format: options.responseFormat } : {}
902
1136
  };
903
- }
904
- extractUsage(usageData) {
1137
+ };
1138
+ extractUsage = (usageData) => {
905
1139
  if (!usageData) {
906
1140
  return void 0;
907
1141
  }
@@ -911,8 +1145,8 @@ var HttpProviderClient = class {
911
1145
  totalTokens: usageData.total_tokens,
912
1146
  promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
913
1147
  };
914
- }
915
- async complete(options) {
1148
+ };
1149
+ complete = async (options) => {
916
1150
  const endpoint = this.resolveEndpoint();
917
1151
  const model = options.model ?? "default";
918
1152
  const headers = this.buildHeaders();
@@ -932,11 +1166,114 @@ var HttpProviderClient = class {
932
1166
  const text = data.choices?.[0]?.message?.content ?? "";
933
1167
  const usage = this.extractUsage(data.usage);
934
1168
  return { text, usage };
935
- }
1169
+ };
936
1170
  };
937
- function createHttpProviderClient(config) {
1171
+ var createHttpProviderClient = (config) => {
938
1172
  return new HttpProviderClient(config);
939
- }
1173
+ };
1174
+
1175
+ // src/secret-guard.ts
1176
+ var SECRET_PATTERNS = [
1177
+ // 1. Clés privées (RSA, OpenSSH, EC, DSA)
1178
+ {
1179
+ type: "PRIVATE_KEY",
1180
+ regex: /-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9_-]+ )?PRIVATE KEY-----/g,
1181
+ mask: "[REDACTED_PRIVATE_KEY]"
1182
+ },
1183
+ // 2. Clés Anthropic Claude (avant OpenAI pour éviter collision sk-)
1184
+ {
1185
+ type: "ANTHROPIC_API_KEY",
1186
+ regex: /\bsk-ant-(?:api\d{2}-)?[A-Za-z0-9_-]{20,}\b/g,
1187
+ mask: "[REDACTED_ANTHROPIC_KEY]"
1188
+ },
1189
+ // 3. Clés OpenAI (standard, projet, admin) - exclut explicitement sk-ant-
1190
+ {
1191
+ type: "OPENAI_API_KEY",
1192
+ regex: /\bsk-(?!ant-)(?:proj-|admin-|org-)?[A-Za-z0-9_-]{20,}\b/g,
1193
+ mask: "[REDACTED_OPENAI_KEY]"
1194
+ },
1195
+ // 4. Clés AWS Access Key ID
1196
+ {
1197
+ type: "AWS_ACCESS_KEY",
1198
+ regex: /\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\b/g,
1199
+ mask: "[REDACTED_AWS_KEY]"
1200
+ },
1201
+ // 5. Tokens GitHub (Personal, OAuth, User, Server)
1202
+ {
1203
+ type: "GITHUB_TOKEN",
1204
+ regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,255}\b/g,
1205
+ mask: "[REDACTED_GITHUB_TOKEN]"
1206
+ },
1207
+ // 6. JSON Web Tokens (JWT)
1208
+ {
1209
+ type: "JWT_TOKEN",
1210
+ regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}\.[A-Za-z0-9._-]{10,}\b/g,
1211
+ mask: "[REDACTED_JWT]"
1212
+ },
1213
+ // 7. Tokens Slack
1214
+ {
1215
+ type: "SLACK_TOKEN",
1216
+ regex: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,}\b/g,
1217
+ mask: "[REDACTED_SLACK_TOKEN]"
1218
+ },
1219
+ // 8. Mots de passe dans URL de connexion (Postgres, Mongo, Redis, MySQL)
1220
+ {
1221
+ type: "DATABASE_CONNECTION_SECRET",
1222
+ regex: /(?:postgres|postgresql|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^:\s\/@]+:([^@\s]+)@/gi,
1223
+ mask: "[REDACTED_DB_SECRET]"
1224
+ }
1225
+ ];
1226
+ var sanitizeSecrets = (input) => {
1227
+ let text = input;
1228
+ const detections = [];
1229
+ for (const pattern of SECRET_PATTERNS) {
1230
+ let matchCount = 0;
1231
+ if (pattern.type === "DATABASE_CONNECTION_SECRET") {
1232
+ text = text.replace(pattern.regex, (fullMatch, password) => {
1233
+ matchCount++;
1234
+ return fullMatch.replace(password, pattern.mask);
1235
+ });
1236
+ } else {
1237
+ text = text.replace(pattern.regex, () => {
1238
+ matchCount++;
1239
+ return pattern.mask;
1240
+ });
1241
+ }
1242
+ if (matchCount > 0) {
1243
+ detections.push({ type: pattern.type, matchedCount: matchCount });
1244
+ }
1245
+ }
1246
+ return {
1247
+ text,
1248
+ hasSecrets: detections.length > 0,
1249
+ detections
1250
+ };
1251
+ };
1252
+ var applyOutputGuards = (rawOutput, options) => {
1253
+ let currentText = rawOutput;
1254
+ let piiCount = 0;
1255
+ if (options?.maskPII) {
1256
+ const piiResult = sanitizePII(currentText);
1257
+ currentText = piiResult.text;
1258
+ piiCount = piiResult.maskedCount;
1259
+ }
1260
+ const secretResult = sanitizeSecrets(currentText);
1261
+ currentText = secretResult.text;
1262
+ if (secretResult.hasSecrets && options?.secretLeakAction === "BLOCK") {
1263
+ const types = secretResult.detections.map((d) => d.type).join(", ");
1264
+ throw new SecretLeakBlockedError(
1265
+ `[AvantGate Output Guard] Response blocked: detected potential secret leak (${types})`,
1266
+ secretResult.detections
1267
+ );
1268
+ }
1269
+ return {
1270
+ text: currentText,
1271
+ sanitized: piiCount > 0 || secretResult.hasSecrets,
1272
+ secretDetected: secretResult.hasSecrets,
1273
+ piiMaskedCount: piiCount,
1274
+ detections: secretResult.detections
1275
+ };
1276
+ };
940
1277
 
941
1278
  // src/control-layer.ts
942
1279
  var AvantGateControlLayer = class {
@@ -956,7 +1293,7 @@ var AvantGateControlLayer = class {
956
1293
  );
957
1294
  }
958
1295
  }
959
- resolveProviderConfig(provider) {
1296
+ resolveProviderConfig = (provider) => {
960
1297
  if (!provider) return void 0;
961
1298
  if (provider.client) return provider;
962
1299
  if (provider.apiKey || provider.baseUrl || provider.provider === "ollama") {
@@ -966,8 +1303,8 @@ var AvantGateControlLayer = class {
966
1303
  };
967
1304
  }
968
1305
  return provider;
969
- }
970
- findProviderConfig(provider, model) {
1306
+ };
1307
+ findProviderConfig = (provider, model) => {
971
1308
  const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
972
1309
  (p) => Boolean(p)
973
1310
  );
@@ -980,8 +1317,8 @@ var AvantGateControlLayer = class {
980
1317
  if (matchModel) return matchModel;
981
1318
  }
982
1319
  return this.config.primary;
983
- }
984
- calculateCost(model, promptTokens, completionTokens, cacheHitTokens = 0, provider) {
1320
+ };
1321
+ calculateCost = (model, promptTokens, completionTokens, cacheHitTokens = 0, provider) => {
985
1322
  const providerCfg = this.findProviderConfig(provider, model);
986
1323
  return calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens, {
987
1324
  provider: provider ?? providerCfg?.provider,
@@ -989,8 +1326,8 @@ var AvantGateControlLayer = class {
989
1326
  customPricing: this.config.customPricing,
990
1327
  adapter: this.cachedPricingAdapter
991
1328
  });
992
- }
993
- applySecurityGuards(userQuery) {
1329
+ };
1330
+ applySecurityGuards = (userQuery) => {
994
1331
  const guard = validateUserInput(userQuery, {
995
1332
  detectInjection: this.config.security?.detectPromptInjection,
996
1333
  maxLength: this.config.security?.maxInputLength
@@ -1002,8 +1339,21 @@ var AvantGateControlLayer = class {
1002
1339
  return sanitizePII(userQuery).text;
1003
1340
  }
1004
1341
  return userQuery;
1005
- }
1006
- checkPreflightBudget(estimatedPromptTokens, targetModel, provider) {
1342
+ };
1343
+ applyOutputSecurityGuards = (rawOutput) => {
1344
+ const isDlpEnabled = this.config.security?.outputDLP ?? this.config.security?.maskPII ?? false;
1345
+ const shouldCheckSecrets = this.config.security?.blockSecretLeaks ?? true;
1346
+ if (!isDlpEnabled && !shouldCheckSecrets) {
1347
+ return rawOutput;
1348
+ }
1349
+ const guardResult = applyOutputGuards(rawOutput, {
1350
+ maskPII: isDlpEnabled,
1351
+ blockSecretLeaks: shouldCheckSecrets,
1352
+ secretLeakAction: this.config.security?.secretLeakAction ?? "REDACT"
1353
+ });
1354
+ return guardResult.text;
1355
+ };
1356
+ checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider) => {
1007
1357
  if (this.config.maxTokenBudget !== void 0 && estimatedPromptTokens > this.config.maxTokenBudget) {
1008
1358
  throw new BudgetExceededError(
1009
1359
  `[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedPromptTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
@@ -1012,7 +1362,7 @@ var AvantGateControlLayer = class {
1012
1362
  if (this.config.maxCostUSD !== void 0) {
1013
1363
  const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
1014
1364
  const providerCfg = this.findProviderConfig(provider, targetModel);
1015
- const price = resolveModelPrice(targetModel, {
1365
+ const price = await resolveModelPriceAsync(targetModel, {
1016
1366
  provider: provider ?? providerCfg?.provider,
1017
1367
  providerPricing: providerCfg?.pricing,
1018
1368
  customPricing: this.config.customPricing,
@@ -1030,8 +1380,8 @@ var AvantGateControlLayer = class {
1030
1380
  );
1031
1381
  }
1032
1382
  }
1033
- }
1034
- checkPostExecutionBudget(tokensTotal, costUSD) {
1383
+ };
1384
+ checkPostExecutionBudget = (tokensTotal, costUSD) => {
1035
1385
  if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
1036
1386
  throw new BudgetExceededError(
1037
1387
  `[AvantGate Budget Guard] Execution total tokens (${tokensTotal}) exceeded maxTokenBudget (${this.config.maxTokenBudget}).`
@@ -1042,16 +1392,16 @@ var AvantGateControlLayer = class {
1042
1392
  `[AvantGate Budget Guard] Execution cost ($${costUSD.toFixed(6)}) exceeded maxCostUSD ($${this.config.maxCostUSD}).`
1043
1393
  );
1044
1394
  }
1045
- }
1046
- buildMessages(systemPrompt, query) {
1395
+ };
1396
+ buildMessages = (systemPrompt, query) => {
1047
1397
  const messages = [];
1048
1398
  if (systemPrompt) {
1049
1399
  messages.push({ role: "system", content: systemPrompt });
1050
1400
  }
1051
1401
  messages.push({ role: "user", content: query });
1052
1402
  return messages;
1053
- }
1054
- resolveTokens(rawUsage, query, text) {
1403
+ };
1404
+ resolveTokens = (rawUsage, query, text) => {
1055
1405
  const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
1056
1406
  const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
1057
1407
  const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
@@ -1061,15 +1411,15 @@ var AvantGateControlLayer = class {
1061
1411
  totalTokens,
1062
1412
  promptCacheHitTokens: rawUsage?.promptCacheHitTokens
1063
1413
  };
1064
- }
1065
- getProviderChain() {
1414
+ };
1415
+ getProviderChain = () => {
1066
1416
  return [
1067
1417
  this.config.primary,
1068
1418
  this.config.fallback,
1069
1419
  this.config.emergencyFallback
1070
1420
  ].filter((provider) => Boolean(provider?.client));
1071
- }
1072
- executeSimulation(query, systemPrompt) {
1421
+ };
1422
+ executeSimulation = (query, systemPrompt) => {
1073
1423
  const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
1074
1424
  const completionTokens = 50;
1075
1425
  const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
@@ -1085,8 +1435,8 @@ var AvantGateControlLayer = class {
1085
1435
  failoverOccurred: false,
1086
1436
  attempts: 1
1087
1437
  };
1088
- }
1089
- async executeOverrideProvider(provider, messages, query, temperature) {
1438
+ };
1439
+ executeOverrideProvider = async (provider, messages, query, temperature) => {
1090
1440
  const response = await provider.complete({
1091
1441
  model: this.config.primary.model,
1092
1442
  messages,
@@ -1100,8 +1450,8 @@ var AvantGateControlLayer = class {
1100
1450
  failoverOccurred: false,
1101
1451
  attempts: 1
1102
1452
  };
1103
- }
1104
- async executeProviderPipeline(messages, query, temperature, modelOverride) {
1453
+ };
1454
+ executeProviderPipeline = async (messages, query, temperature, modelOverride) => {
1105
1455
  const chain = this.getProviderChain();
1106
1456
  let lastError;
1107
1457
  for (let index = 0; index < chain.length; index++) {
@@ -1126,8 +1476,9 @@ var AvantGateControlLayer = class {
1126
1476
  }
1127
1477
  }
1128
1478
  throw lastError;
1129
- }
1130
- assembleResult(output) {
1479
+ };
1480
+ assembleResult = (output) => {
1481
+ const sanitizedText = this.applyOutputSecurityGuards(output.responseText);
1131
1482
  const costUSD = this.calculateCost(
1132
1483
  output.modelUsed,
1133
1484
  output.usage.promptTokens,
@@ -1135,7 +1486,7 @@ var AvantGateControlLayer = class {
1135
1486
  output.usage.promptCacheHitTokens ?? 0
1136
1487
  );
1137
1488
  return {
1138
- text: output.responseText,
1489
+ text: sanitizedText,
1139
1490
  tokens: {
1140
1491
  prompt: output.usage.promptTokens,
1141
1492
  completion: output.usage.completionTokens,
@@ -1146,8 +1497,8 @@ var AvantGateControlLayer = class {
1146
1497
  failoverOccurred: output.failoverOccurred,
1147
1498
  attempts: output.attempts
1148
1499
  };
1149
- }
1150
- async notifyAuditSink(result) {
1500
+ };
1501
+ notifyAuditSink = async (result) => {
1151
1502
  if (!this.config.auditSink) {
1152
1503
  return;
1153
1504
  }
@@ -1159,16 +1510,16 @@ var AvantGateControlLayer = class {
1159
1510
  failoverOccurred: result.failoverOccurred,
1160
1511
  attempts: result.attempts
1161
1512
  });
1162
- }
1513
+ };
1163
1514
  /**
1164
1515
  * Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
1165
1516
  */
1166
- async execute(options) {
1517
+ execute = async (options) => {
1167
1518
  const sanitizedQuery = this.applySecurityGuards(options.userQuery);
1168
1519
  const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
1169
1520
  const promptLength = (options.systemPrompt?.length ?? 0) + sanitizedQuery.length;
1170
1521
  const estimatedPromptTokens = Math.ceil(promptLength / 4);
1171
- this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
1522
+ await this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
1172
1523
  if (!options.providerOverride && this.getProviderChain().length === 0) {
1173
1524
  if (this.config.mockSimulation) {
1174
1525
  const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
@@ -1184,11 +1535,11 @@ var AvantGateControlLayer = class {
1184
1535
  this.checkPostExecutionBudget(result.tokens.total, result.costUSD);
1185
1536
  await this.notifyAuditSink(result);
1186
1537
  return result;
1187
- }
1538
+ };
1188
1539
  /**
1189
1540
  * Exécute une requête et valide/répare le résultat selon un schéma Zod.
1190
1541
  */
1191
- async executeStructured(options) {
1542
+ executeStructured = async (options) => {
1192
1543
  const rawResult = await this.execute({
1193
1544
  userQuery: options.userQuery,
1194
1545
  systemPrompt: options.systemPrompt,
@@ -1210,12 +1561,12 @@ var AvantGateControlLayer = class {
1210
1561
  modelUsed: rawResult.modelUsed,
1211
1562
  failoverOccurred: rawResult.failoverOccurred
1212
1563
  };
1213
- }
1564
+ };
1214
1565
  /**
1215
1566
  * Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
1216
1567
  * Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
1217
1568
  */
1218
- async generateStructuredOutput(options) {
1569
+ generateStructuredOutput = async (options) => {
1219
1570
  const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
1220
1571
  const modelToUse = options.model ?? this.config.primary.model;
1221
1572
  const processedMessages = options.messages.map((msg) => {
@@ -1226,7 +1577,7 @@ var AvantGateControlLayer = class {
1226
1577
  });
1227
1578
  const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
1228
1579
  const estimatedPromptTokens = Math.ceil(promptLength / 4);
1229
- this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
1580
+ await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
1230
1581
  let lastError;
1231
1582
  let accumulatedPromptTokens = 0;
1232
1583
  let accumulatedCompletionTokens = 0;
@@ -1274,10 +1625,11 @@ var AvantGateControlLayer = class {
1274
1625
  }
1275
1626
  accumulatedPromptTokens += attemptPromptTokens;
1276
1627
  accumulatedCompletionTokens += attemptCompletionTokens;
1628
+ const sanitizedResponse = this.applyOutputSecurityGuards(responseText);
1277
1629
  const isFinancial = options.financialNormalizer ?? Boolean(
1278
1630
  this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
1279
1631
  );
1280
- const parsedData = validateWithZod(responseText, options.schema, {
1632
+ const parsedData = validateWithZod(sanitizedResponse, options.schema, {
1281
1633
  financialNormalizer: isFinancial,
1282
1634
  jurisdiction: this.config.features?.finance?.jurisdiction
1283
1635
  });
@@ -1286,7 +1638,7 @@ var AvantGateControlLayer = class {
1286
1638
  this.checkPostExecutionBudget(totalTokens, costUSD);
1287
1639
  const result = {
1288
1640
  data: parsedData,
1289
- rawText: responseText,
1641
+ rawText: sanitizedResponse,
1290
1642
  tokens: {
1291
1643
  prompt: accumulatedPromptTokens,
1292
1644
  completion: accumulatedCompletionTokens,
@@ -1314,11 +1666,11 @@ var AvantGateControlLayer = class {
1314
1666
  }
1315
1667
  }
1316
1668
  throw lastError;
1317
- }
1669
+ };
1318
1670
  };
1319
- function createLLMControlLayer(config) {
1671
+ var createLLMControlLayer = (config) => {
1320
1672
  return new AvantGateControlLayer(config);
1321
- }
1673
+ };
1322
1674
  var createAvantGate = createLLMControlLayer;
1323
1675
 
1324
1676
  // src/prompts/prompt-template.ts
@@ -1741,7 +2093,23 @@ function auditToolResult(data, options) {
1741
2093
  }
1742
2094
 
1743
2095
  // src/agent/isolated-tool.ts
1744
- async function verifyDataAccessGuard(guard, args, context, toolIdentifier) {
2096
+ var verifyRoles = (configuredRoles, context, toolIdentifier) => {
2097
+ if (!configuredRoles || configuredRoles.length === 0) {
2098
+ return;
2099
+ }
2100
+ const userRoles = [
2101
+ ...context.roles ?? [],
2102
+ ...context.role ? [context.role] : []
2103
+ ];
2104
+ const hasAllowedRole = configuredRoles.some((role) => userRoles.includes(role));
2105
+ if (!hasAllowedRole) {
2106
+ throw new ToolAccessDeniedError(
2107
+ toolIdentifier,
2108
+ "Access denied: insufficient role permissions"
2109
+ );
2110
+ }
2111
+ };
2112
+ var verifyDataAccessGuard = async (guard, args, context, toolIdentifier) => {
1745
2113
  if (!guard) {
1746
2114
  return;
1747
2115
  }
@@ -1749,8 +2117,55 @@ async function verifyDataAccessGuard(guard, args, context, toolIdentifier) {
1749
2117
  if (!isAllowed) {
1750
2118
  throw new ToolAccessDeniedError(toolIdentifier, "Access denied by data access guard");
1751
2119
  }
1752
- }
1753
- async function resolveInvalidationTags(config, args, rawResult, context) {
2120
+ };
2121
+ var verifyTenantIsolation = (assertTenant, rawResult, context, toolIdentifier) => {
2122
+ if (!assertTenant) {
2123
+ return;
2124
+ }
2125
+ if (!context.tenantId) {
2126
+ throw new ToolAccessDeniedError(
2127
+ toolIdentifier,
2128
+ "Access denied: missing session tenantId in execution context for multi-tenant tool"
2129
+ );
2130
+ }
2131
+ const recordTenantId = assertTenant(rawResult);
2132
+ if (recordTenantId && recordTenantId !== context.tenantId) {
2133
+ throw new ToolAccessDeniedError(
2134
+ toolIdentifier,
2135
+ `Cross-tenant IDOR access violation: record tenant '${recordTenantId}' does not match session tenant '${context.tenantId}'`
2136
+ );
2137
+ }
2138
+ };
2139
+ var verifyResourceOwnership = async (assertOwnership, rawResult, context, toolIdentifier) => {
2140
+ if (!assertOwnership) {
2141
+ return;
2142
+ }
2143
+ const isOwner = await assertOwnership(rawResult, context);
2144
+ if (!isOwner) {
2145
+ throw new ToolAccessDeniedError(
2146
+ toolIdentifier,
2147
+ "Ownership IDOR access violation: user does not own or have access to this resource"
2148
+ );
2149
+ }
2150
+ };
2151
+ var checkSuspiciousParameters = (parameters, toolName) => {
2152
+ if (!parameters || typeof parameters !== "object") {
2153
+ return;
2154
+ }
2155
+ const zodShape = parameters.shape;
2156
+ if (!zodShape || typeof zodShape !== "object") {
2157
+ return;
2158
+ }
2159
+ const suspiciousKeys = ["tenantId", "tenant_id", "ownerTenantId"];
2160
+ for (const key of suspiciousKeys) {
2161
+ if (key in zodShape) {
2162
+ console.warn(
2163
+ `[avantGate Security Warning] Tool '${toolName}' defines '${key}' as a model parameter. Tenancy should be inferred from the trusted server context (context.tenantId) rather than supplied by the untrusted model to prevent IDOR attacks.`
2164
+ );
2165
+ }
2166
+ }
2167
+ };
2168
+ var resolveInvalidationTags = async (config, args, rawResult, context) => {
1754
2169
  if (!config.invalidationTags) {
1755
2170
  return void 0;
1756
2171
  }
@@ -1759,33 +2174,29 @@ async function resolveInvalidationTags(config, args, rawResult, context) {
1759
2174
  await context.onInvalidationTags(tags);
1760
2175
  }
1761
2176
  return tags;
1762
- }
1763
- async function dispatchClientData(rawResult, callback) {
2177
+ };
2178
+ var dispatchClientData = async (rawResult, callback) => {
1764
2179
  if (!callback) {
1765
2180
  return;
1766
2181
  }
1767
2182
  await callback(rawResult);
1768
- }
1769
- async function produceLlmPayload(rawResult, args, context, transformer) {
2183
+ };
2184
+ var produceLlmPayload = async (rawResult, args, context, transformer) => {
1770
2185
  if (transformer) {
1771
2186
  return await transformer(rawResult, args, context);
1772
2187
  }
1773
2188
  return rawResult;
1774
- }
1775
- function validateLlmDto(payload, schema, toolIdentifier) {
2189
+ };
2190
+ var validateLlmDto = (payload, schema, toolIdentifier) => {
1776
2191
  const parseResult = schema.safeParse(payload);
1777
2192
  if (!parseResult.success) {
1778
2193
  const issues = parseResult.error.issues ?? [];
1779
2194
  const errorMessages = issues.map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join(", ");
1780
- throw new DtoValidationError(
1781
- toolIdentifier,
1782
- errorMessages,
1783
- issues
1784
- );
2195
+ throw new DtoValidationError(toolIdentifier, errorMessages, issues);
1785
2196
  }
1786
2197
  return parseResult.data;
1787
- }
1788
- function protectLlmPayload(payload, toolIdentifier, sanitizePii = true, throwOnPii = false) {
2198
+ };
2199
+ var protectLlmPayload = (payload, toolIdentifier, sanitizePii = true, throwOnPii = false) => {
1789
2200
  if (!sanitizePii) {
1790
2201
  return { sanitized: payload, count: 0 };
1791
2202
  }
@@ -1794,8 +2205,8 @@ function protectLlmPayload(payload, toolIdentifier, sanitizePii = true, throwOnP
1794
2205
  throwOnPii
1795
2206
  });
1796
2207
  return { sanitized: sanitizedData, count: maskedCount };
1797
- }
1798
- async function processLlmPayload(rawResult, args, context, config, toolIdentifier) {
2208
+ };
2209
+ var processLlmPayload = async (rawResult, args, context, config, toolIdentifier) => {
1799
2210
  let payload = await produceLlmPayload(rawResult, args, context, config.llmDto);
1800
2211
  if (config.llmDtoSchema) {
1801
2212
  payload = validateLlmDto(payload, config.llmDtoSchema, toolIdentifier);
@@ -1806,14 +2217,15 @@ async function processLlmPayload(rawResult, args, context, config, toolIdentifie
1806
2217
  config.sanitizePii !== false,
1807
2218
  config.throwOnPii === true
1808
2219
  );
1809
- }
1810
- function createIsolatedTool(config) {
2220
+ };
2221
+ var createIsolatedTool = (config) => {
1811
2222
  const toolId = config.id || config.name;
1812
2223
  const toolAlias = config.alias;
1813
2224
  const clientCallback = config.clientDto;
1814
2225
  const toolIdentifier = toolAlias || config.name;
1815
2226
  const toolImpact = config.impact ?? "READ_ONLY";
1816
2227
  const requireApproval = config.requireApproval ?? toolImpact === "DESTRUCTIVE";
2228
+ checkSuspiciousParameters(config.parameters, config.name);
1817
2229
  const tool = {
1818
2230
  description: config.description,
1819
2231
  parameters: config.parameters,
@@ -1829,11 +2241,12 @@ function createIsolatedTool(config) {
1829
2241
  _requireApproval: requireApproval,
1830
2242
  _lastPiiFilteredCount: 0,
1831
2243
  _lastInvalidationTags: void 0,
1832
- async execute(args, context) {
2244
+ execute: async (args, context) => {
1833
2245
  const updatedContext = {
1834
2246
  ...context,
1835
2247
  callChain: context?.callChain ?? Object.freeze([toolId])
1836
2248
  };
2249
+ verifyRoles(config.roles, updatedContext, toolIdentifier);
1837
2250
  await verifyDataAccessGuard(
1838
2251
  config.dataAccessGuard,
1839
2252
  args,
@@ -1841,6 +2254,13 @@ function createIsolatedTool(config) {
1841
2254
  toolIdentifier
1842
2255
  );
1843
2256
  const rawResult = await config.execute(args, updatedContext);
2257
+ verifyTenantIsolation(config.assertTenant, rawResult, updatedContext, toolIdentifier);
2258
+ await verifyResourceOwnership(
2259
+ config.assertOwnership,
2260
+ rawResult,
2261
+ updatedContext,
2262
+ toolIdentifier
2263
+ );
1844
2264
  const tags = await resolveInvalidationTags(
1845
2265
  config,
1846
2266
  args,
@@ -1863,7 +2283,7 @@ function createIsolatedTool(config) {
1863
2283
  }
1864
2284
  };
1865
2285
  return tool;
1866
- }
2286
+ };
1867
2287
 
1868
2288
  // src/workflow/errors.ts
1869
2289
  var WorkflowSagaRollbackError = class extends Error {
@@ -2159,6 +2579,7 @@ var defineWorkflow = (config) => {
2159
2579
  AvantGateBudgetExceededError,
2160
2580
  AvantGateConfigurationError,
2161
2581
  AvantGateControlLayer,
2582
+ AvantGateSecretLeakBlockedError,
2162
2583
  BudgetExceededError,
2163
2584
  CachedPricingAdapter,
2164
2585
  ConfigurationError,
@@ -2169,10 +2590,13 @@ var defineWorkflow = (config) => {
2169
2590
  PromptRegistry,
2170
2591
  PromptTemplate,
2171
2592
  SEED_MODEL_PRICES,
2593
+ SecretLeakBlockedError,
2594
+ SqlitePricingAdapter,
2172
2595
  WorkflowAbortSignal,
2173
2596
  WorkflowSagaRollbackError,
2174
2597
  WorkflowValidationError,
2175
2598
  ZenLLMControlLayer,
2599
+ applyOutputGuards,
2176
2600
  calculateCostUSD,
2177
2601
  createAvantGate,
2178
2602
  createHttpProviderClient,
@@ -2184,6 +2608,7 @@ var defineWorkflow = (config) => {
2184
2608
  resolveModelPrice,
2185
2609
  resolveModelPriceAsync,
2186
2610
  sanitizePII,
2611
+ sanitizeSecrets,
2187
2612
  validateUserInput,
2188
2613
  validateWithZod
2189
2614
  });