risicare 0.2.2 → 0.3.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 (73) hide show
  1. package/dist/frameworks/instructor.cjs +45 -17
  2. package/dist/frameworks/instructor.cjs.map +1 -1
  3. package/dist/frameworks/instructor.js +47 -17
  4. package/dist/frameworks/instructor.js.map +1 -1
  5. package/dist/frameworks/langchain.cjs +73 -6
  6. package/dist/frameworks/langchain.cjs.map +1 -1
  7. package/dist/frameworks/langchain.d.cts +20 -4
  8. package/dist/frameworks/langchain.d.ts +20 -4
  9. package/dist/frameworks/langchain.js +75 -6
  10. package/dist/frameworks/langchain.js.map +1 -1
  11. package/dist/frameworks/langgraph.cjs +73 -6
  12. package/dist/frameworks/langgraph.cjs.map +1 -1
  13. package/dist/frameworks/langgraph.js +75 -6
  14. package/dist/frameworks/langgraph.js.map +1 -1
  15. package/dist/frameworks/llamaindex.cjs +41 -14
  16. package/dist/frameworks/llamaindex.cjs.map +1 -1
  17. package/dist/frameworks/llamaindex.js +43 -14
  18. package/dist/frameworks/llamaindex.js.map +1 -1
  19. package/dist/index.cjs +1494 -67
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +436 -1
  22. package/dist/index.d.ts +436 -1
  23. package/dist/index.js +1515 -67
  24. package/dist/index.js.map +1 -1
  25. package/dist/providers/anthropic/index.cjs +74 -24
  26. package/dist/providers/anthropic/index.cjs.map +1 -1
  27. package/dist/providers/anthropic/index.js +76 -24
  28. package/dist/providers/anthropic/index.js.map +1 -1
  29. package/dist/providers/bedrock/index.cjs +81 -24
  30. package/dist/providers/bedrock/index.cjs.map +1 -1
  31. package/dist/providers/bedrock/index.js +83 -24
  32. package/dist/providers/bedrock/index.js.map +1 -1
  33. package/dist/providers/cerebras/index.cjs +78 -25
  34. package/dist/providers/cerebras/index.cjs.map +1 -1
  35. package/dist/providers/cerebras/index.js +80 -25
  36. package/dist/providers/cerebras/index.js.map +1 -1
  37. package/dist/providers/cohere/index.cjs +95 -25
  38. package/dist/providers/cohere/index.cjs.map +1 -1
  39. package/dist/providers/cohere/index.js +97 -25
  40. package/dist/providers/cohere/index.js.map +1 -1
  41. package/dist/providers/google/index.cjs +77 -25
  42. package/dist/providers/google/index.cjs.map +1 -1
  43. package/dist/providers/google/index.js +79 -25
  44. package/dist/providers/google/index.js.map +1 -1
  45. package/dist/providers/groq/index.cjs +80 -25
  46. package/dist/providers/groq/index.cjs.map +1 -1
  47. package/dist/providers/groq/index.js +82 -25
  48. package/dist/providers/groq/index.js.map +1 -1
  49. package/dist/providers/huggingface/index.cjs +80 -25
  50. package/dist/providers/huggingface/index.cjs.map +1 -1
  51. package/dist/providers/huggingface/index.js +82 -25
  52. package/dist/providers/huggingface/index.js.map +1 -1
  53. package/dist/providers/mistral/index.cjs +72 -24
  54. package/dist/providers/mistral/index.cjs.map +1 -1
  55. package/dist/providers/mistral/index.js +74 -24
  56. package/dist/providers/mistral/index.js.map +1 -1
  57. package/dist/providers/ollama/index.cjs +83 -25
  58. package/dist/providers/ollama/index.cjs.map +1 -1
  59. package/dist/providers/ollama/index.js +85 -25
  60. package/dist/providers/ollama/index.js.map +1 -1
  61. package/dist/providers/openai/index.cjs +1429 -28
  62. package/dist/providers/openai/index.cjs.map +1 -1
  63. package/dist/providers/openai/index.js +1447 -28
  64. package/dist/providers/openai/index.js.map +1 -1
  65. package/dist/providers/together/index.cjs +80 -25
  66. package/dist/providers/together/index.cjs.map +1 -1
  67. package/dist/providers/together/index.js +82 -25
  68. package/dist/providers/together/index.js.map +1 -1
  69. package/dist/providers/vercel-ai/index.cjs +45 -17
  70. package/dist/providers/vercel-ai/index.cjs.map +1 -1
  71. package/dist/providers/vercel-ai/index.js +47 -17
  72. package/dist/providers/vercel-ai/index.js.map +1 -1
  73. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,1395 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/globals.ts
