risicare 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +26 -10
  2. package/dist/frameworks/instructor.cjs +45 -17
  3. package/dist/frameworks/instructor.cjs.map +1 -1
  4. package/dist/frameworks/instructor.js +47 -17
  5. package/dist/frameworks/instructor.js.map +1 -1
  6. package/dist/frameworks/langchain.cjs +73 -6
  7. package/dist/frameworks/langchain.cjs.map +1 -1
  8. package/dist/frameworks/langchain.d.cts +20 -4
  9. package/dist/frameworks/langchain.d.ts +20 -4
  10. package/dist/frameworks/langchain.js +75 -6
  11. package/dist/frameworks/langchain.js.map +1 -1
  12. package/dist/frameworks/langgraph.cjs +73 -6
  13. package/dist/frameworks/langgraph.cjs.map +1 -1
  14. package/dist/frameworks/langgraph.js +75 -6
  15. package/dist/frameworks/langgraph.js.map +1 -1
  16. package/dist/frameworks/llamaindex.cjs +41 -14
  17. package/dist/frameworks/llamaindex.cjs.map +1 -1
  18. package/dist/frameworks/llamaindex.js +43 -14
  19. package/dist/frameworks/llamaindex.js.map +1 -1
  20. package/dist/index.cjs +1705 -74
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +597 -6
  23. package/dist/index.d.ts +597 -6
  24. package/dist/index.js +1723 -74
  25. package/dist/index.js.map +1 -1
  26. package/dist/providers/anthropic/index.cjs +74 -24
  27. package/dist/providers/anthropic/index.cjs.map +1 -1
  28. package/dist/providers/anthropic/index.js +76 -24
  29. package/dist/providers/anthropic/index.js.map +1 -1
  30. package/dist/providers/bedrock/index.cjs +81 -24
  31. package/dist/providers/bedrock/index.cjs.map +1 -1
  32. package/dist/providers/bedrock/index.js +83 -24
  33. package/dist/providers/bedrock/index.js.map +1 -1
  34. package/dist/providers/cerebras/index.cjs +78 -25
  35. package/dist/providers/cerebras/index.cjs.map +1 -1
  36. package/dist/providers/cerebras/index.js +80 -25
  37. package/dist/providers/cerebras/index.js.map +1 -1
  38. package/dist/providers/cohere/index.cjs +95 -25
  39. package/dist/providers/cohere/index.cjs.map +1 -1
  40. package/dist/providers/cohere/index.js +97 -25
  41. package/dist/providers/cohere/index.js.map +1 -1
  42. package/dist/providers/google/index.cjs +77 -25
  43. package/dist/providers/google/index.cjs.map +1 -1
  44. package/dist/providers/google/index.js +79 -25
  45. package/dist/providers/google/index.js.map +1 -1
  46. package/dist/providers/groq/index.cjs +80 -25
  47. package/dist/providers/groq/index.cjs.map +1 -1
  48. package/dist/providers/groq/index.js +82 -25
  49. package/dist/providers/groq/index.js.map +1 -1
  50. package/dist/providers/huggingface/index.cjs +80 -25
  51. package/dist/providers/huggingface/index.cjs.map +1 -1
  52. package/dist/providers/huggingface/index.js +82 -25
  53. package/dist/providers/huggingface/index.js.map +1 -1
  54. package/dist/providers/mistral/index.cjs +72 -24
  55. package/dist/providers/mistral/index.cjs.map +1 -1
  56. package/dist/providers/mistral/index.js +74 -24
  57. package/dist/providers/mistral/index.js.map +1 -1
  58. package/dist/providers/ollama/index.cjs +83 -25
  59. package/dist/providers/ollama/index.cjs.map +1 -1
  60. package/dist/providers/ollama/index.js +85 -25
  61. package/dist/providers/ollama/index.js.map +1 -1
  62. package/dist/providers/openai/index.cjs +118 -28
  63. package/dist/providers/openai/index.cjs.map +1 -1
  64. package/dist/providers/openai/index.js +120 -28
  65. package/dist/providers/openai/index.js.map +1 -1
  66. package/dist/providers/together/index.cjs +80 -25
  67. package/dist/providers/together/index.cjs.map +1 -1
  68. package/dist/providers/together/index.js +82 -25
  69. package/dist/providers/together/index.js.map +1 -1
  70. package/dist/providers/vercel-ai/index.cjs +45 -17
  71. package/dist/providers/vercel-ai/index.cjs.map +1 -1
  72. package/dist/providers/vercel-ai/index.js +47 -17
  73. package/dist/providers/vercel-ai/index.js.map +1 -1
  74. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -1,3 +1,1419 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ // src/globals.ts
