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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  extractAndCleanJSON,
3
3
  validateWithZod
4
- } from "./chunk-CO26LNFD.mjs";
4
+ } from "./chunk-36ULFTM2.mjs";
5
5
  import {
6
6
  WorkflowAbortSignal,
7
7
  WorkflowSagaRollbackError,
@@ -9,10 +9,13 @@ import {
9
9
  defineWorkflow,
10
10
  executeWorkflow,
11
11
  isWorkflowAbortSignal
12
- } from "./chunk-4XWO3NZW.mjs";
12
+ } from "./chunk-AH5XXSS4.mjs";
13
13
  import {
14
14
  sanitizePII
15
- } from "./chunk-5VA3EKPJ.mjs";
15
+ } from "./chunk-W27A25DK.mjs";
16
+ import {
17
+ __require
18
+ } from "./chunk-Y6FXYEAI.mjs";
16
19
 
17
20
  // src/types.ts
18
21
  var ConfigurationError = class extends Error {
@@ -27,6 +30,221 @@ var BudgetExceededError = class extends Error {
27
30
  this.name = "BudgetExceededError";
28
31
  }
29
32
  };
33
+ var SecretLeakBlockedError = class extends Error {
34
+ detections;
35
+ constructor(message, detections) {
36
+ super(message);
37
+ this.name = "SecretLeakBlockedError";
38
+ this.detections = detections;
39
+ }
40
+ };
41
+
42
+ // src/sqlite-pricing-adapter.ts
43
+ import fs from "fs";
44
+ import path from "path";
45
+ var normalizeIdentifier = (val) => {
46
+ return val.trim().toLowerCase().replace(":", "/");
47
+ };
48
+ var SqlitePricingAdapter = class {
49
+ db;
50
+ tableName;
51
+ constructor(options = {}) {
52
+ this.tableName = options.tableName ?? "model_pricing";
53
+ this.db = options.db ?? this.initDatabase(options.dbPath ?? ":memory:");
54
+ this.initSchema();
55
+ if (options.autoSeed) {
56
+ this.seedDefaultPrices(false);
57
+ }
58
+ }
59
+ initDatabase = (dbPath) => {
60
+ if (dbPath !== ":memory:") {
61
+ const dir = path.dirname(path.resolve(dbPath));
62
+ if (!fs.existsSync(dir)) {
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ }
65
+ }
66
+ try {
67
+ const { DatabaseSync } = __require("sqlite");
68
+ return new DatabaseSync(dbPath);
69
+ } catch (err) {
70
+ throw new ConfigurationError(
71
+ `[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)}`
72
+ );
73
+ }
74
+ };
75
+ initSchema = () => {
76
+ this.db.exec(`
77
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
78
+ provider TEXT NOT NULL,
79
+ model TEXT NOT NULL,
80
+ prompt_usd_per_million REAL NOT NULL,
81
+ completion_usd_per_million REAL NOT NULL,
82
+ cache_hit_usd_per_million REAL,
83
+ is_active INTEGER DEFAULT 1,
84
+ updated_at TEXT NOT NULL,
85
+ PRIMARY KEY (provider, model)
86
+ );
87
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_lookup
88
+ ON ${this.tableName}(provider, model, is_active);
89
+ `);
90
+ };
91
+ /**
92
+ * Implémentation du port avantGate PricingAdapter.
93
+ * Résolution hiérarchique : (provider, model) -> (provider/model) -> (model).
94
+ */
95
+ fetchPrice = (model, provider) => {
96
+ const normModel = normalizeIdentifier(model);
97
+ const normProvider = provider ? normalizeIdentifier(provider) : void 0;
98
+ if (normProvider) {
99
+ const row = this.queryActiveRow(normProvider, normModel);
100
+ if (row) return this.mapRowToPrice(row);
101
+ }
102
+ if (normModel.includes("/")) {
103
+ const firstSlashIdx = normModel.indexOf("/");
104
+ const extractedProvider = normModel.slice(0, firstSlashIdx);
105
+ const extractedModel = normModel.slice(firstSlashIdx + 1);
106
+ const splitRow = this.queryActiveRow(extractedProvider, extractedModel);
107
+ if (splitRow) return this.mapRowToPrice(splitRow);
108
+ }
109
+ const directRow = this.queryByModelOnly(normModel);
110
+ if (directRow) return this.mapRowToPrice(directRow);
111
+ return void 0;
112
+ };
113
+ queryActiveRow = (provider, model) => {
114
+ const stmt = this.db.prepare(`
115
+ SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
116
+ FROM ${this.tableName}
117
+ WHERE provider = ? AND model = ? AND is_active = 1
118
+ LIMIT 1
119
+ `);
120
+ return stmt.get(provider, model);
121
+ };
122
+ queryByModelOnly = (model) => {
123
+ const stmt = this.db.prepare(`
124
+ SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
125
+ FROM ${this.tableName}
126
+ WHERE model = ? AND is_active = 1
127
+ LIMIT 1
128
+ `);
129
+ return stmt.get(model);
130
+ };
131
+ mapRowToPrice = (row) => {
132
+ return {
133
+ promptUSDPerMillion: Number(row.prompt_usd_per_million),
134
+ completionUSDPerMillion: Number(row.completion_usd_per_million),
135
+ cacheHitUSDPerMillion: row.cache_hit_usd_per_million != null ? Number(row.cache_hit_usd_per_million) : void 0
136
+ };
137
+ };
138
+ /**
139
+ * Enregistre ou met à jour la tarification d'un modèle.
140
+ */
141
+ upsertPrice = (provider, model, price, isActive = true) => {
142
+ const normProvider = normalizeIdentifier(provider);
143
+ const normModel = normalizeIdentifier(model);
144
+ const now = (/* @__PURE__ */ new Date()).toISOString();
145
+ const stmt = this.db.prepare(`
146
+ INSERT OR REPLACE INTO ${this.tableName}
147
+ (provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
148
+ VALUES (?, ?, ?, ?, ?, ?, ?)
149
+ `);
150
+ stmt.run(
151
+ normProvider,
152
+ normModel,
153
+ price.promptUSDPerMillion,
154
+ price.completionUSDPerMillion,
155
+ price.cacheHitUSDPerMillion ?? null,
156
+ isActive ? 1 : 0,
157
+ now
158
+ );
159
+ };
160
+ /**
161
+ * Supprime une règle tarifaire de la base de données.
162
+ */
163
+ deletePrice = (provider, model) => {
164
+ const normProvider = normalizeIdentifier(provider);
165
+ const normModel = normalizeIdentifier(model);
166
+ const stmt = this.db.prepare(`
167
+ DELETE FROM ${this.tableName}
168
+ WHERE provider = ? AND model = ?
169
+ `);
170
+ const result = stmt.run(normProvider, normModel);
171
+ return Boolean(result && typeof result.changes === "number" && result.changes > 0);
172
+ };
173
+ /**
174
+ * Retourne l'ensemble des règles tarifaires enregistrées.
175
+ */
176
+ getAllPrices = () => {
177
+ const stmt = this.db.prepare(`
178
+ SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
179
+ FROM ${this.tableName}
180
+ ORDER BY provider ASC, model ASC
181
+ `);
182
+ const rows = stmt.all();
183
+ return rows.map((row) => ({
184
+ provider: row.provider,
185
+ model: row.model,
186
+ price: this.mapRowToPrice(row),
187
+ isActive: Boolean(row.is_active),
188
+ updatedAt: row.updated_at
189
+ }));
190
+ };
191
+ /**
192
+ * Remplit la base avec le catalogue de départ SEED_MODEL_PRICES.
193
+ * @param force Si true, écrase les tarifs existants. Si false, insère uniquement les modèles manquants.
194
+ * @returns Le nombre d'enregistrements insérés.
195
+ */
196
+ seedDefaultPrices = (force = false) => {
197
+ const conflictClause = force ? "INSERT OR REPLACE" : "INSERT OR IGNORE";
198
+ const stmt = this.db.prepare(`
199
+ ${conflictClause} INTO ${this.tableName}
200
+ (provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
201
+ VALUES (?, ?, ?, ?, ?, ?, ?)
202
+ `);
203
+ let inserted = 0;
204
+ const now = (/* @__PURE__ */ new Date()).toISOString();
205
+ for (const [key, price] of Object.entries(SEED_MODEL_PRICES)) {
206
+ const normKey = normalizeIdentifier(key);
207
+ let provider = "direct";
208
+ let model = normKey;
209
+ if (normKey.includes("/")) {
210
+ const slashIdx = normKey.indexOf("/");
211
+ provider = normKey.slice(0, slashIdx);
212
+ model = normKey.slice(slashIdx + 1);
213
+ }
214
+ const res1 = stmt.run(
215
+ provider,
216
+ model,
217
+ price.promptUSDPerMillion,
218
+ price.completionUSDPerMillion,
219
+ price.cacheHitUSDPerMillion ?? null,
220
+ 1,
221
+ now
222
+ );
223
+ if (res1?.changes && res1.changes > 0) inserted++;
224
+ if (provider !== "direct") {
225
+ const res2 = stmt.run(
226
+ "direct",
227
+ normKey,
228
+ price.promptUSDPerMillion,
229
+ price.completionUSDPerMillion,
230
+ price.cacheHitUSDPerMillion ?? null,
231
+ 1,
232
+ now
233
+ );
234
+ if (res2?.changes && res2.changes > 0) inserted++;
235
+ }
236
+ }
237
+ return inserted;
238
+ };
239
+ /**
240
+ * Ferme la connexion SQLite sous-jacente si supporté.
241
+ */
242
+ close = () => {
243
+ if (typeof this.db.close === "function") {
244
+ this.db.close();
245
+ }
246
+ };
247
+ };
30
248
 