34
+ function getClient() {
35
+ return G[PREFIX + "client"];
36
+ }
37
+ function setClient(client) {
38
+ G[PREFIX + "client"] = client;
39
+ }
40
+ function getTracer() {
41
+ return G[PREFIX + "tracer"];
42
+ }
43
+ function setTracer(tracer) {
44
+ G[PREFIX + "tracer"] = tracer;
45
+ }
46
+ function getContextStorage() {
47
+ if (!G[PREFIX + "ctx"]) {
48
+ G[PREFIX + "ctx"] = new import_node_async_hooks.AsyncLocalStorage();
49
+ }
50
+ return G[PREFIX + "ctx"];
51
+ }
52
+ function getRegistry() {
53
+ if (!G[PREFIX + "registry"]) {
54
+ G[PREFIX + "registry"] = /* @__PURE__ */ new Map();
55
+ }
56
+ return G[PREFIX + "registry"];
57
+ }
58
+ function getOpCount() {
59
+ return G[PREFIX + "opcount"] ?? 0;
60
+ }
61
+ function setOpCount(n) {
62
+ G[PREFIX + "opcount"] = n;
63
+ }
64
+ function getDebug() {
65
+ return G[PREFIX + "debug"] ?? false;
66
+ }
67
+ function setDebugFlag(enabled) {
68
+ G[PREFIX + "debug"] = enabled;
69
+ }
70
+ var import_node_async_hooks, G, PREFIX;
71
+ var init_globals = __esm({
72
+ "src/globals.ts"() {
73
+ "use strict";
74
+ import_node_async_hooks = require("async_hooks");
75
+ G = globalThis;
76
+ PREFIX = "__risicare_";
77
+ }
78
+ });
79
+
80
+ // src/utils/log.ts
81
+ function setDebug(enabled) {
82
+ setDebugFlag(enabled);
83
+ }
84
+ function debug(msg) {
85
+ if (getDebug()) {
86
+ process.stderr.write(`[risicare] ${msg}
87
+ `);
88
+ }
89
+ }
90
+ function warn(msg) {
91
+ process.stderr.write(`[risicare] WARNING: ${msg}
92
+ `);
93
+ }
94
+ var init_log = __esm({
95
+ "src/utils/log.ts"() {
96
+ "use strict";
97
+ init_globals();
98
+ }
99
+ });
100
+
101
+ // src/runtime/config.ts
102
+ function resolveFixRuntimeConfig(config) {
103
+ return {
104
+ apiEndpoint: config.apiEndpoint,
105
+ apiKey: config.apiKey,
106
+ enabled: config.enabled ?? true,
107
+ cacheEnabled: config.cacheEnabled ?? true,
108
+ cacheTtlMs: config.cacheTtlMs ?? 3e5,
109
+ cacheMaxEntries: config.cacheMaxEntries ?? 1e3,
110
+ autoRefresh: config.autoRefresh ?? true,
111
+ refreshIntervalMs: config.refreshIntervalMs ?? 6e4,
112
+ dryRun: config.dryRun ?? false,
113
+ abTestingEnabled: config.abTestingEnabled ?? true,
114
+ timeoutMs: config.timeoutMs ?? 1e3,
115
+ debug: config.debug ?? false
116
+ };
117
+ }
118
+ function matchesError(fix, errorCode) {
119
+ if (fix.errorCode === errorCode) {
120
+ return true;
121
+ }
122
+ if (fix.errorCode.endsWith("*")) {
123
+ const prefix = fix.errorCode.slice(0, -1);
124
+ return errorCode.startsWith(prefix);
125
+ }
126
+ return false;
127
+ }
128
+ function shouldApply(fix, sessionHash) {
129
+ if (fix.trafficPercentage >= 100) return true;
130
+ if (fix.trafficPercentage <= 0) return false;
131
+ const bucket = sessionHash % 100;
132
+ return bucket < fix.trafficPercentage;
133
+ }
134
+ function activeFixFromApiResponse(item) {
135
+ return {
136
+ fixId: item.id ?? item.fix_id ?? item.fixId ?? "",
137
+ deploymentId: item.deployment_id ?? item.deploymentId ?? "",
138
+ errorCode: item.error_code ?? item.errorCode ?? "",
139
+ fixType: item.fix_type ?? item.fixType ?? "prompt",
140
+ config: item.config ?? {},
141
+ trafficPercentage: item.traffic_percentage ?? item.trafficPercentage ?? 100,
142
+ version: item.version ?? 1
143
+ };
144
+ }
145
+ var init_config = __esm({
146
+ "src/runtime/config.ts"() {
147
+ "use strict";
148
+ }
149
+ });
150
+
151
+ // src/runtime/cache.ts
152
+ var FixCache;
153
+ var init_cache = __esm({
154
+ "src/runtime/cache.ts"() {
155
+ "use strict";
156
+ init_config();
157
+ init_log();
158
+ FixCache = class {
159
+ _ttlMs;
160
+ _maxEntries;
161
+ _enabled;
162
+ _cache;
163
+ _stats;
164
+ constructor(config) {
165
+ this._enabled = config?.cacheEnabled ?? true;
166
+ this._ttlMs = config?.cacheTtlMs ?? 3e5;
167
+ this._maxEntries = config?.cacheMaxEntries ?? 1e3;
168
+ this._cache = /* @__PURE__ */ new Map();
169
+ this._stats = {
170
+ hits: 0,
171
+ misses: 0,
172
+ evictions: 0,
173
+ size: 0,
174
+ lastRefresh: null
175
+ };
176
+ }
177
+ /** Whether caching is enabled. */
178
+ get enabled() {
179
+ return this._enabled;
180
+ }
181
+ /**
182
+ * Get a fix by error code.
183
+ *
184
+ * Checks exact match first, then scans for wildcard matches.
185
+ * Expired entries are evicted on access.
186
+ */
187
+ get(errorCode) {
188
+ try {
189
+ if (!this._enabled) return null;
190
+ const now = Date.now();
191
+ const exactEntry = this._cache.get(errorCode);
192
+ if (exactEntry) {
193
+ if (exactEntry.expiresAt > now) {
194
+ this._stats.hits++;
195
+ return exactEntry.fix;
196
+ }
197
+ this._cache.delete(errorCode);
198
+ this._stats.evictions++;
199
+ }
200
+ for (const [key, entry] of this._cache) {
201
+ if (entry.expiresAt <= now) {
202
+ this._cache.delete(key);
203
+ this._stats.evictions++;
204
+ continue;
205
+ }
206
+ if (matchesError(entry.fix, errorCode)) {
207
+ this._stats.hits++;
208
+ return entry.fix;
209
+ }
210
+ }
211
+ this._stats.misses++;
212
+ return null;
213
+ } catch (e) {
214
+ debug(`FixCache.get error: ${e}`);
215
+ return null;
216
+ }
217
+ }
218
+ /**
219
+ * Add a single fix to the cache.
220
+ *
221
+ * Evicts the oldest entry if at capacity.
222
+ */
223
+ set(fix) {
224
+ try {
225
+ if (!this._enabled) return;
226
+ if (this._cache.size >= this._maxEntries) {
227
+ this._evictOldest();
228
+ }
229
+ const now = Date.now();
230
+ this._cache.set(fix.errorCode, {
231
+ fix,
232
+ createdAt: now,
233
+ expiresAt: now + this._ttlMs
234
+ });
235
+ this._stats.size = this._cache.size;
236
+ } catch (e) {
237
+ debug(`FixCache.set error: ${e}`);
238
+ }
239
+ }
240
+ /**
241
+ * Replace all cached fixes (bulk refresh).
242
+ *
243
+ * Clears the cache and populates with the given fixes.
244
+ */
245
+ setAll(fixes) {
246
+ try {
247
+ if (!this._enabled) return;
248
+ this._cache.clear();
249
+ const now = Date.now();
250
+ for (const fix of fixes) {
251
+ if (this._cache.size >= this._maxEntries) break;
252
+ this._cache.set(fix.errorCode, {
253
+ fix,
254
+ createdAt: now,
255
+ expiresAt: now + this._ttlMs
256
+ });
257
+ }
258
+ this._stats.size = this._cache.size;
259
+ this._stats.lastRefresh = now;
260
+ } catch (e) {
261
+ debug(`FixCache.setAll error: ${e}`);
262
+ }
263
+ }
264
+ /**
265
+ * Get all non-expired fixes.
266
+ */
267
+ getAll() {
268
+ try {
269
+ const now = Date.now();
270
+ const valid = [];
271
+ const expired = [];
272
+ for (const [key, entry] of this._cache) {
273
+ if (entry.expiresAt > now) {
274
+ valid.push(entry.fix);
275
+ } else {
276
+ expired.push(key);
277
+ }
278
+ }
279
+ for (const key of expired) {
280
+ this._cache.delete(key);
281
+ this._stats.evictions++;
282
+ }
283
+ this._stats.size = this._cache.size;
284
+ return valid;
285
+ } catch (e) {
286
+ debug(`FixCache.getAll error: ${e}`);
287
+ return [];
288
+ }
289
+ }
290
+ /** Clear all cache entries. Returns the number of entries cleared. */
291
+ clear() {
292
+ const count = this._cache.size;
293
+ this._cache.clear();
294
+ this._stats.size = 0;
295
+ return count;
296
+ }
297
+ /** Get cache statistics. */
298
+ get stats() {
299
+ this._stats.size = this._cache.size;
300
+ return { ...this._stats };
301
+ }
302
+ /** Evict the oldest cache entry. */
303
+ _evictOldest() {
304
+ if (this._cache.size === 0) return;
305
+ let oldestKey = null;
306
+ let oldestTime = Infinity;
307
+ for (const [key, entry] of this._cache) {
308
+ if (entry.createdAt < oldestTime) {
309
+ oldestTime = entry.createdAt;
310
+ oldestKey = key;
311
+ }
312
+ }
313
+ if (oldestKey !== null) {
314
+ this._cache.delete(oldestKey);
315
+ this._stats.evictions++;
316
+ }
317
+ }
318
+ };
319
+ }
320
+ });
321
+
322
+ // src/runtime/applier.ts
323
+ function emptyResult(fix, fixType) {
324
+ return {
325
+ applied: false,
326
+ fixId: fix.fixId,
327
+ deploymentId: fix.deploymentId,
328
+ fixType,
329
+ modifications: {}
330
+ };
331
+ }
332
+ var import_node_crypto2, MAX_FIX_CONTENT_LENGTH, FixApplier;
333
+ var init_applier = __esm({
334
+ "src/runtime/applier.ts"() {
335
+ "use strict";
336
+ import_node_crypto2 = require("crypto");
337
+ init_config();
338
+ init_log();
339
+ MAX_FIX_CONTENT_LENGTH = 1e4;
340
+ FixApplier = class {
341
+ _config;
342
+ _cache;
343
+ _applicationLog = [];
344
+ constructor(config, cache) {
345
+ this._config = resolveFixRuntimeConfig(config);
346
+ this._cache = cache;
347
+ }
348
+ // ═══════════════════════════════════════════════════════════════════════════
349
+ // Fix Lookup
350
+ // ═══════════════════════════════════════════════════════════════════════════
351
+ /**
352
+ * Get applicable fix for an error code.
353
+ *
354
+ * Considers A/B testing bucket if sessionId is provided. Uses
355
+ * crypto.createHash('md5') for deterministic bucketing across restarts.
356
+ */
357
+ getFixForError(errorCode, sessionId) {
358
+ try {
359
+ const fix = this._cache.get(errorCode);
360
+ if (!fix) return null;
361
+ if (sessionId && this._config.abTestingEnabled) {
362
+ const hashHex = (0, import_node_crypto2.createHash)("md5").update(sessionId).digest("hex").substring(0, 8);
363
+ const sessionHash = parseInt(hashHex, 16);
364
+ if (!shouldApply(fix, sessionHash)) {
365
+ debug(`Fix ${fix.fixId} not applied (A/B control group)`);
366
+ return null;
367
+ }
368
+ }
369
+ return fix;
370
+ } catch (e) {
371
+ debug(`getFixForError error: ${e}`);
372
+ return null;
373
+ }
374
+ }
375
+ // ═══════════════════════════════════════════════════════════════════════════
376
+ // 1. Prompt Fix
377
+ // ═══════════════════════════════════════════════════════════════════════════
378
+ /**
379
+ * Apply a prompt fix to messages.
380
+ *
381
+ * Supports 4 modification types:
382
+ * - prepend: Add content before first system message
383
+ * - append: Add content after first system message
384
+ * - replace: Regex replace across all messages
385
+ * - few_shot: Insert user/assistant example pairs after system message
386
+ */
387
+ applyPromptFix(fix, messages) {
388
+ try {
389
+ const cfg = fix.config;
390
+ const modificationType = cfg.modification_type ?? cfg.modificationType ?? "append";
391
+ const target = cfg.target ?? "system";
392
+ let content = cfg.content ?? "";
393
+ if (content.length > MAX_FIX_CONTENT_LENGTH) {
394
+ warn(
395
+ `Fix ${fix.fixId} content truncated from ${content.length} to ${MAX_FIX_CONTENT_LENGTH} chars`
396
+ );
397
+ content = content.slice(0, MAX_FIX_CONTENT_LENGTH);
398
+ }
399
+ const result = emptyResult(fix, "prompt");
400
+ if (this._config.dryRun) {
401
+ result.modifications = {
402
+ type: modificationType,
403
+ target,
404
+ contentPreview: content.slice(0, 100)
405
+ };
406
+ return { messages, result };
407
+ }
408
+ let modified = messages.map((m) => ({ ...m }));
409
+ if (modificationType === "prepend") {
410
+ modified = this._prependToTarget(modified, target, content);
411
+ result.applied = true;
412
+ result.modifications = { type: "prepend", target };
413
+ } else if (modificationType === "append") {
414
+ modified = this._appendToTarget(modified, target, content);
415
+ result.applied = true;
416
+ result.modifications = { type: "append", target };
417
+ } else if (modificationType === "replace") {
418
+ const pattern = cfg.pattern ?? "";
419
+ if (pattern) {
420
+ modified = this._replaceInMessages(modified, pattern, content);
421
+ result.applied = true;
422
+ result.modifications = { type: "replace", pattern };
423
+ }
424
+ } else if (modificationType === "few_shot") {
425
+ const examples = cfg.examples ?? [];
426
+ if (examples.length > 0) {
427
+ modified = this._addFewShotExamples(modified, examples);
428
+ result.applied = true;
429
+ result.modifications = { type: "few_shot", count: examples.length };
430
+ }
431
+ }
432
+ return { messages: modified, result };
433
+ } catch (e) {
434
+ debug(`applyPromptFix error: ${e}`);
435
+ return {
436
+ messages,
437
+ result: {
438
+ applied: false,
439
+ fixId: fix.fixId,
440
+ fixType: "prompt",
441
+ modifications: {},
442
+ error: String(e)
443
+ }
444
+ };
445
+ }
446
+ }
447
+ // ═══════════════════════════════════════════════════════════════════════════
448
+ // 2. Parameter Fix
449
+ // ═══════════════════════════════════════════════════════════════════════════
450
+ /**
451
+ * Apply a parameter fix — merge config.parameters into params.
452
+ */
453
+ applyParameterFix(fix, params) {
454
+ try {
455
+ const newParams = fix.config.parameters ?? {};
456
+ const result = emptyResult(fix, "parameter");
457
+ if (this._config.dryRun) {
458
+ result.modifications = { parameters: newParams };
459
+ return { params, result };
460
+ }
461
+ const modified = { ...params, ...newParams };
462
+ result.applied = true;
463
+ result.modifications = { parameters: newParams };
464
+ return { params: modified, result };
465
+ } catch (e) {
466
+ debug(`applyParameterFix error: ${e}`);
467
+ return {
468
+ params,
469
+ result: {
470
+ applied: false,
471
+ fixId: fix.fixId,
472
+ fixType: "parameter",
473
+ modifications: {},
474
+ error: String(e)
475
+ }
476
+ };
477
+ }
478
+ }
479
+ // ═══════════════════════════════════════════════════════════════════════════
480
+ // 3. Retry Fix (async — uses setTimeout for sleeping)
481
+ // ═══════════════════════════════════════════════════════════════════════════
482
+ /**
483
+ * Apply a retry fix to an async operation.
484
+ *
485
+ * Retries with exponential backoff + jitter using
486
+ * `await new Promise(r => setTimeout(r, delay))`.
487
+ */
488
+ async applyRetryFix(fix, operation) {
489
+ const cfg = fix.config;
490
+ const maxRetries = cfg.max_retries ?? cfg.maxRetries ?? 3;
491
+ const initialDelayMs = cfg.initial_delay_ms ?? cfg.initialDelayMs ?? 1e3;
492
+ const maxDelayMs = cfg.max_delay_ms ?? cfg.maxDelayMs ?? 3e4;
493
+ const exponentialBase = cfg.exponential_base ?? cfg.exponentialBase ?? 2;
494
+ const jitter = cfg.jitter ?? true;
495
+ const retryOn = cfg.retry_on ?? cfg.retryOn ?? [];
496
+ const result = emptyResult(fix, "retry");
497
+ if (this._config.dryRun) {
498
+ result.modifications = { maxRetries, initialDelayMs };
499
+ const value = await operation();
500
+ return { value, result };
501
+ }
502
+ let lastError = null;
503
+ let attempts = 0;
504
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
505
+ attempts++;
506
+ try {
507
+ const value = await operation();
508
+ result.applied = true;
509
+ result.modifications = { attempts };
510
+ return { value, result };
511
+ } catch (e) {
512
+ lastError = e instanceof Error ? e : new Error(String(e));
513
+ const errorType = lastError.constructor.name;
514
+ if (retryOn.length > 0 && !retryOn.includes(errorType)) {
515
+ throw lastError;
516
+ }
517
+ if (attempt < maxRetries) {
518
+ let delayMs = Math.min(
519
+ initialDelayMs * Math.pow(exponentialBase, attempt),
520
+ maxDelayMs
521
+ );
522
+ if (jitter) {
523
+ delayMs *= 0.5 + Math.random();
524
+ }
525
+ debug(
526
+ `Retry ${attempt + 1}/${maxRetries} after ${Math.round(delayMs)}ms`
527
+ );
528
+ await new Promise((r) => setTimeout(r, delayMs));
529
+ }
530
+ }
531
+ }
532
+ result.applied = true;
533
+ result.error = lastError?.message;
534
+ result.modifications = { attempts, exhausted: true };
535
+ throw lastError;
536
+ }
537
+ // ═══════════════════════════════════════════════════════════════════════════
538
+ // 4. Fallback Fix
539
+ // ═══════════════════════════════════════════════════════════════════════════
540
+ /**
541
+ * Apply a fallback fix.
542
+ *
543
+ * - 'model': Replace params.model with the fallback model
544
+ * - 'default': Set params._fallbackResponse for the caller to use
545
+ */
546
+ applyFallbackFix(fix, params) {
547
+ try {
548
+ const cfg = fix.config;
549
+ const fallbackType = cfg.fallback_type ?? cfg.fallbackType ?? "model";
550
+ const fallbackConfig = cfg.fallback_config ?? cfg.fallbackConfig ?? {};
551
+ const result = emptyResult(fix, "fallback");
552
+ if (this._config.dryRun) {
553
+ result.modifications = { fallbackType, fallbackConfig };
554
+ return { params, result };
555
+ }
556
+ const modified = { ...params };
557
+ if (fallbackType === "model") {
558
+ const fallbackModel = fallbackConfig.model;
559
+ if (fallbackModel) {
560
+ modified.model = fallbackModel;
561
+ result.applied = true;
562
+ result.modifications = { model: fallbackModel };
563
+ }
564
+ } else if (fallbackType === "default") {
565
+ const defaultResponse = fallbackConfig.response;
566
+ if (defaultResponse !== void 0) {
567
+ modified._fallbackResponse = defaultResponse;
568
+ result.applied = true;
569
+ result.modifications = { defaultResponse: true };
570
+ }
571
+ }
572
+ return { params: modified, result };
573
+ } catch (e) {
574
+ debug(`applyFallbackFix error: ${e}`);
575
+ return {
576
+ params,
577
+ result: {
578
+ applied: false,
579
+ fixId: fix.fixId,
580
+ fixType: "fallback",
581
+ modifications: {},
582
+ error: String(e)
583
+ }
584
+ };
585
+ }
586
+ }
587
+ // ═══════════════════════════════════════════════════════════════════════════
588
+ // 5. Guard Fix
589
+ // ═══════════════════════════════════════════════════════════════════════════
590
+ /**
591
+ * Apply a guard fix (validation).
592
+ *
593
+ * Guard types:
594
+ * - content_filter: Regex-based blocked patterns
595
+ * - format_check: JSON.parse() validity
596
+ * - input_validation / output_validation: min/max length
597
+ */
598
+ applyGuardFix(fix, content, isInput = true) {
599
+ try {
600
+ const cfg = fix.config;
601
+ const guardType = cfg.guard_type ?? cfg.guardType ?? "output_validation";
602
+ const guardConfig = cfg.guard_config ?? cfg.guardConfig ?? {};
603
+ const result = emptyResult(fix, "guard");
604
+ if (isInput && guardType !== "input_validation" && guardType !== "content_filter") {
605
+ return { content, passed: true, result };
606
+ }
607
+ if (!isInput && guardType !== "output_validation" && guardType !== "format_check") {
608
+ return { content, passed: true, result };
609
+ }
610
+ if (this._config.dryRun) {
611
+ result.modifications = { guardType };
612
+ return { content, passed: true, result };
613
+ }
614
+ let passed = true;
615
+ if (guardType === "content_filter") {
616
+ const blockedPatterns = guardConfig.blocked_patterns ?? guardConfig.blockedPatterns ?? [];
617
+ for (const pattern of blockedPatterns) {
618
+ const regex = this._safeCompileRegex(pattern);
619
+ if (regex && regex.test(content)) {
620
+ passed = false;
621
+ break;
622
+ }
623
+ }
624
+ } else if (guardType === "format_check") {
625
+ const requiredFormat = guardConfig.format;
626
+ if (requiredFormat === "json") {
627
+ try {
628
+ JSON.parse(content);
629
+ } catch {
630
+ passed = false;
631
+ }
632
+ }
633
+ } else if (guardType === "input_validation" || guardType === "output_validation") {
634
+ const minLength = guardConfig.min_length ?? guardConfig.minLength ?? 0;
635
+ const maxLength = guardConfig.max_length ?? guardConfig.maxLength ?? Infinity;
636
+ if (content.length < minLength || content.length > maxLength) {
637
+ passed = false;
638
+ }
639
+ }
640
+ result.applied = true;
641
+ result.modifications = { passed, guardType };
642
+ return { content, passed, result };
643
+ } catch (e) {
644
+ debug(`applyGuardFix error: ${e}`);
645
+ return {
646
+ content,
647
+ passed: true,
648
+ result: {
649
+ applied: false,
650
+ fixId: fix.fixId,
651
+ fixType: "guard",
652
+ modifications: {},
653
+ error: String(e)
654
+ }
655
+ };
656
+ }
657
+ }
658
+ // ═══════════════════════════════════════════════════════════════════════════
659
+ // Application Log
660
+ // ═══════════════════════════════════════════════════════════════════════════
661
+ /** Log a fix application result. */
662
+ logApplication(result) {
663
+ this._applicationLog.push(result);
664
+ if (this._applicationLog.length > 1e3) {
665
+ this._applicationLog = this._applicationLog.slice(-500);
666
+ }
667
+ }
668
+ /** Get the application log. */
669
+ getApplicationLog() {
670
+ return [...this._applicationLog];
671
+ }
672
+ // ═══════════════════════════════════════════════════════════════════════════
673
+ // Private Helpers
674
+ // ═══════════════════════════════════════════════════════════════════════════
675
+ /**
676
+ * Safely compile a regex pattern from untrusted input.
677
+ * Returns null if invalid or too long.
678
+ */
679
+ _safeCompileRegex(pattern, maxLength = 500) {
680
+ if (!pattern || pattern.length > maxLength) return null;
681
+ try {
682
+ return new RegExp(pattern, "i");
683
+ } catch {
684
+ debug(`Invalid regex pattern (length=${pattern.length})`);
685
+ return null;
686
+ }
687
+ }
688
+ /** Prepend content to the first message matching the target role. */
689
+ _prependToTarget(messages, target, content) {
690
+ return messages.map((msg) => {
691
+ if (msg.role === target) {
692
+ return {
693
+ ...msg,
694
+ content: content + "\n\n" + (msg.content ?? "")
695
+ };
696
+ }
697
+ return msg;
698
+ });
699
+ }
700
+ /** Append content to the first message matching the target role. */
701
+ _appendToTarget(messages, target, content) {
702
+ return messages.map((msg) => {
703
+ if (msg.role === target) {
704
+ return {
705
+ ...msg,
706
+ content: (msg.content ?? "") + "\n\n" + content
707
+ };
708
+ }
709
+ return msg;
710
+ });
711
+ }
712
+ /** Regex replace across all messages. */
713
+ _replaceInMessages(messages, pattern, replacement) {
714
+ const regex = this._safeCompileRegex(pattern);
715
+ if (!regex) {
716
+ debug("Skipping invalid regex pattern in prompt fix");
717
+ return messages;
718
+ }
719
+ return messages.map((msg) => {
720
+ const msgContent = msg.content;
721
+ if (typeof msgContent === "string") {
722
+ return { ...msg, content: msgContent.replace(regex, replacement) };
723
+ }
724
+ return msg;
725
+ });
726
+ }
727
+ /** Add few-shot examples after the first system message. */
728
+ _addFewShotExamples(messages, examples) {
729
+ const result = [];
730
+ let systemFound = false;
731
+ for (const msg of messages) {
732
+ result.push(msg);
733
+ if (msg.role === "system" && !systemFound) {
734
+ systemFound = true;
735
+ for (const example of examples) {
736
+ if (example.user) {
737
+ result.push({ role: "user", content: example.user });
738
+ }
739
+ if (example.assistant) {
740
+ result.push({ role: "assistant", content: example.assistant });
741
+ }
742
+ }
743
+ }
744
+ }
745
+ return result;
746
+ }
747
+ };
748
+ }
749
+ });
750
+
751
+ // src/runtime/loader.ts
752
+ var SDK_VERSION2, FixLoader;
753
+ var init_loader = __esm({
754
+ "src/runtime/loader.ts"() {
755
+ "use strict";
756
+ init_config();
757
+ init_config();
758
+ init_log();
759
+ SDK_VERSION2 = "0.1.3";
760
+ FixLoader = class _FixLoader {
761
+ _config;
762
+ _cache;
763
+ _refreshTimer = null;
764
+ _lastLoadTime = null;
765
+ _consecutiveFailures = 0;
766
+ _circuitOpenUntil = 0;
767
+ _onLoadCallbacks = [];
768
+ // Circuit breaker settings
769
+ static CIRCUIT_BREAKER_THRESHOLD = 3;
770
+ static CIRCUIT_BREAKER_COOLDOWN_MS = 3e4;
771
+ constructor(config, cache) {
772
+ this._config = resolveFixRuntimeConfig(config);
773
+ this._cache = cache;
774
+ }
775
+ /** Timestamp of last successful load. */
776
+ get lastLoadTime() {
777
+ return this._lastLoadTime;
778
+ }
779
+ /**
780
+ * Start the loader: perform initial load and start background refresh.
781
+ *
782
+ * Never throws — failures are logged and the runtime degrades gracefully.
783
+ */
784
+ start() {
785
+ try {
786
+ if (!this._config.enabled) {
787
+ debug("Fix runtime disabled, skipping loader start");
788
+ return;
789
+ }
790
+ if (!this._config.apiKey) {
791
+ warn("No API key configured, fixes will not be loaded");
792
+ return;
793
+ }
794
+ this.loadSync().catch((e) => {
795
+ debug(`Initial fix load failed: ${e}`);
796
+ });
797
+ if (this._config.autoRefresh) {
798
+ this._startRefreshInterval();
799
+ }
800
+ } catch (e) {
801
+ debug(`FixLoader.start error: ${e}`);
802
+ }
803
+ }
804
+ /** Stop the loader: clear the refresh interval. */
805
+ stop() {
806
+ try {
807
+ if (this._refreshTimer !== null) {
808
+ clearInterval(this._refreshTimer);
809
+ this._refreshTimer = null;
810
+ }
811
+ } catch (e) {
812
+ debug(`FixLoader.stop error: ${e}`);
813
+ }
814
+ }
815
+ /**
816
+ * Register a callback invoked after each successful load.
817
+ */
818
+ onLoad(callback) {
819
+ this._onLoadCallbacks.push(callback);
820
+ }
821
+ /**
822
+ * Load fixes from the API.
823
+ *
824
+ * Name kept as "loadSync" for parity with the Python SDK, but this
825
+ * returns a Promise in JS (there's no synchronous fetch in Node.js).
826
+ */
827
+ async loadSync() {
828
+ if (!this._config.apiKey) {
829
+ return [];
830
+ }
831
+ const now = Date.now();
832
+ if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD && now < this._circuitOpenUntil) {
833
+ debug("Fix loader circuit breaker open \u2014 skipping load");
834
+ return this._cache.getAll();
835
+ }
836
+ try {
837
+ const endpoint = this._config.apiEndpoint.replace(/\/+$/, "");
838
+ const url = `${endpoint}/api/v1/fixes/active`;
839
+ const controller = new AbortController();
840
+ const timeoutId = setTimeout(
841
+ () => controller.abort(),
842
+ this._config.timeoutMs
843
+ );
844
+ const response = await fetch(url, {
845
+ method: "GET",
846
+ headers: {
847
+ "Authorization": `Bearer ${this._config.apiKey}`,
848
+ "Content-Type": "application/json",
849
+ "X-Risicare-SDK-Version": SDK_VERSION2
850
+ },
851
+ signal: controller.signal
852
+ });
853
+ clearTimeout(timeoutId);
854
+ if (!response.ok) {
855
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
856
+ }
857
+ const data = await response.json();
858
+ const fixes = this._parseResponse(data);
859
+ this._onLoadSuccess(fixes);
860
+ return fixes;
861
+ } catch (e) {
862
+ this._onLoadFailure(e);
863
+ throw e;
864
+ }
865
+ }
866
+ /** Get currently cached fixes without hitting the API. */
867
+ getCached() {
868
+ return this._cache.getAll();
869
+ }
870
+ // ─── Private ────────────────────────────────────────────────────────────
871
+ _parseResponse(data) {
872
+ const fixes = [];
873
+ const items = data.fixes ?? data.data ?? [];
874
+ if (!Array.isArray(items)) return fixes;
875
+ for (const item of items) {
876
+ try {
877
+ if (item && typeof item === "object") {
878
+ fixes.push(activeFixFromApiResponse(item));
879
+ }
880
+ } catch (e) {
881
+ debug(`Failed to parse fix item: ${e}`);
882
+ }
883
+ }
884
+ return fixes;
885
+ }
886
+ _onLoadSuccess(fixes) {
887
+ this._lastLoadTime = Date.now();
888
+ this._consecutiveFailures = 0;
889
+ this._cache.setAll(fixes);
890
+ for (const callback of this._onLoadCallbacks) {
891
+ try {
892
+ callback(fixes);
893
+ } catch (e) {
894
+ debug(`Load callback error: ${e}`);
895
+ }
896
+ }
897
+ debug(`Loaded ${fixes.length} active fixes`);
898
+ }
899
+ _onLoadFailure(error) {
900
+ this._consecutiveFailures++;
901
+ if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD) {
902
+ this._circuitOpenUntil = Date.now() + _FixLoader.CIRCUIT_BREAKER_COOLDOWN_MS;
903
+ warn(
904
+ `Fix loader circuit breaker opened after ${this._consecutiveFailures} failures. Cooldown: ${_FixLoader.CIRCUIT_BREAKER_COOLDOWN_MS / 1e3}s`
905
+ );
906
+ } else {
907
+ debug(
908
+ `Fix load failed (attempt ${this._consecutiveFailures}): ${error.message}`
909
+ );
910
+ }
911
+ }
912
+ _startRefreshInterval() {
913
+ const tick = () => {
914
+ this.loadSync().catch((e) => {
915
+ debug(`Background fix refresh failed: ${e}`);
916
+ });
917
+ };
918
+ this._refreshTimer = setInterval(
919
+ () => {
920
+ if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD && Date.now() < this._circuitOpenUntil) {
921
+ return;
922
+ }
923
+ tick();
924
+ },
925
+ this._config.refreshIntervalMs
926
+ );
927
+ this._refreshTimer.unref();
928
+ debug("Started fix refresh interval");
929
+ }
930
+ };
931
+ }
932
+ });
933
+
934
+ // src/runtime/interceptors.ts
935
+ function createInterceptContext(operationType, operationName, options) {
936
+ return {
937
+ operationType,
938
+ operationName,
939
+ sessionId: options?.sessionId,
940
+ traceId: options?.traceId,
941
+ errorCode: options?.errorCode,
942
+ attempt: 1,
943
+ appliedFixes: []
944
+ };
945
+ }
946
+ function classifyError(error) {
947
+ const errorType = error.constructor.name.toLowerCase();
948
+ const errorMsg = error.message.toLowerCase();
949
+ if (errorType.includes("timeout") || errorMsg.includes("timeout") || errorMsg.includes("aborted")) {
950
+ return "TOOL.EXECUTION.TIMEOUT";
951
+ }
952
+ if (errorMsg.includes("rate") && errorMsg.includes("limit")) {
953
+ return "TOOL.EXECUTION.RATE_LIMIT";
954
+ }
955
+ if (errorMsg.includes("429") || errorMsg.includes("too many requests")) {
956
+ return "TOOL.EXECUTION.RATE_LIMIT";
957
+ }
958
+ if (errorType.includes("connection") || errorMsg.includes("connection") || errorMsg.includes("econnrefused") || errorMsg.includes("econnreset") || errorMsg.includes("fetch failed")) {
959
+ return "TOOL.EXECUTION.CONNECTION_ERROR";
960
+ }
961
+ if (errorMsg.includes("auth") || errorMsg.includes("401") || errorMsg.includes("403") || errorMsg.includes("unauthorized") || errorMsg.includes("forbidden")) {
962
+ return "TOOL.EXECUTION.AUTH_ERROR";
963
+ }
964
+ if (errorType.includes("syntaxerror") || errorType.includes("json") || errorMsg.includes("json") || errorMsg.includes("unexpected token")) {
965
+ return "OUTPUT.FORMAT.JSON_INVALID";
966
+ }
967
+ return "TOOL.EXECUTION.FAILURE";
968
+ }
969
+ var DefaultFixInterceptor;
970
+ var init_interceptors = __esm({
971
+ "src/runtime/interceptors.ts"() {
972
+ "use strict";
973
+ init_config();
974
+ init_log();
975
+ DefaultFixInterceptor = class {
976
+ _config;
977
+ _cache;
978
+ _applier;
979
+ constructor(config, cache, applier) {
980
+ this._config = resolveFixRuntimeConfig(config);
981
+ this._cache = cache;
982
+ this._applier = applier;
983
+ }
984
+ /**
985
+ * Apply pre-call fixes.
986
+ *
987
+ * - If retrying after error: look up fix by error_code, apply
988
+ * prompt/parameter/fallback fixes
989
+ * - Runs input guard fixes on all message content
990
+ */
991
+ preCall(ctx, messages, params) {
992
+ try {
993
+ if (!this._config.enabled) {
994
+ return { messages, params };
995
+ }
996
+ let modifiedMessages = messages;
997
+ let modifiedParams = { ...params };
998
+ if (ctx.errorCode) {
999
+ const fix = this._applier.getFixForError(ctx.errorCode, ctx.sessionId);
1000
+ if (fix) {
1001
+ debug(`Applying fix ${fix.fixId} for ${ctx.errorCode}`);
1002
+ if (fix.fixType === "prompt" && messages) {
1003
+ const msgRecords = messages;
1004
+ const { messages: newMessages, result } = this._applier.applyPromptFix(fix, msgRecords);
1005
+ modifiedMessages = newMessages;
1006
+ ctx.appliedFixes.push(result);
1007
+ this._applier.logApplication(result);
1008
+ } else if (fix.fixType === "parameter") {
1009
+ const { params: newParams, result } = this._applier.applyParameterFix(fix, modifiedParams);
1010
+ modifiedParams = newParams;
1011
+ ctx.appliedFixes.push(result);
1012
+ this._applier.logApplication(result);
1013
+ } else if (fix.fixType === "fallback") {
1014
+ const { params: newParams, result } = this._applier.applyFallbackFix(fix, modifiedParams);
1015
+ modifiedParams = newParams;
1016
+ ctx.appliedFixes.push(result);
1017
+ this._applier.logApplication(result);
1018
+ }
1019
+ }
1020
+ }
1021
+ if (modifiedMessages && this._config.enabled) {
1022
+ for (const msg of modifiedMessages) {
1023
+ const content = msg.content;
1024
+ if (typeof content === "string") {
1025
+ for (const fix of this._cache.getAll()) {
1026
+ if (fix.fixType === "guard") {
1027
+ const { result } = this._applier.applyGuardFix(
1028
+ fix,
1029
+ content,
1030
+ true
1031
+ );
1032
+ if (result.applied) {
1033
+ ctx.appliedFixes.push(result);
1034
+ this._applier.logApplication(result);
1035
+ }
1036
+ }
1037
+ }
1038
+ }
1039
+ }
1040
+ }
1041
+ return { messages: modifiedMessages, params: modifiedParams };
1042
+ } catch (e) {
1043
+ debug(`DefaultFixInterceptor.preCall error: ${e}`);
1044
+ return { messages, params };
1045
+ }
1046
+ }
1047
+ /**
1048
+ * Apply post-call fixes (output validation).
1049
+ *
1050
+ * Runs output guard fixes on response content.
1051
+ */
1052
+ postCall(ctx, response) {
1053
+ try {
1054
+ if (!this._config.enabled) {
1055
+ return { response, shouldContinue: true };
1056
+ }
1057
+ for (const fix of this._cache.getAll()) {
1058
+ if (fix.fixType !== "guard") continue;
1059
+ const content = this._extractResponseContent(response);
1060
+ if (!content) continue;
1061
+ const { passed, result } = this._applier.applyGuardFix(
1062
+ fix,
1063
+ content,
1064
+ false
1065
+ );
1066
+ if (result.applied) {
1067
+ ctx.appliedFixes.push(result);
1068
+ this._applier.logApplication(result);
1069
+ }
1070
+ if (!passed) {
1071
+ warn(`Output guard failed for fix ${fix.fixId}`);
1072
+ }
1073
+ }
1074
+ return { response, shouldContinue: true };
1075
+ } catch (e) {
1076
+ debug(`DefaultFixInterceptor.postCall error: ${e}`);
1077
+ return { response, shouldContinue: true };
1078
+ }
1079
+ }
1080
+ /**
1081
+ * Handle errors and decide whether to retry.
1082
+ *
1083
+ * Classifies the error, looks up retry/fallback fixes, and returns
1084
+ * whether the caller should retry.
1085
+ */
1086
+ onError(ctx, error) {
1087
+ try {
1088
+ if (!this._config.enabled) {
1089
+ return { shouldRetry: false, modifiedParams: null };
1090
+ }
1091
+ const errorCode = classifyError(error);
1092
+ ctx.errorCode = errorCode;
1093
+ ctx.errorMessage = error.message;
1094
+ const fix = this._applier.getFixForError(errorCode, ctx.sessionId);
1095
+ if (!fix) {
1096
+ return { shouldRetry: false, modifiedParams: null };
1097
+ }
1098
+ if (fix.fixType === "retry") {
1099
+ const maxRetries = fix.config.max_retries ?? fix.config.maxRetries ?? 3;
1100
+ if (ctx.attempt <= maxRetries) {
1101
+ debug(
1102
+ `Retry fix ${fix.fixId}: attempt ${ctx.attempt}/${maxRetries}`
1103
+ );
1104
+ const result = {
1105
+ applied: true,
1106
+ fixId: fix.fixId,
1107
+ deploymentId: fix.deploymentId,
1108
+ fixType: "retry",
1109
+ modifications: { attempt: ctx.attempt }
1110
+ };
1111
+ ctx.appliedFixes.push(result);
1112
+ this._applier.logApplication(result);
1113
+ return { shouldRetry: true, modifiedParams: null };
1114
+ }
1115
+ }
1116
+ if (fix.fixType === "fallback") {
1117
+ const { params: modifiedParams, result } = this._applier.applyFallbackFix(fix, {});
1118
+ if (result.applied) {
1119
+ ctx.appliedFixes.push(result);
1120
+ this._applier.logApplication(result);
1121
+ return { shouldRetry: true, modifiedParams };
1122
+ }
1123
+ }
1124
+ return { shouldRetry: false, modifiedParams: null };
1125
+ } catch (e) {
1126
+ debug(`DefaultFixInterceptor.onError error: ${e}`);
1127
+ return { shouldRetry: false, modifiedParams: null };
1128
+ }
1129
+ }
1130
+ // ─── Private ────────────────────────────────────────────────────────────
1131
+ /** Extract text content from a response object. */
1132
+ _extractResponseContent(response) {
1133
+ if (typeof response === "string") return response;
1134
+ if (response && typeof response === "object") {
1135
+ const resp = response;
1136
+ const choices = resp.choices;
1137
+ if (Array.isArray(choices) && choices.length > 0) {
1138
+ const first = choices[0];
1139
+ const message = first?.message;
1140
+ if (typeof message?.content === "string") {
1141
+ return message.content;
1142
+ }
1143
+ }
1144
+ const content = resp.content;
1145
+ if (typeof content === "string") return content;
1146
+ if (Array.isArray(content) && content.length > 0) {
1147
+ const first = content[0];
1148
+ if (typeof first?.text === "string") return first.text;
1149
+ }
1150
+ }
1151
+ return null;
1152
+ }
1153
+ };
1154
+ }
1155
+ });
1156
+
1157
+ // src/runtime/runtime.ts
1158
+ var runtime_exports = {};
1159
+ __export(runtime_exports, {
1160
+ FixRuntime: () => FixRuntime,
1161
+ getFixRuntime: () => getFixRuntime,
1162
+ initFixRuntime: () => initFixRuntime,
1163
+ setFixRuntime: () => setFixRuntime,
1164
+ shutdownFixRuntime: () => shutdownFixRuntime
1165
+ });
1166
+ function getFixRuntime() {
1167
+ return G2[RUNTIME_KEY];
1168
+ }
1169
+ function setFixRuntime(runtime) {
1170
+ G2[RUNTIME_KEY] = runtime;
1171
+ }
1172
+ function initFixRuntime(config) {
1173
+ try {
1174
+ const existing = G2[RUNTIME_KEY];
1175
+ if (existing) {
1176
+ debug("Fix runtime already initialized");
1177
+ return existing;
1178
+ }
1179
+ const runtime = new FixRuntime(config);
1180
+ runtime.start();
1181
+ G2[RUNTIME_KEY] = runtime;
1182
+ return runtime;
1183
+ } catch (e) {
1184
+ warn(`initFixRuntime failed: ${e}`);
1185
+ const fallback = new FixRuntime({ ...config, enabled: false });
1186
+ G2[RUNTIME_KEY] = fallback;
1187
+ return fallback;
1188
+ }
1189
+ }
1190
+ function shutdownFixRuntime() {
1191
+ try {
1192
+ const runtime = G2[RUNTIME_KEY];
1193
+ if (runtime) {
1194
+ runtime.stop();
1195
+ }
1196
+ G2[RUNTIME_KEY] = void 0;
1197
+ } catch (e) {
1198
+ debug(`shutdownFixRuntime error: ${e}`);
1199
+ G2[RUNTIME_KEY] = void 0;
1200
+ }
1201
+ }
1202
+ var G2, RUNTIME_KEY, FixRuntime;
1203
+ var init_runtime = __esm({
1204
+ "src/runtime/runtime.ts"() {
1205
+ "use strict";
1206
+ init_cache();
1207
+ init_config();
1208
+ init_applier();
1209
+ init_loader();
1210
+ init_interceptors();
1211
+ init_log();
1212
+ G2 = globalThis;
1213
+ RUNTIME_KEY = "__risicare_fix_runtime";
1214
+ FixRuntime = class {
1215
+ _config;
1216
+ _cache;
1217
+ _loader;
1218
+ _applier;
1219
+ _interceptor;
1220
+ _started = false;
1221
+ // Effectiveness tracking
1222
+ _fixApplications = /* @__PURE__ */ new Map();
1223
+ _fixSuccesses = /* @__PURE__ */ new Map();
1224
+ constructor(config) {
1225
+ this._config = resolveFixRuntimeConfig(config);
1226
+ this._cache = new FixCache(this._config);
1227
+ this._loader = new FixLoader(config, this._cache);
1228
+ this._applier = new FixApplier(config, this._cache);
1229
+ this._interceptor = new DefaultFixInterceptor(
1230
+ config,
1231
+ this._cache,
1232
+ this._applier
1233
+ );
1234
+ }
1235
+ // ─── Accessors ──────────────────────────────────────────────────────────
1236
+ /** Runtime configuration (with defaults resolved). */
1237
+ get config() {
1238
+ return this._config;
1239
+ }
1240
+ /** Whether the runtime is enabled and started. */
1241
+ get isEnabled() {
1242
+ return this._config.enabled && this._started;
1243
+ }
1244
+ /** The fix cache. */
1245
+ get cache() {
1246
+ return this._cache;
1247
+ }
1248
+ /** The fix loader. */
1249
+ get loader() {
1250
+ return this._loader;
1251
+ }
1252
+ /** The fix applier. */
1253
+ get applier() {
1254
+ return this._applier;
1255
+ }
1256
+ /** The interceptor. */
1257
+ get interceptor() {
1258
+ return this._interceptor;
1259
+ }
1260
+ // ─── Lifecycle ──────────────────────────────────────────────────────────
1261
+ /**
1262
+ * Start the runtime.
1263
+ *
1264
+ * Triggers initial fix load and starts background refresh.
1265
+ * Never throws — failures degrade gracefully.
1266
+ */
1267
+ start() {
1268
+ try {
1269
+ if (this._started) return;
1270
+ if (!this._config.enabled) {
1271
+ debug("Fix runtime disabled");
1272
+ return;
1273
+ }
1274
+ debug("Starting fix runtime...");
1275
+ this._loader.start();
1276
+ this._started = true;
1277
+ debug("Fix runtime started");
1278
+ } catch (e) {
1279
+ warn(`Fix runtime start failed: ${e}`);
1280
+ }
1281
+ }
1282
+ /**
1283
+ * Stop the runtime.
1284
+ *
1285
+ * Stops background refresh and clears the cache.
1286
+ * Never throws.
1287
+ */
1288
+ stop() {
1289
+ try {
1290
+ if (!this._started) return;
1291
+ debug("Stopping fix runtime...");
1292
+ this._loader.stop();
1293
+ this._cache.clear();
1294
+ this._started = false;
1295
+ debug("Fix runtime stopped");
1296
+ } catch (e) {
1297
+ debug(`Fix runtime stop error: ${e}`);
1298
+ }
1299
+ }
1300
+ // ─── Intercept API ──────────────────────────────────────────────────────
1301
+ /**
1302
+ * Intercept a call: create context and run preCall fixes.
1303
+ *
1304
+ * Returns modified messages/params and the InterceptContext for
1305
+ * use in subsequent interceptResponse / interceptError calls.
1306
+ */
1307
+ interceptCall(operationType, operationName, messages, params, options) {
1308
+ const ctx = createInterceptContext(operationType, operationName, options);
1309
+ try {
1310
+ if (!this.isEnabled) {
1311
+ return { messages, params, ctx };
1312
+ }
1313
+ const result = this._interceptor.preCall(ctx, messages, params);
1314
+ return { messages: result.messages, params: result.params, ctx };
1315
+ } catch (e) {
1316
+ debug(`interceptCall error: ${e}`);
1317
+ return { messages, params, ctx };
1318
+ }
1319
+ }
1320
+ /**
1321
+ * Intercept a response: run postCall fixes (output validation).
1322
+ */
1323
+ interceptResponse(ctx, response) {
1324
+ try {
1325
+ if (!this.isEnabled) {
1326
+ return { response, shouldContinue: true };
1327
+ }
1328
+ const result = this._interceptor.postCall(ctx, response);
1329
+ this._trackSuccess(ctx);
1330
+ return result;
1331
+ } catch (e) {
1332
+ debug(`interceptResponse error: ${e}`);
1333
+ return { response, shouldContinue: true };
1334
+ }
1335
+ }
1336
+ /**
1337
+ * Intercept an error: decide on retry/fallback.
1338
+ */
1339
+ interceptError(ctx, error) {
1340
+ try {
1341
+ if (!this.isEnabled) {
1342
+ return { shouldRetry: false, modifiedParams: null };
1343
+ }
1344
+ ctx.attempt++;
1345
+ const result = this._interceptor.onError(ctx, error);
1346
+ if (!result.shouldRetry) {
1347
+ this._trackFailure(ctx);
1348
+ }
1349
+ return result;
1350
+ } catch (e) {
1351
+ debug(`interceptError error: ${e}`);
1352
+ return { shouldRetry: false, modifiedParams: null };
1353
+ }
1354
+ }
1355
+ // ─── Direct Fix Access ──────────────────────────────────────────────────
1356
+ /**
1357
+ * Get applicable fix for an error code.
1358
+ */
1359
+ getFix(errorCode, sessionId) {
1360
+ try {
1361
+ if (!this.isEnabled) return null;
1362
+ return this._applier.getFixForError(errorCode, sessionId);
1363
+ } catch (e) {
1364
+ debug(`getFix error: ${e}`);
1365
+ return null;
1366
+ }
1367
+ }
1368
+ /**
1369
+ * Manually refresh fixes from the API.
1370
+ */
1371
+ async refreshFixes() {
1372
+ try {
1373
+ return await this._loader.loadSync();
1374
+ } catch (e) {
1375
+ debug(`refreshFixes error: ${e}`);
1376
+ return [];
1377
+ }
1378
+ }
1379
+ // ─── Effectiveness ──────────────────────────────────────────────────────
1380
+ /** Get effectiveness statistics for all fixes. */
1381
+ getEffectivenessStats() {
1382
+ const stats = {};
1383
+ for (const [fixId, applications] of this._fixApplications) {
1384
+ const successes = this._fixSuccesses.get(fixId) ?? 0;
1385
+ stats[fixId] = {
1386
+ applications,
1387
+ successes,
1388
+ successRate: applications > 0 ? successes / applications : 0
1389
+ };
1390
+ }
1391
+ return stats;
1392
+ }
1393
+ // ─── Private ────────────────────────────────────────────────────────────
1394
+ _trackSuccess(ctx) {
1395
+ for (const result of ctx.appliedFixes) {
1396
+ if (result.applied && result.fixId) {
1397
+ this._fixApplications.set(
1398
+ result.fixId,
1399
+ (this._fixApplications.get(result.fixId) ?? 0) + 1
1400
+ );
1401
+ this._fixSuccesses.set(
1402
+ result.fixId,
1403
+ (this._fixSuccesses.get(result.fixId) ?? 0) + 1
1404
+ );
1405
+ }
1406
+ }
1407
+ }
1408
+ _trackFailure(ctx) {
1409
+ for (const result of ctx.appliedFixes) {
1410
+ if (result.applied && result.fixId) {
1411
+ this._fixApplications.set(
1412
+ result.fixId,
1413
+ (this._fixApplications.get(result.fixId) ?? 0) + 1
1414
+ );
1415
+ }
1416
+ }
1417
+ }
1418
+ };
1419
+ }
1420
+ });
1421
+
30
1422
  // src/index.ts