29
+ import { AsyncLocalStorage } from "async_hooks";
30
+ function getClient() {
31
+ return G[PREFIX + "client"];
32
+ }
33
+ function setClient(client) {
34
+ G[PREFIX + "client"] = client;
35
+ }
36
+ function getTracer() {
37
+ return G[PREFIX + "tracer"];
38
+ }
39
+ function setTracer(tracer) {
40
+ G[PREFIX + "tracer"] = tracer;
41
+ }
42
+ function getContextStorage() {
43
+ if (!G[PREFIX + "ctx"]) {
44
+ G[PREFIX + "ctx"] = new AsyncLocalStorage();
45
+ }
46
+ return G[PREFIX + "ctx"];
47
+ }
48
+ function getRegistry() {
49
+ if (!G[PREFIX + "registry"]) {
50
+ G[PREFIX + "registry"] = /* @__PURE__ */ new Map();
51
+ }
52
+ return G[PREFIX + "registry"];
53
+ }
54
+ function getOpCount() {
55
+ return G[PREFIX + "opcount"] ?? 0;
56
+ }
57
+ function setOpCount(n) {
58
+ G[PREFIX + "opcount"] = n;
59
+ }
60
+ function getDebug() {
61
+ return G[PREFIX + "debug"] ?? false;
62
+ }
63
+ function setDebugFlag(enabled) {
64
+ G[PREFIX + "debug"] = enabled;
65
+ }
66
+ var G, PREFIX;
67
+ var init_globals = __esm({
68
+ "src/globals.ts"() {
69
+ "use strict";
70
+ G = globalThis;
71
+ PREFIX = "__risicare_";
72
+ }
73
+ });
74
+
75
+ // src/utils/log.ts
76
+ function setDebug(enabled) {
77
+ setDebugFlag(enabled);
78
+ }
79
+ function debug(msg) {
80
+ if (getDebug()) {
81
+ process.stderr.write(`[risicare] ${msg}
82
+ `);
83
+ }
84
+ }
85
+ function warn(msg) {
86
+ process.stderr.write(`[risicare] WARNING: ${msg}
87
+ `);
88
+ }
89
+ var init_log = __esm({
90
+ "src/utils/log.ts"() {
91
+ "use strict";
92
+ init_globals();
93
+ }
94
+ });
95
+
96
+ // src/runtime/config.ts
97
+ function resolveFixRuntimeConfig(config) {
98
+ return {
99
+ apiEndpoint: config.apiEndpoint,
100
+ apiKey: config.apiKey,
101
+ enabled: config.enabled ?? true,
102
+ cacheEnabled: config.cacheEnabled ?? true,
103
+ cacheTtlMs: config.cacheTtlMs ?? 3e5,
104
+ cacheMaxEntries: config.cacheMaxEntries ?? 1e3,
105
+ autoRefresh: config.autoRefresh ?? true,
106
+ refreshIntervalMs: config.refreshIntervalMs ?? 6e4,
107
+ dryRun: config.dryRun ?? false,
108
+ abTestingEnabled: config.abTestingEnabled ?? true,
109
+ timeoutMs: config.timeoutMs ?? 1e3,
110
+ debug: config.debug ?? false
111
+ };
112
+ }
113
+ function matchesError(fix, errorCode) {
114
+ if (fix.errorCode === errorCode) {
115
+ return true;
116
+ }
117
+ if (fix.errorCode.endsWith("*")) {
118
+ const prefix = fix.errorCode.slice(0, -1);
119
+ return errorCode.startsWith(prefix);
120
+ }
121
+ return false;
122
+ }
123
+ function shouldApply(fix, sessionHash) {
124
+ if (fix.trafficPercentage >= 100) return true;
125
+ if (fix.trafficPercentage <= 0) return false;
126
+ const bucket = sessionHash % 100;
127
+ return bucket < fix.trafficPercentage;
128
+ }
129
+ function activeFixFromApiResponse(item) {
130
+ return {
131
+ fixId: item.id ?? item.fix_id ?? item.fixId ?? "",
132
+ deploymentId: item.deployment_id ?? item.deploymentId ?? "",
133
+ errorCode: item.error_code ?? item.errorCode ?? "",
134
+ fixType: item.fix_type ?? item.fixType ?? "prompt",
135
+ config: item.config ?? {},
136
+ trafficPercentage: item.traffic_percentage ?? item.trafficPercentage ?? 100,
137
+ version: item.version ?? 1
138
+ };
139
+ }
140
+ var init_config = __esm({
141
+ "src/runtime/config.ts"() {
142
+ "use strict";
143
+ }
144
+ });
145
+
146
+ // src/runtime/cache.ts
147
+ var FixCache;
148
+ var init_cache = __esm({
149
+ "src/runtime/cache.ts"() {
150
+ "use strict";
151
+ init_config();
152
+ init_log();
153
+ FixCache = class {
154
+ _ttlMs;
155
+ _maxEntries;
156
+ _enabled;
157
+ _cache;
158
+ _stats;
159
+ constructor(config) {
160
+ this._enabled = config?.cacheEnabled ?? true;
161
+ this._ttlMs = config?.cacheTtlMs ?? 3e5;
162
+ this._maxEntries = config?.cacheMaxEntries ?? 1e3;
163
+ this._cache = /* @__PURE__ */ new Map();
164
+ this._stats = {
165
+ hits: 0,
166
+ misses: 0,
167
+ evictions: 0,
168
+ size: 0,
169
+ lastRefresh: null
170
+ };
171
+ }
172
+ /** Whether caching is enabled. */
173
+ get enabled() {
174
+ return this._enabled;
175
+ }
176
+ /**
177
+ * Get a fix by error code.
178
+ *
179
+ * Checks exact match first, then scans for wildcard matches.
180
+ * Expired entries are evicted on access.
181
+ */
182
+ get(errorCode) {
183
+ try {
184
+ if (!this._enabled) return null;
185
+ const now = Date.now();
186
+ const exactEntry = this._cache.get(errorCode);
187
+ if (exactEntry) {
188
+ if (exactEntry.expiresAt > now) {
189
+ this._stats.hits++;
190
+ return exactEntry.fix;
191
+ }
192
+ this._cache.delete(errorCode);
193
+ this._stats.evictions++;
194
+ }
195
+ for (const [key, entry] of this._cache) {
196
+ if (entry.expiresAt <= now) {
197
+ this._cache.delete(key);
198
+ this._stats.evictions++;
199
+ continue;
200
+ }
201
+ if (matchesError(entry.fix, errorCode)) {
202
+ this._stats.hits++;
203
+ return entry.fix;
204
+ }
205
+ }
206
+ this._stats.misses++;
207
+ return null;
208
+ } catch (e) {
209
+ debug(`FixCache.get error: ${e}`);
210
+ return null;
211
+ }
212
+ }
213
+ /**
214
+ * Add a single fix to the cache.
215
+ *
216
+ * Evicts the oldest entry if at capacity.
217
+ */
218
+ set(fix) {
219
+ try {
220
+ if (!this._enabled) return;
221
+ if (this._cache.size >= this._maxEntries) {
222
+ this._evictOldest();
223
+ }
224
+ const now = Date.now();
225
+ this._cache.set(fix.errorCode, {
226
+ fix,
227
+ createdAt: now,
228
+ expiresAt: now + this._ttlMs
229
+ });
230
+ this._stats.size = this._cache.size;
231
+ } catch (e) {
232
+ debug(`FixCache.set error: ${e}`);
233
+ }
234
+ }
235
+ /**
236
+ * Replace all cached fixes (bulk refresh).
237
+ *
238
+ * Clears the cache and populates with the given fixes.
239
+ */
240
+ setAll(fixes) {
241
+ try {
242
+ if (!this._enabled) return;
243
+ this._cache.clear();
244
+ const now = Date.now();
245
+ for (const fix of fixes) {
246
+ if (this._cache.size >= this._maxEntries) break;
247
+ this._cache.set(fix.errorCode, {
248
+ fix,
249
+ createdAt: now,
250
+ expiresAt: now + this._ttlMs
251
+ });
252
+ }
253
+ this._stats.size = this._cache.size;
254
+ this._stats.lastRefresh = now;
255
+ } catch (e) {
256
+ debug(`FixCache.setAll error: ${e}`);
257
+ }
258
+ }
259
+ /**
260
+ * Get all non-expired fixes.
261
+ */
262
+ getAll() {
263
+ try {
264
+ const now = Date.now();
265
+ const valid = [];
266
+ const expired = [];
267
+ for (const [key, entry] of this._cache) {
268
+ if (entry.expiresAt > now) {
269
+ valid.push(entry.fix);
270
+ } else {
271
+ expired.push(key);
272
+ }
273
+ }
274
+ for (const key of expired) {
275
+ this._cache.delete(key);
276
+ this._stats.evictions++;
277
+ }
278
+ this._stats.size = this._cache.size;
279
+ return valid;
280
+ } catch (e) {
281
+ debug(`FixCache.getAll error: ${e}`);
282
+ return [];
283
+ }
284
+ }
285
+ /** Clear all cache entries. Returns the number of entries cleared. */
286
+ clear() {
287
+ const count = this._cache.size;
288
+ this._cache.clear();
289
+ this._stats.size = 0;
290
+ return count;
291
+ }
292
+ /** Get cache statistics. */
293
+ get stats() {
294
+ this._stats.size = this._cache.size;
295
+ return { ...this._stats };
296
+ }
297
+ /** Evict the oldest cache entry. */
298
+ _evictOldest() {
299
+ if (this._cache.size === 0) return;
300
+ let oldestKey = null;
301
+ let oldestTime = Infinity;
302
+ for (const [key, entry] of this._cache) {
303
+ if (entry.createdAt < oldestTime) {
304
+ oldestTime = entry.createdAt;
305
+ oldestKey = key;
306
+ }
307
+ }
308
+ if (oldestKey !== null) {
309
+ this._cache.delete(oldestKey);
310
+ this._stats.evictions++;
311
+ }
312
+ }
313
+ };
314
+ }
315
+ });
316
+
317
+ // src/runtime/applier.ts
318
+ import { createHash } from "crypto";
319
+ function emptyResult(fix, fixType) {
320
+ return {
321
+ applied: false,
322
+ fixId: fix.fixId,
323
+ deploymentId: fix.deploymentId,
324
+ fixType,
325
+ modifications: {}
326
+ };
327
+ }
328
+ var MAX_FIX_CONTENT_LENGTH, FixApplier;
329
+ var init_applier = __esm({
330
+ "src/runtime/applier.ts"() {
331
+ "use strict";
332
+ init_config();
333
+ init_log();
334
+ MAX_FIX_CONTENT_LENGTH = 1e4;
335
+ FixApplier = class {
336
+ _config;
337
+ _cache;
338
+ _applicationLog = [];
339
+ constructor(config, cache) {
340
+ this._config = resolveFixRuntimeConfig(config);
341
+ this._cache = cache;
342
+ }
343
+ // ═══════════════════════════════════════════════════════════════════════════
344
+ // Fix Lookup
345
+ // ═══════════════════════════════════════════════════════════════════════════
346
+ /**
347
+ * Get applicable fix for an error code.
348
+ *
349
+ * Considers A/B testing bucket if sessionId is provided. Uses
350
+ * crypto.createHash('md5') for deterministic bucketing across restarts.
351
+ */
352
+ getFixForError(errorCode, sessionId) {
353
+ try {
354
+ const fix = this._cache.get(errorCode);
355
+ if (!fix) return null;
356
+ if (sessionId && this._config.abTestingEnabled) {
357
+ const hashHex = createHash("md5").update(sessionId).digest("hex").substring(0, 8);
358
+ const sessionHash = parseInt(hashHex, 16);
359
+ if (!shouldApply(fix, sessionHash)) {
360
+ debug(`Fix ${fix.fixId} not applied (A/B control group)`);
361
+ return null;
362
+ }
363
+ }
364
+ return fix;
365
+ } catch (e) {
366
+ debug(`getFixForError error: ${e}`);
367
+ return null;
368
+ }
369
+ }
370
+ // ═══════════════════════════════════════════════════════════════════════════
371
+ // 1. Prompt Fix
372
+ // ═══════════════════════════════════════════════════════════════════════════
373
+ /**
374
+ * Apply a prompt fix to messages.
375
+ *
376
+ * Supports 4 modification types:
377
+ * - prepend: Add content before first system message
378
+ * - append: Add content after first system message
379
+ * - replace: Regex replace across all messages
380
+ * - few_shot: Insert user/assistant example pairs after system message
381
+ */
382
+ applyPromptFix(fix, messages) {
383
+ try {
384
+ const cfg = fix.config;
385
+ const modificationType = cfg.modification_type ?? cfg.modificationType ?? "append";
386
+ const target = cfg.target ?? "system";
387
+ let content = cfg.content ?? "";
388
+ if (content.length > MAX_FIX_CONTENT_LENGTH) {
389
+ warn(
390
+ `Fix ${fix.fixId} content truncated from ${content.length} to ${MAX_FIX_CONTENT_LENGTH} chars`
391
+ );
392
+ content = content.slice(0, MAX_FIX_CONTENT_LENGTH);
393
+ }
394
+ const result = emptyResult(fix, "prompt");
395
+ if (this._config.dryRun) {
396
+ result.modifications = {
397
+ type: modificationType,
398
+ target,
399
+ contentPreview: content.slice(0, 100)
400
+ };
401
+ return { messages, result };
402
+ }
403
+ let modified = messages.map((m) => ({ ...m }));
404
+ if (modificationType === "prepend") {
405
+ modified = this._prependToTarget(modified, target, content);
406
+ result.applied = true;
407
+ result.modifications = { type: "prepend", target };
408
+ } else if (modificationType === "append") {
409
+ modified = this._appendToTarget(modified, target, content);
410
+ result.applied = true;
411
+ result.modifications = { type: "append", target };
412
+ } else if (modificationType === "replace") {
413
+ const pattern = cfg.pattern ?? "";
414
+ if (pattern) {
415
+ modified = this._replaceInMessages(modified, pattern, content);
416
+ result.applied = true;
417
+ result.modifications = { type: "replace", pattern };
418
+ }
419
+ } else if (modificationType === "few_shot") {
420
+ const examples = cfg.examples ?? [];
421
+ if (examples.length > 0) {
422
+ modified = this._addFewShotExamples(modified, examples);
423
+ result.applied = true;
424
+ result.modifications = { type: "few_shot", count: examples.length };
425
+ }
426
+ }
427
+ return { messages: modified, result };
428
+ } catch (e) {
429
+ debug(`applyPromptFix error: ${e}`);
430
+ return {
431
+ messages,
432
+ result: {
433
+ applied: false,
434
+ fixId: fix.fixId,
435
+ fixType: "prompt",
436
+ modifications: {},
437
+ error: String(e)
438
+ }
439
+ };
440
+ }
441
+ }
442
+ // ═══════════════════════════════════════════════════════════════════════════
443
+ // 2. Parameter Fix
444
+ // ═══════════════════════════════════════════════════════════════════════════
445
+ /**
446
+ * Apply a parameter fix — merge config.parameters into params.
447
+ */
448
+ applyParameterFix(fix, params) {
449
+ try {
450
+ const newParams = fix.config.parameters ?? {};
451
+ const result = emptyResult(fix, "parameter");
452
+ if (this._config.dryRun) {
453
+ result.modifications = { parameters: newParams };
454
+ return { params, result };
455
+ }
456
+ const modified = { ...params, ...newParams };
457
+ result.applied = true;
458
+ result.modifications = { parameters: newParams };
459
+ return { params: modified, result };
460
+ } catch (e) {
461
+ debug(`applyParameterFix error: ${e}`);
462
+ return {
463
+ params,
464
+ result: {
465
+ applied: false,
466
+ fixId: fix.fixId,
467
+ fixType: "parameter",
468
+ modifications: {},
469
+ error: String(e)
470
+ }
471
+ };
472
+ }
473
+ }
474
+ // ═══════════════════════════════════════════════════════════════════════════
475
+ // 3. Retry Fix (async — uses setTimeout for sleeping)
476
+ // ═══════════════════════════════════════════════════════════════════════════
477
+ /**
478
+ * Apply a retry fix to an async operation.
479
+ *
480
+ * Retries with exponential backoff + jitter using
481
+ * `await new Promise(r => setTimeout(r, delay))`.
482
+ */
483
+ async applyRetryFix(fix, operation) {
484
+ const cfg = fix.config;
485
+ const maxRetries = cfg.max_retries ?? cfg.maxRetries ?? 3;
486
+ const initialDelayMs = cfg.initial_delay_ms ?? cfg.initialDelayMs ?? 1e3;
487
+ const maxDelayMs = cfg.max_delay_ms ?? cfg.maxDelayMs ?? 3e4;
488
+ const exponentialBase = cfg.exponential_base ?? cfg.exponentialBase ?? 2;
489
+ const jitter = cfg.jitter ?? true;
490
+ const retryOn = cfg.retry_on ?? cfg.retryOn ?? [];
491
+ const result = emptyResult(fix, "retry");
492
+ if (this._config.dryRun) {
493
+ result.modifications = { maxRetries, initialDelayMs };
494
+ const value = await operation();
495
+ return { value, result };
496
+ }
497
+ let lastError = null;
498
+ let attempts = 0;
499
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
500
+ attempts++;
501
+ try {
502
+ const value = await operation();
503
+ result.applied = true;
504
+ result.modifications = { attempts };
505
+ return { value, result };
506
+ } catch (e) {
507
+ lastError = e instanceof Error ? e : new Error(String(e));
508
+ const errorType = lastError.constructor.name;
509
+ if (retryOn.length > 0 && !retryOn.includes(errorType)) {
510
+ throw lastError;
511
+ }
512
+ if (attempt < maxRetries) {
513
+ let delayMs = Math.min(
514
+ initialDelayMs * Math.pow(exponentialBase, attempt),
515
+ maxDelayMs
516
+ );
517
+ if (jitter) {
518
+ delayMs *= 0.5 + Math.random();
519
+ }
520
+ debug(
521
+ `Retry ${attempt + 1}/${maxRetries} after ${Math.round(delayMs)}ms`
522
+ );
523
+ await new Promise((r) => setTimeout(r, delayMs));
524
+ }
525
+ }
526
+ }
527
+ result.applied = true;
528
+ result.error = lastError?.message;
529
+ result.modifications = { attempts, exhausted: true };
530
+ throw lastError;
531
+ }
532
+ // ═══════════════════════════════════════════════════════════════════════════
533
+ // 4. Fallback Fix
534
+ // ═══════════════════════════════════════════════════════════════════════════
535
+ /**
536
+ * Apply a fallback fix.
537
+ *
538
+ * - 'model': Replace params.model with the fallback model
539
+ * - 'default': Set params._fallbackResponse for the caller to use
540
+ */
541
+ applyFallbackFix(fix, params) {
542
+ try {
543
+ const cfg = fix.config;
544
+ const fallbackType = cfg.fallback_type ?? cfg.fallbackType ?? "model";
545
+ const fallbackConfig = cfg.fallback_config ?? cfg.fallbackConfig ?? {};
546
+ const result = emptyResult(fix, "fallback");
547
+ if (this._config.dryRun) {
548
+ result.modifications = { fallbackType, fallbackConfig };
549
+ return { params, result };
550
+ }
551
+ const modified = { ...params };
552
+ if (fallbackType === "model") {
553
+ const fallbackModel = fallbackConfig.model;
554
+ if (fallbackModel) {
555
+ modified.model = fallbackModel;
556
+ result.applied = true;
557
+ result.modifications = { model: fallbackModel };
558
+ }
559
+ } else if (fallbackType === "default") {
560
+ const defaultResponse = fallbackConfig.response;
561
+ if (defaultResponse !== void 0) {
562
+ modified._fallbackResponse = defaultResponse;
563
+ result.applied = true;
564
+ result.modifications = { defaultResponse: true };
565
+ }
566
+ }
567
+ return { params: modified, result };
568
+ } catch (e) {
569
+ debug(`applyFallbackFix error: ${e}`);
570
+ return {
571
+ params,
572
+ result: {
573
+ applied: false,
574
+ fixId: fix.fixId,
575
+ fixType: "fallback",
576
+ modifications: {},
577
+ error: String(e)
578
+ }
579
+ };
580
+ }
581
+ }
582
+ // ═══════════════════════════════════════════════════════════════════════════
583
+ // 5. Guard Fix
584
+ // ═══════════════════════════════════════════════════════════════════════════
585
+ /**
586
+ * Apply a guard fix (validation).
587
+ *
588
+ * Guard types:
589
+ * - content_filter: Regex-based blocked patterns
590
+ * - format_check: JSON.parse() validity
591
+ * - input_validation / output_validation: min/max length
592
+ */
593
+ applyGuardFix(fix, content, isInput = true) {
594
+ try {
595
+ const cfg = fix.config;
596
+ const guardType = cfg.guard_type ?? cfg.guardType ?? "output_validation";
597
+ const guardConfig = cfg.guard_config ?? cfg.guardConfig ?? {};
598
+ const result = emptyResult(fix, "guard");
599
+ if (isInput && guardType !== "input_validation" && guardType !== "content_filter") {
600
+ return { content, passed: true, result };
601
+ }
602
+ if (!isInput && guardType !== "output_validation" && guardType !== "format_check") {
603
+ return { content, passed: true, result };
604
+ }
605
+ if (this._config.dryRun) {
606
+ result.modifications = { guardType };
607
+ return { content, passed: true, result };
608
+ }
609
+ let passed = true;
610
+ if (guardType === "content_filter") {
611
+ const blockedPatterns = guardConfig.blocked_patterns ?? guardConfig.blockedPatterns ?? [];
612
+ for (const pattern of blockedPatterns) {
613
+ const regex = this._safeCompileRegex(pattern);
614
+ if (regex && regex.test(content)) {
615
+ passed = false;
616
+ break;
617
+ }
618
+ }
619
+ } else if (guardType === "format_check") {
620
+ const requiredFormat = guardConfig.format;
621
+ if (requiredFormat === "json") {
622
+ try {
623
+ JSON.parse(content);
624
+ } catch {
625
+ passed = false;
626
+ }
627
+ }
628
+ } else if (guardType === "input_validation" || guardType === "output_validation") {
629
+ const minLength = guardConfig.min_length ?? guardConfig.minLength ?? 0;
630
+ const maxLength = guardConfig.max_length ?? guardConfig.maxLength ?? Infinity;
631
+ if (content.length < minLength || content.length > maxLength) {
632
+ passed = false;
633
+ }
634
+ }
635
+ result.applied = true;
636
+ result.modifications = { passed, guardType };
637
+ return { content, passed, result };
638
+ } catch (e) {
639
+ debug(`applyGuardFix error: ${e}`);
640
+ return {
641
+ content,
642
+ passed: true,
643
+ result: {
644
+ applied: false,
645
+ fixId: fix.fixId,
646
+ fixType: "guard",
647
+ modifications: {},
648
+ error: String(e)
649
+ }
650
+ };
651
+ }
652
+ }
653
+ // ═══════════════════════════════════════════════════════════════════════════
654
+ // Application Log
655
+ // ═══════════════════════════════════════════════════════════════════════════
656
+ /** Log a fix application result. */
657
+ logApplication(result) {
658
+ this._applicationLog.push(result);
659
+ if (this._applicationLog.length > 1e3) {
660
+ this._applicationLog = this._applicationLog.slice(-500);
661
+ }
662
+ }
663
+ /** Get the application log. */
664
+ getApplicationLog() {
665
+ return [...this._applicationLog];
666
+ }
667
+ // ═══════════════════════════════════════════════════════════════════════════
668
+ // Private Helpers
669
+ // ═══════════════════════════════════════════════════════════════════════════
670
+ /**
671
+ * Safely compile a regex pattern from untrusted input.
672
+ * Returns null if invalid or too long.
673
+ */
674
+ _safeCompileRegex(pattern, maxLength = 500) {
675
+ if (!pattern || pattern.length > maxLength) return null;
676
+ try {
677
+ return new RegExp(pattern, "i");
678
+ } catch {
679
+ debug(`Invalid regex pattern (length=${pattern.length})`);
680
+ return null;
681
+ }
682
+ }
683
+ /** Prepend content to the first message matching the target role. */
684
+ _prependToTarget(messages, target, content) {
685
+ return messages.map((msg) => {
686
+ if (msg.role === target) {
687
+ return {
688
+ ...msg,
689
+ content: content + "\n\n" + (msg.content ?? "")
690
+ };
691
+ }
692
+ return msg;
693
+ });
694
+ }
695
+ /** Append content to the first message matching the target role. */
696
+ _appendToTarget(messages, target, content) {
697
+ return messages.map((msg) => {
698
+ if (msg.role === target) {
699
+ return {
700
+ ...msg,
701
+ content: (msg.content ?? "") + "\n\n" + content
702
+ };
703
+ }
704
+ return msg;
705
+ });
706
+ }
707
+ /** Regex replace across all messages. */
708
+ _replaceInMessages(messages, pattern, replacement) {
709
+ const regex = this._safeCompileRegex(pattern);
710
+ if (!regex) {
711
+ debug("Skipping invalid regex pattern in prompt fix");
712
+ return messages;
713
+ }
714
+ return messages.map((msg) => {
715
+ const msgContent = msg.content;
716
+ if (typeof msgContent === "string") {
717
+ return { ...msg, content: msgContent.replace(regex, replacement) };
718
+ }
719
+ return msg;
720
+ });
721
+ }
722
+ /** Add few-shot examples after the first system message. */
723
+ _addFewShotExamples(messages, examples) {
724
+ const result = [];
725
+ let systemFound = false;
726
+ for (const msg of messages) {
727
+ result.push(msg);
728
+ if (msg.role === "system" && !systemFound) {
729
+ systemFound = true;
730
+ for (const example of examples) {
731
+ if (example.user) {
732
+ result.push({ role: "user", content: example.user });
733
+ }
734
+ if (example.assistant) {
735
+ result.push({ role: "assistant", content: example.assistant });
736
+ }
737
+ }
738
+ }
739
+ }
740
+ return result;
741
+ }
742
+ };
743
+ }
744
+ });
745
+
746
+ // src/runtime/loader.ts
747
+ var SDK_VERSION2, FixLoader;
748
+ var init_loader = __esm({
749
+ "src/runtime/loader.ts"() {
750
+ "use strict";
751
+ init_config();
752
+ init_config();
753
+ init_log();
754
+ SDK_VERSION2 = "0.1.3";
755
+ FixLoader = class _FixLoader {
756
+ _config;
757
+ _cache;
758
+ _refreshTimer = null;
759
+ _lastLoadTime = null;
760
+ _consecutiveFailures = 0;
761
+ _circuitOpenUntil = 0;
762
+ _onLoadCallbacks = [];
763
+ // Circuit breaker settings
764
+ static CIRCUIT_BREAKER_THRESHOLD = 3;
765
+ static CIRCUIT_BREAKER_COOLDOWN_MS = 3e4;
766
+ constructor(config, cache) {
767
+ this._config = resolveFixRuntimeConfig(config);
768
+ this._cache = cache;
769
+ }
770
+ /** Timestamp of last successful load. */
771
+ get lastLoadTime() {
772
+ return this._lastLoadTime;
773
+ }
774
+ /**
775
+ * Start the loader: perform initial load and start background refresh.
776
+ *
777
+ * Never throws — failures are logged and the runtime degrades gracefully.
778
+ */
779
+ start() {
780
+ try {
781
+ if (!this._config.enabled) {
782
+ debug("Fix runtime disabled, skipping loader start");
783
+ return;
784
+ }
785
+ if (!this._config.apiKey) {
786
+ warn("No API key configured, fixes will not be loaded");
787
+ return;
788
+ }
789
+ this.loadSync().catch((e) => {
790
+ debug(`Initial fix load failed: ${e}`);
791
+ });
792
+ if (this._config.autoRefresh) {
793
+ this._startRefreshInterval();
794
+ }
795
+ } catch (e) {
796
+ debug(`FixLoader.start error: ${e}`);
797
+ }
798
+ }
799
+ /** Stop the loader: clear the refresh interval. */
800
+ stop() {
801
+ try {
802
+ if (this._refreshTimer !== null) {
803
+ clearInterval(this._refreshTimer);
804
+ this._refreshTimer = null;
805
+ }
806
+ } catch (e) {
807
+ debug(`FixLoader.stop error: ${e}`);
808
+ }
809
+ }
810
+ /**
811
+ * Register a callback invoked after each successful load.
812
+ */
813
+ onLoad(callback) {
814
+ this._onLoadCallbacks.push(callback);
815
+ }
816
+ /**
817
+ * Load fixes from the API.
818
+ *
819
+ * Name kept as "loadSync" for parity with the Python SDK, but this
820
+ * returns a Promise in JS (there's no synchronous fetch in Node.js).
821
+ */
822
+ async loadSync() {
823
+ if (!this._config.apiKey) {
824
+ return [];
825
+ }
826
+ const now = Date.now();
827
+ if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD && now < this._circuitOpenUntil) {
828
+ debug("Fix loader circuit breaker open \u2014 skipping load");
829
+ return this._cache.getAll();
830
+ }
831
+ try {
832
+ const endpoint = this._config.apiEndpoint.replace(/\/+$/, "");
833
+ const url = `${endpoint}/api/v1/fixes/active`;
834
+ const controller = new AbortController();
835
+ const timeoutId = setTimeout(
836
+ () => controller.abort(),
837
+ this._config.timeoutMs
838
+ );
839
+ const response = await fetch(url, {
840
+ method: "GET",
841
+ headers: {
842
+ "Authorization": `Bearer ${this._config.apiKey}`,
843
+ "Content-Type": "application/json",
844
+ "X-Risicare-SDK-Version": SDK_VERSION2
845
+ },
846
+ signal: controller.signal
847
+ });
848
+ clearTimeout(timeoutId);
849
+ if (!response.ok) {
850
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
851
+ }
852
+ const data = await response.json();
853
+ const fixes = this._parseResponse(data);
854
+ this._onLoadSuccess(fixes);
855
+ return fixes;
856
+ } catch (e) {
857
+ this._onLoadFailure(e);
858
+ throw e;
859
+ }
860
+ }
861
+ /** Get currently cached fixes without hitting the API. */
862
+ getCached() {
863
+ return this._cache.getAll();
864
+ }
865
+ // ─── Private ────────────────────────────────────────────────────────────
866
+ _parseResponse(data) {
867
+ const fixes = [];
868
+ const items = data.fixes ?? data.data ?? [];
869
+ if (!Array.isArray(items)) return fixes;
870
+ for (const item of items) {
871
+ try {
872
+ if (item && typeof item === "object") {
873
+ fixes.push(activeFixFromApiResponse(item));
874
+ }
875
+ } catch (e) {
876
+ debug(`Failed to parse fix item: ${e}`);
877
+ }
878
+ }
879
+ return fixes;
880
+ }
881
+ _onLoadSuccess(fixes) {
882
+ this._lastLoadTime = Date.now();
883
+ this._consecutiveFailures = 0;
884
+ this._cache.setAll(fixes);
885
+ for (const callback of this._onLoadCallbacks) {
886
+ try {
887
+ callback(fixes);
888
+ } catch (e) {
889
+ debug(`Load callback error: ${e}`);
890
+ }
891
+ }
892
+ debug(`Loaded ${fixes.length} active fixes`);
893
+ }
894
+ _onLoadFailure(error) {
895
+ this._consecutiveFailures++;
896
+ if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD) {
897
+ this._circuitOpenUntil = Date.now() + _FixLoader.CIRCUIT_BREAKER_COOLDOWN_MS;
898
+ warn(
899
+ `Fix loader circuit breaker opened after ${this._consecutiveFailures} failures. Cooldown: ${_FixLoader.CIRCUIT_BREAKER_COOLDOWN_MS / 1e3}s`
900
+ );
901
+ } else {
902
+ debug(
903
+ `Fix load failed (attempt ${this._consecutiveFailures}): ${error.message}`
904
+ );
905
+ }
906
+ }
907
+ _startRefreshInterval() {
908
+ const tick = () => {
909
+ this.loadSync().catch((e) => {
910
+ debug(`Background fix refresh failed: ${e}`);
911
+ });
912
+ };
913
+ this._refreshTimer = setInterval(
914
+ () => {
915
+ if (this._consecutiveFailures >= _FixLoader.CIRCUIT_BREAKER_THRESHOLD && Date.now() < this._circuitOpenUntil) {
916
+ return;
917
+ }
918
+ tick();
919
+ },
920
+ this._config.refreshIntervalMs
921
+ );
922
+ this._refreshTimer.unref();
923
+ debug("Started fix refresh interval");
924
+ }
925
+ };
926
+ }
927
+ });
928
+
929
+ // src/runtime/interceptors.ts
930
+ function createInterceptContext(operationType, operationName, options) {
931
+ return {
932
+ operationType,
933
+ operationName,
934
+ sessionId: options?.sessionId,
935
+ traceId: options?.traceId,
936
+ errorCode: options?.errorCode,
937
+ attempt: 1,
938
+ appliedFixes: []
939
+ };
940
+ }
941
+ function classifyError(error) {
942
+ const errorType = error.constructor.name.toLowerCase();
943
+ const errorMsg = error.message.toLowerCase();
944
+ if (errorType.includes("timeout") || errorMsg.includes("timeout") || errorMsg.includes("aborted")) {
945
+ return "TOOL.EXECUTION.TIMEOUT";
946
+ }
947
+ if (errorMsg.includes("rate") && errorMsg.includes("limit")) {
948
+ return "TOOL.EXECUTION.RATE_LIMIT";
949
+ }
950
+ if (errorMsg.includes("429") || errorMsg.includes("too many requests")) {
951
+ return "TOOL.EXECUTION.RATE_LIMIT";
952
+ }
953
+ if (errorType.includes("connection") || errorMsg.includes("connection") || errorMsg.includes("econnrefused") || errorMsg.includes("econnreset") || errorMsg.includes("fetch failed")) {
954
+ return "TOOL.EXECUTION.CONNECTION_ERROR";
955
+ }
956
+ if (errorMsg.includes("auth") || errorMsg.includes("401") || errorMsg.includes("403") || errorMsg.includes("unauthorized") || errorMsg.includes("forbidden")) {
957
+ return "TOOL.EXECUTION.AUTH_ERROR";
958
+ }
959
+ if (errorType.includes("syntaxerror") || errorType.includes("json") || errorMsg.includes("json") || errorMsg.includes("unexpected token")) {
960
+ return "OUTPUT.FORMAT.JSON_INVALID";
961
+ }
962
+ return "TOOL.EXECUTION.FAILURE";
963
+ }
964
+ var DefaultFixInterceptor;
965
+ var init_interceptors = __esm({
966
+ "src/runtime/interceptors.ts"() {
967
+ "use strict";
968
+ init_config();
969
+ init_log();
970
+ DefaultFixInterceptor = class {
971
+ _config;
972
+ _cache;
973
+ _applier;
974
+ constructor(config, cache, applier) {
975
+ this._config = resolveFixRuntimeConfig(config);
976
+ this._cache = cache;
977
+ this._applier = applier;
978
+ }
979
+ /**
980
+ * Apply pre-call fixes.
981
+ *
982
+ * - If retrying after error: look up fix by error_code, apply
983
+ * prompt/parameter/fallback fixes
984
+ * - Runs input guard fixes on all message content
985
+ */
986
+ preCall(ctx, messages, params) {
987
+ try {
988
+ if (!this._config.enabled) {
989
+ return { messages, params };
990
+ }
991
+ let modifiedMessages = messages;
992
+ let modifiedParams = { ...params };
993
+ if (ctx.errorCode) {
994
+ const fix = this._applier.getFixForError(ctx.errorCode, ctx.sessionId);
995
+ if (fix) {
996
+ debug(`Applying fix ${fix.fixId} for ${ctx.errorCode}`);
997
+ if (fix.fixType === "prompt" && messages) {
998
+ const msgRecords = messages;
999
+ const { messages: newMessages, result } = this._applier.applyPromptFix(fix, msgRecords);
1000
+ modifiedMessages = newMessages;
1001
+ ctx.appliedFixes.push(result);
1002
+ this._applier.logApplication(result);
1003
+ } else if (fix.fixType === "parameter") {
1004
+ const { params: newParams, result } = this._applier.applyParameterFix(fix, modifiedParams);
1005
+ modifiedParams = newParams;
1006
+ ctx.appliedFixes.push(result);
1007
+ this._applier.logApplication(result);
1008
+ } else if (fix.fixType === "fallback") {
1009
+ const { params: newParams, result } = this._applier.applyFallbackFix(fix, modifiedParams);
1010
+ modifiedParams = newParams;
1011
+ ctx.appliedFixes.push(result);
1012
+ this._applier.logApplication(result);
1013
+ }
1014
+ }
1015
+ }
1016
+ if (modifiedMessages && this._config.enabled) {
1017
+ for (const msg of modifiedMessages) {
1018
+ const content = msg.content;
1019
+ if (typeof content === "string") {
1020
+ for (const fix of this._cache.getAll()) {
1021
+ if (fix.fixType === "guard") {
1022
+ const { result } = this._applier.applyGuardFix(
1023
+ fix,
1024
+ content,
1025
+ true
1026
+ );
1027
+ if (result.applied) {
1028
+ ctx.appliedFixes.push(result);
1029
+ this._applier.logApplication(result);
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ }
1036
+ return { messages: modifiedMessages, params: modifiedParams };
1037
+ } catch (e) {
1038
+ debug(`DefaultFixInterceptor.preCall error: ${e}`);
1039
+ return { messages, params };
1040
+ }
1041
+ }
1042
+ /**
1043
+ * Apply post-call fixes (output validation).
1044
+ *
1045
+ * Runs output guard fixes on response content.
1046
+ */
1047
+ postCall(ctx, response) {
1048
+ try {
1049
+ if (!this._config.enabled) {
1050
+ return { response, shouldContinue: true };
1051
+ }
1052
+ for (const fix of this._cache.getAll()) {
1053
+ if (fix.fixType !== "guard") continue;
1054
+ const content = this._extractResponseContent(response);
1055
+ if (!content) continue;
1056
+ const { passed, result } = this._applier.applyGuardFix(
1057
+ fix,
1058
+ content,
1059
+ false
1060
+ );
1061
+ if (result.applied) {
1062
+ ctx.appliedFixes.push(result);
1063
+ this._applier.logApplication(result);
1064
+ }
1065
+ if (!passed) {
1066
+ warn(`Output guard failed for fix ${fix.fixId}`);
1067
+ }
1068
+ }
1069
+ return { response, shouldContinue: true };
1070
+ } catch (e) {
1071
+ debug(`DefaultFixInterceptor.postCall error: ${e}`);
1072
+ return { response, shouldContinue: true };
1073
+ }
1074
+ }
1075
+ /**
1076
+ * Handle errors and decide whether to retry.
1077
+ *
1078
+ * Classifies the error, looks up retry/fallback fixes, and returns
1079
+ * whether the caller should retry.
1080
+ */
1081
+ onError(ctx, error) {
1082
+ try {
1083
+ if (!this._config.enabled) {
1084
+ return { shouldRetry: false, modifiedParams: null };
1085
+ }
1086
+ const errorCode = classifyError(error);
1087
+ ctx.errorCode = errorCode;
1088
+ ctx.errorMessage = error.message;
1089
+ const fix = this._applier.getFixForError(errorCode, ctx.sessionId);
1090
+ if (!fix) {
1091
+ return { shouldRetry: false, modifiedParams: null };
1092
+ }
1093
+ if (fix.fixType === "retry") {
1094
+ const maxRetries = fix.config.max_retries ?? fix.config.maxRetries ?? 3;
1095
+ if (ctx.attempt <= maxRetries) {
1096
+ debug(
1097
+ `Retry fix ${fix.fixId}: attempt ${ctx.attempt}/${maxRetries}`
1098
+ );
1099
+ const result = {
1100
+ applied: true,
1101
+ fixId: fix.fixId,
1102
+ deploymentId: fix.deploymentId,
1103
+ fixType: "retry",
1104
+ modifications: { attempt: ctx.attempt }
1105
+ };
1106
+ ctx.appliedFixes.push(result);
1107
+ this._applier.logApplication(result);
1108
+ return { shouldRetry: true, modifiedParams: null };
1109
+ }
1110
+ }
1111
+ if (fix.fixType === "fallback") {
1112
+ const { params: modifiedParams, result } = this._applier.applyFallbackFix(fix, {});
1113
+ if (result.applied) {
1114
+ ctx.appliedFixes.push(result);
1115
+ this._applier.logApplication(result);
1116
+ return { shouldRetry: true, modifiedParams };
1117
+ }
1118
+ }
1119
+ return { shouldRetry: false, modifiedParams: null };
1120
+ } catch (e) {
1121
+ debug(`DefaultFixInterceptor.onError error: ${e}`);
1122
+ return { shouldRetry: false, modifiedParams: null };
1123
+ }
1124
+ }
1125
+ // ─── Private ────────────────────────────────────────────────────────────
1126
+ /** Extract text content from a response object. */
1127
+ _extractResponseContent(response) {
1128
+ if (typeof response === "string") return response;
1129
+ if (response && typeof response === "object") {
1130
+ const resp = response;
1131
+ const choices = resp.choices;
1132
+ if (Array.isArray(choices) && choices.length > 0) {
1133
+ const first = choices[0];
1134
+ const message = first?.message;
1135
+ if (typeof message?.content === "string") {
1136
+ return message.content;
1137
+ }
1138
+ }
1139
+ const content = resp.content;
1140
+ if (typeof content === "string") return content;
1141
+ if (Array.isArray(content) && content.length > 0) {
1142
+ const first = content[0];
1143
+ if (typeof first?.text === "string") return first.text;
1144
+ }
1145
+ }
1146
+ return null;
1147
+ }
1148
+ };
1149
+ }
1150
+ });
1151
+
1152
+ // src/runtime/runtime.ts
1153
+ var runtime_exports = {};
1154
+ __export(runtime_exports, {
1155
+ FixRuntime: () => FixRuntime,
1156
+ getFixRuntime: () => getFixRuntime,
1157
+ initFixRuntime: () => initFixRuntime,
1158
+ setFixRuntime: () => setFixRuntime,
1159
+ shutdownFixRuntime: () => shutdownFixRuntime
1160
+ });
1161
+ function getFixRuntime() {
1162
+ return G2[RUNTIME_KEY];
1163
+ }
1164
+ function setFixRuntime(runtime) {
1165
+ G2[RUNTIME_KEY] = runtime;
1166
+ }
1167
+ function initFixRuntime(config) {
1168
+ try {
1169
+ const existing = G2[RUNTIME_KEY];
1170
+ if (existing) {
1171
+ debug("Fix runtime already initialized");
1172
+ return existing;
1173
+ }
1174
+ const runtime = new FixRuntime(config);
1175
+ runtime.start();
1176
+ G2[RUNTIME_KEY] = runtime;
1177
+ return runtime;
1178
+ } catch (e) {
1179
+ warn(`initFixRuntime failed: ${e}`);
1180
+ const fallback = new FixRuntime({ ...config, enabled: false });
1181
+ G2[RUNTIME_KEY] = fallback;
1182
+ return fallback;
1183
+ }
1184
+ }
1185
+ function shutdownFixRuntime() {
1186
+ try {
1187
+ const runtime = G2[RUNTIME_KEY];
1188
+ if (runtime) {
1189
+ runtime.stop();
1190
+ }
1191
+ G2[RUNTIME_KEY] = void 0;
1192
+ } catch (e) {
1193
+ debug(`shutdownFixRuntime error: ${e}`);
1194
+ G2[RUNTIME_KEY] = void 0;
1195
+ }
1196
+ }
1197
+ var G2, RUNTIME_KEY, FixRuntime;
1198
+ var init_runtime = __esm({
1199
+ "src/runtime/runtime.ts"() {
1200
+ "use strict";
1201
+ init_cache();
1202
+ init_config();
1203
+ init_applier();
1204
+ init_loader();
1205
+ init_interceptors();
1206
+ init_log();
1207
+ G2 = globalThis;
1208
+ RUNTIME_KEY = "__risicare_fix_runtime";
1209
+ FixRuntime = class {
1210
+ _config;
1211
+ _cache;
1212
+ _loader;
1213
+ _applier;
1214
+ _interceptor;
1215
+ _started = false;
1216
+ // Effectiveness tracking
1217
+ _fixApplications = /* @__PURE__ */ new Map();
1218
+ _fixSuccesses = /* @__PURE__ */ new Map();
1219
+ constructor(config) {
1220
+ this._config = resolveFixRuntimeConfig(config);
1221
+ this._cache = new FixCache(this._config);
1222
+ this._loader = new FixLoader(config, this._cache);
1223
+ this._applier = new FixApplier(config, this._cache);
1224
+ this._interceptor = new DefaultFixInterceptor(
1225
+ config,
1226
+ this._cache,
1227
+ this._applier
1228
+ );
1229
+ }
1230
+ // ─── Accessors ──────────────────────────────────────────────────────────
1231
+ /** Runtime configuration (with defaults resolved). */
1232
+ get config() {
1233
+ return this._config;
1234
+ }
1235
+ /** Whether the runtime is enabled and started. */
1236
+ get isEnabled() {
1237
+ return this._config.enabled && this._started;
1238
+ }
1239
+ /** The fix cache. */
1240
+ get cache() {
1241
+ return this._cache;
1242
+ }
1243
+ /** The fix loader. */
1244
+ get loader() {
1245
+ return this._loader;
1246
+ }
1247
+ /** The fix applier. */
1248
+ get applier() {
1249
+ return this._applier;
1250
+ }
1251
+ /** The interceptor. */
1252
+ get interceptor() {
1253
+ return this._interceptor;
1254
+ }
1255
+ // ─── Lifecycle ──────────────────────────────────────────────────────────
1256
+ /**
1257
+ * Start the runtime.
1258
+ *
1259
+ * Triggers initial fix load and starts background refresh.
1260
+ * Never throws — failures degrade gracefully.
1261
+ */
1262
+ start() {
1263
+ try {
1264
+ if (this._started) return;
1265
+ if (!this._config.enabled) {
1266
+ debug("Fix runtime disabled");
1267
+ return;
1268
+ }
1269
+ debug("Starting fix runtime...");
1270
+ this._loader.start();
1271
+ this._started = true;
1272
+ debug("Fix runtime started");
1273
+ } catch (e) {
1274
+ warn(`Fix runtime start failed: ${e}`);
1275
+ }
1276
+ }
1277
+ /**
1278
+ * Stop the runtime.
1279
+ *
1280
+ * Stops background refresh and clears the cache.
1281
+ * Never throws.
1282
+ */
1283
+ stop() {
1284
+ try {
1285
+ if (!this._started) return;
1286
+ debug("Stopping fix runtime...");
1287
+ this._loader.stop();
1288
+ this._cache.clear();
1289
+ this._started = false;
1290
+ debug("Fix runtime stopped");
1291
+ } catch (e) {
1292
+ debug(`Fix runtime stop error: ${e}`);
1293
+ }
1294
+ }
1295
+ // ─── Intercept API ──────────────────────────────────────────────────────
1296
+ /**
1297
+ * Intercept a call: create context and run preCall fixes.
1298
+ *
1299
+ * Returns modified messages/params and the InterceptContext for
1300
+ * use in subsequent interceptResponse / interceptError calls.
1301
+ */
1302
+ interceptCall(operationType, operationName, messages, params, options) {
1303
+ const ctx = createInterceptContext(operationType, operationName, options);
1304
+ try {
1305
+ if (!this.isEnabled) {
1306
+ return { messages, params, ctx };
1307
+ }
1308
+ const result = this._interceptor.preCall(ctx, messages, params);
1309
+ return { messages: result.messages, params: result.params, ctx };
1310
+ } catch (e) {
1311
+ debug(`interceptCall error: ${e}`);
1312
+ return { messages, params, ctx };
1313
+ }
1314
+ }
1315
+ /**
1316
+ * Intercept a response: run postCall fixes (output validation).
1317
+ */
1318
+ interceptResponse(ctx, response) {
1319
+ try {
1320
+ if (!this.isEnabled) {
1321
+ return { response, shouldContinue: true };
1322
+ }
1323
+ const result = this._interceptor.postCall(ctx, response);
1324
+ this._trackSuccess(ctx);
1325
+ return result;
1326
+ } catch (e) {
1327
+ debug(`interceptResponse error: ${e}`);
1328
+ return { response, shouldContinue: true };
1329
+ }
1330
+ }
1331
+ /**
1332
+ * Intercept an error: decide on retry/fallback.
1333
+ */
1334
+ interceptError(ctx, error) {
1335
+ try {
1336
+ if (!this.isEnabled) {
1337
+ return { shouldRetry: false, modifiedParams: null };
1338
+ }
1339
+ ctx.attempt++;
1340
+ const result = this._interceptor.onError(ctx, error);
1341
+ if (!result.shouldRetry) {
1342
+ this._trackFailure(ctx);
1343
+ }
1344
+ return result;
1345
+ } catch (e) {
1346
+ debug(`interceptError error: ${e}`);
1347
+ return { shouldRetry: false, modifiedParams: null };
1348
+ }
1349
+ }
1350
+ // ─── Direct Fix Access ──────────────────────────────────────────────────
1351
+ /**
1352
+ * Get applicable fix for an error code.
1353
+ */
1354
+ getFix(errorCode, sessionId) {
1355
+ try {
1356
+ if (!this.isEnabled) return null;
1357
+ return this._applier.getFixForError(errorCode, sessionId);
1358
+ } catch (e) {
1359
+ debug(`getFix error: ${e}`);
1360
+ return null;
1361
+ }
1362
+ }
1363
+ /**
1364
+ * Manually refresh fixes from the API.
1365
+ */
1366
+ async refreshFixes() {
1367
+ try {
1368
+ return await this._loader.loadSync();
1369
+ } catch (e) {
1370
+ debug(`refreshFixes error: ${e}`);
1371
+ return [];
1372
+ }
1373
+ }
1374
+ // ─── Effectiveness ──────────────────────────────────────────────────────
1375
+ /** Get effectiveness statistics for all fixes. */
1376
+ getEffectivenessStats() {
1377
+ const stats = {};
1378
+ for (const [fixId, applications] of this._fixApplications) {
1379
+ const successes = this._fixSuccesses.get(fixId) ?? 0;
1380
+ stats[fixId] = {
1381
+ applications,
1382
+ successes,
1383
+ successRate: applications > 0 ? successes / applications : 0
1384
+ };
1385
+ }
1386
+ return stats;
1387
+ }
1388
+ // ─── Private ────────────────────────────────────────────────────────────
1389
+ _trackSuccess(ctx) {
1390
+ for (const result of ctx.appliedFixes) {
1391
+ if (result.applied && result.fixId) {
1392
+ this._fixApplications.set(
1393
+ result.fixId,
1394
+ (this._fixApplications.get(result.fixId) ?? 0) + 1
1395
+ );
1396
+ this._fixSuccesses.set(
1397
+ result.fixId,
1398
+ (this._fixSuccesses.get(result.fixId) ?? 0) + 1
1399
+ );
1400
+ }
1401
+ }
1402
+ }
1403
+ _trackFailure(ctx) {
1404
+ for (const result of ctx.appliedFixes) {
1405
+ if (result.applied && result.fixId) {
1406
+ this._fixApplications.set(
1407
+ result.fixId,
1408
+ (this._fixApplications.get(result.fixId) ?? 0) + 1
1409
+ );
1410
+ }
1411
+ }
1412
+ }
1413
+ };
1414
+ }
1415
+ });
1416
+
1
1417
  // src/config.ts
2
1418
  var DEFAULT_ENDPOINT = "https://app.risicare.ai";
3
1419
  function resolveConfig(config) {
@@ -403,48 +1819,8 @@ function shouldSample(traceId, sampleRate) {
403
1819
  return hash / 4294967295 < sampleRate;
404
1820
  }
405
1821
 
406
- // src/globals.ts
407
- import { AsyncLocalStorage } from "async_hooks";
408
- var G = globalThis;
409
- var PREFIX = "__risicare_";
410
- function getClient() {
411
- return G[PREFIX + "client"];
412
- }
413
- function setClient(client) {
414
- G[PREFIX + "client"] = client;
415
- }
416
- function getTracer() {
417
- return G[PREFIX + "tracer"];
418
- }
419
- function setTracer(tracer) {
420
- G[PREFIX + "tracer"] = tracer;
421
- }
422
- function getContextStorage() {
423
- if (!G[PREFIX + "ctx"]) {
424
- G[PREFIX + "ctx"] = new AsyncLocalStorage();
425
- }
426
- return G[PREFIX + "ctx"];
427
- }
428
- function getRegistry() {
429
- if (!G[PREFIX + "registry"]) {
430
- G[PREFIX + "registry"] = /* @__PURE__ */ new Map();
431
- }
432
- return G[PREFIX + "registry"];
433
- }
434
- function getOpCount() {
435
- return G[PREFIX + "opcount"] ?? 0;
436
- }
437
- function setOpCount(n) {
438
- G[PREFIX + "opcount"] = n;
439
- }
440
- function getDebug() {
441
- return G[PREFIX + "debug"] ?? false;
442
- }
443
- function setDebugFlag(enabled) {
444
- G[PREFIX + "debug"] = enabled;
445
- }
446
-
447
1822
  // src/context/storage.ts
1823
+ init_globals();
448
1824
  function storage() {
449
1825
  return getContextStorage();
450
1826
  }
@@ -546,7 +1922,8 @@ var Tracer = class {
546
1922
  const session2 = getCurrentSession();
547
1923
  const agent2 = getCurrentAgent();
548
1924
  const phase = getCurrentPhase();
549
- const traceId = opts.traceId ?? parentSpan?.traceId ?? generateTraceId();
1925
+ const ctx = getContext();
1926
+ const traceId = opts.traceId ?? parentSpan?.traceId ?? ctx._rootTraceId ?? generateTraceId();
550
1927
  if (!parentSpan && this._sampleRate < 1) {
551
1928
  if (!shouldSample(traceId, this._sampleRate)) {
552
1929
  return fn(NOOP_SPAN);
@@ -641,22 +2018,8 @@ var Tracer = class {
641
2018
  }
642
2019
  };
643
2020
 
644
- // src/utils/log.ts
645
- function setDebug(enabled) {
646
- setDebugFlag(enabled);
647
- }
648
- function debug(msg) {
649
- if (getDebug()) {
650
- process.stderr.write(`[risicare] ${msg}
651
- `);
652
- }
653
- }
654
- function warn(msg) {
655
- process.stderr.write(`[risicare] WARNING: ${msg}
656
- `);
657
- }
658
-
659
2021
  // src/exporters/batch.ts
2022
+ init_log();
660
2023
  var BatchSpanProcessor = class _BatchSpanProcessor {
661
2024
  _exporters;
662
2025
  _batchSize;
@@ -677,7 +2040,14 @@ var BatchSpanProcessor = class _BatchSpanProcessor {
677
2040
  failedExports = 0;
678
2041
  constructor(options) {
679
2042
  this._exporters = [...options.exporters];
680
- this._batchSize = options.batchSize ?? 100;
2043
+ const requestedBatchSize = options.batchSize ?? 100;
2044
+ const clampedBatchSize = Math.max(1, Math.min(requestedBatchSize, 1e4));
2045
+ if (clampedBatchSize !== requestedBatchSize) {
2046
+ warn(
2047
+ `batchSize=${requestedBatchSize} is outside the allowed range [1, 10000]; clamping to ${clampedBatchSize}`
2048
+ );
2049
+ }
2050
+ this._batchSize = clampedBatchSize;
681
2051
  this._batchTimeoutMs = options.batchTimeoutMs ?? 1e3;
682
2052
  this._maxQueueSize = options.maxQueueSize ?? 1e4;
683
2053
  this._debug = options.debug ?? false;
@@ -807,6 +2177,7 @@ var BatchSpanProcessor = class _BatchSpanProcessor {
807
2177
  };
808
2178
 
809
2179
  // src/exporters/http.ts
2180
+ init_log();
810
2181
  var SDK_VERSION = "0.1.1";
811
2182
  var HttpExporter = class {
812
2183
  name = "http";
@@ -817,11 +2188,15 @@ var HttpExporter = class {
817
2188
  _timeoutMs;
818
2189
  _maxRetries;
819
2190
  _compress;
820
- // Circuit breaker
2191
+ // Circuit breaker (CON-201 / B-16). Threshold and cooldown sized for
2192
+ // a "30-second prod restart" scenario: with default 1s batch flush a
2193
+ // 5-failure threshold opens the circuit ~5s into an outage and the 60s
2194
+ // cooldown gives the gateway time to come back before the half-open
2195
+ // probe fires. Lower numbers (was 3@30s) tripped on transient blips.
821
2196
  _consecutiveFailures = 0;
822
2197
  _circuitOpenUntil = 0;
823
- _circuitBreakerThreshold = 3;
824
- _circuitBreakerCooldownMs = 3e4;
2198
+ _circuitBreakerThreshold = 5;
2199
+ _circuitBreakerCooldownMs = 6e4;
825
2200
  constructor(options) {
826
2201
  this._endpoint = options.endpoint.replace(/\/+$/, "");
827
2202
  this._apiKey = options.apiKey;
@@ -932,6 +2307,8 @@ var ConsoleExporter = class {
932
2307
  };
933
2308
 
934
2309
  // src/client.ts
2310
+ init_log();
2311
+ init_globals();
935
2312
  var RisicareClient = class {
936
2313
  config;
937
2314
  processor;
@@ -971,6 +2348,29 @@ var RisicareClient = class {
971
2348
  traceContent: this.config.traceContent
972
2349
  });
973
2350
  this.processor.start();
2351
+ if (this.config.apiKey && this.config.enabled) {
2352
+ try {
2353
+ const { initFixRuntime: initFixRuntime2 } = (init_runtime(), __toCommonJS(runtime_exports));
2354
+ initFixRuntime2({
2355
+ apiEndpoint: this.config.endpoint,
2356
+ apiKey: this.config.apiKey,
2357
+ enabled: true,
2358
+ debug: this.config.debug
2359
+ });
2360
+ } catch (e) {
2361
+ const errName = e instanceof Error ? e.name : "Error";
2362
+ const errMsg = e instanceof Error ? e.message : String(e);
2363
+ try {
2364
+ console.warn(
2365
+ `[risicare] FixRuntime initialization failed \u2014 auto-fix is OFFLINE for this process. endpoint=${this.config.endpoint} error=${errName}: ${errMsg}. Tracing + reporting still work; only fix application is disabled. Pass debug:true in init() options for the full traceback.`
2366
+ );
2367
+ } catch {
2368
+ }
2369
+ if (this.config.debug) {
2370
+ debug(`FixRuntime init traceback: ${e instanceof Error && e.stack ? e.stack : String(e)}`);
2371
+ }
2372
+ }
2373
+ }
974
2374
  this._registerShutdownHooks();
975
2375
  setDebug(this.config.debug);
976
2376
  debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);
@@ -1022,6 +2422,11 @@ function init(config) {
1022
2422
  setTracer(client.tracer);
1023
2423
  }
1024
2424
  async function shutdown() {
2425
+ try {
2426
+ const { shutdownFixRuntime: shutdownFixRuntime2 } = (init_runtime(), __toCommonJS(runtime_exports));
2427
+ shutdownFixRuntime2();
2428
+ } catch {
2429
+ }
1025
2430
  const client = getClient();
1026
2431
  if (!client) return;
1027
2432
  await client.shutdown();
@@ -1063,12 +2468,39 @@ function getMetrics() {
1063
2468
  queueUtilization: 0
1064
2469
  };
1065
2470
  }
2471
+ var _ERROR_DEDUP_TTL_MS = 5 * 60 * 1e3;
2472
+ var _ERROR_DEDUP_MAX = 1e3;
2473
+ var _recentErrors = /* @__PURE__ */ new Map();
2474
+ function _errorFingerprint(err) {
2475
+ const raw = `${err.constructor?.name ?? "Error"}:${String(err.message ?? "").slice(0, 200)}`;
2476
+ const { createHash: createHash2 } = __require("crypto");
2477
+ return createHash2("sha256").update(raw).digest("hex").slice(0, 16);
2478
+ }
2479
+ function _isDuplicateError(fingerprint) {
2480
+ const now = Date.now();
2481
+ for (const [fp, ts] of _recentErrors) {
2482
+ if (now - ts > _ERROR_DEDUP_TTL_MS) _recentErrors.delete(fp);
2483
+ else break;
2484
+ }
2485
+ if (_recentErrors.has(fingerprint)) return true;
2486
+ if (_recentErrors.size >= _ERROR_DEDUP_MAX) {
2487
+ const oldest = _recentErrors.keys().next().value;
2488
+ if (oldest !== void 0) _recentErrors.delete(oldest);
2489
+ }
2490
+ _recentErrors.set(fingerprint, now);
2491
+ return false;
2492
+ }
1066
2493
  function reportError(error, options) {
1067
2494
  try {
1068
2495
  const tracer = getTracer2();
1069
2496
  if (!tracer) return;
1070
- const err = error instanceof Error ? error : new Error(String(error));
2497
+ const err = error instanceof Error ? error : new Error(String(error ?? "unknown"));
1071
2498
  const spanName = options?.name ?? `error:${err.constructor.name}`;
2499
+ const fp = _errorFingerprint(err);
2500
+ if (_isDuplicateError(fp)) {
2501
+ debug(`reportError: duplicate suppressed (fp=${fp.slice(0, 8)})`);
2502
+ return;
2503
+ }
1072
2504
  tracer.startSpan({ name: spanName, kind: "internal" /* INTERNAL */ }, (span) => {
1073
2505
  span.setStatus("error" /* ERROR */, err.message);
1074
2506
  span.setAttribute("error", true);
@@ -1088,7 +2520,7 @@ function reportError(error, options) {
1088
2520
  }
1089
2521
  function score(traceId, name, value, options) {
1090
2522
  try {
1091
- if (typeof value !== "number" || value < 0 || value > 1) {
2523
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
1092
2524
  debug(`score: value must be in [0.0, 1.0], got ${value}. Score not sent.`);
1093
2525
  return;
1094
2526
  }
@@ -1138,7 +2570,7 @@ function withAgent(options, fn) {
1138
2570
  }
1139
2571
 
1140
2572
  // src/decorators/agent.ts
1141
- function agent(options, fn) {
2573
+ function _wrapAgent(options, fn) {
1142
2574
  const spanName = `agent:${options.name ?? "agent"}`;
1143
2575
  return (...args) => {
1144
2576
  const tracer = getTracer2();
@@ -1152,6 +2584,12 @@ function agent(options, fn) {
1152
2584
  });
1153
2585
  };
1154
2586
  }
2587
+ function agent(options, fn) {
2588
+ if (fn === void 0) {
2589
+ return (realFn) => _wrapAgent(options, realFn);
2590
+ }
2591
+ return _wrapAgent(options, fn);
2592
+ }
1155
2593
 
1156
2594
  // src/context/session.ts
1157
2595
  function withSession(options, fn) {
@@ -1166,12 +2604,18 @@ function withSession(options, fn) {
1166
2604
  }
1167
2605
 
1168
2606
  // src/decorators/session.ts
1169
- function session(optionsOrResolver, fn) {
2607
+ function _wrapSession(optionsOrResolver, fn) {
1170
2608
  return (...args) => {
1171
2609
  const options = typeof optionsOrResolver === "function" ? optionsOrResolver(...args) : optionsOrResolver;
1172
2610
  return withSession(options, () => fn(...args));
1173
2611
  };
1174
2612
  }
2613
+ function session(optionsOrResolver, fn) {
2614
+ if (fn === void 0) {
2615
+ return (realFn) => _wrapSession(optionsOrResolver, realFn);
2616
+ }
2617
+ return _wrapSession(optionsOrResolver, fn);
2618
+ }
1175
2619
 
1176
2620
  // src/context/phase.ts
1177
2621
  function withPhase(phase, fn) {
@@ -1283,8 +2727,22 @@ function getTraceContext() {
1283
2727
  const span = getCurrentSpan();
1284
2728
  const session2 = getCurrentSession();
1285
2729
  const agent2 = getCurrentAgent();
2730
+ let traceId;
2731
+ if (span) {
2732
+ traceId = span.traceId;
2733
+ } else {
2734
+ const ctx = getContext();
2735
+ if (ctx._rootTraceId) {
2736
+ traceId = ctx._rootTraceId;
2737
+ } else {
2738
+ traceId = generateTraceId();
2739
+ if (ctx.session || ctx.agent) {
2740
+ ctx._rootTraceId = traceId;
2741
+ }
2742
+ }
2743
+ }
1286
2744
  return {
1287
- traceId: span?.traceId ?? generateTraceId(),
2745
+ traceId,
1288
2746
  spanId: span?.spanId ?? generateSpanId(),
1289
2747
  sessionId: session2?.sessionId,
1290
2748
  agentId: agent2?.agentId
@@ -1352,6 +2810,7 @@ function extractTraceContext(headers) {
1352
2810
  }
1353
2811
 
1354
2812
  // src/context/registry.ts
2813
+ init_globals();
1355
2814
  var DEFAULT_TTL_MS = 6e4;
1356
2815
  var MAX_ENTRIES = 1e4;
1357
2816
  var CLEANUP_INTERVAL = 100;
@@ -1421,10 +2880,34 @@ async function* tracedStream(source, options) {
1421
2880
  }
1422
2881
  }
1423
2882
 
2883
+ // src/context/dedup.ts
2884
+ function suppressProviderInstrumentation(fn) {
2885
+ return runWithContext({ _suppressProviderInstrumentation: true }, fn);
2886
+ }
2887
+ function isProviderInstrumentationSuppressed() {
2888
+ return getContext()._suppressProviderInstrumentation === true;
2889
+ }
2890
+
1424
2891
  // src/frameworks/langchain.ts
1425
2892
  var RisicareCallbackHandler = class {
1426
2893
  name = "RisicareCallbackHandler";
1427
2894
  _spans = /* @__PURE__ */ new Map();
2895
+ /**
2896
+ * Run a function with provider instrumentation suppressed.
2897
+ *
2898
+ * Wrap your chain.invoke() call with this to prevent duplicate spans.
2899
+ * The handler creates LLM/tool spans, so provider proxies (patchOpenAI, etc.)
2900
+ * should be suppressed during chain execution.
2901
+ *
2902
+ * @example
2903
+ * const handler = new RisicareCallbackHandler();
2904
+ * const result = await handler.withSuppression(() =>
2905
+ * chain.invoke(input, { callbacks: [handler] })
2906
+ * );
2907
+ */
2908
+ withSuppression(fn) {
2909
+ return suppressProviderInstrumentation(fn);
2910
+ }
1428
2911
  // ── Chain lifecycle ────────────────────────────────────────────────────────
1429
2912
  handleChainStart(chain, _inputs, runId, parentRunId) {
1430
2913
  const tracer = getTracer2();
@@ -1602,6 +3085,7 @@ function instrumentLangGraph(graph) {
1602
3085
  }
1603
3086
 
1604
3087
  // src/frameworks/instructor.ts
3088
+ init_log();
1605
3089
  function patchInstructor(client) {
1606
3090
  return new Proxy(client, {
1607
3091
  get(target, prop, receiver) {
@@ -1652,14 +3136,6 @@ function patchInstructor(client) {
1652
3136
  });
1653
3137
  }
1654
3138
 
1655
- // src/context/dedup.ts
1656
- function suppressProviderInstrumentation(fn) {
1657
- return runWithContext({ _suppressProviderInstrumentation: true }, fn);
1658
- }
1659
- function isProviderInstrumentationSuppressed() {
1660
- return getContext()._suppressProviderInstrumentation === true;
1661
- }
1662
-
1663
3139
  // src/frameworks/llamaindex.ts
1664
3140
  var RisicareLlamaIndexHandler = class {
1665
3141
  _spans = /* @__PURE__ */ new Map();
@@ -1758,14 +3234,183 @@ var RisicareLlamaIndexHandler = class {
1758
3234
  return "component";
1759
3235
  }
1760
3236
  };
3237
+
3238
+ // src/index.ts
3239
+ init_runtime();
3240
+
3241
+ // src/webhooks.ts
3242
+ import { createHmac, timingSafeEqual } from "crypto";
3243
+ import { Buffer as Buffer2 } from "buffer";
3244
+ var DEFAULT_TIMESTAMP_TOLERANCE_S = 300;
3245
+ var SIGNATURE_HEADER = "X-Risicare-Signature";
3246
+ var TIMESTAMP_HEADER = "X-Risicare-Timestamp";
3247
+ var SUPPORTED_SCHEMES = ["v1"];
3248
+ var HEX_SHA256_RE = /^[0-9a-f]{64}$/;
3249
+ var WebhookVerificationError = class _WebhookVerificationError extends Error {
3250
+ constructor(message) {
3251
+ super(message);
3252
+ this.name = "WebhookVerificationError";
3253
+ Object.setPrototypeOf(this, _WebhookVerificationError.prototype);
3254
+ }
3255
+ };
3256
+ function getHeader(headers, name) {
3257
+ const lower = name.toLowerCase();
3258
+ if (typeof headers.get === "function") {
3259
+ const accessor = headers;
3260
+ const v = accessor.get(name) ?? accessor.get(lower);
3261
+ return v ?? void 0;
3262
+ }
3263
+ const dict = headers;
3264
+ const raw = dict[name] ?? dict[lower];
3265
+ if (raw === void 0) {
3266
+ return void 0;
3267
+ }
3268
+ if (Array.isArray(raw)) {
3269
+ return raw[0];
3270
+ }
3271
+ return raw;
3272
+ }
3273
+ function coercePayload(payload) {
3274
+ if (typeof payload === "string") {
3275
+ return Buffer2.from(payload, "utf-8");
3276
+ }
3277
+ if (payload instanceof Uint8Array) {
3278
+ return Buffer2.from(
3279
+ payload.buffer,
3280
+ payload.byteOffset,
3281
+ payload.byteLength
3282
+ );
3283
+ }
3284
+ if (payload instanceof ArrayBuffer) {
3285
+ return Buffer2.from(payload);
3286
+ }
3287
+ throw new WebhookVerificationError(
3288
+ `payload must be Uint8Array, ArrayBuffer, Buffer, or string, got ${typeof payload}`
3289
+ );
3290
+ }
3291
+ function parseSignatureHeader(raw) {
3292
+ const parts = /* @__PURE__ */ Object.create(null);
3293
+ for (const rawSegment of raw.split(",")) {
3294
+ const segment = rawSegment.trim();
3295
+ if (segment === "") {
3296
+ continue;
3297
+ }
3298
+ const eqIdx = segment.indexOf("=");
3299
+ if (eqIdx === -1) {
3300
+ throw new WebhookVerificationError(
3301
+ `Malformed segment in ${SIGNATURE_HEADER}: ${JSON.stringify(segment)}`
3302
+ );
3303
+ }
3304
+ const key = segment.slice(0, eqIdx).trim();
3305
+ const value = segment.slice(eqIdx + 1).trim();
3306
+ parts[key] = value;
3307
+ }
3308
+ if (!("t" in parts)) {
3309
+ throw new WebhookVerificationError(
3310
+ `Missing \`t=\` (timestamp) in ${SIGNATURE_HEADER}`
3311
+ );
3312
+ }
3313
+ let scheme;
3314
+ for (const candidate of SUPPORTED_SCHEMES) {
3315
+ if (candidate in parts) {
3316
+ scheme = candidate;
3317
+ break;
3318
+ }
3319
+ }
3320
+ if (scheme === void 0) {
3321
+ const keys = Object.keys(parts).sort();
3322
+ throw new WebhookVerificationError(
3323
+ `No supported signature scheme in ${SIGNATURE_HEADER}; expected one of (${SUPPORTED_SCHEMES.map((s) => `'${s}'`).join(", ")}), got keys [${keys.map((k) => `'${k}'`).join(", ")}]`
3324
+ );
3325
+ }
3326
+ const tRaw = parts["t"];
3327
+ if (!/^-?\d+$/.test(tRaw)) {
3328
+ throw new WebhookVerificationError(
3329
+ `Non-integer timestamp in ${SIGNATURE_HEADER}: ${JSON.stringify(tRaw)}`
3330
+ );
3331
+ }
3332
+ const ts = parseInt(tRaw, 10);
3333
+ if (!Number.isFinite(ts)) {
3334
+ throw new WebhookVerificationError(
3335
+ `Non-integer timestamp in ${SIGNATURE_HEADER}: ${JSON.stringify(tRaw)}`
3336
+ );
3337
+ }
3338
+ const sigHex = parts[scheme];
3339
+ if (sigHex === void 0 || sigHex === "") {
3340
+ throw new WebhookVerificationError(
3341
+ `Empty ${scheme}= value in ${SIGNATURE_HEADER}`
3342
+ );
3343
+ }
3344
+ return [ts, sigHex];
3345
+ }
3346
+ function safeHexEqual(computed, expected) {
3347
+ if (!HEX_SHA256_RE.test(expected)) {
3348
+ return false;
3349
+ }
3350
+ const a = Buffer2.from(computed, "hex");
3351
+ const b = Buffer2.from(expected, "hex");
3352
+ if (a.length !== b.length) {
3353
+ return false;
3354
+ }
3355
+ return timingSafeEqual(a, b);
3356
+ }
3357
+ function verifyWebhookSignature(payload, headers, secret, opts = {}) {
3358
+ const toleranceS = opts.toleranceS ?? DEFAULT_TIMESTAMP_TOLERANCE_S;
3359
+ const payloadBytes = coercePayload(payload);
3360
+ const sigHeader = getHeader(headers, SIGNATURE_HEADER);
3361
+ if (sigHeader === void 0 || sigHeader === "") {
3362
+ throw new WebhookVerificationError(
3363
+ `Missing ${SIGNATURE_HEADER} header`
3364
+ );
3365
+ }
3366
+ const tsHeader = getHeader(headers, TIMESTAMP_HEADER);
3367
+ if (tsHeader === void 0 || tsHeader === "") {
3368
+ throw new WebhookVerificationError(
3369
+ `Missing ${TIMESTAMP_HEADER} header`
3370
+ );
3371
+ }
3372
+ const [tsInSig, expectedHex] = parseSignatureHeader(sigHeader);
3373
+ if (!/^-?\d+$/.test(tsHeader)) {
3374
+ throw new WebhookVerificationError(
3375
+ `Non-integer ${TIMESTAMP_HEADER}: ${JSON.stringify(tsHeader)}`
3376
+ );
3377
+ }
3378
+ const tsStandalone = parseInt(tsHeader, 10);
3379
+ if (!Number.isFinite(tsStandalone)) {
3380
+ throw new WebhookVerificationError(
3381
+ `Non-integer ${TIMESTAMP_HEADER}: ${JSON.stringify(tsHeader)}`
3382
+ );
3383
+ }
3384
+ if (tsStandalone !== tsInSig) {
3385
+ throw new WebhookVerificationError(
3386
+ `Timestamp mismatch: ${TIMESTAMP_HEADER}=${tsStandalone}, signature t=${tsInSig}`
3387
+ );
3388
+ }
3389
+ const now = opts._now !== void 0 ? Math.trunc(opts._now) : Math.floor(Date.now() / 1e3);
3390
+ const skew = Math.abs(now - tsInSig);
3391
+ if (skew > toleranceS) {
3392
+ throw new WebhookVerificationError(
3393
+ `Webhook timestamp outside tolerance: skew=${skew}s, max=${toleranceS}s`
3394
+ );
3395
+ }
3396
+ const hmac = createHmac("sha256", secret);
3397
+ hmac.update(`${tsInSig}.`);
3398
+ hmac.update(payloadBytes);
3399
+ const computed = hmac.digest("hex");
3400
+ if (!safeHexEqual(computed, expectedHex)) {
3401
+ throw new WebhookVerificationError("Webhook signature mismatch");
3402
+ }
3403
+ }
1761
3404
  export {
1762
3405
  AgentRole,
3406
+ DEFAULT_TIMESTAMP_TOLERANCE_S,
1763
3407
  MessageType,
1764
3408
  RisicareCallbackHandler,
1765
3409
  RisicareLlamaIndexHandler,
1766
3410
  SemanticPhase,
1767
3411
  SpanKind,
1768
3412
  SpanStatus,
3413
+ WebhookVerificationError,
1769
3414
  agent,
1770
3415
  disable,
1771
3416
  enable,
@@ -1780,12 +3425,14 @@ export {
1780
3425
  getCurrentSpan,
1781
3426
  getCurrentSpanId,
1782
3427
  getCurrentTraceId,
3428
+ getFixRuntime,
1783
3429
  getMetrics,
1784
3430
  getSpanById,
1785
3431
  getTraceContent,
1786
3432
  getTraceContext,
1787
3433
  getTracer2 as getTracer,
1788
3434
  init,
3435
+ initFixRuntime,
1789
3436
  injectTraceContext,
1790
3437
  instrumentLangGraph,
1791
3438
  isEnabled,
@@ -1796,6 +3443,7 @@ export {
1796
3443
  score,
1797
3444
  session,
1798
3445
  shutdown,
3446
+ shutdownFixRuntime,
1799
3447
  suppressProviderInstrumentation,
1800
3448
  traceAct,
1801
3449
  traceCoordinate,
@@ -1806,6 +3454,7 @@ export {
1806
3454
  traceThink,
1807
3455
  tracedStream,
1808
3456
  unregisterSpan,
3457
+ verifyWebhookSignature,
1809
3458
  withAgent,
1810
3459
  withPhase,
1811
3460
  withSession