31
249
  // src/pricing.ts
32
250
  var SEED_MODEL_PRICES = {
@@ -52,9 +270,9 @@ var SEED_MODEL_PRICES = {
52
270
  // Local Ollama (Toujours $0)
53
271
  "ollama": { promptUSDPerMillion: 0, completionUSDPerMillion: 0 }
54
272
  };
55
- function normalizeKey(identifier) {
273
+ var normalizeKey = (identifier) => {
56
274
  return identifier.trim().toLowerCase().replace(":", "/");
57
- }
275
+ };
58
276
  var CachedPricingAdapter = class {
59
277
  cache = /* @__PURE__ */ new Map();
60
278
  ttlMs;
@@ -63,7 +281,7 @@ var CachedPricingAdapter = class {
63
281
  this.delegate = delegate;
64
282
  this.ttlMs = ttlMs;
65
283
  }
66
- async fetchPrice(model, provider) {
284
+ fetchPrice = async (model, provider) => {
67
285
  const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
68
286
  const cached = this.cache.get(key);
69
287
  if (cached && Date.now() < cached.expiresAt) {
@@ -81,72 +299,76 @@ var CachedPricingAdapter = class {
81
299
  }
82
300
  }
83
301
  return void 0;
84
- }
85
- peek(model, provider) {
302
+ };
303
+ peek = (model, provider) => {
86
304
  const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
87
- return this.cache.get(key)?.price;
88
- }
89
- clearCache() {
305
+ const cached = this.cache.get(key);
306
+ if (cached && Date.now() < cached.expiresAt) {
307
+ return cached.price;
308
+ }
309
+ return void 0;
310
+ };
311
+ clearCache = () => {
90
312
  this.cache.clear();
91
- }
92
- invalidate(model, provider) {
313
+ };
314
+ invalidate = (model, provider) => {
93
315
  if (!model) {
94
316
  this.clearCache();
95
317
  return;
96
318
  }
97
319
  const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
98
320
  this.cache.delete(key);
99
- }
321
+ };
100
322
  };
101
- var PricingRegistry = class {
323
+ var PricingRegistry = class _PricingRegistry {
102
324
  static prices = /* @__PURE__ */ new Map();
103
325
  static adapter;
104
- static registerPrice(identifier, price) {
105
- this.prices.set(normalizeKey(identifier), price);
106
- }
107
- static registerDistributorPrices(distributor, priceMap) {
326
+ static registerPrice = (identifier, price) => {
327
+ _PricingRegistry.prices.set(normalizeKey(identifier), price);
328
+ };
329
+ static registerDistributorPrices = (distributor, priceMap) => {
108
330
  for (const [model, price] of Object.entries(priceMap)) {
109
- this.prices.set(normalizeKey(`${distributor}/${model}`), price);
331
+ _PricingRegistry.prices.set(normalizeKey(`${distributor}/${model}`), price);
110
332
  }
111
- }
112
- static registerPrices(prices) {
333
+ };
334
+ static registerPrices = (prices) => {
113
335
  for (const [key, price] of Object.entries(prices)) {
114
- this.prices.set(normalizeKey(key), price);
336
+ _PricingRegistry.prices.set(normalizeKey(key), price);
115
337
  }
116
- }
117
- static getPrice(model, provider) {
338
+ };
339
+ static getPrice = (model, provider) => {
118
340
  if (provider) {
119
341
  const distributorKey = normalizeKey(`${provider}/${model}`);
120
- const directDistributorPrice = this.prices.get(distributorKey);
342
+ const directDistributorPrice = _PricingRegistry.prices.get(distributorKey);
121
343
  if (directDistributorPrice) {
122
344
  return directDistributorPrice;
123
345
  }
124
346
  }
125
- return this.prices.get(normalizeKey(model));
126
- }
127
- static setAdapter(adapter) {
128
- this.adapter = adapter;
129
- }
130
- static getAdapter() {
131
- return this.adapter;
132
- }
133
- static clear() {
134
- this.prices.clear();
135
- this.adapter = void 0;
136
- }
347
+ return _PricingRegistry.prices.get(normalizeKey(model));
348
+ };
349
+ static setAdapter = (adapter) => {
350
+ _PricingRegistry.adapter = adapter;
351
+ };
352
+ static getAdapter = () => {
353
+ return _PricingRegistry.adapter;
354
+ };
355
+ static clear = () => {
356
+ _PricingRegistry.prices.clear();
357
+ _PricingRegistry.adapter = void 0;
358
+ };
137
359
  /**
138
360
  * Réinitialise le registre en chargeant le catalogue d'exemple SEED_MODEL_PRICES.
139
361
  */
140
- static loadSeedPrices() {
141
- this.registerPrices(SEED_MODEL_PRICES);
142
- }
143
- static getAllPrices() {
362
+ static loadSeedPrices = () => {
363
+ _PricingRegistry.registerPrices(SEED_MODEL_PRICES);
364
+ };
365
+ static getAllPrices = () => {
144
366
  const out = {};
145
- for (const [k, v] of this.prices.entries()) {
367
+ for (const [k, v] of _PricingRegistry.prices.entries()) {
146
368
  out[k] = v;
147
369
  }
148
370
  return out;
149
- }
371
+ };
150
372
  };
151
373
  var DEFAULT_MODEL_PRICES = new Proxy({}, {
152
374
  get(_target, prop) {
@@ -166,7 +388,7 @@ var DEFAULT_MODEL_PRICES = new Proxy({}, {
166
388
  return void 0;
167
389
  }
168
390
  });
169
- function resolveModelPrice(model, options) {
391
+ var resolveModelPrice = (model, options) => {
170
392
  const opts = typeof options === "string" ? { provider: options } : options ?? {};
171
393
  if (opts.providerPricing) {
172
394
  return opts.providerPricing;
@@ -197,8 +419,8 @@ function resolveModelPrice(model, options) {
197
419
  return { promptUSDPerMillion: 0, completionUSDPerMillion: 0 };
198
420
  }
199
421
  return void 0;
200
- }
201
- async function resolveModelPriceAsync(model, options) {
422
+ };
423
+ var resolveModelPriceAsync = async (model, options) => {
202
424
  const opts = typeof options === "string" ? { provider: options } : options ?? {};
203
425
  const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
204
426
  if (activeAdapter) {
@@ -211,8 +433,8 @@ async function resolveModelPriceAsync(model, options) {
211
433
  }
212
434
  }
213
435
  return resolveModelPrice(model, options);
214
- }
215
- function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens = 0, options) {
436
+ };
437
+ var calculateCostUSD = (model, promptTokens, completionTokens, cacheHitTokens = 0, options) => {
216
438
  const pricing = resolveModelPrice(model, options);
217
439
  if (!pricing) {
218
440
  return 0;
@@ -221,7 +443,7 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
221
443
  const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
222
444
  const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
223
445
  return Math.max(0, promptCost + completionCost - cacheDiscount);
224
- }
446
+ };
225
447
 
226
448
  // src/input-guard.ts
227
449
  var INJECTION_PATTERNS = [
@@ -232,7 +454,7 @@ var INJECTION_PATTERNS = [
232
454
  /output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
233
455
  /repeat\s+(?:the\s+words\s+above|everything\s+above)/i
234
456
  ];
235
- function validateUserInput(input, options) {
457
+ var validateUserInput = (input, options) => {
236
458
  const maxLength = options?.maxLength ?? 5e4;
237
459
  if (input.length > maxLength) {
238
460
  return {
@@ -251,7 +473,7 @@ function validateUserInput(input, options) {
251
473
  }
252
474
  }
253
475
  return { valid: true };
254
- }
476
+ };
255
477
 
256
478
  // src/providers/http-client.ts
257
479
  var DEFAULT_BASE_URLS = {
@@ -272,14 +494,14 @@ var HttpProviderClient = class {
272
494
  this.apiKey = config.apiKey;
273
495
  this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
274
496
  }
275
- resolveEndpoint() {
497
+ resolveEndpoint = () => {
276
498
  const trimmed = this.baseUrl.replace(/\/+$/, "");
277
499
  if (trimmed.endsWith("/chat/completions")) {
278
500
  return trimmed;
279
501
  }
280
502
  return `${trimmed}/chat/completions`;
281
- }
282
- buildHeaders() {
503
+ };
504
+ buildHeaders = () => {
283
505
  const headers = {
284
506
  "Content-Type": "application/json"
285
507
  };
@@ -291,8 +513,8 @@ var HttpProviderClient = class {
291
513
  headers["X-Title"] = "AvantGate";
292
514
  }
293
515
  return headers;
294
- }
295
- buildPayload(options, model) {
516
+ };
517
+ buildPayload = (options, model) => {
296
518
  return {
297
519
  model,
298
520
  messages: options.messages.map((m) => ({
@@ -302,8 +524,8 @@ var HttpProviderClient = class {
302
524
  temperature: options.temperature ?? 0.2,
303
525
  ...options.responseFormat ? { response_format: options.responseFormat } : {}
304
526
  };
305
- }
306
- extractUsage(usageData) {
527
+ };
528
+ extractUsage = (usageData) => {
307
529
  if (!usageData) {
308
530
  return void 0;
309
531
  }
@@ -313,8 +535,8 @@ var HttpProviderClient = class {
313
535
  totalTokens: usageData.total_tokens,
314
536
  promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
315
537
  };
316
- }
317
- async complete(options) {
538
+ };
539
+ complete = async (options) => {
318
540
  const endpoint = this.resolveEndpoint();
319
541
  const model = options.model ?? "default";
320
542
  const headers = this.buildHeaders();
@@ -334,11 +556,114 @@ var HttpProviderClient = class {
334
556
  const text = data.choices?.[0]?.message?.content ?? "";
335
557
  const usage = this.extractUsage(data.usage);
336
558
  return { text, usage };
337
- }
559
+ };
338
560
  };
339
- function createHttpProviderClient(config) {
561
+ var createHttpProviderClient = (config) => {
340
562
  return new HttpProviderClient(config);
341
- }
563
+ };
564
+
565
+ // src/secret-guard.ts
566
+ var SECRET_PATTERNS = [
567
+ // 1. Clés privées (RSA, OpenSSH, EC, DSA)
568
+ {
569
+ type: "PRIVATE_KEY",
570
+ regex: /-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9_-]+ )?PRIVATE KEY-----/g,
571
+ mask: "[REDACTED_PRIVATE_KEY]"
572
+ },
573
+ // 2. Clés Anthropic Claude (avant OpenAI pour éviter collision sk-)
574
+ {
575
+ type: "ANTHROPIC_API_KEY",
576
+ regex: /\bsk-ant-(?:api\d{2}-)?[A-Za-z0-9_-]{20,}\b/g,
577
+ mask: "[REDACTED_ANTHROPIC_KEY]"
578
+ },
579
+ // 3. Clés OpenAI (standard, projet, admin) - exclut explicitement sk-ant-
580
+ {
581
+ type: "OPENAI_API_KEY",
582
+ regex: /\bsk-(?!ant-)(?:proj-|admin-|org-)?[A-Za-z0-9_-]{20,}\b/g,
583
+ mask: "[REDACTED_OPENAI_KEY]"
584
+ },
585
+ // 4. Clés AWS Access Key ID
586
+ {
587
+ type: "AWS_ACCESS_KEY",
588
+ regex: /\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\b/g,
589
+ mask: "[REDACTED_AWS_KEY]"
590
+ },
591
+ // 5. Tokens GitHub (Personal, OAuth, User, Server)
592
+ {
593
+ type: "GITHUB_TOKEN",
594
+ regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,255}\b/g,
595
+ mask: "[REDACTED_GITHUB_TOKEN]"
596
+ },
597
+ // 6. JSON Web Tokens (JWT)
598
+ {
599
+ type: "JWT_TOKEN",
600
+ regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}\.[A-Za-z0-9._-]{10,}\b/g,
601
+ mask: "[REDACTED_JWT]"
602
+ },
603
+ // 7. Tokens Slack
604
+ {
605
+ type: "SLACK_TOKEN",
606
+ regex: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,}\b/g,
607
+ mask: "[REDACTED_SLACK_TOKEN]"
608
+ },
609
+ // 8. Mots de passe dans URL de connexion (Postgres, Mongo, Redis, MySQL)
610
+ {
611
+ type: "DATABASE_CONNECTION_SECRET",
612
+ regex: /(?:postgres|postgresql|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^:\s\/@]+:([^@\s]+)@/gi,
613
+ mask: "[REDACTED_DB_SECRET]"
614
+ }
615
+ ];
616
+ var sanitizeSecrets = (input) => {
617
+ let text = input;
618
+ const detections = [];
619
+ for (const pattern of SECRET_PATTERNS) {
620
+ let matchCount = 0;
621
+ if (pattern.type === "DATABASE_CONNECTION_SECRET") {
622
+ text = text.replace(pattern.regex, (fullMatch, password) => {
623
+ matchCount++;
624
+ return fullMatch.replace(password, pattern.mask);
625
+ });
626
+ } else {
627
+ text = text.replace(pattern.regex, () => {
628
+ matchCount++;
629
+ return pattern.mask;
630
+ });
631
+ }
632
+ if (matchCount > 0) {
633
+ detections.push({ type: pattern.type, matchedCount: matchCount });
634
+ }
635
+ }
636
+ return {
637
+ text,
638
+ hasSecrets: detections.length > 0,
639
+ detections
640
+ };
641
+ };
642
+ var applyOutputGuards = (rawOutput, options) => {
643
+ let currentText = rawOutput;
644
+ let piiCount = 0;
645
+ if (options?.maskPII) {
646
+ const piiResult = sanitizePII(currentText);
647
+ currentText = piiResult.text;
648
+ piiCount = piiResult.maskedCount;
649
+ }
650
+ const secretResult = sanitizeSecrets(currentText);
651
+ currentText = secretResult.text;
652
+ if (secretResult.hasSecrets && options?.secretLeakAction === "BLOCK") {
653
+ const types = secretResult.detections.map((d) => d.type).join(", ");
654
+ throw new SecretLeakBlockedError(
655
+ `[AvantGate Output Guard] Response blocked: detected potential secret leak (${types})`,
656
+ secretResult.detections
657
+ );
658
+ }
659
+ return {
660
+ text: currentText,
661
+ sanitized: piiCount > 0 || secretResult.hasSecrets,
662
+ secretDetected: secretResult.hasSecrets,
663
+ piiMaskedCount: piiCount,
664
+ detections: secretResult.detections
665
+ };
666
+ };
342
667
 
343
668
  // src/control-layer.ts
344
669
  var AvantGateControlLayer = class {
@@ -358,7 +683,7 @@ var AvantGateControlLayer = class {
358
683
  );
359
684
  }
360
685
  }
361
- resolveProviderConfig(provider) {
686
+ resolveProviderConfig = (provider) => {
362
687
  if (!provider) return void 0;
363
688
  if (provider.client) return provider;
364
689
  if (provider.apiKey || provider.baseUrl || provider.provider === "ollama") {
@@ -368,8 +693,8 @@ var AvantGateControlLayer = class {
368
693
  };
369
694
  }
370
695
  return provider;
371
- }
372
- findProviderConfig(provider, model) {
696
+ };
697
+ findProviderConfig = (provider, model) => {
373
698
  const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
374
699
  (p) => Boolean(p)
375
700
  );
@@ -382,8 +707,8 @@ var AvantGateControlLayer = class {
382
707
  if (matchModel) return matchModel;
383
708
  }
384
709
  return this.config.primary;
385
- }
386
- calculateCost(model, promptTokens, completionTokens, cacheHitTokens = 0, provider) {
710
+ };
711
+ calculateCost = (model, promptTokens, completionTokens, cacheHitTokens = 0, provider) => {
387
712
  const providerCfg = this.findProviderConfig(provider, model);
388
713
  return calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens, {
389
714
  provider: provider ?? providerCfg?.provider,
@@ -391,8 +716,8 @@ var AvantGateControlLayer = class {
391
716
  customPricing: this.config.customPricing,
392
717
  adapter: this.cachedPricingAdapter
393
718
  });
394
- }
395
- applySecurityGuards(userQuery) {
719
+ };
720
+ applySecurityGuards = (userQuery) => {
396
721
  const guard = validateUserInput(userQuery, {
397
722
  detectInjection: this.config.security?.detectPromptInjection,
398
723
  maxLength: this.config.security?.maxInputLength
@@ -404,8 +729,21 @@ var AvantGateControlLayer = class {
404
729
  return sanitizePII(userQuery).text;
405
730
  }
406
731
  return userQuery;
407
- }
408
- checkPreflightBudget(estimatedPromptTokens, targetModel, provider) {
732
+ };
733
+ applyOutputSecurityGuards = (rawOutput) => {
734
+ const isDlpEnabled = this.config.security?.outputDLP ?? this.config.security?.maskPII ?? false;
735
+ const shouldCheckSecrets = this.config.security?.blockSecretLeaks ?? true;
736
+ if (!isDlpEnabled && !shouldCheckSecrets) {
737
+ return rawOutput;
738
+ }
739
+ const guardResult = applyOutputGuards(rawOutput, {
740
+ maskPII: isDlpEnabled,
741
+ blockSecretLeaks: shouldCheckSecrets,
742
+ secretLeakAction: this.config.security?.secretLeakAction ?? "REDACT"
743
+ });
744
+ return guardResult.text;
745
+ };
746
+ checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider) => {
409
747
  if (this.config.maxTokenBudget !== void 0 && estimatedPromptTokens > this.config.maxTokenBudget) {
410
748
  throw new BudgetExceededError(
411
749
  `[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedPromptTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
@@ -414,7 +752,7 @@ var AvantGateControlLayer = class {
414
752
  if (this.config.maxCostUSD !== void 0) {
415
753
  const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
416
754
  const providerCfg = this.findProviderConfig(provider, targetModel);
417
- const price = resolveModelPrice(targetModel, {
755
+ const price = await resolveModelPriceAsync(targetModel, {
418
756
  provider: provider ?? providerCfg?.provider,
419
757
  providerPricing: providerCfg?.pricing,
420
758
  customPricing: this.config.customPricing,
@@ -432,8 +770,8 @@ var AvantGateControlLayer = class {
432
770
  );
433
771
  }
434
772
  }
435
- }
436
- checkPostExecutionBudget(tokensTotal, costUSD) {
773
+ };
774
+ checkPostExecutionBudget = (tokensTotal, costUSD) => {
437
775
  if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
438
776
  throw new BudgetExceededError(
439
777
  `[AvantGate Budget Guard] Execution total tokens (${tokensTotal}) exceeded maxTokenBudget (${this.config.maxTokenBudget}).`
@@ -444,16 +782,16 @@ var AvantGateControlLayer = class {
444
782
  `[AvantGate Budget Guard] Execution cost ($${costUSD.toFixed(6)}) exceeded maxCostUSD ($${this.config.maxCostUSD}).`
445
783
  );
446
784
  }
447
- }
448
- buildMessages(systemPrompt, query) {
785
+ };
786
+ buildMessages = (systemPrompt, query) => {
449
787
  const messages = [];
450
788
  if (systemPrompt) {
451
789
  messages.push({ role: "system", content: systemPrompt });
452
790
  }
453
791
  messages.push({ role: "user", content: query });
454
792
  return messages;
455
- }
456
- resolveTokens(rawUsage, query, text) {
793
+ };
794
+ resolveTokens = (rawUsage, query, text) => {
457
795
  const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
458
796
  const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
459
797
  const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
@@ -463,15 +801,15 @@ var AvantGateControlLayer = class {
463
801
  totalTokens,
464
802
  promptCacheHitTokens: rawUsage?.promptCacheHitTokens
465
803
  };
466
- }
467
- getProviderChain() {
804
+ };
805
+ getProviderChain = () => {
468
806
  return [
469
807
  this.config.primary,
470
808
  this.config.fallback,
471
809
  this.config.emergencyFallback
472
810
  ].filter((provider) => Boolean(provider?.client));
473
- }
474
- executeSimulation(query, systemPrompt) {
811
+ };
812
+ executeSimulation = (query, systemPrompt) => {
475
813
  const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
476
814
  const completionTokens = 50;
477
815
  const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
@@ -487,8 +825,8 @@ var AvantGateControlLayer = class {
487
825
  failoverOccurred: false,
488
826
  attempts: 1
489
827
  };
490
- }
491
- async executeOverrideProvider(provider, messages, query, temperature) {
828
+ };
829
+ executeOverrideProvider = async (provider, messages, query, temperature) => {
492
830
  const response = await provider.complete({
493
831
  model: this.config.primary.model,
494
832
  messages,
@@ -502,8 +840,8 @@ var AvantGateControlLayer = class {
502
840
  failoverOccurred: false,
503
841
  attempts: 1
504
842
  };
505
- }
506
- async executeProviderPipeline(messages, query, temperature, modelOverride) {
843
+ };
844
+ executeProviderPipeline = async (messages, query, temperature, modelOverride) => {
507
845
  const chain = this.getProviderChain();
508
846
  let lastError;
509
847
  for (let index = 0; index < chain.length; index++) {
@@ -528,8 +866,9 @@ var AvantGateControlLayer = class {
528
866
  }
529
867
  }
530
868
  throw lastError;
531
- }
532
- assembleResult(output) {
869
+ };
870
+ assembleResult = (output) => {
871
+ const sanitizedText = this.applyOutputSecurityGuards(output.responseText);
533
872
  const costUSD = this.calculateCost(
534
873
  output.modelUsed,
535
874
  output.usage.promptTokens,
@@ -537,7 +876,7 @@ var AvantGateControlLayer = class {
537
876
  output.usage.promptCacheHitTokens ?? 0
538
877
  );
539
878
  return {
540
- text: output.responseText,
879
+ text: sanitizedText,
541
880
  tokens: {
542
881
  prompt: output.usage.promptTokens,
543
882
  completion: output.usage.completionTokens,
@@ -548,8 +887,8 @@ var AvantGateControlLayer = class {
548
887
  failoverOccurred: output.failoverOccurred,
549
888
  attempts: output.attempts
550
889
  };
551
- }
552
- async notifyAuditSink(result) {
890
+ };
891
+ notifyAuditSink = async (result) => {
553
892
  if (!this.config.auditSink) {
554
893
  return;
555
894
  }
@@ -561,16 +900,16 @@ var AvantGateControlLayer = class {
561
900
  failoverOccurred: result.failoverOccurred,
562
901
  attempts: result.attempts
563
902
  });
564
- }
903
+ };
565
904
  /**
566
905
  * Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
567
906
  */
568
- async execute(options) {
907
+ execute = async (options) => {
569
908
  const sanitizedQuery = this.applySecurityGuards(options.userQuery);
570
909
  const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
571
910
  const promptLength = (options.systemPrompt?.length ?? 0) + sanitizedQuery.length;
572
911
  const estimatedPromptTokens = Math.ceil(promptLength / 4);
573
- this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
912
+ await this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
574
913
  if (!options.providerOverride && this.getProviderChain().length === 0) {
575
914
  if (this.config.mockSimulation) {
576
915
  const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
@@ -586,11 +925,11 @@ var AvantGateControlLayer = class {
586
925
  this.checkPostExecutionBudget(result.tokens.total, result.costUSD);
587
926
  await this.notifyAuditSink(result);
588
927
  return result;
589
- }
928
+ };
590
929
  /**
591
930
  * Exécute une requête et valide/répare le résultat selon un schéma Zod.
592
931
  */
593
- async executeStructured(options) {
932
+ executeStructured = async (options) => {
594
933
  const rawResult = await this.execute({
595
934
  userQuery: options.userQuery,
596
935
  systemPrompt: options.systemPrompt,
@@ -612,12 +951,12 @@ var AvantGateControlLayer = class {
612
951
  modelUsed: rawResult.modelUsed,
613
952
  failoverOccurred: rawResult.failoverOccurred
614
953
  };
615
- }
954
+ };
616
955
  /**
617
956
  * Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
618
957
  * Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
619
958
  */
620
- async generateStructuredOutput(options) {
959
+ generateStructuredOutput = async (options) => {
621
960
  const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
622
961
  const modelToUse = options.model ?? this.config.primary.model;
623
962
  const processedMessages = options.messages.map((msg) => {
@@ -628,7 +967,7 @@ var AvantGateControlLayer = class {
628
967
  });
629
968
  const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
630
969
  const estimatedPromptTokens = Math.ceil(promptLength / 4);
631
- this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
970
+ await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
632
971
  let lastError;
633
972
  let accumulatedPromptTokens = 0;
634
973
  let accumulatedCompletionTokens = 0;
@@ -676,10 +1015,11 @@ var AvantGateControlLayer = class {
676
1015
  }
677
1016
  accumulatedPromptTokens += attemptPromptTokens;
678
1017
  accumulatedCompletionTokens += attemptCompletionTokens;
1018
+ const sanitizedResponse = this.applyOutputSecurityGuards(responseText);
679
1019
  const isFinancial = options.financialNormalizer ?? Boolean(
680
1020
  this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
681
1021
  );
682
- const parsedData = validateWithZod(responseText, options.schema, {
1022
+ const parsedData = validateWithZod(sanitizedResponse, options.schema, {
683
1023
  financialNormalizer: isFinancial,
684
1024
  jurisdiction: this.config.features?.finance?.jurisdiction
685
1025
  });
@@ -688,7 +1028,7 @@ var AvantGateControlLayer = class {
688
1028
  this.checkPostExecutionBudget(totalTokens, costUSD);
689
1029
  const result = {
690
1030
  data: parsedData,
691
- rawText: responseText,
1031
+ rawText: sanitizedResponse,
692
1032
  tokens: {
693
1033
  prompt: accumulatedPromptTokens,
694
1034
  completion: accumulatedCompletionTokens,
@@ -716,11 +1056,11 @@ var AvantGateControlLayer = class {
716
1056
  }
717
1057
  }
718
1058
  throw lastError;
719
- }
1059
+ };
720
1060
  };
721
- function createLLMControlLayer(config) {
1061
+ var createLLMControlLayer = (config) => {
722
1062
  return new AvantGateControlLayer(config);
723
- }
1063
+ };
724
1064
  var createAvantGate = createLLMControlLayer;
725
1065
 
726
1066
  // src/prompts/prompt-template.ts
@@ -1041,6 +1381,7 @@ export {
1041
1381
  BudgetExceededError as AvantGateBudgetExceededError,
1042
1382
  ConfigurationError as AvantGateConfigurationError,
1043
1383
  AvantGateControlLayer,
1384
+ SecretLeakBlockedError as AvantGateSecretLeakBlockedError,
1044
1385
  BudgetExceededError,
1045
1386
  CachedPricingAdapter,
1046
1387
  ConfigurationError,
@@ -1051,10 +1392,13 @@ export {
1051
1392
  PromptRegistry,
1052
1393
  PromptTemplate,
1053
1394
  SEED_MODEL_PRICES,
1395
+ SecretLeakBlockedError,
1396
+ SqlitePricingAdapter,
1054
1397
  WorkflowAbortSignal,
1055
1398
  WorkflowSagaRollbackError,
1056
1399
  WorkflowValidationError,
1057
1400
  AvantGateControlLayer as ZenLLMControlLayer,
1401
+ applyOutputGuards,
1058
1402
  calculateCostUSD,
1059
1403
  createAvantGate,
1060
1404
  createHttpProviderClient,
@@ -1066,6 +1410,7 @@ export {
1066
1410
  resolveModelPrice,
1067
1411
  resolveModelPriceAsync,
1068
1412
  sanitizePII,
1413
+ sanitizeSecrets,
1069
1414
  validateUserInput,
1070
1415
  validateWithZod
1071
1416
  };