@observyze/sdk 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,10 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res) => function __init() {
7
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
- };
9
8
  var __export = (target, all) => {
10
9
  for (var name in all)
11
10
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -18,303 +17,38 @@ var __copyProps = (to, from, except, desc) => {
18
17
  }
19
18
  return to;
20
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
21
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
29
 
23
- // src/types.ts
24
- var SpanType, TraceStatus;
25
- var init_types = __esm({
26
- "src/types.ts"() {
27
- "use strict";
28
- SpanType = /* @__PURE__ */ ((SpanType3) => {
29
- SpanType3["LLM"] = "llm";
30
- SpanType3["TOOL"] = "tool";
31
- SpanType3["AGENT"] = "agent";
32
- SpanType3["CHAIN"] = "chain";
33
- SpanType3["RETRIEVAL"] = "retrieval";
34
- return SpanType3;
35
- })(SpanType || {});
36
- TraceStatus = /* @__PURE__ */ ((TraceStatus2) => {
37
- TraceStatus2["SUCCESS"] = "success";
38
- TraceStatus2["ERROR"] = "error";
39
- TraceStatus2["TIMEOUT"] = "timeout";
40
- TraceStatus2["RUNNING"] = "running";
41
- return TraceStatus2;
42
- })(TraceStatus || {});
43
- }
44
- });
45
-
46
- // src/instrumentation/openai.ts
47
- function wrapOpenAI(client, nwClient) {
48
- const anyClient = client;
49
- const originalCreate = client.chat.completions.create.bind(client.chat.completions);
50
- client.chat.completions.create = async function(params, options) {
51
- const trace = nwClient.startTrace(`openai.chat.completions.create`, {
52
- provider: "openai",
53
- model: params.model
54
- });
55
- const span = trace.startSpan("chat.completions.create", "llm" /* LLM */);
56
- span.setMetadata("model", params.model);
57
- span.setMetadata("provider", "openai");
58
- if (params.temperature !== void 0) span.setMetadata("temperature", params.temperature);
59
- if (params.max_tokens !== void 0) span.setMetadata("max_tokens", params.max_tokens);
60
- span.setInput({
61
- model: params.model,
62
- messages: params.messages,
63
- temperature: params.temperature,
64
- max_tokens: params.max_tokens
65
- });
66
- const startTime = Date.now();
67
- try {
68
- const response = await originalCreate(params, options);
69
- if (params.stream) {
70
- return wrapOpenAIStream(response, span, trace, startTime);
71
- }
72
- const completionResponse = response;
73
- const latency = Date.now() - startTime;
74
- span.setOutput({
75
- id: completionResponse.id,
76
- model: completionResponse.model,
77
- choices: completionResponse.choices
78
- });
79
- if (completionResponse.usage) {
80
- span.setTokens({
81
- input: completionResponse.usage.prompt_tokens,
82
- output: completionResponse.usage.completion_tokens,
83
- total: completionResponse.usage.total_tokens
84
- });
85
- }
86
- span.setMetadata("latency_ms", latency);
87
- span.end();
88
- trace.end();
89
- return response;
90
- } catch (error) {
91
- const latency = Date.now() - startTime;
92
- span.setMetadata("latency_ms", latency);
93
- span.setError(error);
94
- span.end();
95
- trace.end();
96
- throw error;
97
- }
98
- };
99
- return client;
100
- }
101
- function wrapOpenAIStream(stream, span, trace, startTime) {
102
- const bufferedChunks = [];
103
- let streamId = "";
104
- let streamModel = "";
105
- return {
106
- [Symbol.asyncIterator]: async function* () {
107
- try {
108
- for await (const chunk of stream) {
109
- if (chunk.id) streamId = chunk.id;
110
- if (chunk.model) streamModel = chunk.model;
111
- const delta = chunk.choices[0]?.delta;
112
- if (delta?.content) {
113
- bufferedChunks.push(delta.content);
114
- }
115
- yield chunk;
116
- }
117
- const latency = Date.now() - startTime;
118
- const completeOutput = bufferedChunks.join("");
119
- span.setOutput({
120
- id: streamId,
121
- model: streamModel,
122
- content: completeOutput
123
- });
124
- span.setMetadata("latency_ms", latency);
125
- span.setMetadata("streaming", true);
126
- span.end();
127
- trace.end();
128
- } catch (error) {
129
- const latency = Date.now() - startTime;
130
- span.setMetadata("latency_ms", latency);
131
- span.setError(error);
132
- span.end();
133
- trace.end();
134
- throw error;
135
- }
136
- }
137
- };
138
- }
139
- var init_openai = __esm({
140
- "src/instrumentation/openai.ts"() {
141
- "use strict";
142
- init_types();
143
- }
144
- });
145
-
146
- // src/instrumentation/anthropic.ts
147
- function wrapAnthropic(client, nwClient) {
148
- const anyClient = client;
149
- const originalCreate = client.messages.create.bind(client.messages);
150
- client.messages.create = async function(params, options) {
151
- const trace = nwClient.startTrace(`anthropic.messages.create`, {
152
- provider: "anthropic",
153
- model: params.model
154
- });
155
- const span = trace.startSpan("messages.create", "llm" /* LLM */);
156
- span.setMetadata("model", params.model);
157
- span.setMetadata("provider", "anthropic");
158
- if (params.temperature !== void 0) span.setMetadata("temperature", params.temperature);
159
- if (params.max_tokens !== void 0) span.setMetadata("max_tokens", params.max_tokens);
160
- if (params.system !== void 0) span.setMetadata("system", params.system);
161
- span.setInput({
162
- model: params.model,
163
- messages: params.messages,
164
- max_tokens: params.max_tokens,
165
- temperature: params.temperature,
166
- system: params.system
167
- });
168
- const startTime = Date.now();
169
- try {
170
- const response = await originalCreate(params, options);
171
- if (params.stream) {
172
- return wrapAnthropicStream(response, span, trace, startTime);
173
- }
174
- const messageResponse = response;
175
- const latency = Date.now() - startTime;
176
- span.setOutput({
177
- id: messageResponse.id,
178
- model: messageResponse.model,
179
- role: messageResponse.role,
180
- content: messageResponse.content,
181
- stop_reason: messageResponse.stop_reason
182
- });
183
- if (messageResponse.usage) {
184
- span.setTokens({
185
- input: messageResponse.usage.input_tokens,
186
- output: messageResponse.usage.output_tokens,
187
- total: messageResponse.usage.input_tokens + messageResponse.usage.output_tokens
188
- });
189
- }
190
- span.setMetadata("latency_ms", latency);
191
- span.end();
192
- trace.end();
193
- return response;
194
- } catch (error) {
195
- const latency = Date.now() - startTime;
196
- span.setMetadata("latency_ms", latency);
197
- span.setError(error);
198
- span.end();
199
- trace.end();
200
- throw error;
201
- }
202
- };
203
- return client;
204
- }
205
- function wrapAnthropicStream(stream, span, trace, startTime) {
206
- const bufferedChunks = [];
207
- let messageId = "";
208
- let messageModel = "";
209
- let stopReason = null;
210
- let inputTokens = 0;
211
- let outputTokens = 0;
212
- return {
213
- [Symbol.asyncIterator]: async function* () {
214
- try {
215
- for await (const event of stream) {
216
- if (event.type === "message_start" && event.message) {
217
- messageId = event.message.id;
218
- messageModel = event.message.model;
219
- if (event.message.usage) {
220
- inputTokens = event.message.usage.input_tokens;
221
- }
222
- }
223
- if (event.type === "content_block_delta" && event.delta?.text) {
224
- bufferedChunks.push(event.delta.text);
225
- }
226
- if (event.type === "message_delta" && event.delta) {
227
- if (event.delta.stop_reason) {
228
- stopReason = event.delta.stop_reason;
229
- }
230
- if (event.usage?.output_tokens) {
231
- outputTokens = event.usage.output_tokens;
232
- }
233
- }
234
- yield event;
235
- }
236
- const latency = Date.now() - startTime;
237
- const completeOutput = bufferedChunks.join("");
238
- span.setOutput({
239
- id: messageId,
240
- model: messageModel,
241
- content: completeOutput,
242
- stop_reason: stopReason
243
- });
244
- if (inputTokens > 0 || outputTokens > 0) {
245
- span.setTokens({
246
- input: inputTokens,
247
- output: outputTokens,
248
- total: inputTokens + outputTokens
249
- });
250
- }
251
- span.setMetadata("latency_ms", latency);
252
- span.setMetadata("streaming", true);
253
- span.end();
254
- trace.end();
255
- } catch (error) {
256
- const latency = Date.now() - startTime;
257
- span.setMetadata("latency_ms", latency);
258
- span.setError(error);
259
- span.end();
260
- trace.end();
261
- throw error;
262
- }
263
- }
264
- };
265
- }
266
- var init_anthropic = __esm({
267
- "src/instrumentation/anthropic.ts"() {
268
- "use strict";
269
- init_types();
270
- }
271
- });
272
-
273
- // src/instrumentation/index.ts
274
- var instrumentation_exports = {};
275
- __export(instrumentation_exports, {
276
- wrap: () => wrap,
277
- wrapAnthropic: () => wrapAnthropic,
278
- wrapOpenAI: () => wrapOpenAI
279
- });
280
- function wrap(client, nwClient) {
281
- if ("chat" in client && client.chat && "completions" in client.chat) {
282
- return wrapOpenAI(client, nwClient);
283
- }
284
- if ("messages" in client && client.messages && "create" in client.messages) {
285
- return wrapAnthropic(client, nwClient);
286
- }
287
- throw new Error(
288
- "Observyze SDK: Unsupported client type. Supported clients: OpenAI, Anthropic"
289
- );
290
- }
291
- var init_instrumentation = __esm({
292
- "src/instrumentation/index.ts"() {
293
- "use strict";
294
- init_openai();
295
- init_anthropic();
296
- init_openai();
297
- init_anthropic();
298
- }
299
- });
300
-
301
30
  // src/index.ts
302
31
  var index_exports = {};
303
32
  __export(index_exports, {
304
33
  ObservyzeClient: () => ObservyzeClient,
305
34
  ObservyzeSpanExporter: () => ObservyzeSpanExporter,
306
35
  Span: () => Span,
307
- SpanType: () => SpanType,
36
+ SpanType: () => import_types.SpanType,
308
37
  Trace: () => Trace,
309
- TraceStatus: () => TraceStatus,
38
+ TraceStatus: () => import_types.TraceStatus,
310
39
  wrap: () => wrap,
311
40
  wrapAnthropic: () => wrapAnthropic,
312
41
  wrapOpenAI: () => wrapOpenAI
313
42
  });
314
43
  module.exports = __toCommonJS(index_exports);
315
44
 
45
+ // src/client.ts
46
+ var import_debug3 = __toESM(require("debug"));
47
+
48
+ // src/types.ts
49
+ var import_types = require("@observyze/types");
50
+
316
51
  // src/trace.ts
317
- init_types();
318
52
  var import_crypto = require("crypto");
319
53
  function generateId() {
320
54
  return `${Date.now()}-${(0, import_crypto.randomUUID)().substring(0, 8)}`;
@@ -417,7 +151,7 @@ var Trace = class {
417
151
  organization_id: organizationId,
418
152
  project_id: projectId,
419
153
  name,
420
- status: "running" /* RUNNING */,
154
+ status: import_types.TraceStatus.RUNNING,
421
155
  start_time: new Date(this.startTime),
422
156
  end_time: new Date(this.startTime),
423
157
  // Will be updated on end()
@@ -485,47 +219,326 @@ var Trace = class {
485
219
  /**
486
220
  * End the trace with a final status
487
221
  */
488
- end(status = "success" /* SUCCESS */) {
222
+ end(status = import_types.TraceStatus.SUCCESS) {
489
223
  if (this.ended) {
490
224
  return;
491
225
  }
492
- const endTime = Date.now();
493
- this.data.end_time = new Date(endTime);
494
- this.data.duration_ms = endTime - this.startTime;
495
- this.data.status = status;
496
- this.data.spans = this.spans.map((span) => span.toJSON());
497
- this.ended = true;
498
- }
499
- /**
500
- * Get the trace ID
501
- */
502
- get id() {
503
- return this.data.trace_id;
226
+ const endTime = Date.now();
227
+ this.data.end_time = new Date(endTime);
228
+ this.data.duration_ms = endTime - this.startTime;
229
+ this.data.status = status;
230
+ this.data.spans = this.spans.map((span) => span.toJSON());
231
+ this.ended = true;
232
+ }
233
+ /**
234
+ * Get the trace ID
235
+ */
236
+ get id() {
237
+ return this.data.trace_id;
238
+ }
239
+ /**
240
+ * Check if the trace has ended
241
+ */
242
+ get isEnded() {
243
+ return this.ended;
244
+ }
245
+ /**
246
+ * Get the trace data for serialization
247
+ */
248
+ toJSON() {
249
+ return { ...this.data };
250
+ }
251
+ };
252
+
253
+ // src/instrumentation/openai.ts
254
+ var import_debug = __toESM(require("debug"));
255
+ var log = (0, import_debug.default)("observyze:sdk");
256
+ function wrapOpenAI(client, nwClient) {
257
+ const anyClient = client;
258
+ if (nwClient.getConfig().enableProxyRedirect && anyClient.baseURL && anyClient.apiKey) {
259
+ const isAlreadyRedirected = anyClient.baseURL.includes("/api/v1/proxy/openai");
260
+ if (!isAlreadyRedirected) {
261
+ const originalApiKey = anyClient.apiKey;
262
+ anyClient.baseURL = `${nwClient.getConfig().endpoint}/api/v1/proxy/openai/v1`;
263
+ anyClient.apiKey = nwClient.getConfig().apiKey;
264
+ anyClient.defaultHeaders = {
265
+ ...anyClient.defaultHeaders,
266
+ "x-provider-key": originalApiKey
267
+ };
268
+ if (nwClient.getConfig().debug) {
269
+ log("[Observyze SDK] Transparently redirected OpenAI client to proxy gateway:", anyClient.baseURL);
270
+ }
271
+ }
272
+ }
273
+ const originalCreate = client.chat.completions.create.bind(client.chat.completions);
274
+ client.chat.completions.create = async function(params, options) {
275
+ const isProxyRedirected = nwClient.getConfig().enableProxyRedirect && anyClient.baseURL?.includes("/api/v1/proxy/openai");
276
+ if (isProxyRedirected) {
277
+ return originalCreate(params, options);
278
+ }
279
+ const trace = nwClient.startTrace(`openai.chat.completions.create`, {
280
+ provider: "openai",
281
+ model: params.model
282
+ });
283
+ const span = trace.startSpan("chat.completions.create", import_types.SpanType.LLM);
284
+ span.setMetadata("model", params.model);
285
+ span.setMetadata("provider", "openai");
286
+ if (params.temperature !== void 0) span.setMetadata("temperature", params.temperature);
287
+ if (params.max_tokens !== void 0) span.setMetadata("max_tokens", params.max_tokens);
288
+ span.setInput({
289
+ model: params.model,
290
+ messages: params.messages,
291
+ temperature: params.temperature,
292
+ max_tokens: params.max_tokens
293
+ });
294
+ const startTime = Date.now();
295
+ try {
296
+ const response = await originalCreate(params, options);
297
+ if (params.stream) {
298
+ return wrapOpenAIStream(response, span, trace, startTime);
299
+ }
300
+ const completionResponse = response;
301
+ const latency = Date.now() - startTime;
302
+ span.setOutput({
303
+ id: completionResponse.id,
304
+ model: completionResponse.model,
305
+ choices: completionResponse.choices
306
+ });
307
+ if (completionResponse.usage) {
308
+ span.setTokens({
309
+ input: completionResponse.usage.prompt_tokens,
310
+ output: completionResponse.usage.completion_tokens,
311
+ total: completionResponse.usage.total_tokens
312
+ });
313
+ }
314
+ span.setMetadata("latency_ms", latency);
315
+ span.end();
316
+ trace.end();
317
+ return response;
318
+ } catch (error) {
319
+ const latency = Date.now() - startTime;
320
+ span.setMetadata("latency_ms", latency);
321
+ span.setError(error);
322
+ span.end();
323
+ trace.end();
324
+ throw error;
325
+ }
326
+ };
327
+ return client;
328
+ }
329
+ function wrapOpenAIStream(stream, span, trace, startTime) {
330
+ const bufferedChunks = [];
331
+ let streamId = "";
332
+ let streamModel = "";
333
+ return {
334
+ [Symbol.asyncIterator]: async function* () {
335
+ try {
336
+ for await (const chunk of stream) {
337
+ if (chunk.id) streamId = chunk.id;
338
+ if (chunk.model) streamModel = chunk.model;
339
+ const delta = chunk.choices[0]?.delta;
340
+ if (delta?.content) {
341
+ bufferedChunks.push(delta.content);
342
+ }
343
+ yield chunk;
344
+ }
345
+ const latency = Date.now() - startTime;
346
+ const completeOutput = bufferedChunks.join("");
347
+ span.setOutput({
348
+ id: streamId,
349
+ model: streamModel,
350
+ content: completeOutput
351
+ });
352
+ span.setMetadata("latency_ms", latency);
353
+ span.setMetadata("streaming", true);
354
+ span.end();
355
+ trace.end();
356
+ } catch (error) {
357
+ const latency = Date.now() - startTime;
358
+ span.setMetadata("latency_ms", latency);
359
+ span.setError(error);
360
+ span.end();
361
+ trace.end();
362
+ throw error;
363
+ }
364
+ }
365
+ };
366
+ }
367
+
368
+ // src/instrumentation/anthropic.ts
369
+ var import_debug2 = __toESM(require("debug"));
370
+ var log2 = (0, import_debug2.default)("observyze:sdk");
371
+ function wrapAnthropic(client, nwClient) {
372
+ const anyClient = client;
373
+ if (nwClient.getConfig().enableProxyRedirect && anyClient.baseURL && anyClient.apiKey) {
374
+ const isAlreadyRedirected = anyClient.baseURL.includes("/api/v1/proxy/anthropic");
375
+ if (!isAlreadyRedirected) {
376
+ const originalApiKey = anyClient.apiKey;
377
+ anyClient.baseURL = `${nwClient.getConfig().endpoint}/api/v1/proxy/anthropic/v1`;
378
+ anyClient.apiKey = nwClient.getConfig().apiKey;
379
+ anyClient.defaultHeaders = {
380
+ ...anyClient.defaultHeaders,
381
+ "x-provider-key": originalApiKey
382
+ };
383
+ if (nwClient.getConfig().debug) {
384
+ log2("[Observyze SDK] Transparently redirected Anthropic client to proxy gateway:", anyClient.baseURL);
385
+ }
386
+ }
504
387
  }
505
- /**
506
- * Check if the trace has ended
507
- */
508
- get isEnded() {
509
- return this.ended;
388
+ const originalCreate = client.messages.create.bind(client.messages);
389
+ client.messages.create = async function(params, options) {
390
+ const isProxyRedirected = nwClient.getConfig().enableProxyRedirect && anyClient.baseURL?.includes("/api/v1/proxy/anthropic");
391
+ if (isProxyRedirected) {
392
+ return originalCreate(params, options);
393
+ }
394
+ const trace = nwClient.startTrace(`anthropic.messages.create`, {
395
+ provider: "anthropic",
396
+ model: params.model
397
+ });
398
+ const span = trace.startSpan("messages.create", import_types.SpanType.LLM);
399
+ span.setMetadata("model", params.model);
400
+ span.setMetadata("provider", "anthropic");
401
+ if (params.temperature !== void 0) span.setMetadata("temperature", params.temperature);
402
+ if (params.max_tokens !== void 0) span.setMetadata("max_tokens", params.max_tokens);
403
+ if (params.system !== void 0) span.setMetadata("system", params.system);
404
+ span.setInput({
405
+ model: params.model,
406
+ messages: params.messages,
407
+ max_tokens: params.max_tokens,
408
+ temperature: params.temperature,
409
+ system: params.system
410
+ });
411
+ const startTime = Date.now();
412
+ try {
413
+ const response = await originalCreate(params, options);
414
+ if (params.stream) {
415
+ return wrapAnthropicStream(response, span, trace, startTime);
416
+ }
417
+ const messageResponse = response;
418
+ const latency = Date.now() - startTime;
419
+ span.setOutput({
420
+ id: messageResponse.id,
421
+ model: messageResponse.model,
422
+ role: messageResponse.role,
423
+ content: messageResponse.content,
424
+ stop_reason: messageResponse.stop_reason
425
+ });
426
+ if (messageResponse.usage) {
427
+ span.setTokens({
428
+ input: messageResponse.usage.input_tokens,
429
+ output: messageResponse.usage.output_tokens,
430
+ total: messageResponse.usage.input_tokens + messageResponse.usage.output_tokens
431
+ });
432
+ }
433
+ span.setMetadata("latency_ms", latency);
434
+ span.end();
435
+ trace.end();
436
+ return response;
437
+ } catch (error) {
438
+ const latency = Date.now() - startTime;
439
+ span.setMetadata("latency_ms", latency);
440
+ span.setError(error);
441
+ span.end();
442
+ trace.end();
443
+ throw error;
444
+ }
445
+ };
446
+ return client;
447
+ }
448
+ function wrapAnthropicStream(stream, span, trace, startTime) {
449
+ const bufferedChunks = [];
450
+ let messageId = "";
451
+ let messageModel = "";
452
+ let stopReason = null;
453
+ let inputTokens = 0;
454
+ let outputTokens = 0;
455
+ return {
456
+ [Symbol.asyncIterator]: async function* () {
457
+ try {
458
+ for await (const event of stream) {
459
+ if (event.type === "message_start" && event.message) {
460
+ messageId = event.message.id;
461
+ messageModel = event.message.model;
462
+ if (event.message.usage) {
463
+ inputTokens = event.message.usage.input_tokens;
464
+ }
465
+ }
466
+ if (event.type === "content_block_delta" && event.delta?.text) {
467
+ bufferedChunks.push(event.delta.text);
468
+ }
469
+ if (event.type === "message_delta" && event.delta) {
470
+ if (event.delta.stop_reason) {
471
+ stopReason = event.delta.stop_reason;
472
+ }
473
+ if (event.usage?.output_tokens) {
474
+ outputTokens = event.usage.output_tokens;
475
+ }
476
+ }
477
+ yield event;
478
+ }
479
+ const latency = Date.now() - startTime;
480
+ const completeOutput = bufferedChunks.join("");
481
+ span.setOutput({
482
+ id: messageId,
483
+ model: messageModel,
484
+ content: completeOutput,
485
+ stop_reason: stopReason
486
+ });
487
+ if (inputTokens > 0 || outputTokens > 0) {
488
+ span.setTokens({
489
+ input: inputTokens,
490
+ output: outputTokens,
491
+ total: inputTokens + outputTokens
492
+ });
493
+ }
494
+ span.setMetadata("latency_ms", latency);
495
+ span.setMetadata("streaming", true);
496
+ span.end();
497
+ trace.end();
498
+ } catch (error) {
499
+ const latency = Date.now() - startTime;
500
+ span.setMetadata("latency_ms", latency);
501
+ span.setError(error);
502
+ span.end();
503
+ trace.end();
504
+ throw error;
505
+ }
506
+ }
507
+ };
508
+ }
509
+
510
+ // src/instrumentation/index.ts
511
+ function wrap(client, nwClient) {
512
+ if ("chat" in client && client.chat && "completions" in client.chat) {
513
+ return wrapOpenAI(client, nwClient);
510
514
  }
511
- /**
512
- * Get the trace data for serialization
513
- */
514
- toJSON() {
515
- return { ...this.data };
515
+ if ("messages" in client && client.messages && "create" in client.messages) {
516
+ return wrapAnthropic(client, nwClient);
516
517
  }
517
- };
518
+ throw new Error(
519
+ "Observyze SDK: Unsupported client type. Supported clients: OpenAI, Anthropic"
520
+ );
521
+ }
518
522
 
519
523
  // src/client.ts
520
- init_types();
524
+ var import_fs = __toESM(require("fs"));
525
+ var import_path = __toESM(require("path"));
526
+ var log3 = (0, import_debug3.default)("observyze:sdk");
521
527
  var DEFAULT_CONFIG = {
522
- endpoint: "https://api.observyze.com",
528
+ endpoint: "http://localhost:3001",
523
529
  batchSize: 100,
524
530
  flushInterval: 5e3,
525
531
  enableAutoInstrumentation: true,
526
532
  debug: false,
527
533
  dryRun: false,
528
- enablePiiRedaction: true
534
+ enablePiiRedaction: true,
535
+ hallucinationThreshold: 0.8,
536
+ safetyThreshold: 0.9,
537
+ confidenceThreshold: 0.4,
538
+ evalEndpoint: process.env.EVAL_ENDPOINT || (process.env.NODE_ENV === "production" ? "https://api.observyze.com" : "http://localhost:3001"),
539
+ enableCircuitBreaker: true,
540
+ failClosed: true,
541
+ enableProxyRedirect: true
529
542
  };
530
543
  var ObservyzeClient = class _ObservyzeClient {
531
544
  config;
@@ -534,7 +547,37 @@ var ObservyzeClient = class _ObservyzeClient {
534
547
  isShuttingDown = false;
535
548
  MAX_QUEUE_SIZE = 1e3;
536
549
  RETRY_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
537
- // ms: 1s → 2s → 4s → 8s → 16s → 30s
550
+ /**
551
+ * Parse a JSON API error response and extract trace_id, error code, and message.
552
+ * The api-gateway error handler includes these fields in every error response.
553
+ */
554
+ static parseApiError(_response, body) {
555
+ try {
556
+ const parsed = JSON.parse(body);
557
+ const error = parsed.error || parsed;
558
+ return {
559
+ traceId: error.trace_id || "unknown",
560
+ code: error.code || "UNKNOWN_ERROR",
561
+ message: error.message || body.slice(0, 200)
562
+ };
563
+ } catch {
564
+ return {
565
+ traceId: "unknown",
566
+ code: "UNKNOWN_ERROR",
567
+ message: body.slice(0, 200)
568
+ };
569
+ }
570
+ }
571
+ /**
572
+ * Format an API error into a user-friendly message with trace_id for correlation.
573
+ * Example output:
574
+ * "Observyze API error (401 [ref: err_a1b2c3d4]): MISSING_PROVIDER_KEY — No API key configured..."
575
+ */
576
+ static formatApiError(response, body) {
577
+ const { traceId, code, message } = _ObservyzeClient.parseApiError(response, body);
578
+ const prefix = traceId !== "unknown" ? ` [ref: ${traceId}]` : "";
579
+ return `Observyze API error (${response.status}${prefix}): ${code} \u2014 ${message}`;
580
+ }
538
581
  constructor(config) {
539
582
  if (!config.apiKey) {
540
583
  throw new Error("Observyze SDK: apiKey is required");
@@ -547,7 +590,7 @@ var ObservyzeClient = class _ObservyzeClient {
547
590
  };
548
591
  this.startFlushTimer();
549
592
  if (this.config.debug) {
550
- console.log("[Observyze SDK] Initialized with config:", {
593
+ log3("[Observyze SDK] Initialized with config:", {
551
594
  endpoint: this.config.endpoint,
552
595
  batchSize: this.config.batchSize,
553
596
  flushInterval: this.config.flushInterval,
@@ -568,7 +611,7 @@ var ObservyzeClient = class _ObservyzeClient {
568
611
  trace.setMetadataAll(metadata);
569
612
  }
570
613
  const originalEnd = trace.end.bind(trace);
571
- trace.end = (status = "success" /* SUCCESS */) => {
614
+ trace.end = (status = import_types.TraceStatus.SUCCESS) => {
572
615
  originalEnd(status);
573
616
  this.bufferTrace(trace);
574
617
  };
@@ -580,23 +623,23 @@ var ObservyzeClient = class _ObservyzeClient {
580
623
  bufferTrace(trace) {
581
624
  if (!trace.isEnded) {
582
625
  if (this.config.debug) {
583
- console.warn("[Observyze SDK] Attempted to buffer a trace that has not ended");
626
+ log3.extend("warn")("[Observyze SDK] Attempted to buffer a trace that has not ended");
584
627
  }
585
628
  return;
586
629
  }
587
630
  if (this.traceBuffer.length >= this.MAX_QUEUE_SIZE) {
588
631
  if (this.config.debug) {
589
- console.warn(`[Observyze SDK] Queue at max capacity (${this.MAX_QUEUE_SIZE}), dropping oldest trace`);
632
+ log3.extend("warn")(`[Observyze SDK] Queue at max capacity (${this.MAX_QUEUE_SIZE}), dropping oldest trace`);
590
633
  }
591
634
  this.traceBuffer.shift();
592
635
  }
593
636
  this.traceBuffer.push(trace);
594
637
  if (this.config.debug) {
595
- console.log(`[Observyze SDK] Buffered trace ${trace.id} (${this.traceBuffer.length}/${this.config.batchSize})`);
638
+ log3(`[Observyze SDK] Buffered trace ${trace.id} (${this.traceBuffer.length}/${this.config.batchSize})`);
596
639
  }
597
640
  if (this.traceBuffer.length >= this.config.batchSize) {
598
641
  this.flush().catch((err) => {
599
- console.error("[Observyze SDK] Error flushing buffer:", err);
642
+ log3.extend("error")("[Observyze SDK] Error flushing buffer:", err);
600
643
  });
601
644
  }
602
645
  }
@@ -610,7 +653,7 @@ var ObservyzeClient = class _ObservyzeClient {
610
653
  this.flushTimer = setInterval(() => {
611
654
  if (this.traceBuffer.length > 0) {
612
655
  this.flush().catch((err) => {
613
- console.error("[Observyze SDK] Error in auto-flush:", err);
656
+ log3.extend("error")("[Observyze SDK] Error in auto-flush:", err);
614
657
  });
615
658
  }
616
659
  }, this.config.flushInterval);
@@ -627,11 +670,11 @@ var ObservyzeClient = class _ObservyzeClient {
627
670
  }
628
671
  const tracesToSend = this.traceBuffer.splice(0, this.config.batchSize);
629
672
  if (this.config.debug) {
630
- console.log(`[Observyze SDK] Flushing ${tracesToSend.length} traces`);
673
+ log3(`[Observyze SDK] Flushing ${tracesToSend.length} traces`);
631
674
  }
632
675
  if (this.config.dryRun) {
633
676
  if (this.config.debug) {
634
- console.log("[Observyze SDK] Dry-run mode: traces not sent");
677
+ log3("[Observyze SDK] Dry-run mode: traces not sent");
635
678
  }
636
679
  return;
637
680
  }
@@ -642,15 +685,15 @@ var ObservyzeClient = class _ObservyzeClient {
642
685
  if (remainingSpace > 0) {
643
686
  this.traceBuffer.unshift(...tracesToSend.slice(0, remainingSpace));
644
687
  if (this.config.debug) {
645
- console.log(`[Observyze SDK] Re-queued ${Math.min(tracesToSend.length, remainingSpace)} traces after failure`);
688
+ log3(`[Observyze SDK] Re-queued ${Math.min(tracesToSend.length, remainingSpace)} traces after failure`);
646
689
  }
647
690
  } else {
648
691
  if (this.config.debug) {
649
- console.warn(`[Observyze SDK] Queue full, dropped ${tracesToSend.length} traces`);
692
+ log3.extend("warn")(`[Observyze SDK] Queue full, dropped ${tracesToSend.length} traces`);
650
693
  }
651
694
  }
652
695
  if (this.config.debug) {
653
- console.error("[Observyze SDK] Failed to send traces after retries:", error);
696
+ log3.extend("error")("[Observyze SDK] Failed to send traces after retries:", error);
654
697
  }
655
698
  throw error;
656
699
  }
@@ -680,10 +723,11 @@ var ObservyzeClient = class _ObservyzeClient {
680
723
  });
681
724
  if (!response.ok) {
682
725
  const errorBody = await response.text();
683
- throw new Error(`Ingestion failed: ${response.status} ${errorBody}`);
726
+ const formatted = _ObservyzeClient.formatApiError(response, errorBody);
727
+ throw new Error(`[Observyze SDK] Trace ingestion failed. ${formatted}`);
684
728
  }
685
729
  if (this.config.debug) {
686
- console.log(`[Observyze SDK] Successfully sent ${traces.length} traces${attempt > 0 ? ` (after ${attempt} retries)` : ""}`);
730
+ log3(`[Observyze SDK] Successfully sent ${traces.length} traces${attempt > 0 ? ` (after ${attempt} retries)` : ""}`);
687
731
  }
688
732
  return;
689
733
  } catch (error) {
@@ -693,7 +737,7 @@ var ObservyzeClient = class _ObservyzeClient {
693
737
  }
694
738
  const delay = this.RETRY_DELAYS[attempt];
695
739
  if (this.config.debug) {
696
- console.warn(`[Observyze SDK] Attempt ${attempt + 1} failed, retrying in ${delay}ms...`, error);
740
+ log3.extend("warn")(`[Observyze SDK] Attempt ${attempt + 1} failed, retrying in ${delay}ms...`, error);
697
741
  }
698
742
  await new Promise((resolve) => setTimeout(resolve, delay));
699
743
  }
@@ -709,7 +753,7 @@ var ObservyzeClient = class _ObservyzeClient {
709
753
  }
710
754
  this.isShuttingDown = true;
711
755
  if (this.config.debug) {
712
- console.log("[Observyze SDK] Shutting down...");
756
+ log3("[Observyze SDK] Shutting down...");
713
757
  }
714
758
  if (this.flushTimer) {
715
759
  clearInterval(this.flushTimer);
@@ -718,10 +762,10 @@ var ObservyzeClient = class _ObservyzeClient {
718
762
  try {
719
763
  await this.flush();
720
764
  } catch (error) {
721
- console.error("[Observyze SDK] Error during shutdown flush:", error);
765
+ log3.extend("error")("[Observyze SDK] Error during shutdown flush:", error);
722
766
  }
723
767
  if (this.config.debug) {
724
- console.log("[Observyze SDK] Shutdown complete");
768
+ log3("[Observyze SDK] Shutdown complete");
725
769
  }
726
770
  }
727
771
  /**
@@ -758,8 +802,70 @@ var ObservyzeClient = class _ObservyzeClient {
758
802
  * ```
759
803
  */
760
804
  wrap(client) {
761
- const { wrap: wrapClient } = (init_instrumentation(), __toCommonJS(instrumentation_exports));
762
- return wrapClient(client, this);
805
+ return wrap(client, this);
806
+ }
807
+ /**
808
+ * Verify that the SDK can reach Observyze and send traces end-to-end.
809
+ *
810
+ * This is the definitive "is my integration working?" test for SDK users.
811
+ * It sends a real test trace through the EXACT same pipeline used by
812
+ * `flush()` / the wrapped LLM clients (same endpoint, apiKey, retry logic),
813
+ * so a successful call proves the whole chain works from your code:
814
+ * - apiKey is valid and authorized
815
+ * - endpoint is reachable from your environment
816
+ * - organization / project resolution works
817
+ * - the ingest pipeline accepts and stores traces
818
+ *
819
+ * @example
820
+ * ```typescript
821
+ * const nw = new ObservyzeClient({ apiKey: process.env.OBSERVYZE_API_KEY })
822
+ * const result = await nw.testConnection()
823
+ * // { ok: true, traceId: 'nw_xxx', message: 'Connection successful...' }
824
+ * ```
825
+ *
826
+ * The returned traceId can be searched in the Observyze dashboard (Traces →
827
+ * search the trace name "Observyze Connection Test") to confirm it landed.
828
+ */
829
+ async testConnection() {
830
+ if (this.config.dryRun) {
831
+ return {
832
+ ok: false,
833
+ message: "Dry-run mode is enabled, so no trace was actually sent. Set dryRun: false to run a real connection test."
834
+ };
835
+ }
836
+ const trace = new Trace(
837
+ "Observyze Connection Test",
838
+ this.config.organizationId,
839
+ this.config.projectId
840
+ );
841
+ const span = trace.startSpan("connection-test", import_types.SpanType.LLM);
842
+ span.setInput({ prompt: "Observyze SDK connection test" });
843
+ span.setOutput({ response: "Connection successful" });
844
+ span.setTokens({ input: 5, output: 4, total: 9 });
845
+ span.setMetadata("source", "sdk-test-connection");
846
+ span.end();
847
+ trace.setMetadata("source", "sdk-test-connection");
848
+ trace.addTag("setup-test");
849
+ trace.end(import_types.TraceStatus.SUCCESS);
850
+ try {
851
+ await this.sendWithRetry([trace]);
852
+ return {
853
+ ok: true,
854
+ traceId: trace.id,
855
+ message: `Connection successful. Test trace ${trace.id} was sent to Observyze. Search for "Observyze Connection Test" in Dashboard \u2192 Traces to confirm it landed.`
856
+ };
857
+ } catch (error) {
858
+ const rawMessage = error?.message || String(error);
859
+ const statusMatch = rawMessage.match(/\((\d{3})/);
860
+ const isNetworkFailure = !statusMatch && /fetch|network|ENOTFOUND|ECONNREFUSED|ETIMEDOUT/i.test(rawMessage);
861
+ const defaultEndpoint = "http://localhost:3001";
862
+ const hint = isNetworkFailure && this.config.endpoint === defaultEndpoint ? ` You are using the default endpoint (${defaultEndpoint}). For production, set endpoint: "https://api.observyze.com" in the ObservyzeClient config, then re-run.` : isNetworkFailure ? " Check that your endpoint is reachable from this environment (firewalls, proxies, DNS) and that you are not using a local endpoint in production." : "";
863
+ return {
864
+ ok: false,
865
+ ...statusMatch ? { status: parseInt(statusMatch[1], 10) } : {},
866
+ message: rawMessage + hint
867
+ };
868
+ }
763
869
  }
764
870
  /**
765
871
  * Sync local agent .history file to Observyze cloud
@@ -770,13 +876,11 @@ var ObservyzeClient = class _ObservyzeClient {
770
876
  if (typeof process === "undefined" || !process.versions?.node) {
771
877
  throw new Error("syncLocalHistory is only available in Node.js environments");
772
878
  }
773
- const fs = require("fs");
774
- const path = require("path");
775
- const fullPath = path.resolve(process.cwd(), filePath);
776
- if (!fs.existsSync(fullPath)) {
879
+ const fullPath = import_path.default.resolve(process.cwd(), filePath);
880
+ if (!import_fs.default.existsSync(fullPath)) {
777
881
  throw new Error(`History file not found: ${fullPath}`);
778
882
  }
779
- const content = fs.readFileSync(fullPath, "utf-8");
883
+ const content = import_fs.default.readFileSync(fullPath, "utf-8");
780
884
  let items = [];
781
885
  try {
782
886
  items = JSON.parse(content);
@@ -787,7 +891,7 @@ var ObservyzeClient = class _ObservyzeClient {
787
891
  items = [items];
788
892
  }
789
893
  if (this.config.debug) {
790
- console.log(`[Observyze SDK] Syncing ${items.length} traces from ${filePath}`);
894
+ log3(`[Observyze SDK] Syncing ${items.length} traces from ${filePath}`);
791
895
  }
792
896
  for (let i = 0; i < items.length; i += this.config.batchSize) {
793
897
  const batch = items.slice(i, i + this.config.batchSize);
@@ -801,14 +905,15 @@ var ObservyzeClient = class _ObservyzeClient {
801
905
  });
802
906
  if (!response.ok) {
803
907
  const errorBody = await response.text();
804
- throw new Error(`Batch sync failed: ${response.status} ${errorBody}`);
908
+ const formatted = _ObservyzeClient.formatApiError(response, errorBody);
909
+ throw new Error(`[Observyze SDK] Local history sync failed. ${formatted}`);
805
910
  }
806
911
  if (this.config.debug) {
807
- console.log(`[Observyze SDK] Synced batch of ${batch.length} traces from local history`);
912
+ log3(`[Observyze SDK] Synced batch of ${batch.length} traces from local history`);
808
913
  }
809
914
  }
810
915
  } catch (err) {
811
- console.error("[Observyze SDK] Failed to sync local history:", err);
916
+ log3.extend("error")("[Observyze SDK] Failed to sync local history:", err);
812
917
  throw err;
813
918
  }
814
919
  }
@@ -905,14 +1010,225 @@ var ObservyzeClient = class _ObservyzeClient {
905
1010
  }
906
1011
  return data;
907
1012
  }
1013
+ /**
1014
+ * Phase 4: Autonomous Circuit Breakers (Requirement 4.1)
1015
+ * Evaluate a trace or text for hallucination in real-time.
1016
+ * If hallucination score > hallucinationThreshold, the SDK blocks execution.
1017
+ *
1018
+ * Returns GuardrailResult with score: null when evaluation couldn't be performed.
1019
+ * In failClosed mode, null scores result in blocked execution.
1020
+ * In failOpen mode, null scores allow execution through.
1021
+ */
1022
+ async checkGuardrails(content) {
1023
+ if (!this.config.enableCircuitBreaker) {
1024
+ return { pass: true, score: 0, safetyScore: 0, evaluationSource: "disabled" };
1025
+ }
1026
+ try {
1027
+ if (this.config.debug) {
1028
+ log3(`[Observyze Guardrail] Analyzing payload for hallucination anomalies...`);
1029
+ }
1030
+ const evalEndpoint = this.config.evalEndpoint;
1031
+ const payload = typeof content === "string" ? { text: content, organization_id: this.config.organizationId } : { trace: content, organization_id: this.config.organizationId };
1032
+ const controller = new AbortController();
1033
+ const timeout = setTimeout(() => controller.abort(), 5e3);
1034
+ let evalResult = null;
1035
+ try {
1036
+ const response = await fetch(`${evalEndpoint}/api/v1/evaluate/hallucination`, {
1037
+ method: "POST",
1038
+ headers: {
1039
+ "Content-Type": "application/json",
1040
+ "Authorization": `Bearer ${this.config.apiKey}`
1041
+ },
1042
+ body: JSON.stringify(payload),
1043
+ signal: controller.signal
1044
+ });
1045
+ clearTimeout(timeout);
1046
+ if (response.ok) {
1047
+ evalResult = await response.json();
1048
+ } else {
1049
+ const errorBody = await response.text();
1050
+ const { traceId, code, message } = _ObservyzeClient.parseApiError(response, errorBody);
1051
+ if (this.config.debug) {
1052
+ log3.extend("warn")(`[Observyze Guardrail] Eval returned ${response.status} [${code}] (ref: ${traceId}): ${message}`);
1053
+ }
1054
+ }
1055
+ } catch (fetchError) {
1056
+ clearTimeout(timeout);
1057
+ if (fetchError.name === "AbortError") {
1058
+ if (this.config.debug) {
1059
+ log3.extend("warn")("[Observyze Guardrail] Evaluation timed out after 5s");
1060
+ }
1061
+ } else if (this.config.debug) {
1062
+ log3.extend("warn")("[Observyze Guardrail] Evaluation request failed:", fetchError.message);
1063
+ }
1064
+ }
1065
+ let hallucinationScore = 0;
1066
+ let safetyScore = 0;
1067
+ let evaluationSource = "live";
1068
+ let confidence = null;
1069
+ if (evalResult) {
1070
+ hallucinationScore = evalResult.score ?? evalResult.hallucination_score ?? null;
1071
+ safetyScore = evalResult.safety_score ?? 0;
1072
+ evaluationSource = evalResult.evaluation_source ?? "live";
1073
+ confidence = evalResult.confidence ?? null;
1074
+ if (hallucinationScore === null) {
1075
+ if (this.config.failClosed) {
1076
+ if (this.config.debug) {
1077
+ log3.extend("warn")("[Observyze Guardrail] Eval returned null score \u2014 failing closed (blocking)");
1078
+ }
1079
+ return {
1080
+ pass: false,
1081
+ score: null,
1082
+ confidence: null,
1083
+ safetyScore: null,
1084
+ evaluationSource: "error",
1085
+ fallbackReason: evalResult.message || "Evaluation failed to produce a score",
1086
+ reason: "Evaluation service failed to produce a score. Fail-closed: execution blocked."
1087
+ };
1088
+ }
1089
+ if (this.config.debug) {
1090
+ log3("[Observyze Guardrail] Eval returned null score \u2014 allowing (fail-open)");
1091
+ }
1092
+ return {
1093
+ pass: true,
1094
+ score: null,
1095
+ confidence: null,
1096
+ safetyScore: null,
1097
+ evaluationSource: "error",
1098
+ fallbackReason: evalResult.message || "Evaluation failed to produce a score"
1099
+ };
1100
+ }
1101
+ } else if (this.config.failClosed) {
1102
+ if (this.config.debug) {
1103
+ log3.extend("warn")("[Observyze Guardrail] Eval unavailable \u2014 failing closed (blocking)");
1104
+ }
1105
+ return {
1106
+ pass: false,
1107
+ score: null,
1108
+ confidence: null,
1109
+ safetyScore: null,
1110
+ evaluationSource: "error",
1111
+ fallbackReason: "Evaluation service unreachable",
1112
+ reason: "Evaluation service unreachable. Fail-closed: execution blocked."
1113
+ };
1114
+ } else {
1115
+ if (this.config.debug) {
1116
+ log3("[Observyze Guardrail] Eval unavailable \u2014 allowing (fail-open)");
1117
+ }
1118
+ return {
1119
+ pass: true,
1120
+ score: null,
1121
+ confidence: null,
1122
+ safetyScore: null,
1123
+ evaluationSource: "error",
1124
+ fallbackReason: "Evaluation service unreachable"
1125
+ };
1126
+ }
1127
+ const hallThreshold = this.config.hallucinationThreshold;
1128
+ const safeThreshold = this.config.safetyThreshold;
1129
+ const confThreshold = this.config.confidenceThreshold;
1130
+ if (confidence !== null && confidence < confThreshold) {
1131
+ if (hallucinationScore >= hallThreshold) {
1132
+ if (this.config.debug) {
1133
+ log3.extend("warn")(`[Observyze Guardrail] High score (${hallucinationScore.toFixed(2)}) but low confidence (${confidence.toFixed(2)}). Alerting only.`);
1134
+ }
1135
+ return {
1136
+ pass: true,
1137
+ score: hallucinationScore,
1138
+ confidence,
1139
+ safetyScore,
1140
+ evaluationSource,
1141
+ reason: `Score ${hallucinationScore.toFixed(2)} but confidence ${confidence.toFixed(2)} is low. Execution allowed with alert.`
1142
+ };
1143
+ }
1144
+ }
1145
+ if (hallucinationScore >= hallThreshold) {
1146
+ if (this.config.debug) {
1147
+ log3.extend("warn")(`[Observyze Guardrail] Hallucination circuit breached! Score: ${hallucinationScore.toFixed(2)} >= ${hallThreshold}`);
1148
+ }
1149
+ return {
1150
+ pass: false,
1151
+ score: hallucinationScore,
1152
+ confidence,
1153
+ safetyScore,
1154
+ evaluationSource,
1155
+ reason: `Hallucination score ${hallucinationScore.toFixed(2)} exceeds threshold ${hallThreshold}. Execution blocked for human review.`
1156
+ };
1157
+ }
1158
+ if (safetyScore !== null && safetyScore >= safeThreshold) {
1159
+ if (this.config.debug) {
1160
+ log3.extend("warn")(`[Observyze Guardrail] Safety circuit breached! Score: ${safetyScore.toFixed(2)} >= ${safeThreshold}`);
1161
+ }
1162
+ return {
1163
+ pass: false,
1164
+ score: hallucinationScore,
1165
+ confidence,
1166
+ safetyScore,
1167
+ evaluationSource,
1168
+ reason: `Safety score ${safetyScore.toFixed(2)} exceeds threshold ${safeThreshold}. Execution blocked for safety review.`
1169
+ };
1170
+ }
1171
+ return { pass: true, score: hallucinationScore, confidence, safetyScore, evaluationSource };
1172
+ } catch (err) {
1173
+ log3.extend("error")("[Observyze Guardrail] Failed to evaluate:", err);
1174
+ if (this.config.failClosed) {
1175
+ return { pass: false, score: null, confidence: null, safetyScore: null, evaluationSource: "error", fallbackReason: "Guardrail exception", reason: "Guardrail error \u2014 fail-closed: execution blocked." };
1176
+ }
1177
+ return { pass: true, score: null, confidence: null, safetyScore: null, evaluationSource: "error", fallbackReason: "Guardrail exception" };
1178
+ }
1179
+ }
1180
+ /**
1181
+ * Phase 4: Autonomous Circuit Breakers
1182
+ * Execute an agent action wrapped with the Circuit Breaker.
1183
+ * Pauses execution if hallucination score >= hallucinationThreshold and requests human review.
1184
+ * @throws Error when execution is blocked by circuit breaker
1185
+ */
1186
+ async executeWithCircuitBreaker(agentExecution, traceContext) {
1187
+ if (!this.config.enableCircuitBreaker) {
1188
+ return await agentExecution();
1189
+ }
1190
+ const guardResult = await this.checkGuardrails(traceContext || "execution context");
1191
+ if (!guardResult.pass) {
1192
+ const error = new Error(`[Observyze] Execution Blocked by Autonomous Circuit Breaker. Hallucination: ${guardResult.score?.toFixed(2) ?? "N/A"}, Safety: ${(guardResult.safetyScore ?? 0)?.toFixed(2) ?? "N/A"}. Reason: ${guardResult.reason}. Human approval required before agent can continue.`);
1193
+ if (this.config.debug) {
1194
+ log3.extend("error")("[Observyze CircuitBreaker] Execution blocked:", error.message);
1195
+ }
1196
+ throw error;
1197
+ }
1198
+ if (this.config.debug) {
1199
+ log3(`[Observyze CircuitBreaker] Execution allowed. Hallucination: ${guardResult.score?.toFixed(2) ?? "N/A"}, Safety: ${(guardResult.safetyScore ?? 0)?.toFixed(2) ?? "N/A"}`);
1200
+ }
1201
+ return await agentExecution();
1202
+ }
1203
+ /**
1204
+ * Phase 4: Bug Bounty Protocol (Automated) (Requirement 4.2)
1205
+ * Automatically shard persistent failure cases to external security researcher endpoints (e.g. HackerOne wrapper)
1206
+ */
1207
+ async reportBugBounty(traceId, securityEndpoint, failureContext) {
1208
+ try {
1209
+ if (this.config.debug) {
1210
+ log3(`[Observyze SDK] Sharding persistent failure case ${traceId} to Bug Bounty Protocol endpoint...`);
1211
+ }
1212
+ await fetch(securityEndpoint, {
1213
+ method: "POST",
1214
+ headers: { "Content-Type": "application/json" },
1215
+ body: JSON.stringify({
1216
+ alert: "persistent_failure_sharded",
1217
+ trace_id: traceId,
1218
+ context: failureContext,
1219
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1220
+ })
1221
+ });
1222
+ if (this.config.debug) {
1223
+ log3(`[Observyze SDK] Bug Bounty payload successfully transmitted.`);
1224
+ }
1225
+ } catch (err) {
1226
+ log3.extend("error")("[Observyze Bug Bounty] Failed to shard failure case:", err);
1227
+ }
1228
+ }
908
1229
  };
909
1230
 
910
- // src/index.ts
911
- init_types();
912
- init_instrumentation();
913
-
914
1231
  // src/opentelemetry/exporter.ts
915
- init_types();
916
1232
  var ObservyzeSpanExporter = class {
917
1233
  client;
918
1234
  config;
@@ -982,9 +1298,9 @@ var ObservyzeSpanExporter = class {
982
1298
  const statusCode = status?.code;
983
1299
  if (statusCode === 2) {
984
1300
  oSpan.setError(new Error(status?.message || "OTel span error"));
985
- trace.end("error" /* ERROR */);
1301
+ trace.end(import_types.TraceStatus.ERROR);
986
1302
  } else {
987
- trace.end("success" /* SUCCESS */);
1303
+ trace.end(import_types.TraceStatus.SUCCESS);
988
1304
  }
989
1305
  }
990
1306
  resultCallback({ code: 0 });