31
1423
  var src_exports = {};
32
1424
  __export(src_exports, {
@@ -51,12 +1443,14 @@ __export(src_exports, {
51
1443
  getCurrentSpan: () => getCurrentSpan,
52
1444
  getCurrentSpanId: () => getCurrentSpanId,
53
1445
  getCurrentTraceId: () => getCurrentTraceId,
1446
+ getFixRuntime: () => getFixRuntime,
54
1447
  getMetrics: () => getMetrics,
55
1448
  getSpanById: () => getSpanById,
56
1449
  getTraceContent: () => getTraceContent,
57
1450
  getTraceContext: () => getTraceContext,
58
1451
  getTracer: () => getTracer2,
59
1452
  init: () => init,
1453
+ initFixRuntime: () => initFixRuntime,
60
1454
  injectTraceContext: () => injectTraceContext,
61
1455
  instrumentLangGraph: () => instrumentLangGraph,
62
1456
  isEnabled: () => isEnabled,
@@ -67,6 +1461,7 @@ __export(src_exports, {
67
1461
  score: () => score,
68
1462
  session: () => session,
69
1463
  shutdown: () => shutdown,
1464
+ shutdownFixRuntime: () => shutdownFixRuntime,
70
1465
  suppressProviderInstrumentation: () => suppressProviderInstrumentation,
71
1466
  traceAct: () => traceAct,
72
1467
  traceCoordinate: () => traceCoordinate,
@@ -488,48 +1883,8 @@ function shouldSample(traceId, sampleRate) {
488
1883
  return hash / 4294967295 < sampleRate;
489
1884
  }
490
1885
 
491
- // src/globals.ts
492
- var import_node_async_hooks = require("async_hooks");
493
- var G = globalThis;
494
- var PREFIX = "__risicare_";
495
- function getClient() {
496
- return G[PREFIX + "client"];
497
- }
498
- function setClient(client) {
499
- G[PREFIX + "client"] = client;
500
- }
501
- function getTracer() {
502
- return G[PREFIX + "tracer"];
503
- }
504
- function setTracer(tracer) {
505
- G[PREFIX + "tracer"] = tracer;
506
- }
507
- function getContextStorage() {
508
- if (!G[PREFIX + "ctx"]) {
509
- G[PREFIX + "ctx"] = new import_node_async_hooks.AsyncLocalStorage();
510
- }
511
- return G[PREFIX + "ctx"];
512
- }
513
- function getRegistry() {
514
- if (!G[PREFIX + "registry"]) {
515
- G[PREFIX + "registry"] = /* @__PURE__ */ new Map();
516
- }
517
- return G[PREFIX + "registry"];
518
- }
519
- function getOpCount() {
520
- return G[PREFIX + "opcount"] ?? 0;
521
- }
522
- function setOpCount(n) {
523
- G[PREFIX + "opcount"] = n;
524
- }
525
- function getDebug() {
526
- return G[PREFIX + "debug"] ?? false;
527
- }
528
- function setDebugFlag(enabled) {
529
- G[PREFIX + "debug"] = enabled;
530
- }
531
-
532
1886
  // src/context/storage.ts
1887
+ init_globals();
533
1888
  function storage() {
534
1889
  return getContextStorage();
535
1890
  }
@@ -631,7 +1986,8 @@ var Tracer = class {
631
1986
  const session2 = getCurrentSession();
632
1987
  const agent2 = getCurrentAgent();
633
1988
  const phase = getCurrentPhase();
634
- const traceId = opts.traceId ?? parentSpan?.traceId ?? generateTraceId();
1989
+ const ctx = getContext();
1990
+ const traceId = opts.traceId ?? parentSpan?.traceId ?? ctx._rootTraceId ?? generateTraceId();
635
1991
  if (!parentSpan && this._sampleRate < 1) {
636
1992
  if (!shouldSample(traceId, this._sampleRate)) {
637
1993
  return fn(NOOP_SPAN);
@@ -726,22 +2082,8 @@ var Tracer = class {
726
2082
  }
727
2083
  };
728
2084
 
729
- // src/utils/log.ts
730
- function setDebug(enabled) {
731
- setDebugFlag(enabled);
732
- }
733
- function debug(msg) {
734
- if (getDebug()) {
735
- process.stderr.write(`[risicare] ${msg}
736
- `);
737
- }
738
- }
739
- function warn(msg) {
740
- process.stderr.write(`[risicare] WARNING: ${msg}
741
- `);
742
- }
743
-
744
2085
  // src/exporters/batch.ts
2086
+ init_log();
745
2087
  var BatchSpanProcessor = class _BatchSpanProcessor {
746
2088
  _exporters;
747
2089
  _batchSize;
@@ -892,6 +2234,7 @@ var BatchSpanProcessor = class _BatchSpanProcessor {
892
2234
  };
893
2235
 
894
2236
  // src/exporters/http.ts
2237
+ init_log();
895
2238
  var SDK_VERSION = "0.1.1";
896
2239
  var HttpExporter = class {
897
2240
  name = "http";
@@ -1017,6 +2360,8 @@ var ConsoleExporter = class {
1017
2360
  };
1018
2361
 
1019
2362
  // src/client.ts
2363
+ init_log();
2364
+ init_globals();
1020
2365
  var RisicareClient = class {
1021
2366
  config;
1022
2367
  processor;
@@ -1056,6 +2401,18 @@ var RisicareClient = class {
1056
2401
  traceContent: this.config.traceContent
1057
2402
  });
1058
2403
  this.processor.start();
2404
+ if (this.config.apiKey && this.config.enabled) {
2405
+ try {
2406
+ const { initFixRuntime: initFixRuntime2 } = (init_runtime(), __toCommonJS(runtime_exports));
2407
+ initFixRuntime2({
2408
+ apiEndpoint: this.config.endpoint,
2409
+ apiKey: this.config.apiKey,
2410
+ enabled: true,
2411
+ debug: this.config.debug
2412
+ });
2413
+ } catch {
2414
+ }
2415
+ }
1059
2416
  this._registerShutdownHooks();
1060
2417
  setDebug(this.config.debug);
1061
2418
  debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);
@@ -1107,6 +2464,11 @@ function init(config) {
1107
2464
  setTracer(client.tracer);
1108
2465
  }
1109
2466
  async function shutdown() {
2467
+ try {
2468
+ const { shutdownFixRuntime: shutdownFixRuntime2 } = (init_runtime(), __toCommonJS(runtime_exports));
2469
+ shutdownFixRuntime2();
2470
+ } catch {
2471
+ }
1110
2472
  const client = getClient();
1111
2473
  if (!client) return;
1112
2474
  await client.shutdown();
@@ -1148,12 +2510,39 @@ function getMetrics() {
1148
2510
  queueUtilization: 0
1149
2511
  };
1150
2512
  }
2513
+ var _ERROR_DEDUP_TTL_MS = 5 * 60 * 1e3;
2514
+ var _ERROR_DEDUP_MAX = 1e3;
2515
+ var _recentErrors = /* @__PURE__ */ new Map();
2516
+ function _errorFingerprint(err) {
2517
+ const raw = `${err.constructor?.name ?? "Error"}:${String(err.message ?? "").slice(0, 200)}`;
2518
+ const { createHash: createHash2 } = require("crypto");
2519
+ return createHash2("sha256").update(raw).digest("hex").slice(0, 16);
2520
+ }
2521
+ function _isDuplicateError(fingerprint) {
2522
+ const now = Date.now();
2523
+ for (const [fp, ts] of _recentErrors) {
2524
+ if (now - ts > _ERROR_DEDUP_TTL_MS) _recentErrors.delete(fp);
2525
+ else break;
2526
+ }
2527
+ if (_recentErrors.has(fingerprint)) return true;
2528
+ if (_recentErrors.size >= _ERROR_DEDUP_MAX) {
2529
+ const oldest = _recentErrors.keys().next().value;
2530
+ if (oldest !== void 0) _recentErrors.delete(oldest);
2531
+ }
2532
+ _recentErrors.set(fingerprint, now);
2533
+ return false;
2534
+ }
1151
2535
  function reportError(error, options) {
1152
2536
  try {
1153
2537
  const tracer = getTracer2();
1154
2538
  if (!tracer) return;
1155
- const err = error instanceof Error ? error : new Error(String(error));
2539
+ const err = error instanceof Error ? error : new Error(String(error ?? "unknown"));
1156
2540
  const spanName = options?.name ?? `error:${err.constructor.name}`;
2541
+ const fp = _errorFingerprint(err);
2542
+ if (_isDuplicateError(fp)) {
2543
+ debug(`reportError: duplicate suppressed (fp=${fp.slice(0, 8)})`);
2544
+ return;
2545
+ }
1157
2546
  tracer.startSpan({ name: spanName, kind: "internal" /* INTERNAL */ }, (span) => {
1158
2547
  span.setStatus("error" /* ERROR */, err.message);
1159
2548
  span.setAttribute("error", true);
@@ -1368,8 +2757,22 @@ function getTraceContext() {
1368
2757
  const span = getCurrentSpan();
1369
2758
  const session2 = getCurrentSession();
1370
2759
  const agent2 = getCurrentAgent();
2760
+ let traceId;
2761
+ if (span) {
2762
+ traceId = span.traceId;
2763
+ } else {
2764
+ const ctx = getContext();
2765
+ if (ctx._rootTraceId) {
2766
+ traceId = ctx._rootTraceId;
2767
+ } else {
2768
+ traceId = generateTraceId();
2769
+ if (ctx.session || ctx.agent) {
2770
+ ctx._rootTraceId = traceId;
2771
+ }
2772
+ }
2773
+ }
1371
2774
  return {
1372
- traceId: span?.traceId ?? generateTraceId(),
2775
+ traceId,
1373
2776
  spanId: span?.spanId ?? generateSpanId(),
1374
2777
  sessionId: session2?.sessionId,
1375
2778
  agentId: agent2?.agentId
@@ -1437,6 +2840,7 @@ function extractTraceContext(headers) {
1437
2840
  }
1438
2841
 
1439
2842
  // src/context/registry.ts
2843
+ init_globals();
1440
2844
  var DEFAULT_TTL_MS = 6e4;
1441
2845
  var MAX_ENTRIES = 1e4;
1442
2846
  var CLEANUP_INTERVAL = 100;
@@ -1506,10 +2910,34 @@ async function* tracedStream(source, options) {
1506
2910
  }
1507
2911
  }
1508
2912
 
2913
+ // src/context/dedup.ts
2914
+ function suppressProviderInstrumentation(fn) {
2915
+ return runWithContext({ _suppressProviderInstrumentation: true }, fn);
2916
+ }
2917
+ function isProviderInstrumentationSuppressed() {
2918
+ return getContext()._suppressProviderInstrumentation === true;
2919
+ }
2920
+
1509
2921
  // src/frameworks/langchain.ts
1510
2922
  var RisicareCallbackHandler = class {
1511
2923
  name = "RisicareCallbackHandler";
1512
2924
  _spans = /* @__PURE__ */ new Map();
2925
+ /**
2926
+ * Run a function with provider instrumentation suppressed.
2927
+ *
2928
+ * Wrap your chain.invoke() call with this to prevent duplicate spans.
2929
+ * The handler creates LLM/tool spans, so provider proxies (patchOpenAI, etc.)
2930
+ * should be suppressed during chain execution.
2931
+ *
2932
+ * @example
2933
+ * const handler = new RisicareCallbackHandler();
2934
+ * const result = await handler.withSuppression(() =>
2935
+ * chain.invoke(input, { callbacks: [handler] })
2936
+ * );
2937
+ */
2938
+ withSuppression(fn) {
2939
+ return suppressProviderInstrumentation(fn);
2940
+ }
1513
2941
  // ── Chain lifecycle ────────────────────────────────────────────────────────
1514
2942
  handleChainStart(chain, _inputs, runId, parentRunId) {
1515
2943
  const tracer = getTracer2();
@@ -1687,6 +3115,7 @@ function instrumentLangGraph(graph) {
1687
3115
  }
1688
3116
 
1689
3117
  // src/frameworks/instructor.ts
3118
+ init_log();
1690
3119
  function patchInstructor(client) {
1691
3120
  return new Proxy(client, {
1692
3121
  get(target, prop, receiver) {
@@ -1737,14 +3166,6 @@ function patchInstructor(client) {
1737
3166
  });
1738
3167
  }
1739
3168
 
1740
- // src/context/dedup.ts
1741
- function suppressProviderInstrumentation(fn) {
1742
- return runWithContext({ _suppressProviderInstrumentation: true }, fn);
1743
- }
1744
- function isProviderInstrumentationSuppressed() {
1745
- return getContext()._suppressProviderInstrumentation === true;
1746
- }
1747
-
1748
3169
  // src/frameworks/llamaindex.ts
1749
3170
  var RisicareLlamaIndexHandler = class {
1750
3171
  _spans = /* @__PURE__ */ new Map();
@@ -1843,6 +3264,9 @@ var RisicareLlamaIndexHandler = class {
1843
3264
  return "component";
1844
3265
  }
1845
3266
  };
3267
+
3268
+ // src/index.ts
3269
+ init_runtime();
1846
3270
  // Annotate the CommonJS export names for ESM import in node:
1847
3271
  0 && (module.exports = {
1848
3272
  AgentRole,
@@ -1866,12 +3290,14 @@ var RisicareLlamaIndexHandler = class {
1866
3290
  getCurrentSpan,
1867
3291
  getCurrentSpanId,
1868
3292
  getCurrentTraceId,
3293
+ getFixRuntime,
1869
3294
  getMetrics,
1870
3295
  getSpanById,
1871
3296
  getTraceContent,
1872
3297
  getTraceContext,
1873
3298
  getTracer,
1874
3299
  init,
3300
+ initFixRuntime,
1875
3301
  injectTraceContext,
1876
3302
  instrumentLangGraph,
1877
3303
  isEnabled,
@@ -1882,6 +3308,7 @@ var RisicareLlamaIndexHandler = class {
1882
3308
  score,
1883
3309
  session,
1884
3310
  shutdown,
3311
+ shutdownFixRuntime,
1885
3312
  suppressProviderInstrumentation,
1886
3313
  traceAct,
1887
3314
  traceCoordinate,