@crevanta/stelvara-sdk 0.1.0-alpha.1 → 0.1.0-alpha.2

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/auto.cjs CHANGED
@@ -360,6 +360,20 @@ function isInitialized() {
360
360
  // src/auto.ts
361
361
  var ANTHROPIC_PATTERN = "api.anthropic.com/v1/messages";
362
362
  var OPENAI_PATTERN = "api.openai.com/v1/chat/completions";
363
+ var OPENAI_COMPAT_PATTERNS = [
364
+ { pattern: "api.groq.com/", providerName: "groq" },
365
+ { pattern: "api.together.xyz/", providerName: "together" },
366
+ { pattern: "api.mistral.ai/", providerName: "mistral" },
367
+ { pattern: "api.fireworks.ai/", providerName: "fireworks" },
368
+ { pattern: "api.perplexity.ai/", providerName: "perplexity" },
369
+ { pattern: "api.deepseek.com/", providerName: "deepseek" },
370
+ { pattern: "openrouter.ai/api/", providerName: "openrouter" },
371
+ // Azure OpenAI: https://{resource}.openai.azure.com/openai/deployments/{model}/chat/completions
372
+ { pattern: "openai.azure.com/openai/deployments/", providerName: "azure_openai" },
373
+ // Local Ollama (OpenAI-compatible mode)
374
+ { pattern: "localhost:11434/", providerName: "ollama" },
375
+ { pattern: "127.0.0.1:11434/", providerName: "ollama" }
376
+ ];
363
377
  var _originalFetch = null;
364
378
  var _installed = false;
365
379
  var _sessionId = null;
@@ -375,8 +389,11 @@ function readEnvVar(name) {
375
389
  return void 0;
376
390
  }
377
391
  function detectProvider(url) {
378
- if (url.includes(ANTHROPIC_PATTERN)) return "anthropic";
379
- if (url.includes(OPENAI_PATTERN)) return "openai";
392
+ if (url.includes(ANTHROPIC_PATTERN)) return { format: "anthropic", name: "anthropic" };
393
+ if (url.includes(OPENAI_PATTERN)) return { format: "openai", name: "openai" };
394
+ for (const { pattern, providerName } of OPENAI_COMPAT_PATTERNS) {
395
+ if (url.includes(pattern)) return { format: "openai", name: providerName };
396
+ }
380
397
  return null;
381
398
  }
382
399
  function resolveUrl(input) {
@@ -407,7 +424,7 @@ function extractLastUserMessage(messages) {
407
424
  }
408
425
  return "";
409
426
  }
410
- function extractAnthropicTrace(reqBody, resBody, durationMs) {
427
+ function extractAnthropicTrace(reqBody, resBody, durationMs, ttfbMs, providerName = "anthropic") {
411
428
  const messages = reqBody.messages ?? [];
412
429
  const userMsg = extractLastUserMessage(messages);
413
430
  const systemMsg = typeof reqBody.system === "string" ? reqBody.system : "";
@@ -415,9 +432,9 @@ function extractAnthropicTrace(reqBody, resBody, durationMs) {
415
432
  input: { user_message: userMsg },
416
433
  model: {
417
434
  name: resBody.model ?? reqBody.model ?? "unknown",
418
- provider: "anthropic"
435
+ provider: providerName
419
436
  },
420
- performance: { duration_ms: durationMs }
437
+ performance: { duration_ms: durationMs, ttfb_ms: ttfbMs }
421
438
  };
422
439
  const prompt = {};
423
440
  if (systemMsg) prompt.system = systemMsg;
@@ -448,13 +465,13 @@ function extractAnthropicTrace(reqBody, resBody, durationMs) {
448
465
  if (total > 0) {
449
466
  payload.response = { ...payload.response, tokens_used: total };
450
467
  }
451
- payload.performance = { duration_ms: durationMs };
468
+ payload.performance = { ...payload.performance, duration_ms: durationMs };
452
469
  if (inputTokens) payload.performance.tokens_input = inputTokens;
453
470
  if (outputTokens) payload.performance.tokens_output = outputTokens;
454
471
  }
455
472
  return payload;
456
473
  }
457
- function extractOpenAITrace(reqBody, resBody, durationMs) {
474
+ function extractOpenAITrace(reqBody, resBody, durationMs, ttfbMs, providerName = "openai") {
458
475
  const messages = reqBody.messages ?? [];
459
476
  let systemMsg = "";
460
477
  let userMsg = "";
@@ -468,9 +485,9 @@ function extractOpenAITrace(reqBody, resBody, durationMs) {
468
485
  input: { user_message: userMsg },
469
486
  model: {
470
487
  name: resBody.model ?? reqBody.model ?? "unknown",
471
- provider: "openai"
488
+ provider: providerName
472
489
  },
473
- performance: { duration_ms: durationMs }
490
+ performance: { duration_ms: durationMs, ttfb_ms: ttfbMs }
474
491
  };
475
492
  const prompt = {};
476
493
  if (systemMsg) prompt.system = systemMsg;
@@ -512,7 +529,7 @@ function extractOpenAITrace(reqBody, resBody, durationMs) {
512
529
  if (total > 0) {
513
530
  payload.response = { ...payload.response, tokens_used: total };
514
531
  }
515
- payload.performance = { duration_ms: durationMs };
532
+ payload.performance = { ...payload.performance, duration_ms: durationMs };
516
533
  if (promptTokens) payload.performance.tokens_input = promptTokens;
517
534
  if (completionTokens) payload.performance.tokens_output = completionTokens;
518
535
  }
@@ -528,7 +545,8 @@ function makeAnthropicState() {
528
545
  toolCalls: [],
529
546
  currentToolName: "",
530
547
  currentToolArgs: "",
531
- done: false
548
+ done: false,
549
+ firstTokenAt: null
532
550
  };
533
551
  }
534
552
  function makeOpenAIState() {
@@ -537,7 +555,8 @@ function makeOpenAIState() {
537
555
  textContent: "",
538
556
  toolCallMap: /* @__PURE__ */ new Map(),
539
557
  finishReason: "",
540
- done: false
558
+ done: false,
559
+ firstTokenAt: null
541
560
  };
542
561
  }
543
562
  function processAnthropicSSE(line, state) {
@@ -566,6 +585,7 @@ function processAnthropicSSE(line, state) {
566
585
  case "content_block_delta": {
567
586
  const delta = event.delta;
568
587
  if (delta?.type === "text_delta" && typeof delta.text === "string") {
588
+ if (state.firstTokenAt === null) state.firstTokenAt = Date.now();
569
589
  state.textContent += delta.text;
570
590
  }
571
591
  if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
@@ -621,6 +641,8 @@ function processOpenAISSE(line, state) {
621
641
  const delta = choice.delta;
622
642
  if (delta) {
623
643
  if (typeof delta.content === "string") {
644
+ if (state.firstTokenAt === null && delta.content.length > 0)
645
+ state.firstTokenAt = Date.now();
624
646
  state.textContent += delta.content;
625
647
  }
626
648
  if (typeof choice.finish_reason === "string") {
@@ -648,14 +670,14 @@ function processOpenAISSE(line, state) {
648
670
  } catch {
649
671
  }
650
672
  }
651
- function buildAnthropicStreamTrace(reqBody, state, durationMs) {
673
+ function buildAnthropicStreamTrace(reqBody, state, durationMs, ttfbMs, timeToFirstTokenMs, streamingDurationMs, providerName = "anthropic") {
652
674
  const messages = reqBody.messages ?? [];
653
675
  const userMsg = extractLastUserMessage(messages);
654
676
  const systemMsg = typeof reqBody.system === "string" ? reqBody.system : "";
655
677
  const total = state.inputTokens + state.outputTokens;
656
678
  const payload = {
657
679
  input: { user_message: userMsg },
658
- model: { name: state.model || reqBody.model || "unknown", provider: "anthropic" },
680
+ model: { name: state.model || reqBody.model || "unknown", provider: providerName },
659
681
  response: {
660
682
  text: state.textContent,
661
683
  finish_reason: state.stopReason || void 0,
@@ -663,6 +685,9 @@ function buildAnthropicStreamTrace(reqBody, state, durationMs) {
663
685
  },
664
686
  performance: {
665
687
  duration_ms: durationMs,
688
+ ttfb_ms: ttfbMs,
689
+ time_to_first_token_ms: timeToFirstTokenMs,
690
+ streaming_duration_ms: streamingDurationMs,
666
691
  tokens_input: state.inputTokens || void 0,
667
692
  tokens_output: state.outputTokens || void 0
668
693
  }
@@ -674,7 +699,7 @@ function buildAnthropicStreamTrace(reqBody, state, durationMs) {
674
699
  if (state.toolCalls.length > 0) payload.tool_calls = state.toolCalls;
675
700
  return payload;
676
701
  }
677
- function buildOpenAIStreamTrace(reqBody, state, durationMs) {
702
+ function buildOpenAIStreamTrace(reqBody, state, durationMs, ttfbMs, timeToFirstTokenMs, streamingDurationMs, providerName = "openai") {
678
703
  const messages = reqBody.messages ?? [];
679
704
  let systemMsg = "";
680
705
  for (const msg of messages) {
@@ -686,12 +711,17 @@ function buildOpenAIStreamTrace(reqBody, state, durationMs) {
686
711
  const userMsg = extractLastUserMessage(messages);
687
712
  const payload = {
688
713
  input: { user_message: userMsg },
689
- model: { name: state.model || reqBody.model || "unknown", provider: "openai" },
714
+ model: { name: state.model || reqBody.model || "unknown", provider: providerName },
690
715
  response: {
691
716
  text: state.textContent || void 0,
692
717
  finish_reason: state.finishReason || void 0
693
718
  },
694
- performance: { duration_ms: durationMs }
719
+ performance: {
720
+ duration_ms: durationMs,
721
+ ttfb_ms: ttfbMs,
722
+ time_to_first_token_ms: timeToFirstTokenMs,
723
+ streaming_duration_ms: streamingDurationMs
724
+ }
695
725
  };
696
726
  const prompt = {};
697
727
  if (systemMsg) prompt.system = systemMsg;
@@ -712,11 +742,11 @@ function buildOpenAIStreamTrace(reqBody, state, durationMs) {
712
742
  }
713
743
  return payload;
714
744
  }
715
- function wrapStreamingResponse(response, provider, reqBody, startTime) {
745
+ function wrapStreamingResponse(response, provider, reqBody, startTime, ttfbMs) {
716
746
  const body = response.body;
717
747
  if (!body) return response;
718
748
  const decoder = new TextDecoder();
719
- const state = provider === "anthropic" ? makeAnthropicState() : makeOpenAIState();
749
+ const state = provider.format === "anthropic" ? makeAnthropicState() : makeOpenAIState();
720
750
  let buffer = "";
721
751
  const transformedStream = new ReadableStream({
722
752
  async start(controller) {
@@ -732,7 +762,7 @@ function wrapStreamingResponse(response, provider, reqBody, startTime) {
732
762
  for (const line of lines) {
733
763
  const trimmed = line.trim();
734
764
  if (!trimmed) continue;
735
- if (provider === "anthropic") {
765
+ if (provider.format === "anthropic") {
736
766
  processAnthropicSSE(trimmed, state);
737
767
  } else {
738
768
  processOpenAISSE(trimmed, state);
@@ -740,7 +770,7 @@ function wrapStreamingResponse(response, provider, reqBody, startTime) {
740
770
  }
741
771
  }
742
772
  if (buffer.trim()) {
743
- if (provider === "anthropic") {
773
+ if (provider.format === "anthropic") {
744
774
  processAnthropicSSE(buffer.trim(), state);
745
775
  } else {
746
776
  processOpenAISSE(buffer.trim(), state);
@@ -748,8 +778,28 @@ function wrapStreamingResponse(response, provider, reqBody, startTime) {
748
778
  }
749
779
  state.done = true;
750
780
  try {
751
- const durationMs = Date.now() - startTime;
752
- const tracePayload = provider === "anthropic" ? buildAnthropicStreamTrace(reqBody, state, durationMs) : buildOpenAIStreamTrace(reqBody, state, durationMs);
781
+ const endTime = Date.now();
782
+ const durationMs = endTime - startTime;
783
+ const firstTokenAt = state.firstTokenAt;
784
+ const timeToFirstTokenMs = firstTokenAt ? firstTokenAt - startTime : void 0;
785
+ const streamingDurationMs = firstTokenAt ? endTime - firstTokenAt : void 0;
786
+ const tracePayload = provider.format === "anthropic" ? buildAnthropicStreamTrace(
787
+ reqBody,
788
+ state,
789
+ durationMs,
790
+ ttfbMs,
791
+ timeToFirstTokenMs,
792
+ streamingDurationMs,
793
+ provider.name
794
+ ) : buildOpenAIStreamTrace(
795
+ reqBody,
796
+ state,
797
+ durationMs,
798
+ ttfbMs,
799
+ timeToFirstTokenMs,
800
+ streamingDurationMs,
801
+ provider.name
802
+ );
753
803
  const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;
754
804
  const traceType = hasToolCalls ? "tool_use" : "chat";
755
805
  if (isInitialized()) {
@@ -792,10 +842,11 @@ async function patchedFetch(input, init2) {
792
842
  }
793
843
  try {
794
844
  const response = await _originalFetch(input, init2);
845
+ const ttfbMs = Date.now() - startTime;
795
846
  const isStreaming = reqBody?.stream === true;
796
847
  if (isStreaming && reqBody) {
797
848
  try {
798
- return wrapStreamingResponse(response, provider, reqBody, startTime);
849
+ return wrapStreamingResponse(response, provider, reqBody, startTime, ttfbMs);
799
850
  } catch {
800
851
  return response;
801
852
  }
@@ -805,7 +856,7 @@ async function patchedFetch(input, init2) {
805
856
  const clone = response.clone();
806
857
  const resBody = await clone.json();
807
858
  const durationMs = Date.now() - startTime;
808
- const tracePayload = provider === "anthropic" ? extractAnthropicTrace(reqBody, resBody, durationMs) : extractOpenAITrace(reqBody, resBody, durationMs);
859
+ const tracePayload = provider.format === "anthropic" ? extractAnthropicTrace(reqBody, resBody, durationMs, ttfbMs, provider.name) : extractOpenAITrace(reqBody, resBody, durationMs, ttfbMs, provider.name);
809
860
  const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;
810
861
  const traceType = hasToolCalls ? "tool_use" : "chat";
811
862
  if (isInitialized()) {
package/dist/auto.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auto.ts","../src/buffer.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["/**\n * Stelvara Auto-Instrumentation — patch globalThis.fetch to capture LLM traces.\n *\n * Usage:\n * import '@crevanta/stelvara-sdk/auto';\n *\n * Set STELVARA_API_KEY and STELVARA_AGENT_ID as environment variables.\n * Optionally call setSessionId() for conversation tracking.\n */\n\nimport { init, captureTrace, isInitialized } from './index';\nimport type { TracePayload, TraceType, ToolCall } from './types';\n\n// ── Provider URL patterns ──\n\nconst ANTHROPIC_PATTERN = 'api.anthropic.com/v1/messages';\nconst OPENAI_PATTERN = 'api.openai.com/v1/chat/completions';\n\ntype Provider = 'anthropic' | 'openai';\n\n// ── Module state ──\n\nlet _originalFetch: typeof globalThis.fetch | null = null;\nlet _installed = false;\nlet _sessionId: string | null = null;\nlet _inFlight = false;\n\n// ── Environment variable reader (Node + Deno compatible) ──\n\nfunction readEnvVar(name: string): string | undefined {\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const g = globalThis as any;\n if (typeof g.Deno !== 'undefined' && g.Deno.env?.get) {\n return g.Deno.env.get(name) as string | undefined;\n }\n if (typeof g.process !== 'undefined' && g.process.env) {\n return g.process.env[name] as string | undefined;\n }\n /* eslint-enable @typescript-eslint/no-explicit-any */\n return undefined;\n}\n\n// ── Provider detection ──\n\nfunction detectProvider(url: string): Provider | null {\n if (url.includes(ANTHROPIC_PATTERN)) return 'anthropic';\n if (url.includes(OPENAI_PATTERN)) return 'openai';\n return null;\n}\n\n// ── URL extraction from fetch input ──\n\nfunction resolveUrl(input: string | URL | Request): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.href;\n return input.url;\n}\n\n// ── Request body parsing ──\n\nfunction parseBodySync(init: RequestInit | undefined): Record<string, unknown> | null {\n if (!init?.body || typeof init.body !== 'string') return null;\n try {\n return JSON.parse(init.body) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n// ── Anthropic extraction helpers ──\n\nfunction extractLastUserMessage(messages: unknown[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i] as Record<string, unknown>;\n if (msg?.role !== 'user') continue;\n const content = msg.content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n for (const block of content) {\n const b = block as Record<string, unknown>;\n if (b?.type === 'text' && typeof b.text === 'string') return b.text;\n }\n }\n }\n return '';\n}\n\nfunction extractAnthropicTrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: 'anthropic',\n },\n performance: { duration_ms: durationMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response content blocks\n const contentBlocks = (resBody.content ?? []) as Array<Record<string, unknown>>;\n const toolCalls: ToolCall[] = [];\n\n for (const block of contentBlocks) {\n if (block.type === 'text' && typeof block.text === 'string' && !payload.response?.text) {\n payload.response = { ...payload.response, text: block.text };\n }\n if (block.type === 'tool_use') {\n toolCalls.push({\n name: (block.name as string) ?? 'unknown',\n arguments: (block.input as Record<string, unknown>) ?? undefined,\n });\n }\n }\n\n if (toolCalls.length > 0) payload.tool_calls = toolCalls;\n\n // Finish reason\n if (typeof resBody.stop_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: resBody.stop_reason };\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const inputTokens = usage.input_tokens ?? 0;\n const outputTokens = usage.output_tokens ?? 0;\n const total = inputTokens + outputTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { duration_ms: durationMs };\n if (inputTokens) payload.performance.tokens_input = inputTokens;\n if (outputTokens) payload.performance.tokens_output = outputTokens;\n }\n\n return payload;\n}\n\n// ── OpenAI extraction helpers ──\n\nfunction extractOpenAITrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n\n // Extract system and user messages\n let systemMsg = '';\n let userMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n }\n }\n userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: 'openai',\n },\n performance: { duration_ms: durationMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response\n const choices = (resBody.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === 'string') {\n payload.response = { ...payload.response, text: message.content };\n }\n\n // Tool calls\n const rawToolCalls = message.tool_calls as Array<Record<string, unknown>> | undefined;\n if (rawToolCalls && rawToolCalls.length > 0) {\n payload.tool_calls = rawToolCalls.map((tc) => {\n const fn = tc.function as Record<string, unknown> | undefined;\n const entry: ToolCall = { name: (fn?.name as string) ?? 'unknown' };\n if (typeof fn?.arguments === 'string') {\n try {\n entry.arguments = JSON.parse(fn.arguments);\n } catch {\n entry.arguments = { raw: fn.arguments };\n }\n }\n return entry;\n });\n }\n }\n\n if (typeof choice.finish_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: choice.finish_reason };\n }\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const total = usage.total_tokens ?? promptTokens + completionTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { duration_ms: durationMs };\n if (promptTokens) payload.performance.tokens_input = promptTokens;\n if (completionTokens) payload.performance.tokens_output = completionTokens;\n }\n\n return payload;\n}\n\n// ── SSE Streaming state ──\n\ninterface AnthropicSSEState {\n model: string;\n inputTokens: number;\n outputTokens: number;\n stopReason: string;\n textContent: string;\n toolCalls: ToolCall[];\n currentToolName: string;\n currentToolArgs: string;\n done: boolean;\n}\n\ninterface OpenAISSEState {\n model: string;\n textContent: string;\n toolCallMap: Map<number, { name: string; argumentsRaw: string }>;\n finishReason: string;\n done: boolean;\n}\n\nfunction makeAnthropicState(): AnthropicSSEState {\n return {\n model: '',\n inputTokens: 0,\n outputTokens: 0,\n stopReason: '',\n textContent: '',\n toolCalls: [],\n currentToolName: '',\n currentToolArgs: '',\n done: false,\n };\n}\n\nfunction makeOpenAIState(): OpenAISSEState {\n return {\n model: '',\n textContent: '',\n toolCallMap: new Map(),\n finishReason: '',\n done: false,\n };\n}\n\n// ── SSE line processors ──\n\nfunction processAnthropicSSE(line: string, state: AnthropicSSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n switch (event.type) {\n case 'message_start': {\n const msg = event.message as Record<string, unknown> | undefined;\n if (msg) {\n if (typeof msg.model === 'string') state.model = msg.model;\n const usage = msg.usage as Record<string, number> | undefined;\n if (usage?.input_tokens) state.inputTokens = usage.input_tokens;\n }\n break;\n }\n case 'content_block_start': {\n const block = event.content_block as Record<string, unknown> | undefined;\n if (block?.type === 'tool_use') {\n state.currentToolName = (block.name as string) ?? 'unknown';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'content_block_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && typeof delta.text === 'string') {\n state.textContent += delta.text;\n }\n if (delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') {\n state.currentToolArgs += delta.partial_json;\n }\n break;\n }\n case 'content_block_stop': {\n if (state.currentToolName) {\n const tc: ToolCall = { name: state.currentToolName };\n if (state.currentToolArgs) {\n try {\n tc.arguments = JSON.parse(state.currentToolArgs);\n } catch {\n tc.arguments = { raw: state.currentToolArgs };\n }\n }\n state.toolCalls.push(tc);\n state.currentToolName = '';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'message_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (typeof delta?.stop_reason === 'string') state.stopReason = delta.stop_reason;\n const usage = event.usage as Record<string, number> | undefined;\n if (usage?.output_tokens) state.outputTokens = usage.output_tokens;\n break;\n }\n case 'message_stop':\n state.done = true;\n break;\n }\n } catch {\n // Ignore malformed SSE lines\n }\n}\n\nfunction processOpenAISSE(line: string, state: OpenAISSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6).trim();\n if (json === '[DONE]') {\n state.done = true;\n return;\n }\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n if (typeof event.model === 'string' && !state.model) {\n state.model = event.model;\n }\n const choices = (event.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const delta = choice.delta as Record<string, unknown> | undefined;\n if (delta) {\n if (typeof delta.content === 'string') {\n state.textContent += delta.content;\n }\n if (typeof choice.finish_reason === 'string') {\n state.finishReason = choice.finish_reason;\n }\n // Tool call deltas\n const toolCallDeltas = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCallDeltas) {\n for (const tcd of toolCallDeltas) {\n const idx = (tcd.index as number) ?? 0;\n const existing = state.toolCallMap.get(idx);\n const fn = tcd.function as Record<string, unknown> | undefined;\n if (!existing) {\n state.toolCallMap.set(idx, {\n name: (fn?.name as string) ?? '',\n argumentsRaw: (fn?.arguments as string) ?? '',\n });\n } else {\n if (fn?.name) existing.name += fn.name as string;\n if (fn?.arguments) existing.argumentsRaw += fn.arguments as string;\n }\n }\n }\n }\n }\n } catch {\n // Ignore malformed SSE chunks\n }\n}\n\n// ── Build trace from streaming state ──\n\nfunction buildAnthropicStreamTrace(\n reqBody: Record<string, unknown>,\n state: AnthropicSSEState,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n const total = state.inputTokens + state.outputTokens;\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: 'anthropic' },\n response: {\n text: state.textContent,\n finish_reason: state.stopReason || undefined,\n tokens_used: total > 0 ? total : undefined,\n },\n performance: {\n duration_ms: durationMs,\n tokens_input: state.inputTokens || undefined,\n tokens_output: state.outputTokens || undefined,\n },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n if (state.toolCalls.length > 0) payload.tool_calls = state.toolCalls;\n\n return payload;\n}\n\nfunction buildOpenAIStreamTrace(\n reqBody: Record<string, unknown>,\n state: OpenAISSEState,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n let systemMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n break;\n }\n }\n const userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: 'openai' },\n response: {\n text: state.textContent || undefined,\n finish_reason: state.finishReason || undefined,\n },\n performance: { duration_ms: durationMs },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Assemble tool calls\n if (state.toolCallMap.size > 0) {\n payload.tool_calls = Array.from(state.toolCallMap.values()).map((tc) => {\n const entry: ToolCall = { name: tc.name };\n if (tc.argumentsRaw) {\n try {\n entry.arguments = JSON.parse(tc.argumentsRaw);\n } catch {\n entry.arguments = { raw: tc.argumentsRaw };\n }\n }\n return entry;\n });\n }\n\n return payload;\n}\n\n// ── Stream wrapping ──\n\nfunction wrapStreamingResponse(\n response: Response,\n provider: Provider,\n reqBody: Record<string, unknown>,\n startTime: number\n): Response {\n const body = response.body;\n if (!body) return response;\n\n const decoder = new TextDecoder();\n const state = provider === 'anthropic' ? makeAnthropicState() : makeOpenAIState();\n let buffer = '';\n\n const transformedStream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n // Pass through unchanged\n controller.enqueue(value);\n\n // Parse SSE lines\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (provider === 'anthropic') {\n processAnthropicSSE(trimmed, state as AnthropicSSEState);\n } else {\n processOpenAISSE(trimmed, state as OpenAISSEState);\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim()) {\n if (provider === 'anthropic') {\n processAnthropicSSE(buffer.trim(), state as AnthropicSSEState);\n } else {\n processOpenAISSE(buffer.trim(), state as OpenAISSEState);\n }\n }\n\n // Fire trace on stream end\n state.done = true;\n try {\n const durationMs = Date.now() - startTime;\n const tracePayload =\n provider === 'anthropic'\n ? buildAnthropicStreamTrace(reqBody, state as AnthropicSSEState, durationMs)\n : buildOpenAIStreamTrace(reqBody, state as OpenAISSEState, durationMs);\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture break the stream\n }\n\n controller.close();\n } catch (err) {\n controller.error(err);\n } finally {\n reader.releaseLock();\n }\n },\n });\n\n return new Response(transformedStream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n// ── Patched fetch ──\n\nasync function patchedFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {\n // Prevent recursion when TraceBuffer flushes\n if (_inFlight) {\n return _originalFetch!(input, init);\n }\n\n const url = resolveUrl(input);\n const provider = detectProvider(url);\n\n // Pass through non-LLM calls\n if (!provider) {\n return _originalFetch!(input, init);\n }\n\n _inFlight = true;\n const startTime = Date.now();\n let reqBody: Record<string, unknown> | null = null;\n\n try {\n reqBody = parseBodySync(init);\n } catch {\n // Can't parse request — still make the call\n }\n\n try {\n const response = await _originalFetch!(input, init);\n\n // Check if streaming\n const isStreaming = reqBody?.stream === true;\n\n if (isStreaming && reqBody) {\n try {\n return wrapStreamingResponse(response, provider, reqBody, startTime);\n } catch {\n return response;\n }\n }\n\n // Non-streaming: clone and parse\n if (reqBody) {\n try {\n const clone = response.clone();\n const resBody = (await clone.json()) as Record<string, unknown>;\n const durationMs = Date.now() - startTime;\n\n const tracePayload =\n provider === 'anthropic'\n ? extractAnthropicTrace(reqBody, resBody, durationMs)\n : extractOpenAITrace(reqBody, resBody, durationMs);\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture fail the request\n }\n }\n\n return response;\n } finally {\n _inFlight = false;\n }\n}\n\n// ── Public API ──\n\n/**\n * Install the fetch auto-instrumentation.\n * Reads STELVARA_API_KEY and STELVARA_AGENT_ID from environment variables.\n * Safe to call multiple times — idempotent.\n */\nexport function install(options?: { apiKey?: string; agentId?: string; endpoint?: string }): void {\n if (_installed) return;\n\n const apiKey = options?.apiKey ?? readEnvVar('STELVARA_API_KEY');\n const agentId = options?.agentId ?? readEnvVar('STELVARA_AGENT_ID');\n\n if (!isInitialized() && apiKey && agentId) {\n init({ apiKey, agentId, endpoint: options?.endpoint });\n }\n\n _originalFetch = globalThis.fetch;\n globalThis.fetch = patchedFetch as typeof globalThis.fetch;\n _installed = true;\n}\n\n/**\n * Remove the fetch patch and restore original fetch.\n */\nexport function uninstall(): void {\n if (!_installed || !_originalFetch) return;\n globalThis.fetch = _originalFetch;\n _originalFetch = null;\n _installed = false;\n}\n\n/**\n * Set a session ID for all auto-captured traces.\n * Call with null to clear.\n */\nexport function setSessionId(id: string | null): void {\n _sessionId = id;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n if (_installed) uninstall();\n _sessionId = null;\n}\n\n// ── Side-effect: auto-install on import ──\ninstall();\n","/**\n * Async trace buffer with batched delivery and retry logic.\n *\n * Uses setInterval for periodic flushing (JS equivalent of Python's\n * background thread). Never blocks the caller.\n */\n\nimport type { TraceEnvelope, IngestionResponse } from './types';\n\nconst delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nexport type FlushSuccessCallback = (\n batch: TraceEnvelope[],\n traceIds: string[],\n) => void;\n\nexport interface BufferConfig {\n endpoint: string;\n apiKey: string;\n maxSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n onFlushSuccess?: FlushSuccessCallback;\n}\n\nexport class TraceBuffer {\n private queue: TraceEnvelope[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private running = true;\n private readonly config: BufferConfig;\n private shutdownHook: (() => void) | null = null;\n\n constructor(config: BufferConfig) {\n this.config = config;\n this.start();\n }\n\n /**\n * Add a trace to the buffer.\n * Returns false if the buffer is shut down.\n * Drops oldest trace if buffer is full.\n */\n enqueue(trace: TraceEnvelope): boolean {\n if (!this.running) return false;\n\n if (this.queue.length >= this.config.maxSize) {\n this.queue.shift(); // drop oldest\n }\n this.queue.push(trace);\n return true;\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.queue.length;\n }\n\n /**\n * Flush all pending traces to the API.\n * Sends in batches of batchSize.\n */\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n this.flushing = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.queue.splice(0, this.config.batchSize);\n await this.sendBatch(batch);\n }\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining traces and stop the buffer.\n * Resolves when done or after timeoutMs.\n */\n async shutdown(timeoutMs = 5000): Promise<void> {\n if (!this.running) return;\n this.running = false;\n\n // Stop periodic flush\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // Remove shutdown hooks\n this.removeShutdownHook();\n\n // Final flush with timeout\n const flushPromise = this.flush();\n const timeoutPromise = delay(timeoutMs);\n\n await Promise.race([flushPromise, timeoutPromise]);\n }\n\n /**\n * Send a single batch to the ingestion API with retry logic.\n */\n private async sendBatch(batch: TraceEnvelope[]): Promise<void> {\n const url = `${this.config.endpoint}/traces`;\n\n // Strip _localKey from envelopes for the API call\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const cleanBatch = batch.map(({ _localKey, ...clean }) => clean);\n\n for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(cleanBatch),\n });\n\n if (response.status === 201) {\n const data = (await response.json()) as IngestionResponse;\n const traceIds = data.trace_ids ?? [];\n this.config.onFlushSuccess?.(batch, traceIds);\n return;\n }\n\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfterSec = retryAfterHeader\n ? parseInt(retryAfterHeader, 10)\n : 2 ** attempt;\n await delay(retryAfterSec * 1000);\n continue;\n }\n\n // Non-retryable HTTP error (400, 401, 403, etc.) — drop batch\n return;\n } catch {\n // Network error — exponential backoff\n const backoffMs = 2 ** attempt * 1000;\n await delay(backoffMs);\n }\n }\n\n // Exhausted retries — batch is dropped\n }\n\n private start(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n\n this.installShutdownHook();\n }\n\n private installShutdownHook(): void {\n const handler = (): void => {\n void this.shutdown();\n };\n\n // Browser: use beforeunload if available\n const g = globalThis as Record<string, unknown>;\n if (\n typeof g.window !== 'undefined' &&\n typeof (g.window as Record<string, unknown>).addEventListener ===\n 'function'\n ) {\n const win = g.window as {\n addEventListener: (e: string, fn: () => void) => void;\n removeEventListener: (e: string, fn: () => void) => void;\n };\n win.addEventListener('beforeunload', handler);\n this.shutdownHook = () => win.removeEventListener('beforeunload', handler);\n } else if (\n typeof process !== 'undefined' &&\n typeof process.on === 'function'\n ) {\n // Node.js: use beforeExit\n process.on('beforeExit', handler);\n this.shutdownHook = () => process.removeListener('beforeExit', handler);\n }\n }\n\n private removeShutdownHook(): void {\n this.shutdownHook?.();\n this.shutdownHook = null;\n }\n}\n","/**\n * StelvaraClient — manages trace capture, buffering, and outcome tagging.\n *\n * Port of the Python SDK's client.py adapted for TypeScript/fetch.\n */\n\nimport { TraceBuffer } from './buffer';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n TraceEnvelope,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nconst DEFAULT_ENDPOINT =\n 'https://auinkdnurzlaitpwhknm.supabase.co/functions/v1';\n\ninterface ResolvedConfig {\n apiKey: string;\n agentId: string;\n endpoint: string;\n bufferSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n enabled: boolean;\n}\n\nfunction resolveConfig(config: StelvaraConfig): ResolvedConfig {\n if (!config.apiKey) throw new Error('apiKey is required');\n if (!config.agentId) throw new Error('agentId is required');\n\n return {\n apiKey: config.apiKey,\n agentId: config.agentId,\n endpoint: (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, ''),\n bufferSize: config.bufferSize ?? 100,\n batchSize: config.batchSize ?? 50,\n flushIntervalMs: config.flushIntervalMs ?? 5000,\n maxRetries: config.maxRetries ?? 3,\n enabled: config.enabled ?? true,\n };\n}\n\nexport class StelvaraClient {\n private readonly config: ResolvedConfig;\n private readonly buffer: TraceBuffer;\n private readonly traceIdMap: Map<string, string> = new Map();\n private traceCounter = 0;\n\n constructor(config: StelvaraConfig) {\n this.config = resolveConfig(config);\n\n this.buffer = new TraceBuffer({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxSize: this.config.bufferSize,\n batchSize: this.config.batchSize,\n flushIntervalMs: this.config.flushIntervalMs,\n maxRetries: this.config.maxRetries,\n onFlushSuccess: (batch, traceIds) =>\n this.registerTraceIds(batch, traceIds),\n });\n }\n\n /**\n * Capture a trace payload and enqueue for delivery.\n * Returns a local trace key for use with tagOutcome().\n * Returns null if the client is disabled.\n */\n captureTrace(\n payload: TracePayload,\n options?: CaptureTraceOptions,\n ): string | null {\n if (!this.config.enabled) return null;\n\n const localKey = `stv_local_${++this.traceCounter}`;\n\n const envelope: TraceEnvelope = {\n agent_id: this.config.agentId,\n payload,\n timestamp: new Date().toISOString(),\n _localKey: localKey,\n };\n\n if (options?.sessionId != null) {\n envelope.session_id = options.sessionId;\n }\n if (options?.parentTraceId != null) {\n envelope.parent_trace_id = options.parentTraceId;\n }\n if (options?.traceType != null) {\n envelope.trace_type = options.traceType;\n }\n\n this.buffer.enqueue(envelope);\n return localKey;\n }\n\n /**\n * Tag a trace with business outcomes.\n * Resolves local trace keys to server UUIDs automatically.\n * Fire-and-forget — catches errors internally.\n */\n async tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>,\n ): Promise<void> {\n if (!this.config.enabled) return;\n\n let resolvedId = traceId;\n if (traceId.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(traceId);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${traceId} not yet flushed — outcome may fail`,\n );\n }\n }\n\n const url = `${this.config.endpoint}/outcomes`;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ trace_id: resolvedId, outcomes }),\n });\n } catch {\n console.warn(`[stelvara] Failed to tag outcome for trace ${resolvedId}`);\n }\n }\n\n /**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await client.tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\n async tagOutcomesBatch(\n entries: OutcomeBatchEntry[],\n ): Promise<OutcomeBatchResponse | null> {\n if (!this.config.enabled) return null;\n\n if (entries.length === 0) {\n throw new Error('entries must not be empty');\n }\n if (entries.length > 100) {\n throw new Error('entries must not exceed 100 items');\n }\n\n // Resolve local trace keys to server UUIDs\n const resolvedEntries = entries.map((entry) => {\n let resolvedId = entry.trace_id;\n if (entry.trace_id.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(entry.trace_id);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${entry.trace_id} not yet flushed — batch entry may fail`,\n );\n }\n }\n return { trace_id: resolvedId, outcomes: entry.outcomes };\n });\n\n const url = `${this.config.endpoint}/outcomes-batch`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ entries: resolvedEntries }),\n });\n\n return (await response.json()) as OutcomeBatchResponse;\n }\n\n /**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n *\n * @example\n * const result = await client.tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\n async tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>,\n ): Promise<SessionOutcomeResponse | null> {\n if (!this.config.enabled) return null;\n\n const url = `${this.config.endpoint}/outcomes-batch/session`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ session_id: sessionId, outcomes }),\n });\n\n return (await response.json()) as SessionOutcomeResponse;\n }\n\n /**\n * Flush pending traces and shut down the client.\n */\n async shutdown(timeoutMs?: number): Promise<void> {\n await this.buffer.shutdown(timeoutMs);\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.buffer.pendingCount;\n }\n\n /**\n * Map local trace keys to server-assigned trace IDs after flush.\n */\n private registerTraceIds(\n batch: TraceEnvelope[],\n traceIds: string[],\n ): void {\n for (let i = 0; i < batch.length; i++) {\n const localKey = batch[i]._localKey;\n if (localKey && traceIds[i]) {\n this.traceIdMap.set(localKey, traceIds[i]);\n }\n }\n }\n}\n","/**\n * Stelvara TypeScript SDK — AI Behavior Control Plane.\n *\n * Usage:\n * import { init, captureTrace, tagOutcome, shutdown } from '@crevanta/stelvara-sdk';\n *\n * init({ apiKey: 'stv_live_...', agentId: 'uuid' });\n * const traceKey = captureTrace({ input: { user_message: 'Hello' } });\n * await shutdown();\n */\n\nexport { StelvaraClient } from './client';\nexport type {\n TracePayload,\n TraceEnvelope,\n StelvaraConfig,\n OutcomeRequest,\n TraceType,\n CaptureTraceOptions,\n TraceInput,\n TracePrompt,\n TraceModel,\n TraceResponse,\n ToolCall,\n TraceDecision,\n TracePerformance,\n IngestionResponse,\n OutcomeBatchEntry,\n OutcomeBatchResult,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nimport { StelvaraClient } from './client';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\n// Module-level singleton\nlet _client: StelvaraClient | null = null;\n\n/**\n * Initialize the Stelvara SDK.\n * Must be called before captureTrace() or tagOutcome().\n *\n * Calling init() again will shut down the previous client.\n */\nexport function init(config: StelvaraConfig): void {\n if (_client !== null) {\n void _client.shutdown();\n }\n _client = new StelvaraClient(config);\n}\n\n/**\n * Capture a trace payload using the module-level client.\n * Returns a local trace key for use with tagOutcome().\n *\n * @throws {Error} If init() has not been called.\n */\nexport function captureTrace(payload: TracePayload, options?: CaptureTraceOptions): string | null {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.captureTrace(payload, options);\n}\n\n/**\n * Tag a trace with business outcomes.\n *\n * @throws {Error} If init() has not been called.\n */\nexport async function tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>\n): Promise<void> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcome(traceId, outcomes);\n}\n\n/**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\nexport async function tagOutcomesBatch(\n entries: OutcomeBatchEntry[]\n): Promise<OutcomeBatchResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcomesBatch(entries);\n}\n\n/**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n *\n * @example\n * const result = await tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\nexport async function tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>\n): Promise<SessionOutcomeResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagSessionOutcome(sessionId, outcomes);\n}\n\n/**\n * Flush pending traces and shut down the SDK.\n * Safe to call multiple times. After shutdown, init() must be called again.\n */\nexport async function shutdown(): Promise<void> {\n if (_client !== null) {\n await _client.shutdown();\n _client = null;\n }\n}\n\n/**\n * Check if the SDK has been initialized.\n */\nexport function isInitialized(): boolean {\n return _client !== null;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n _client = null;\n}\n"],"mappings":"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","names":["init"]}
1
+ {"version":3,"sources":["../src/auto.ts","../src/buffer.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["/**\n * Stelvara Auto-Instrumentation — patch globalThis.fetch to capture LLM traces.\n *\n * Usage:\n * import '@crevanta/stelvara-sdk/auto';\n *\n * Set STELVARA_API_KEY and STELVARA_AGENT_ID as environment variables.\n * Optionally call setSessionId() for conversation tracking.\n */\n\nimport { init, captureTrace, isInitialized } from './index';\nimport type { TracePayload, TraceType, ToolCall } from './types';\n\n// ── Provider URL patterns ──\n\nconst ANTHROPIC_PATTERN = 'api.anthropic.com/v1/messages';\nconst OPENAI_PATTERN = 'api.openai.com/v1/chat/completions';\n\n/**\n * OpenAI-compatible providers that use the same SSE / response format.\n * Each entry maps a URL substring to the provider label stored in the trace.\n */\nconst OPENAI_COMPAT_PATTERNS: Array<{ pattern: string; providerName: string }> = [\n { pattern: 'api.groq.com/', providerName: 'groq' },\n { pattern: 'api.together.xyz/', providerName: 'together' },\n { pattern: 'api.mistral.ai/', providerName: 'mistral' },\n { pattern: 'api.fireworks.ai/', providerName: 'fireworks' },\n { pattern: 'api.perplexity.ai/', providerName: 'perplexity' },\n { pattern: 'api.deepseek.com/', providerName: 'deepseek' },\n { pattern: 'openrouter.ai/api/', providerName: 'openrouter' },\n // Azure OpenAI: https://{resource}.openai.azure.com/openai/deployments/{model}/chat/completions\n { pattern: 'openai.azure.com/openai/deployments/', providerName: 'azure_openai' },\n // Local Ollama (OpenAI-compatible mode)\n { pattern: 'localhost:11434/', providerName: 'ollama' },\n { pattern: '127.0.0.1:11434/', providerName: 'ollama' },\n];\n\n/** Protocol-level format: Anthropic Messages vs OpenAI Chat Completions. */\ntype ProviderFormat = 'anthropic' | 'openai';\n\n/** Detected provider info: wire format + label for the trace. */\ninterface DetectedProvider {\n format: ProviderFormat;\n name: string;\n}\n\n// ── Module state ──\n\nlet _originalFetch: typeof globalThis.fetch | null = null;\nlet _installed = false;\nlet _sessionId: string | null = null;\nlet _inFlight = false;\n\n// ── Environment variable reader (Node + Deno compatible) ──\n\nfunction readEnvVar(name: string): string | undefined {\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const g = globalThis as any;\n if (typeof g.Deno !== 'undefined' && g.Deno.env?.get) {\n return g.Deno.env.get(name) as string | undefined;\n }\n if (typeof g.process !== 'undefined' && g.process.env) {\n return g.process.env[name] as string | undefined;\n }\n /* eslint-enable @typescript-eslint/no-explicit-any */\n return undefined;\n}\n\n// ── Provider detection ──\n\nfunction detectProvider(url: string): DetectedProvider | null {\n if (url.includes(ANTHROPIC_PATTERN)) return { format: 'anthropic', name: 'anthropic' };\n if (url.includes(OPENAI_PATTERN)) return { format: 'openai', name: 'openai' };\n // OpenAI-compatible providers (Groq, Together, Mistral, Azure, Ollama, etc.)\n for (const { pattern, providerName } of OPENAI_COMPAT_PATTERNS) {\n if (url.includes(pattern)) return { format: 'openai', name: providerName };\n }\n return null;\n}\n\n// ── URL extraction from fetch input ──\n\nfunction resolveUrl(input: string | URL | Request): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.href;\n return input.url;\n}\n\n// ── Request body parsing ──\n\nfunction parseBodySync(init: RequestInit | undefined): Record<string, unknown> | null {\n if (!init?.body || typeof init.body !== 'string') return null;\n try {\n return JSON.parse(init.body) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n// ── Anthropic extraction helpers ──\n\nfunction extractLastUserMessage(messages: unknown[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i] as Record<string, unknown>;\n if (msg?.role !== 'user') continue;\n const content = msg.content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n for (const block of content) {\n const b = block as Record<string, unknown>;\n if (b?.type === 'text' && typeof b.text === 'string') return b.text;\n }\n }\n }\n return '';\n}\n\nfunction extractAnthropicTrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number,\n ttfbMs?: number,\n providerName: string = 'anthropic'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: providerName,\n },\n performance: { duration_ms: durationMs, ttfb_ms: ttfbMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response content blocks\n const contentBlocks = (resBody.content ?? []) as Array<Record<string, unknown>>;\n const toolCalls: ToolCall[] = [];\n\n for (const block of contentBlocks) {\n if (block.type === 'text' && typeof block.text === 'string' && !payload.response?.text) {\n payload.response = { ...payload.response, text: block.text };\n }\n if (block.type === 'tool_use') {\n toolCalls.push({\n name: (block.name as string) ?? 'unknown',\n arguments: (block.input as Record<string, unknown>) ?? undefined,\n });\n }\n }\n\n if (toolCalls.length > 0) payload.tool_calls = toolCalls;\n\n // Finish reason\n if (typeof resBody.stop_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: resBody.stop_reason };\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const inputTokens = usage.input_tokens ?? 0;\n const outputTokens = usage.output_tokens ?? 0;\n const total = inputTokens + outputTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { ...payload.performance, duration_ms: durationMs };\n if (inputTokens) payload.performance.tokens_input = inputTokens;\n if (outputTokens) payload.performance.tokens_output = outputTokens;\n }\n\n return payload;\n}\n\n// ── OpenAI extraction helpers ──\n\nfunction extractOpenAITrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number,\n ttfbMs?: number,\n providerName: string = 'openai'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n\n // Extract system and user messages\n let systemMsg = '';\n let userMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n }\n }\n userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: providerName,\n },\n performance: { duration_ms: durationMs, ttfb_ms: ttfbMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response\n const choices = (resBody.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === 'string') {\n payload.response = { ...payload.response, text: message.content };\n }\n\n // Tool calls\n const rawToolCalls = message.tool_calls as Array<Record<string, unknown>> | undefined;\n if (rawToolCalls && rawToolCalls.length > 0) {\n payload.tool_calls = rawToolCalls.map((tc) => {\n const fn = tc.function as Record<string, unknown> | undefined;\n const entry: ToolCall = { name: (fn?.name as string) ?? 'unknown' };\n if (typeof fn?.arguments === 'string') {\n try {\n entry.arguments = JSON.parse(fn.arguments);\n } catch {\n entry.arguments = { raw: fn.arguments };\n }\n }\n return entry;\n });\n }\n }\n\n if (typeof choice.finish_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: choice.finish_reason };\n }\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const total = usage.total_tokens ?? promptTokens + completionTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { ...payload.performance, duration_ms: durationMs };\n if (promptTokens) payload.performance.tokens_input = promptTokens;\n if (completionTokens) payload.performance.tokens_output = completionTokens;\n }\n\n return payload;\n}\n\n// ── SSE Streaming state ──\n\ninterface AnthropicSSEState {\n model: string;\n inputTokens: number;\n outputTokens: number;\n stopReason: string;\n textContent: string;\n toolCalls: ToolCall[];\n currentToolName: string;\n currentToolArgs: string;\n done: boolean;\n /** Timestamp when the first content token was received (streaming). */\n firstTokenAt: number | null;\n}\n\ninterface OpenAISSEState {\n model: string;\n textContent: string;\n toolCallMap: Map<number, { name: string; argumentsRaw: string }>;\n finishReason: string;\n done: boolean;\n /** Timestamp when the first content token was received (streaming). */\n firstTokenAt: number | null;\n}\n\nfunction makeAnthropicState(): AnthropicSSEState {\n return {\n model: '',\n inputTokens: 0,\n outputTokens: 0,\n stopReason: '',\n textContent: '',\n toolCalls: [],\n currentToolName: '',\n currentToolArgs: '',\n done: false,\n firstTokenAt: null,\n };\n}\n\nfunction makeOpenAIState(): OpenAISSEState {\n return {\n model: '',\n textContent: '',\n toolCallMap: new Map(),\n finishReason: '',\n done: false,\n firstTokenAt: null,\n };\n}\n\n// ── SSE line processors ──\n\nfunction processAnthropicSSE(line: string, state: AnthropicSSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n switch (event.type) {\n case 'message_start': {\n const msg = event.message as Record<string, unknown> | undefined;\n if (msg) {\n if (typeof msg.model === 'string') state.model = msg.model;\n const usage = msg.usage as Record<string, number> | undefined;\n if (usage?.input_tokens) state.inputTokens = usage.input_tokens;\n }\n break;\n }\n case 'content_block_start': {\n const block = event.content_block as Record<string, unknown> | undefined;\n if (block?.type === 'tool_use') {\n state.currentToolName = (block.name as string) ?? 'unknown';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'content_block_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && typeof delta.text === 'string') {\n if (state.firstTokenAt === null) state.firstTokenAt = Date.now();\n state.textContent += delta.text;\n }\n if (delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') {\n state.currentToolArgs += delta.partial_json;\n }\n break;\n }\n case 'content_block_stop': {\n if (state.currentToolName) {\n const tc: ToolCall = { name: state.currentToolName };\n if (state.currentToolArgs) {\n try {\n tc.arguments = JSON.parse(state.currentToolArgs);\n } catch {\n tc.arguments = { raw: state.currentToolArgs };\n }\n }\n state.toolCalls.push(tc);\n state.currentToolName = '';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'message_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (typeof delta?.stop_reason === 'string') state.stopReason = delta.stop_reason;\n const usage = event.usage as Record<string, number> | undefined;\n if (usage?.output_tokens) state.outputTokens = usage.output_tokens;\n break;\n }\n case 'message_stop':\n state.done = true;\n break;\n }\n } catch {\n // Ignore malformed SSE lines\n }\n}\n\nfunction processOpenAISSE(line: string, state: OpenAISSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6).trim();\n if (json === '[DONE]') {\n state.done = true;\n return;\n }\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n if (typeof event.model === 'string' && !state.model) {\n state.model = event.model;\n }\n const choices = (event.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const delta = choice.delta as Record<string, unknown> | undefined;\n if (delta) {\n if (typeof delta.content === 'string') {\n if (state.firstTokenAt === null && delta.content.length > 0)\n state.firstTokenAt = Date.now();\n state.textContent += delta.content;\n }\n if (typeof choice.finish_reason === 'string') {\n state.finishReason = choice.finish_reason;\n }\n // Tool call deltas\n const toolCallDeltas = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCallDeltas) {\n for (const tcd of toolCallDeltas) {\n const idx = (tcd.index as number) ?? 0;\n const existing = state.toolCallMap.get(idx);\n const fn = tcd.function as Record<string, unknown> | undefined;\n if (!existing) {\n state.toolCallMap.set(idx, {\n name: (fn?.name as string) ?? '',\n argumentsRaw: (fn?.arguments as string) ?? '',\n });\n } else {\n if (fn?.name) existing.name += fn.name as string;\n if (fn?.arguments) existing.argumentsRaw += fn.arguments as string;\n }\n }\n }\n }\n }\n } catch {\n // Ignore malformed SSE chunks\n }\n}\n\n// ── Build trace from streaming state ──\n\nfunction buildAnthropicStreamTrace(\n reqBody: Record<string, unknown>,\n state: AnthropicSSEState,\n durationMs: number,\n ttfbMs?: number,\n timeToFirstTokenMs?: number,\n streamingDurationMs?: number,\n providerName: string = 'anthropic'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n const total = state.inputTokens + state.outputTokens;\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: providerName },\n response: {\n text: state.textContent,\n finish_reason: state.stopReason || undefined,\n tokens_used: total > 0 ? total : undefined,\n },\n performance: {\n duration_ms: durationMs,\n ttfb_ms: ttfbMs,\n time_to_first_token_ms: timeToFirstTokenMs,\n streaming_duration_ms: streamingDurationMs,\n tokens_input: state.inputTokens || undefined,\n tokens_output: state.outputTokens || undefined,\n },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n if (state.toolCalls.length > 0) payload.tool_calls = state.toolCalls;\n\n return payload;\n}\n\nfunction buildOpenAIStreamTrace(\n reqBody: Record<string, unknown>,\n state: OpenAISSEState,\n durationMs: number,\n ttfbMs?: number,\n timeToFirstTokenMs?: number,\n streamingDurationMs?: number,\n providerName: string = 'openai'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n let systemMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n break;\n }\n }\n const userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: providerName },\n response: {\n text: state.textContent || undefined,\n finish_reason: state.finishReason || undefined,\n },\n performance: {\n duration_ms: durationMs,\n ttfb_ms: ttfbMs,\n time_to_first_token_ms: timeToFirstTokenMs,\n streaming_duration_ms: streamingDurationMs,\n },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Assemble tool calls\n if (state.toolCallMap.size > 0) {\n payload.tool_calls = Array.from(state.toolCallMap.values()).map((tc) => {\n const entry: ToolCall = { name: tc.name };\n if (tc.argumentsRaw) {\n try {\n entry.arguments = JSON.parse(tc.argumentsRaw);\n } catch {\n entry.arguments = { raw: tc.argumentsRaw };\n }\n }\n return entry;\n });\n }\n\n return payload;\n}\n\n// ── Stream wrapping ──\n\nfunction wrapStreamingResponse(\n response: Response,\n provider: DetectedProvider,\n reqBody: Record<string, unknown>,\n startTime: number,\n ttfbMs: number\n): Response {\n const body = response.body;\n if (!body) return response;\n\n const decoder = new TextDecoder();\n const state = provider.format === 'anthropic' ? makeAnthropicState() : makeOpenAIState();\n let buffer = '';\n\n const transformedStream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n // Pass through unchanged\n controller.enqueue(value);\n\n // Parse SSE lines\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (provider.format === 'anthropic') {\n processAnthropicSSE(trimmed, state as AnthropicSSEState);\n } else {\n processOpenAISSE(trimmed, state as OpenAISSEState);\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim()) {\n if (provider.format === 'anthropic') {\n processAnthropicSSE(buffer.trim(), state as AnthropicSSEState);\n } else {\n processOpenAISSE(buffer.trim(), state as OpenAISSEState);\n }\n }\n\n // Fire trace on stream end\n state.done = true;\n try {\n const endTime = Date.now();\n const durationMs = endTime - startTime;\n const firstTokenAt = state.firstTokenAt;\n const timeToFirstTokenMs = firstTokenAt ? firstTokenAt - startTime : undefined;\n const streamingDurationMs = firstTokenAt ? endTime - firstTokenAt : undefined;\n\n const tracePayload =\n provider.format === 'anthropic'\n ? buildAnthropicStreamTrace(\n reqBody,\n state as AnthropicSSEState,\n durationMs,\n ttfbMs,\n timeToFirstTokenMs,\n streamingDurationMs,\n provider.name\n )\n : buildOpenAIStreamTrace(\n reqBody,\n state as OpenAISSEState,\n durationMs,\n ttfbMs,\n timeToFirstTokenMs,\n streamingDurationMs,\n provider.name\n );\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture break the stream\n }\n\n controller.close();\n } catch (err) {\n controller.error(err);\n } finally {\n reader.releaseLock();\n }\n },\n });\n\n return new Response(transformedStream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n// ── Patched fetch ──\n\nasync function patchedFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {\n // Prevent recursion when TraceBuffer flushes\n if (_inFlight) {\n return _originalFetch!(input, init);\n }\n\n const url = resolveUrl(input);\n const provider = detectProvider(url);\n\n // Pass through non-LLM calls\n if (!provider) {\n return _originalFetch!(input, init);\n }\n\n _inFlight = true;\n const startTime = Date.now();\n let reqBody: Record<string, unknown> | null = null;\n\n try {\n reqBody = parseBodySync(init);\n } catch {\n // Can't parse request — still make the call\n }\n\n try {\n const response = await _originalFetch!(input, init);\n const ttfbMs = Date.now() - startTime;\n\n // Check if streaming\n const isStreaming = reqBody?.stream === true;\n\n if (isStreaming && reqBody) {\n try {\n return wrapStreamingResponse(response, provider, reqBody, startTime, ttfbMs);\n } catch {\n return response;\n }\n }\n\n // Non-streaming: clone and parse\n if (reqBody) {\n try {\n const clone = response.clone();\n const resBody = (await clone.json()) as Record<string, unknown>;\n const durationMs = Date.now() - startTime;\n\n const tracePayload =\n provider.format === 'anthropic'\n ? extractAnthropicTrace(reqBody, resBody, durationMs, ttfbMs, provider.name)\n : extractOpenAITrace(reqBody, resBody, durationMs, ttfbMs, provider.name);\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture fail the request\n }\n }\n\n return response;\n } finally {\n _inFlight = false;\n }\n}\n\n// ── Public API ──\n\n/**\n * Install the fetch auto-instrumentation.\n * Reads STELVARA_API_KEY and STELVARA_AGENT_ID from environment variables.\n * Safe to call multiple times — idempotent.\n */\nexport function install(options?: { apiKey?: string; agentId?: string; endpoint?: string }): void {\n if (_installed) return;\n\n const apiKey = options?.apiKey ?? readEnvVar('STELVARA_API_KEY');\n const agentId = options?.agentId ?? readEnvVar('STELVARA_AGENT_ID');\n\n if (!isInitialized() && apiKey && agentId) {\n init({ apiKey, agentId, endpoint: options?.endpoint });\n }\n\n _originalFetch = globalThis.fetch;\n globalThis.fetch = patchedFetch as typeof globalThis.fetch;\n _installed = true;\n}\n\n/**\n * Remove the fetch patch and restore original fetch.\n */\nexport function uninstall(): void {\n if (!_installed || !_originalFetch) return;\n globalThis.fetch = _originalFetch;\n _originalFetch = null;\n _installed = false;\n}\n\n/**\n * Set a session ID for all auto-captured traces.\n * Call with null to clear.\n */\nexport function setSessionId(id: string | null): void {\n _sessionId = id;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n if (_installed) uninstall();\n _sessionId = null;\n}\n\n// ── Side-effect: auto-install on import ──\ninstall();\n","/**\n * Async trace buffer with batched delivery and retry logic.\n *\n * Uses setInterval for periodic flushing (JS equivalent of Python's\n * background thread). Never blocks the caller.\n */\n\nimport type { TraceEnvelope, IngestionResponse } from './types';\n\nconst delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nexport type FlushSuccessCallback = (\n batch: TraceEnvelope[],\n traceIds: string[],\n) => void;\n\nexport interface BufferConfig {\n endpoint: string;\n apiKey: string;\n maxSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n onFlushSuccess?: FlushSuccessCallback;\n}\n\nexport class TraceBuffer {\n private queue: TraceEnvelope[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private running = true;\n private readonly config: BufferConfig;\n private shutdownHook: (() => void) | null = null;\n\n constructor(config: BufferConfig) {\n this.config = config;\n this.start();\n }\n\n /**\n * Add a trace to the buffer.\n * Returns false if the buffer is shut down.\n * Drops oldest trace if buffer is full.\n */\n enqueue(trace: TraceEnvelope): boolean {\n if (!this.running) return false;\n\n if (this.queue.length >= this.config.maxSize) {\n this.queue.shift(); // drop oldest\n }\n this.queue.push(trace);\n return true;\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.queue.length;\n }\n\n /**\n * Flush all pending traces to the API.\n * Sends in batches of batchSize.\n */\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n this.flushing = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.queue.splice(0, this.config.batchSize);\n await this.sendBatch(batch);\n }\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining traces and stop the buffer.\n * Resolves when done or after timeoutMs.\n */\n async shutdown(timeoutMs = 5000): Promise<void> {\n if (!this.running) return;\n this.running = false;\n\n // Stop periodic flush\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // Remove shutdown hooks\n this.removeShutdownHook();\n\n // Final flush with timeout\n const flushPromise = this.flush();\n const timeoutPromise = delay(timeoutMs);\n\n await Promise.race([flushPromise, timeoutPromise]);\n }\n\n /**\n * Send a single batch to the ingestion API with retry logic.\n */\n private async sendBatch(batch: TraceEnvelope[]): Promise<void> {\n const url = `${this.config.endpoint}/traces`;\n\n // Strip _localKey from envelopes for the API call\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const cleanBatch = batch.map(({ _localKey, ...clean }) => clean);\n\n for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(cleanBatch),\n });\n\n if (response.status === 201) {\n const data = (await response.json()) as IngestionResponse;\n const traceIds = data.trace_ids ?? [];\n this.config.onFlushSuccess?.(batch, traceIds);\n return;\n }\n\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfterSec = retryAfterHeader\n ? parseInt(retryAfterHeader, 10)\n : 2 ** attempt;\n await delay(retryAfterSec * 1000);\n continue;\n }\n\n // Non-retryable HTTP error (400, 401, 403, etc.) — drop batch\n return;\n } catch {\n // Network error — exponential backoff\n const backoffMs = 2 ** attempt * 1000;\n await delay(backoffMs);\n }\n }\n\n // Exhausted retries — batch is dropped\n }\n\n private start(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n\n this.installShutdownHook();\n }\n\n private installShutdownHook(): void {\n const handler = (): void => {\n void this.shutdown();\n };\n\n // Browser: use beforeunload if available\n const g = globalThis as Record<string, unknown>;\n if (\n typeof g.window !== 'undefined' &&\n typeof (g.window as Record<string, unknown>).addEventListener ===\n 'function'\n ) {\n const win = g.window as {\n addEventListener: (e: string, fn: () => void) => void;\n removeEventListener: (e: string, fn: () => void) => void;\n };\n win.addEventListener('beforeunload', handler);\n this.shutdownHook = () => win.removeEventListener('beforeunload', handler);\n } else if (\n typeof process !== 'undefined' &&\n typeof process.on === 'function'\n ) {\n // Node.js: use beforeExit\n process.on('beforeExit', handler);\n this.shutdownHook = () => process.removeListener('beforeExit', handler);\n }\n }\n\n private removeShutdownHook(): void {\n this.shutdownHook?.();\n this.shutdownHook = null;\n }\n}\n","/**\n * StelvaraClient — manages trace capture, buffering, and outcome tagging.\n *\n * Port of the Python SDK's client.py adapted for TypeScript/fetch.\n */\n\nimport { TraceBuffer } from './buffer';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n TraceEnvelope,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nconst DEFAULT_ENDPOINT =\n 'https://auinkdnurzlaitpwhknm.supabase.co/functions/v1';\n\ninterface ResolvedConfig {\n apiKey: string;\n agentId: string;\n endpoint: string;\n bufferSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n enabled: boolean;\n}\n\nfunction resolveConfig(config: StelvaraConfig): ResolvedConfig {\n if (!config.apiKey) throw new Error('apiKey is required');\n if (!config.agentId) throw new Error('agentId is required');\n\n return {\n apiKey: config.apiKey,\n agentId: config.agentId,\n endpoint: (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, ''),\n bufferSize: config.bufferSize ?? 100,\n batchSize: config.batchSize ?? 50,\n flushIntervalMs: config.flushIntervalMs ?? 5000,\n maxRetries: config.maxRetries ?? 3,\n enabled: config.enabled ?? true,\n };\n}\n\nexport class StelvaraClient {\n private readonly config: ResolvedConfig;\n private readonly buffer: TraceBuffer;\n private readonly traceIdMap: Map<string, string> = new Map();\n private traceCounter = 0;\n\n constructor(config: StelvaraConfig) {\n this.config = resolveConfig(config);\n\n this.buffer = new TraceBuffer({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxSize: this.config.bufferSize,\n batchSize: this.config.batchSize,\n flushIntervalMs: this.config.flushIntervalMs,\n maxRetries: this.config.maxRetries,\n onFlushSuccess: (batch, traceIds) =>\n this.registerTraceIds(batch, traceIds),\n });\n }\n\n /**\n * Capture a trace payload and enqueue for delivery.\n * Returns a local trace key for use with tagOutcome().\n * Returns null if the client is disabled.\n */\n captureTrace(\n payload: TracePayload,\n options?: CaptureTraceOptions,\n ): string | null {\n if (!this.config.enabled) return null;\n\n const localKey = `stv_local_${++this.traceCounter}`;\n\n const envelope: TraceEnvelope = {\n agent_id: this.config.agentId,\n payload,\n timestamp: new Date().toISOString(),\n _localKey: localKey,\n };\n\n if (options?.sessionId != null) {\n envelope.session_id = options.sessionId;\n }\n if (options?.parentTraceId != null) {\n envelope.parent_trace_id = options.parentTraceId;\n }\n if (options?.traceType != null) {\n envelope.trace_type = options.traceType;\n }\n\n this.buffer.enqueue(envelope);\n return localKey;\n }\n\n /**\n * Tag a trace with business outcomes.\n * Resolves local trace keys to server UUIDs automatically.\n * Fire-and-forget — catches errors internally.\n */\n async tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>,\n ): Promise<void> {\n if (!this.config.enabled) return;\n\n let resolvedId = traceId;\n if (traceId.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(traceId);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${traceId} not yet flushed — outcome may fail`,\n );\n }\n }\n\n const url = `${this.config.endpoint}/outcomes`;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ trace_id: resolvedId, outcomes }),\n });\n } catch {\n console.warn(`[stelvara] Failed to tag outcome for trace ${resolvedId}`);\n }\n }\n\n /**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await client.tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\n async tagOutcomesBatch(\n entries: OutcomeBatchEntry[],\n ): Promise<OutcomeBatchResponse | null> {\n if (!this.config.enabled) return null;\n\n if (entries.length === 0) {\n throw new Error('entries must not be empty');\n }\n if (entries.length > 100) {\n throw new Error('entries must not exceed 100 items');\n }\n\n // Resolve local trace keys to server UUIDs\n const resolvedEntries = entries.map((entry) => {\n let resolvedId = entry.trace_id;\n if (entry.trace_id.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(entry.trace_id);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${entry.trace_id} not yet flushed — batch entry may fail`,\n );\n }\n }\n return { trace_id: resolvedId, outcomes: entry.outcomes };\n });\n\n const url = `${this.config.endpoint}/outcomes-batch`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ entries: resolvedEntries }),\n });\n\n return (await response.json()) as OutcomeBatchResponse;\n }\n\n /**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n *\n * @example\n * const result = await client.tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\n async tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>,\n ): Promise<SessionOutcomeResponse | null> {\n if (!this.config.enabled) return null;\n\n const url = `${this.config.endpoint}/outcomes-batch/session`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ session_id: sessionId, outcomes }),\n });\n\n return (await response.json()) as SessionOutcomeResponse;\n }\n\n /**\n * Flush pending traces and shut down the client.\n */\n async shutdown(timeoutMs?: number): Promise<void> {\n await this.buffer.shutdown(timeoutMs);\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.buffer.pendingCount;\n }\n\n /**\n * Map local trace keys to server-assigned trace IDs after flush.\n */\n private registerTraceIds(\n batch: TraceEnvelope[],\n traceIds: string[],\n ): void {\n for (let i = 0; i < batch.length; i++) {\n const localKey = batch[i]._localKey;\n if (localKey && traceIds[i]) {\n this.traceIdMap.set(localKey, traceIds[i]);\n }\n }\n }\n}\n","/**\n * Stelvara TypeScript SDK — AI Behavior Control Plane.\n *\n * Usage:\n * import { init, captureTrace, tagOutcome, shutdown } from '@crevanta/stelvara-sdk';\n *\n * init({ apiKey: 'stv_live_...', agentId: 'uuid' });\n * const traceKey = captureTrace({ input: { user_message: 'Hello' } });\n * await shutdown();\n */\n\nexport { StelvaraClient } from './client';\nexport type {\n TracePayload,\n TraceEnvelope,\n StelvaraConfig,\n OutcomeRequest,\n TraceType,\n CaptureTraceOptions,\n TraceInput,\n TracePrompt,\n TraceModel,\n TraceResponse,\n ToolCall,\n TraceDecision,\n TracePerformance,\n IngestionResponse,\n OutcomeBatchEntry,\n OutcomeBatchResult,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nimport { StelvaraClient } from './client';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\n// Module-level singleton\nlet _client: StelvaraClient | null = null;\n\n/**\n * Initialize the Stelvara SDK.\n * Must be called before captureTrace() or tagOutcome().\n *\n * Calling init() again will shut down the previous client.\n */\nexport function init(config: StelvaraConfig): void {\n if (_client !== null) {\n void _client.shutdown();\n }\n _client = new StelvaraClient(config);\n}\n\n/**\n * Capture a trace payload using the module-level client.\n * Returns a local trace key for use with tagOutcome().\n *\n * @throws {Error} If init() has not been called.\n */\nexport function captureTrace(payload: TracePayload, options?: CaptureTraceOptions): string | null {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.captureTrace(payload, options);\n}\n\n/**\n * Tag a trace with business outcomes.\n *\n * @throws {Error} If init() has not been called.\n */\nexport async function tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>\n): Promise<void> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcome(traceId, outcomes);\n}\n\n/**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\nexport async function tagOutcomesBatch(\n entries: OutcomeBatchEntry[]\n): Promise<OutcomeBatchResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcomesBatch(entries);\n}\n\n/**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n *\n * @example\n * const result = await tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\nexport async function tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>\n): Promise<SessionOutcomeResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagSessionOutcome(sessionId, outcomes);\n}\n\n/**\n * Flush pending traces and shut down the SDK.\n * Safe to call multiple times. After shutdown, init() must be called again.\n */\nexport async function shutdown(): Promise<void> {\n if (_client !== null) {\n await _client.shutdown();\n _client = null;\n }\n}\n\n/**\n * Check if the SDK has been initialized.\n */\nexport function isInitialized(): boolean {\n return _client !== null;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n _client = null;\n}\n"],"mappings":"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","names":["init"]}
package/dist/auto.js CHANGED
@@ -331,6 +331,20 @@ function isInitialized() {
331
331
  // src/auto.ts
332
332
  var ANTHROPIC_PATTERN = "api.anthropic.com/v1/messages";
333
333
  var OPENAI_PATTERN = "api.openai.com/v1/chat/completions";
334
+ var OPENAI_COMPAT_PATTERNS = [
335
+ { pattern: "api.groq.com/", providerName: "groq" },
336
+ { pattern: "api.together.xyz/", providerName: "together" },
337
+ { pattern: "api.mistral.ai/", providerName: "mistral" },
338
+ { pattern: "api.fireworks.ai/", providerName: "fireworks" },
339
+ { pattern: "api.perplexity.ai/", providerName: "perplexity" },
340
+ { pattern: "api.deepseek.com/", providerName: "deepseek" },
341
+ { pattern: "openrouter.ai/api/", providerName: "openrouter" },
342
+ // Azure OpenAI: https://{resource}.openai.azure.com/openai/deployments/{model}/chat/completions
343
+ { pattern: "openai.azure.com/openai/deployments/", providerName: "azure_openai" },
344
+ // Local Ollama (OpenAI-compatible mode)
345
+ { pattern: "localhost:11434/", providerName: "ollama" },
346
+ { pattern: "127.0.0.1:11434/", providerName: "ollama" }
347
+ ];
334
348
  var _originalFetch = null;
335
349
  var _installed = false;
336
350
  var _sessionId = null;
@@ -346,8 +360,11 @@ function readEnvVar(name) {
346
360
  return void 0;
347
361
  }
348
362
  function detectProvider(url) {
349
- if (url.includes(ANTHROPIC_PATTERN)) return "anthropic";
350
- if (url.includes(OPENAI_PATTERN)) return "openai";
363
+ if (url.includes(ANTHROPIC_PATTERN)) return { format: "anthropic", name: "anthropic" };
364
+ if (url.includes(OPENAI_PATTERN)) return { format: "openai", name: "openai" };
365
+ for (const { pattern, providerName } of OPENAI_COMPAT_PATTERNS) {
366
+ if (url.includes(pattern)) return { format: "openai", name: providerName };
367
+ }
351
368
  return null;
352
369
  }
353
370
  function resolveUrl(input) {
@@ -378,7 +395,7 @@ function extractLastUserMessage(messages) {
378
395
  }
379
396
  return "";
380
397
  }
381
- function extractAnthropicTrace(reqBody, resBody, durationMs) {
398
+ function extractAnthropicTrace(reqBody, resBody, durationMs, ttfbMs, providerName = "anthropic") {
382
399
  const messages = reqBody.messages ?? [];
383
400
  const userMsg = extractLastUserMessage(messages);
384
401
  const systemMsg = typeof reqBody.system === "string" ? reqBody.system : "";
@@ -386,9 +403,9 @@ function extractAnthropicTrace(reqBody, resBody, durationMs) {
386
403
  input: { user_message: userMsg },
387
404
  model: {
388
405
  name: resBody.model ?? reqBody.model ?? "unknown",
389
- provider: "anthropic"
406
+ provider: providerName
390
407
  },
391
- performance: { duration_ms: durationMs }
408
+ performance: { duration_ms: durationMs, ttfb_ms: ttfbMs }
392
409
  };
393
410
  const prompt = {};
394
411
  if (systemMsg) prompt.system = systemMsg;
@@ -419,13 +436,13 @@ function extractAnthropicTrace(reqBody, resBody, durationMs) {
419
436
  if (total > 0) {
420
437
  payload.response = { ...payload.response, tokens_used: total };
421
438
  }
422
- payload.performance = { duration_ms: durationMs };
439
+ payload.performance = { ...payload.performance, duration_ms: durationMs };
423
440
  if (inputTokens) payload.performance.tokens_input = inputTokens;
424
441
  if (outputTokens) payload.performance.tokens_output = outputTokens;
425
442
  }
426
443
  return payload;
427
444
  }
428
- function extractOpenAITrace(reqBody, resBody, durationMs) {
445
+ function extractOpenAITrace(reqBody, resBody, durationMs, ttfbMs, providerName = "openai") {
429
446
  const messages = reqBody.messages ?? [];
430
447
  let systemMsg = "";
431
448
  let userMsg = "";
@@ -439,9 +456,9 @@ function extractOpenAITrace(reqBody, resBody, durationMs) {
439
456
  input: { user_message: userMsg },
440
457
  model: {
441
458
  name: resBody.model ?? reqBody.model ?? "unknown",
442
- provider: "openai"
459
+ provider: providerName
443
460
  },
444
- performance: { duration_ms: durationMs }
461
+ performance: { duration_ms: durationMs, ttfb_ms: ttfbMs }
445
462
  };
446
463
  const prompt = {};
447
464
  if (systemMsg) prompt.system = systemMsg;
@@ -483,7 +500,7 @@ function extractOpenAITrace(reqBody, resBody, durationMs) {
483
500
  if (total > 0) {
484
501
  payload.response = { ...payload.response, tokens_used: total };
485
502
  }
486
- payload.performance = { duration_ms: durationMs };
503
+ payload.performance = { ...payload.performance, duration_ms: durationMs };
487
504
  if (promptTokens) payload.performance.tokens_input = promptTokens;
488
505
  if (completionTokens) payload.performance.tokens_output = completionTokens;
489
506
  }
@@ -499,7 +516,8 @@ function makeAnthropicState() {
499
516
  toolCalls: [],
500
517
  currentToolName: "",
501
518
  currentToolArgs: "",
502
- done: false
519
+ done: false,
520
+ firstTokenAt: null
503
521
  };
504
522
  }
505
523
  function makeOpenAIState() {
@@ -508,7 +526,8 @@ function makeOpenAIState() {
508
526
  textContent: "",
509
527
  toolCallMap: /* @__PURE__ */ new Map(),
510
528
  finishReason: "",
511
- done: false
529
+ done: false,
530
+ firstTokenAt: null
512
531
  };
513
532
  }
514
533
  function processAnthropicSSE(line, state) {
@@ -537,6 +556,7 @@ function processAnthropicSSE(line, state) {
537
556
  case "content_block_delta": {
538
557
  const delta = event.delta;
539
558
  if (delta?.type === "text_delta" && typeof delta.text === "string") {
559
+ if (state.firstTokenAt === null) state.firstTokenAt = Date.now();
540
560
  state.textContent += delta.text;
541
561
  }
542
562
  if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
@@ -592,6 +612,8 @@ function processOpenAISSE(line, state) {
592
612
  const delta = choice.delta;
593
613
  if (delta) {
594
614
  if (typeof delta.content === "string") {
615
+ if (state.firstTokenAt === null && delta.content.length > 0)
616
+ state.firstTokenAt = Date.now();
595
617
  state.textContent += delta.content;
596
618
  }
597
619
  if (typeof choice.finish_reason === "string") {
@@ -619,14 +641,14 @@ function processOpenAISSE(line, state) {
619
641
  } catch {
620
642
  }
621
643
  }
622
- function buildAnthropicStreamTrace(reqBody, state, durationMs) {
644
+ function buildAnthropicStreamTrace(reqBody, state, durationMs, ttfbMs, timeToFirstTokenMs, streamingDurationMs, providerName = "anthropic") {
623
645
  const messages = reqBody.messages ?? [];
624
646
  const userMsg = extractLastUserMessage(messages);
625
647
  const systemMsg = typeof reqBody.system === "string" ? reqBody.system : "";
626
648
  const total = state.inputTokens + state.outputTokens;
627
649
  const payload = {
628
650
  input: { user_message: userMsg },
629
- model: { name: state.model || reqBody.model || "unknown", provider: "anthropic" },
651
+ model: { name: state.model || reqBody.model || "unknown", provider: providerName },
630
652
  response: {
631
653
  text: state.textContent,
632
654
  finish_reason: state.stopReason || void 0,
@@ -634,6 +656,9 @@ function buildAnthropicStreamTrace(reqBody, state, durationMs) {
634
656
  },
635
657
  performance: {
636
658
  duration_ms: durationMs,
659
+ ttfb_ms: ttfbMs,
660
+ time_to_first_token_ms: timeToFirstTokenMs,
661
+ streaming_duration_ms: streamingDurationMs,
637
662
  tokens_input: state.inputTokens || void 0,
638
663
  tokens_output: state.outputTokens || void 0
639
664
  }
@@ -645,7 +670,7 @@ function buildAnthropicStreamTrace(reqBody, state, durationMs) {
645
670
  if (state.toolCalls.length > 0) payload.tool_calls = state.toolCalls;
646
671
  return payload;
647
672
  }
648
- function buildOpenAIStreamTrace(reqBody, state, durationMs) {
673
+ function buildOpenAIStreamTrace(reqBody, state, durationMs, ttfbMs, timeToFirstTokenMs, streamingDurationMs, providerName = "openai") {
649
674
  const messages = reqBody.messages ?? [];
650
675
  let systemMsg = "";
651
676
  for (const msg of messages) {
@@ -657,12 +682,17 @@ function buildOpenAIStreamTrace(reqBody, state, durationMs) {
657
682
  const userMsg = extractLastUserMessage(messages);
658
683
  const payload = {
659
684
  input: { user_message: userMsg },
660
- model: { name: state.model || reqBody.model || "unknown", provider: "openai" },
685
+ model: { name: state.model || reqBody.model || "unknown", provider: providerName },
661
686
  response: {
662
687
  text: state.textContent || void 0,
663
688
  finish_reason: state.finishReason || void 0
664
689
  },
665
- performance: { duration_ms: durationMs }
690
+ performance: {
691
+ duration_ms: durationMs,
692
+ ttfb_ms: ttfbMs,
693
+ time_to_first_token_ms: timeToFirstTokenMs,
694
+ streaming_duration_ms: streamingDurationMs
695
+ }
666
696
  };
667
697
  const prompt = {};
668
698
  if (systemMsg) prompt.system = systemMsg;
@@ -683,11 +713,11 @@ function buildOpenAIStreamTrace(reqBody, state, durationMs) {
683
713
  }
684
714
  return payload;
685
715
  }
686
- function wrapStreamingResponse(response, provider, reqBody, startTime) {
716
+ function wrapStreamingResponse(response, provider, reqBody, startTime, ttfbMs) {
687
717
  const body = response.body;
688
718
  if (!body) return response;
689
719
  const decoder = new TextDecoder();
690
- const state = provider === "anthropic" ? makeAnthropicState() : makeOpenAIState();
720
+ const state = provider.format === "anthropic" ? makeAnthropicState() : makeOpenAIState();
691
721
  let buffer = "";
692
722
  const transformedStream = new ReadableStream({
693
723
  async start(controller) {
@@ -703,7 +733,7 @@ function wrapStreamingResponse(response, provider, reqBody, startTime) {
703
733
  for (const line of lines) {
704
734
  const trimmed = line.trim();
705
735
  if (!trimmed) continue;
706
- if (provider === "anthropic") {
736
+ if (provider.format === "anthropic") {
707
737
  processAnthropicSSE(trimmed, state);
708
738
  } else {
709
739
  processOpenAISSE(trimmed, state);
@@ -711,7 +741,7 @@ function wrapStreamingResponse(response, provider, reqBody, startTime) {
711
741
  }
712
742
  }
713
743
  if (buffer.trim()) {
714
- if (provider === "anthropic") {
744
+ if (provider.format === "anthropic") {
715
745
  processAnthropicSSE(buffer.trim(), state);
716
746
  } else {
717
747
  processOpenAISSE(buffer.trim(), state);
@@ -719,8 +749,28 @@ function wrapStreamingResponse(response, provider, reqBody, startTime) {
719
749
  }
720
750
  state.done = true;
721
751
  try {
722
- const durationMs = Date.now() - startTime;
723
- const tracePayload = provider === "anthropic" ? buildAnthropicStreamTrace(reqBody, state, durationMs) : buildOpenAIStreamTrace(reqBody, state, durationMs);
752
+ const endTime = Date.now();
753
+ const durationMs = endTime - startTime;
754
+ const firstTokenAt = state.firstTokenAt;
755
+ const timeToFirstTokenMs = firstTokenAt ? firstTokenAt - startTime : void 0;
756
+ const streamingDurationMs = firstTokenAt ? endTime - firstTokenAt : void 0;
757
+ const tracePayload = provider.format === "anthropic" ? buildAnthropicStreamTrace(
758
+ reqBody,
759
+ state,
760
+ durationMs,
761
+ ttfbMs,
762
+ timeToFirstTokenMs,
763
+ streamingDurationMs,
764
+ provider.name
765
+ ) : buildOpenAIStreamTrace(
766
+ reqBody,
767
+ state,
768
+ durationMs,
769
+ ttfbMs,
770
+ timeToFirstTokenMs,
771
+ streamingDurationMs,
772
+ provider.name
773
+ );
724
774
  const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;
725
775
  const traceType = hasToolCalls ? "tool_use" : "chat";
726
776
  if (isInitialized()) {
@@ -763,10 +813,11 @@ async function patchedFetch(input, init2) {
763
813
  }
764
814
  try {
765
815
  const response = await _originalFetch(input, init2);
816
+ const ttfbMs = Date.now() - startTime;
766
817
  const isStreaming = reqBody?.stream === true;
767
818
  if (isStreaming && reqBody) {
768
819
  try {
769
- return wrapStreamingResponse(response, provider, reqBody, startTime);
820
+ return wrapStreamingResponse(response, provider, reqBody, startTime, ttfbMs);
770
821
  } catch {
771
822
  return response;
772
823
  }
@@ -776,7 +827,7 @@ async function patchedFetch(input, init2) {
776
827
  const clone = response.clone();
777
828
  const resBody = await clone.json();
778
829
  const durationMs = Date.now() - startTime;
779
- const tracePayload = provider === "anthropic" ? extractAnthropicTrace(reqBody, resBody, durationMs) : extractOpenAITrace(reqBody, resBody, durationMs);
830
+ const tracePayload = provider.format === "anthropic" ? extractAnthropicTrace(reqBody, resBody, durationMs, ttfbMs, provider.name) : extractOpenAITrace(reqBody, resBody, durationMs, ttfbMs, provider.name);
780
831
  const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;
781
832
  const traceType = hasToolCalls ? "tool_use" : "chat";
782
833
  if (isInitialized()) {
package/dist/auto.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/buffer.ts","../src/client.ts","../src/index.ts","../src/auto.ts"],"sourcesContent":["/**\n * Async trace buffer with batched delivery and retry logic.\n *\n * Uses setInterval for periodic flushing (JS equivalent of Python's\n * background thread). Never blocks the caller.\n */\n\nimport type { TraceEnvelope, IngestionResponse } from './types';\n\nconst delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nexport type FlushSuccessCallback = (\n batch: TraceEnvelope[],\n traceIds: string[],\n) => void;\n\nexport interface BufferConfig {\n endpoint: string;\n apiKey: string;\n maxSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n onFlushSuccess?: FlushSuccessCallback;\n}\n\nexport class TraceBuffer {\n private queue: TraceEnvelope[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private running = true;\n private readonly config: BufferConfig;\n private shutdownHook: (() => void) | null = null;\n\n constructor(config: BufferConfig) {\n this.config = config;\n this.start();\n }\n\n /**\n * Add a trace to the buffer.\n * Returns false if the buffer is shut down.\n * Drops oldest trace if buffer is full.\n */\n enqueue(trace: TraceEnvelope): boolean {\n if (!this.running) return false;\n\n if (this.queue.length >= this.config.maxSize) {\n this.queue.shift(); // drop oldest\n }\n this.queue.push(trace);\n return true;\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.queue.length;\n }\n\n /**\n * Flush all pending traces to the API.\n * Sends in batches of batchSize.\n */\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n this.flushing = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.queue.splice(0, this.config.batchSize);\n await this.sendBatch(batch);\n }\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining traces and stop the buffer.\n * Resolves when done or after timeoutMs.\n */\n async shutdown(timeoutMs = 5000): Promise<void> {\n if (!this.running) return;\n this.running = false;\n\n // Stop periodic flush\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // Remove shutdown hooks\n this.removeShutdownHook();\n\n // Final flush with timeout\n const flushPromise = this.flush();\n const timeoutPromise = delay(timeoutMs);\n\n await Promise.race([flushPromise, timeoutPromise]);\n }\n\n /**\n * Send a single batch to the ingestion API with retry logic.\n */\n private async sendBatch(batch: TraceEnvelope[]): Promise<void> {\n const url = `${this.config.endpoint}/traces`;\n\n // Strip _localKey from envelopes for the API call\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const cleanBatch = batch.map(({ _localKey, ...clean }) => clean);\n\n for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(cleanBatch),\n });\n\n if (response.status === 201) {\n const data = (await response.json()) as IngestionResponse;\n const traceIds = data.trace_ids ?? [];\n this.config.onFlushSuccess?.(batch, traceIds);\n return;\n }\n\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfterSec = retryAfterHeader\n ? parseInt(retryAfterHeader, 10)\n : 2 ** attempt;\n await delay(retryAfterSec * 1000);\n continue;\n }\n\n // Non-retryable HTTP error (400, 401, 403, etc.) — drop batch\n return;\n } catch {\n // Network error — exponential backoff\n const backoffMs = 2 ** attempt * 1000;\n await delay(backoffMs);\n }\n }\n\n // Exhausted retries — batch is dropped\n }\n\n private start(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n\n this.installShutdownHook();\n }\n\n private installShutdownHook(): void {\n const handler = (): void => {\n void this.shutdown();\n };\n\n // Browser: use beforeunload if available\n const g = globalThis as Record<string, unknown>;\n if (\n typeof g.window !== 'undefined' &&\n typeof (g.window as Record<string, unknown>).addEventListener ===\n 'function'\n ) {\n const win = g.window as {\n addEventListener: (e: string, fn: () => void) => void;\n removeEventListener: (e: string, fn: () => void) => void;\n };\n win.addEventListener('beforeunload', handler);\n this.shutdownHook = () => win.removeEventListener('beforeunload', handler);\n } else if (\n typeof process !== 'undefined' &&\n typeof process.on === 'function'\n ) {\n // Node.js: use beforeExit\n process.on('beforeExit', handler);\n this.shutdownHook = () => process.removeListener('beforeExit', handler);\n }\n }\n\n private removeShutdownHook(): void {\n this.shutdownHook?.();\n this.shutdownHook = null;\n }\n}\n","/**\n * StelvaraClient — manages trace capture, buffering, and outcome tagging.\n *\n * Port of the Python SDK's client.py adapted for TypeScript/fetch.\n */\n\nimport { TraceBuffer } from './buffer';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n TraceEnvelope,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nconst DEFAULT_ENDPOINT =\n 'https://auinkdnurzlaitpwhknm.supabase.co/functions/v1';\n\ninterface ResolvedConfig {\n apiKey: string;\n agentId: string;\n endpoint: string;\n bufferSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n enabled: boolean;\n}\n\nfunction resolveConfig(config: StelvaraConfig): ResolvedConfig {\n if (!config.apiKey) throw new Error('apiKey is required');\n if (!config.agentId) throw new Error('agentId is required');\n\n return {\n apiKey: config.apiKey,\n agentId: config.agentId,\n endpoint: (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, ''),\n bufferSize: config.bufferSize ?? 100,\n batchSize: config.batchSize ?? 50,\n flushIntervalMs: config.flushIntervalMs ?? 5000,\n maxRetries: config.maxRetries ?? 3,\n enabled: config.enabled ?? true,\n };\n}\n\nexport class StelvaraClient {\n private readonly config: ResolvedConfig;\n private readonly buffer: TraceBuffer;\n private readonly traceIdMap: Map<string, string> = new Map();\n private traceCounter = 0;\n\n constructor(config: StelvaraConfig) {\n this.config = resolveConfig(config);\n\n this.buffer = new TraceBuffer({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxSize: this.config.bufferSize,\n batchSize: this.config.batchSize,\n flushIntervalMs: this.config.flushIntervalMs,\n maxRetries: this.config.maxRetries,\n onFlushSuccess: (batch, traceIds) =>\n this.registerTraceIds(batch, traceIds),\n });\n }\n\n /**\n * Capture a trace payload and enqueue for delivery.\n * Returns a local trace key for use with tagOutcome().\n * Returns null if the client is disabled.\n */\n captureTrace(\n payload: TracePayload,\n options?: CaptureTraceOptions,\n ): string | null {\n if (!this.config.enabled) return null;\n\n const localKey = `stv_local_${++this.traceCounter}`;\n\n const envelope: TraceEnvelope = {\n agent_id: this.config.agentId,\n payload,\n timestamp: new Date().toISOString(),\n _localKey: localKey,\n };\n\n if (options?.sessionId != null) {\n envelope.session_id = options.sessionId;\n }\n if (options?.parentTraceId != null) {\n envelope.parent_trace_id = options.parentTraceId;\n }\n if (options?.traceType != null) {\n envelope.trace_type = options.traceType;\n }\n\n this.buffer.enqueue(envelope);\n return localKey;\n }\n\n /**\n * Tag a trace with business outcomes.\n * Resolves local trace keys to server UUIDs automatically.\n * Fire-and-forget — catches errors internally.\n */\n async tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>,\n ): Promise<void> {\n if (!this.config.enabled) return;\n\n let resolvedId = traceId;\n if (traceId.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(traceId);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${traceId} not yet flushed — outcome may fail`,\n );\n }\n }\n\n const url = `${this.config.endpoint}/outcomes`;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ trace_id: resolvedId, outcomes }),\n });\n } catch {\n console.warn(`[stelvara] Failed to tag outcome for trace ${resolvedId}`);\n }\n }\n\n /**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await client.tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\n async tagOutcomesBatch(\n entries: OutcomeBatchEntry[],\n ): Promise<OutcomeBatchResponse | null> {\n if (!this.config.enabled) return null;\n\n if (entries.length === 0) {\n throw new Error('entries must not be empty');\n }\n if (entries.length > 100) {\n throw new Error('entries must not exceed 100 items');\n }\n\n // Resolve local trace keys to server UUIDs\n const resolvedEntries = entries.map((entry) => {\n let resolvedId = entry.trace_id;\n if (entry.trace_id.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(entry.trace_id);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${entry.trace_id} not yet flushed — batch entry may fail`,\n );\n }\n }\n return { trace_id: resolvedId, outcomes: entry.outcomes };\n });\n\n const url = `${this.config.endpoint}/outcomes-batch`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ entries: resolvedEntries }),\n });\n\n return (await response.json()) as OutcomeBatchResponse;\n }\n\n /**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n *\n * @example\n * const result = await client.tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\n async tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>,\n ): Promise<SessionOutcomeResponse | null> {\n if (!this.config.enabled) return null;\n\n const url = `${this.config.endpoint}/outcomes-batch/session`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ session_id: sessionId, outcomes }),\n });\n\n return (await response.json()) as SessionOutcomeResponse;\n }\n\n /**\n * Flush pending traces and shut down the client.\n */\n async shutdown(timeoutMs?: number): Promise<void> {\n await this.buffer.shutdown(timeoutMs);\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.buffer.pendingCount;\n }\n\n /**\n * Map local trace keys to server-assigned trace IDs after flush.\n */\n private registerTraceIds(\n batch: TraceEnvelope[],\n traceIds: string[],\n ): void {\n for (let i = 0; i < batch.length; i++) {\n const localKey = batch[i]._localKey;\n if (localKey && traceIds[i]) {\n this.traceIdMap.set(localKey, traceIds[i]);\n }\n }\n }\n}\n","/**\n * Stelvara TypeScript SDK — AI Behavior Control Plane.\n *\n * Usage:\n * import { init, captureTrace, tagOutcome, shutdown } from '@crevanta/stelvara-sdk';\n *\n * init({ apiKey: 'stv_live_...', agentId: 'uuid' });\n * const traceKey = captureTrace({ input: { user_message: 'Hello' } });\n * await shutdown();\n */\n\nexport { StelvaraClient } from './client';\nexport type {\n TracePayload,\n TraceEnvelope,\n StelvaraConfig,\n OutcomeRequest,\n TraceType,\n CaptureTraceOptions,\n TraceInput,\n TracePrompt,\n TraceModel,\n TraceResponse,\n ToolCall,\n TraceDecision,\n TracePerformance,\n IngestionResponse,\n OutcomeBatchEntry,\n OutcomeBatchResult,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nimport { StelvaraClient } from './client';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\n// Module-level singleton\nlet _client: StelvaraClient | null = null;\n\n/**\n * Initialize the Stelvara SDK.\n * Must be called before captureTrace() or tagOutcome().\n *\n * Calling init() again will shut down the previous client.\n */\nexport function init(config: StelvaraConfig): void {\n if (_client !== null) {\n void _client.shutdown();\n }\n _client = new StelvaraClient(config);\n}\n\n/**\n * Capture a trace payload using the module-level client.\n * Returns a local trace key for use with tagOutcome().\n *\n * @throws {Error} If init() has not been called.\n */\nexport function captureTrace(payload: TracePayload, options?: CaptureTraceOptions): string | null {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.captureTrace(payload, options);\n}\n\n/**\n * Tag a trace with business outcomes.\n *\n * @throws {Error} If init() has not been called.\n */\nexport async function tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>\n): Promise<void> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcome(traceId, outcomes);\n}\n\n/**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\nexport async function tagOutcomesBatch(\n entries: OutcomeBatchEntry[]\n): Promise<OutcomeBatchResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcomesBatch(entries);\n}\n\n/**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n *\n * @example\n * const result = await tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\nexport async function tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>\n): Promise<SessionOutcomeResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagSessionOutcome(sessionId, outcomes);\n}\n\n/**\n * Flush pending traces and shut down the SDK.\n * Safe to call multiple times. After shutdown, init() must be called again.\n */\nexport async function shutdown(): Promise<void> {\n if (_client !== null) {\n await _client.shutdown();\n _client = null;\n }\n}\n\n/**\n * Check if the SDK has been initialized.\n */\nexport function isInitialized(): boolean {\n return _client !== null;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n _client = null;\n}\n","/**\n * Stelvara Auto-Instrumentation — patch globalThis.fetch to capture LLM traces.\n *\n * Usage:\n * import '@crevanta/stelvara-sdk/auto';\n *\n * Set STELVARA_API_KEY and STELVARA_AGENT_ID as environment variables.\n * Optionally call setSessionId() for conversation tracking.\n */\n\nimport { init, captureTrace, isInitialized } from './index';\nimport type { TracePayload, TraceType, ToolCall } from './types';\n\n// ── Provider URL patterns ──\n\nconst ANTHROPIC_PATTERN = 'api.anthropic.com/v1/messages';\nconst OPENAI_PATTERN = 'api.openai.com/v1/chat/completions';\n\ntype Provider = 'anthropic' | 'openai';\n\n// ── Module state ──\n\nlet _originalFetch: typeof globalThis.fetch | null = null;\nlet _installed = false;\nlet _sessionId: string | null = null;\nlet _inFlight = false;\n\n// ── Environment variable reader (Node + Deno compatible) ──\n\nfunction readEnvVar(name: string): string | undefined {\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const g = globalThis as any;\n if (typeof g.Deno !== 'undefined' && g.Deno.env?.get) {\n return g.Deno.env.get(name) as string | undefined;\n }\n if (typeof g.process !== 'undefined' && g.process.env) {\n return g.process.env[name] as string | undefined;\n }\n /* eslint-enable @typescript-eslint/no-explicit-any */\n return undefined;\n}\n\n// ── Provider detection ──\n\nfunction detectProvider(url: string): Provider | null {\n if (url.includes(ANTHROPIC_PATTERN)) return 'anthropic';\n if (url.includes(OPENAI_PATTERN)) return 'openai';\n return null;\n}\n\n// ── URL extraction from fetch input ──\n\nfunction resolveUrl(input: string | URL | Request): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.href;\n return input.url;\n}\n\n// ── Request body parsing ──\n\nfunction parseBodySync(init: RequestInit | undefined): Record<string, unknown> | null {\n if (!init?.body || typeof init.body !== 'string') return null;\n try {\n return JSON.parse(init.body) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n// ── Anthropic extraction helpers ──\n\nfunction extractLastUserMessage(messages: unknown[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i] as Record<string, unknown>;\n if (msg?.role !== 'user') continue;\n const content = msg.content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n for (const block of content) {\n const b = block as Record<string, unknown>;\n if (b?.type === 'text' && typeof b.text === 'string') return b.text;\n }\n }\n }\n return '';\n}\n\nfunction extractAnthropicTrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: 'anthropic',\n },\n performance: { duration_ms: durationMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response content blocks\n const contentBlocks = (resBody.content ?? []) as Array<Record<string, unknown>>;\n const toolCalls: ToolCall[] = [];\n\n for (const block of contentBlocks) {\n if (block.type === 'text' && typeof block.text === 'string' && !payload.response?.text) {\n payload.response = { ...payload.response, text: block.text };\n }\n if (block.type === 'tool_use') {\n toolCalls.push({\n name: (block.name as string) ?? 'unknown',\n arguments: (block.input as Record<string, unknown>) ?? undefined,\n });\n }\n }\n\n if (toolCalls.length > 0) payload.tool_calls = toolCalls;\n\n // Finish reason\n if (typeof resBody.stop_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: resBody.stop_reason };\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const inputTokens = usage.input_tokens ?? 0;\n const outputTokens = usage.output_tokens ?? 0;\n const total = inputTokens + outputTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { duration_ms: durationMs };\n if (inputTokens) payload.performance.tokens_input = inputTokens;\n if (outputTokens) payload.performance.tokens_output = outputTokens;\n }\n\n return payload;\n}\n\n// ── OpenAI extraction helpers ──\n\nfunction extractOpenAITrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n\n // Extract system and user messages\n let systemMsg = '';\n let userMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n }\n }\n userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: 'openai',\n },\n performance: { duration_ms: durationMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response\n const choices = (resBody.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === 'string') {\n payload.response = { ...payload.response, text: message.content };\n }\n\n // Tool calls\n const rawToolCalls = message.tool_calls as Array<Record<string, unknown>> | undefined;\n if (rawToolCalls && rawToolCalls.length > 0) {\n payload.tool_calls = rawToolCalls.map((tc) => {\n const fn = tc.function as Record<string, unknown> | undefined;\n const entry: ToolCall = { name: (fn?.name as string) ?? 'unknown' };\n if (typeof fn?.arguments === 'string') {\n try {\n entry.arguments = JSON.parse(fn.arguments);\n } catch {\n entry.arguments = { raw: fn.arguments };\n }\n }\n return entry;\n });\n }\n }\n\n if (typeof choice.finish_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: choice.finish_reason };\n }\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const total = usage.total_tokens ?? promptTokens + completionTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { duration_ms: durationMs };\n if (promptTokens) payload.performance.tokens_input = promptTokens;\n if (completionTokens) payload.performance.tokens_output = completionTokens;\n }\n\n return payload;\n}\n\n// ── SSE Streaming state ──\n\ninterface AnthropicSSEState {\n model: string;\n inputTokens: number;\n outputTokens: number;\n stopReason: string;\n textContent: string;\n toolCalls: ToolCall[];\n currentToolName: string;\n currentToolArgs: string;\n done: boolean;\n}\n\ninterface OpenAISSEState {\n model: string;\n textContent: string;\n toolCallMap: Map<number, { name: string; argumentsRaw: string }>;\n finishReason: string;\n done: boolean;\n}\n\nfunction makeAnthropicState(): AnthropicSSEState {\n return {\n model: '',\n inputTokens: 0,\n outputTokens: 0,\n stopReason: '',\n textContent: '',\n toolCalls: [],\n currentToolName: '',\n currentToolArgs: '',\n done: false,\n };\n}\n\nfunction makeOpenAIState(): OpenAISSEState {\n return {\n model: '',\n textContent: '',\n toolCallMap: new Map(),\n finishReason: '',\n done: false,\n };\n}\n\n// ── SSE line processors ──\n\nfunction processAnthropicSSE(line: string, state: AnthropicSSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n switch (event.type) {\n case 'message_start': {\n const msg = event.message as Record<string, unknown> | undefined;\n if (msg) {\n if (typeof msg.model === 'string') state.model = msg.model;\n const usage = msg.usage as Record<string, number> | undefined;\n if (usage?.input_tokens) state.inputTokens = usage.input_tokens;\n }\n break;\n }\n case 'content_block_start': {\n const block = event.content_block as Record<string, unknown> | undefined;\n if (block?.type === 'tool_use') {\n state.currentToolName = (block.name as string) ?? 'unknown';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'content_block_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && typeof delta.text === 'string') {\n state.textContent += delta.text;\n }\n if (delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') {\n state.currentToolArgs += delta.partial_json;\n }\n break;\n }\n case 'content_block_stop': {\n if (state.currentToolName) {\n const tc: ToolCall = { name: state.currentToolName };\n if (state.currentToolArgs) {\n try {\n tc.arguments = JSON.parse(state.currentToolArgs);\n } catch {\n tc.arguments = { raw: state.currentToolArgs };\n }\n }\n state.toolCalls.push(tc);\n state.currentToolName = '';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'message_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (typeof delta?.stop_reason === 'string') state.stopReason = delta.stop_reason;\n const usage = event.usage as Record<string, number> | undefined;\n if (usage?.output_tokens) state.outputTokens = usage.output_tokens;\n break;\n }\n case 'message_stop':\n state.done = true;\n break;\n }\n } catch {\n // Ignore malformed SSE lines\n }\n}\n\nfunction processOpenAISSE(line: string, state: OpenAISSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6).trim();\n if (json === '[DONE]') {\n state.done = true;\n return;\n }\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n if (typeof event.model === 'string' && !state.model) {\n state.model = event.model;\n }\n const choices = (event.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const delta = choice.delta as Record<string, unknown> | undefined;\n if (delta) {\n if (typeof delta.content === 'string') {\n state.textContent += delta.content;\n }\n if (typeof choice.finish_reason === 'string') {\n state.finishReason = choice.finish_reason;\n }\n // Tool call deltas\n const toolCallDeltas = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCallDeltas) {\n for (const tcd of toolCallDeltas) {\n const idx = (tcd.index as number) ?? 0;\n const existing = state.toolCallMap.get(idx);\n const fn = tcd.function as Record<string, unknown> | undefined;\n if (!existing) {\n state.toolCallMap.set(idx, {\n name: (fn?.name as string) ?? '',\n argumentsRaw: (fn?.arguments as string) ?? '',\n });\n } else {\n if (fn?.name) existing.name += fn.name as string;\n if (fn?.arguments) existing.argumentsRaw += fn.arguments as string;\n }\n }\n }\n }\n }\n } catch {\n // Ignore malformed SSE chunks\n }\n}\n\n// ── Build trace from streaming state ──\n\nfunction buildAnthropicStreamTrace(\n reqBody: Record<string, unknown>,\n state: AnthropicSSEState,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n const total = state.inputTokens + state.outputTokens;\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: 'anthropic' },\n response: {\n text: state.textContent,\n finish_reason: state.stopReason || undefined,\n tokens_used: total > 0 ? total : undefined,\n },\n performance: {\n duration_ms: durationMs,\n tokens_input: state.inputTokens || undefined,\n tokens_output: state.outputTokens || undefined,\n },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n if (state.toolCalls.length > 0) payload.tool_calls = state.toolCalls;\n\n return payload;\n}\n\nfunction buildOpenAIStreamTrace(\n reqBody: Record<string, unknown>,\n state: OpenAISSEState,\n durationMs: number\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n let systemMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n break;\n }\n }\n const userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: 'openai' },\n response: {\n text: state.textContent || undefined,\n finish_reason: state.finishReason || undefined,\n },\n performance: { duration_ms: durationMs },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Assemble tool calls\n if (state.toolCallMap.size > 0) {\n payload.tool_calls = Array.from(state.toolCallMap.values()).map((tc) => {\n const entry: ToolCall = { name: tc.name };\n if (tc.argumentsRaw) {\n try {\n entry.arguments = JSON.parse(tc.argumentsRaw);\n } catch {\n entry.arguments = { raw: tc.argumentsRaw };\n }\n }\n return entry;\n });\n }\n\n return payload;\n}\n\n// ── Stream wrapping ──\n\nfunction wrapStreamingResponse(\n response: Response,\n provider: Provider,\n reqBody: Record<string, unknown>,\n startTime: number\n): Response {\n const body = response.body;\n if (!body) return response;\n\n const decoder = new TextDecoder();\n const state = provider === 'anthropic' ? makeAnthropicState() : makeOpenAIState();\n let buffer = '';\n\n const transformedStream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n // Pass through unchanged\n controller.enqueue(value);\n\n // Parse SSE lines\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (provider === 'anthropic') {\n processAnthropicSSE(trimmed, state as AnthropicSSEState);\n } else {\n processOpenAISSE(trimmed, state as OpenAISSEState);\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim()) {\n if (provider === 'anthropic') {\n processAnthropicSSE(buffer.trim(), state as AnthropicSSEState);\n } else {\n processOpenAISSE(buffer.trim(), state as OpenAISSEState);\n }\n }\n\n // Fire trace on stream end\n state.done = true;\n try {\n const durationMs = Date.now() - startTime;\n const tracePayload =\n provider === 'anthropic'\n ? buildAnthropicStreamTrace(reqBody, state as AnthropicSSEState, durationMs)\n : buildOpenAIStreamTrace(reqBody, state as OpenAISSEState, durationMs);\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture break the stream\n }\n\n controller.close();\n } catch (err) {\n controller.error(err);\n } finally {\n reader.releaseLock();\n }\n },\n });\n\n return new Response(transformedStream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n// ── Patched fetch ──\n\nasync function patchedFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {\n // Prevent recursion when TraceBuffer flushes\n if (_inFlight) {\n return _originalFetch!(input, init);\n }\n\n const url = resolveUrl(input);\n const provider = detectProvider(url);\n\n // Pass through non-LLM calls\n if (!provider) {\n return _originalFetch!(input, init);\n }\n\n _inFlight = true;\n const startTime = Date.now();\n let reqBody: Record<string, unknown> | null = null;\n\n try {\n reqBody = parseBodySync(init);\n } catch {\n // Can't parse request — still make the call\n }\n\n try {\n const response = await _originalFetch!(input, init);\n\n // Check if streaming\n const isStreaming = reqBody?.stream === true;\n\n if (isStreaming && reqBody) {\n try {\n return wrapStreamingResponse(response, provider, reqBody, startTime);\n } catch {\n return response;\n }\n }\n\n // Non-streaming: clone and parse\n if (reqBody) {\n try {\n const clone = response.clone();\n const resBody = (await clone.json()) as Record<string, unknown>;\n const durationMs = Date.now() - startTime;\n\n const tracePayload =\n provider === 'anthropic'\n ? extractAnthropicTrace(reqBody, resBody, durationMs)\n : extractOpenAITrace(reqBody, resBody, durationMs);\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture fail the request\n }\n }\n\n return response;\n } finally {\n _inFlight = false;\n }\n}\n\n// ── Public API ──\n\n/**\n * Install the fetch auto-instrumentation.\n * Reads STELVARA_API_KEY and STELVARA_AGENT_ID from environment variables.\n * Safe to call multiple times — idempotent.\n */\nexport function install(options?: { apiKey?: string; agentId?: string; endpoint?: string }): void {\n if (_installed) return;\n\n const apiKey = options?.apiKey ?? readEnvVar('STELVARA_API_KEY');\n const agentId = options?.agentId ?? readEnvVar('STELVARA_AGENT_ID');\n\n if (!isInitialized() && apiKey && agentId) {\n init({ apiKey, agentId, endpoint: options?.endpoint });\n }\n\n _originalFetch = globalThis.fetch;\n globalThis.fetch = patchedFetch as typeof globalThis.fetch;\n _installed = true;\n}\n\n/**\n * Remove the fetch patch and restore original fetch.\n */\nexport function uninstall(): void {\n if (!_installed || !_originalFetch) return;\n globalThis.fetch = _originalFetch;\n _originalFetch = null;\n _installed = false;\n}\n\n/**\n * Set a session ID for all auto-captured traces.\n * Call with null to clear.\n */\nexport function setSessionId(id: string | null): void {\n _sessionId = id;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n if (_installed) uninstall();\n _sessionId = null;\n}\n\n// ── Side-effect: auto-install on import ──\ninstall();\n"],"mappings":"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","names":["init"]}
1
+ {"version":3,"sources":["../src/buffer.ts","../src/client.ts","../src/index.ts","../src/auto.ts"],"sourcesContent":["/**\n * Async trace buffer with batched delivery and retry logic.\n *\n * Uses setInterval for periodic flushing (JS equivalent of Python's\n * background thread). Never blocks the caller.\n */\n\nimport type { TraceEnvelope, IngestionResponse } from './types';\n\nconst delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nexport type FlushSuccessCallback = (\n batch: TraceEnvelope[],\n traceIds: string[],\n) => void;\n\nexport interface BufferConfig {\n endpoint: string;\n apiKey: string;\n maxSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n onFlushSuccess?: FlushSuccessCallback;\n}\n\nexport class TraceBuffer {\n private queue: TraceEnvelope[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private running = true;\n private readonly config: BufferConfig;\n private shutdownHook: (() => void) | null = null;\n\n constructor(config: BufferConfig) {\n this.config = config;\n this.start();\n }\n\n /**\n * Add a trace to the buffer.\n * Returns false if the buffer is shut down.\n * Drops oldest trace if buffer is full.\n */\n enqueue(trace: TraceEnvelope): boolean {\n if (!this.running) return false;\n\n if (this.queue.length >= this.config.maxSize) {\n this.queue.shift(); // drop oldest\n }\n this.queue.push(trace);\n return true;\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.queue.length;\n }\n\n /**\n * Flush all pending traces to the API.\n * Sends in batches of batchSize.\n */\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n this.flushing = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.queue.splice(0, this.config.batchSize);\n await this.sendBatch(batch);\n }\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining traces and stop the buffer.\n * Resolves when done or after timeoutMs.\n */\n async shutdown(timeoutMs = 5000): Promise<void> {\n if (!this.running) return;\n this.running = false;\n\n // Stop periodic flush\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // Remove shutdown hooks\n this.removeShutdownHook();\n\n // Final flush with timeout\n const flushPromise = this.flush();\n const timeoutPromise = delay(timeoutMs);\n\n await Promise.race([flushPromise, timeoutPromise]);\n }\n\n /**\n * Send a single batch to the ingestion API with retry logic.\n */\n private async sendBatch(batch: TraceEnvelope[]): Promise<void> {\n const url = `${this.config.endpoint}/traces`;\n\n // Strip _localKey from envelopes for the API call\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const cleanBatch = batch.map(({ _localKey, ...clean }) => clean);\n\n for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(cleanBatch),\n });\n\n if (response.status === 201) {\n const data = (await response.json()) as IngestionResponse;\n const traceIds = data.trace_ids ?? [];\n this.config.onFlushSuccess?.(batch, traceIds);\n return;\n }\n\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfterSec = retryAfterHeader\n ? parseInt(retryAfterHeader, 10)\n : 2 ** attempt;\n await delay(retryAfterSec * 1000);\n continue;\n }\n\n // Non-retryable HTTP error (400, 401, 403, etc.) — drop batch\n return;\n } catch {\n // Network error — exponential backoff\n const backoffMs = 2 ** attempt * 1000;\n await delay(backoffMs);\n }\n }\n\n // Exhausted retries — batch is dropped\n }\n\n private start(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n\n this.installShutdownHook();\n }\n\n private installShutdownHook(): void {\n const handler = (): void => {\n void this.shutdown();\n };\n\n // Browser: use beforeunload if available\n const g = globalThis as Record<string, unknown>;\n if (\n typeof g.window !== 'undefined' &&\n typeof (g.window as Record<string, unknown>).addEventListener ===\n 'function'\n ) {\n const win = g.window as {\n addEventListener: (e: string, fn: () => void) => void;\n removeEventListener: (e: string, fn: () => void) => void;\n };\n win.addEventListener('beforeunload', handler);\n this.shutdownHook = () => win.removeEventListener('beforeunload', handler);\n } else if (\n typeof process !== 'undefined' &&\n typeof process.on === 'function'\n ) {\n // Node.js: use beforeExit\n process.on('beforeExit', handler);\n this.shutdownHook = () => process.removeListener('beforeExit', handler);\n }\n }\n\n private removeShutdownHook(): void {\n this.shutdownHook?.();\n this.shutdownHook = null;\n }\n}\n","/**\n * StelvaraClient — manages trace capture, buffering, and outcome tagging.\n *\n * Port of the Python SDK's client.py adapted for TypeScript/fetch.\n */\n\nimport { TraceBuffer } from './buffer';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n TraceEnvelope,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nconst DEFAULT_ENDPOINT =\n 'https://auinkdnurzlaitpwhknm.supabase.co/functions/v1';\n\ninterface ResolvedConfig {\n apiKey: string;\n agentId: string;\n endpoint: string;\n bufferSize: number;\n batchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n enabled: boolean;\n}\n\nfunction resolveConfig(config: StelvaraConfig): ResolvedConfig {\n if (!config.apiKey) throw new Error('apiKey is required');\n if (!config.agentId) throw new Error('agentId is required');\n\n return {\n apiKey: config.apiKey,\n agentId: config.agentId,\n endpoint: (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, ''),\n bufferSize: config.bufferSize ?? 100,\n batchSize: config.batchSize ?? 50,\n flushIntervalMs: config.flushIntervalMs ?? 5000,\n maxRetries: config.maxRetries ?? 3,\n enabled: config.enabled ?? true,\n };\n}\n\nexport class StelvaraClient {\n private readonly config: ResolvedConfig;\n private readonly buffer: TraceBuffer;\n private readonly traceIdMap: Map<string, string> = new Map();\n private traceCounter = 0;\n\n constructor(config: StelvaraConfig) {\n this.config = resolveConfig(config);\n\n this.buffer = new TraceBuffer({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxSize: this.config.bufferSize,\n batchSize: this.config.batchSize,\n flushIntervalMs: this.config.flushIntervalMs,\n maxRetries: this.config.maxRetries,\n onFlushSuccess: (batch, traceIds) =>\n this.registerTraceIds(batch, traceIds),\n });\n }\n\n /**\n * Capture a trace payload and enqueue for delivery.\n * Returns a local trace key for use with tagOutcome().\n * Returns null if the client is disabled.\n */\n captureTrace(\n payload: TracePayload,\n options?: CaptureTraceOptions,\n ): string | null {\n if (!this.config.enabled) return null;\n\n const localKey = `stv_local_${++this.traceCounter}`;\n\n const envelope: TraceEnvelope = {\n agent_id: this.config.agentId,\n payload,\n timestamp: new Date().toISOString(),\n _localKey: localKey,\n };\n\n if (options?.sessionId != null) {\n envelope.session_id = options.sessionId;\n }\n if (options?.parentTraceId != null) {\n envelope.parent_trace_id = options.parentTraceId;\n }\n if (options?.traceType != null) {\n envelope.trace_type = options.traceType;\n }\n\n this.buffer.enqueue(envelope);\n return localKey;\n }\n\n /**\n * Tag a trace with business outcomes.\n * Resolves local trace keys to server UUIDs automatically.\n * Fire-and-forget — catches errors internally.\n */\n async tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>,\n ): Promise<void> {\n if (!this.config.enabled) return;\n\n let resolvedId = traceId;\n if (traceId.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(traceId);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${traceId} not yet flushed — outcome may fail`,\n );\n }\n }\n\n const url = `${this.config.endpoint}/outcomes`;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ trace_id: resolvedId, outcomes }),\n });\n } catch {\n console.warn(`[stelvara] Failed to tag outcome for trace ${resolvedId}`);\n }\n }\n\n /**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await client.tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\n async tagOutcomesBatch(\n entries: OutcomeBatchEntry[],\n ): Promise<OutcomeBatchResponse | null> {\n if (!this.config.enabled) return null;\n\n if (entries.length === 0) {\n throw new Error('entries must not be empty');\n }\n if (entries.length > 100) {\n throw new Error('entries must not exceed 100 items');\n }\n\n // Resolve local trace keys to server UUIDs\n const resolvedEntries = entries.map((entry) => {\n let resolvedId = entry.trace_id;\n if (entry.trace_id.startsWith('stv_local_')) {\n const serverId = this.traceIdMap.get(entry.trace_id);\n if (serverId) {\n resolvedId = serverId;\n } else {\n console.warn(\n `[stelvara] Trace ${entry.trace_id} not yet flushed — batch entry may fail`,\n );\n }\n }\n return { trace_id: resolvedId, outcomes: entry.outcomes };\n });\n\n const url = `${this.config.endpoint}/outcomes-batch`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ entries: resolvedEntries }),\n });\n\n return (await response.json()) as OutcomeBatchResponse;\n }\n\n /**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n *\n * @example\n * const result = await client.tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\n async tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>,\n ): Promise<SessionOutcomeResponse | null> {\n if (!this.config.enabled) return null;\n\n const url = `${this.config.endpoint}/outcomes-batch/session`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ session_id: sessionId, outcomes }),\n });\n\n return (await response.json()) as SessionOutcomeResponse;\n }\n\n /**\n * Flush pending traces and shut down the client.\n */\n async shutdown(timeoutMs?: number): Promise<void> {\n await this.buffer.shutdown(timeoutMs);\n }\n\n /** Number of traces waiting to be flushed. */\n get pendingCount(): number {\n return this.buffer.pendingCount;\n }\n\n /**\n * Map local trace keys to server-assigned trace IDs after flush.\n */\n private registerTraceIds(\n batch: TraceEnvelope[],\n traceIds: string[],\n ): void {\n for (let i = 0; i < batch.length; i++) {\n const localKey = batch[i]._localKey;\n if (localKey && traceIds[i]) {\n this.traceIdMap.set(localKey, traceIds[i]);\n }\n }\n }\n}\n","/**\n * Stelvara TypeScript SDK — AI Behavior Control Plane.\n *\n * Usage:\n * import { init, captureTrace, tagOutcome, shutdown } from '@crevanta/stelvara-sdk';\n *\n * init({ apiKey: 'stv_live_...', agentId: 'uuid' });\n * const traceKey = captureTrace({ input: { user_message: 'Hello' } });\n * await shutdown();\n */\n\nexport { StelvaraClient } from './client';\nexport type {\n TracePayload,\n TraceEnvelope,\n StelvaraConfig,\n OutcomeRequest,\n TraceType,\n CaptureTraceOptions,\n TraceInput,\n TracePrompt,\n TraceModel,\n TraceResponse,\n ToolCall,\n TraceDecision,\n TracePerformance,\n IngestionResponse,\n OutcomeBatchEntry,\n OutcomeBatchResult,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\nimport { StelvaraClient } from './client';\nimport type {\n StelvaraConfig,\n TracePayload,\n CaptureTraceOptions,\n OutcomeBatchEntry,\n OutcomeBatchResponse,\n SessionOutcomeResponse,\n} from './types';\n\n// Module-level singleton\nlet _client: StelvaraClient | null = null;\n\n/**\n * Initialize the Stelvara SDK.\n * Must be called before captureTrace() or tagOutcome().\n *\n * Calling init() again will shut down the previous client.\n */\nexport function init(config: StelvaraConfig): void {\n if (_client !== null) {\n void _client.shutdown();\n }\n _client = new StelvaraClient(config);\n}\n\n/**\n * Capture a trace payload using the module-level client.\n * Returns a local trace key for use with tagOutcome().\n *\n * @throws {Error} If init() has not been called.\n */\nexport function captureTrace(payload: TracePayload, options?: CaptureTraceOptions): string | null {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.captureTrace(payload, options);\n}\n\n/**\n * Tag a trace with business outcomes.\n *\n * @throws {Error} If init() has not been called.\n */\nexport async function tagOutcome(\n traceId: string,\n outcomes: Record<string, unknown>\n): Promise<void> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcome(traceId, outcomes);\n}\n\n/**\n * Tag multiple traces with business outcomes in a single API call.\n *\n * @param entries - Array of { trace_id, outcomes } objects (max 100).\n * @returns Response with inserted count, failed count, and per-entry results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n * @throws {Error} If entries is empty or exceeds 100 items.\n *\n * @example\n * const result = await tagOutcomesBatch([\n * { trace_id: 'uuid-1', outcomes: { revenue_impact: 45 } },\n * { trace_id: 'uuid-2', outcomes: { customer_satisfied: 1 } },\n * ]);\n * console.log(`Tagged ${result.inserted} outcomes`);\n */\nexport async function tagOutcomesBatch(\n entries: OutcomeBatchEntry[]\n): Promise<OutcomeBatchResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagOutcomesBatch(entries);\n}\n\n/**\n * Tag all traces in a session with business outcomes.\n * The server resolves trace IDs by session_id.\n *\n * @param sessionId - Session identifier.\n * @param outcomes - Dict of outcome metrics applied to every trace in the session.\n * @returns Response with traces_found, inserted count, and per-trace results.\n * Returns null if SDK is disabled.\n * @throws {Error} If init() has not been called.\n *\n * @example\n * const result = await tagSessionOutcome('session-uuid', {\n * revenue_impact: 150,\n * customer_satisfied: 1,\n * });\n * console.log(`Tagged ${result.traces_found} traces`);\n */\nexport async function tagSessionOutcome(\n sessionId: string,\n outcomes: Record<string, unknown>\n): Promise<SessionOutcomeResponse | null> {\n if (_client === null) {\n throw new Error('Stelvara not initialized. Call init() first.');\n }\n return _client.tagSessionOutcome(sessionId, outcomes);\n}\n\n/**\n * Flush pending traces and shut down the SDK.\n * Safe to call multiple times. After shutdown, init() must be called again.\n */\nexport async function shutdown(): Promise<void> {\n if (_client !== null) {\n await _client.shutdown();\n _client = null;\n }\n}\n\n/**\n * Check if the SDK has been initialized.\n */\nexport function isInitialized(): boolean {\n return _client !== null;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n _client = null;\n}\n","/**\n * Stelvara Auto-Instrumentation — patch globalThis.fetch to capture LLM traces.\n *\n * Usage:\n * import '@crevanta/stelvara-sdk/auto';\n *\n * Set STELVARA_API_KEY and STELVARA_AGENT_ID as environment variables.\n * Optionally call setSessionId() for conversation tracking.\n */\n\nimport { init, captureTrace, isInitialized } from './index';\nimport type { TracePayload, TraceType, ToolCall } from './types';\n\n// ── Provider URL patterns ──\n\nconst ANTHROPIC_PATTERN = 'api.anthropic.com/v1/messages';\nconst OPENAI_PATTERN = 'api.openai.com/v1/chat/completions';\n\n/**\n * OpenAI-compatible providers that use the same SSE / response format.\n * Each entry maps a URL substring to the provider label stored in the trace.\n */\nconst OPENAI_COMPAT_PATTERNS: Array<{ pattern: string; providerName: string }> = [\n { pattern: 'api.groq.com/', providerName: 'groq' },\n { pattern: 'api.together.xyz/', providerName: 'together' },\n { pattern: 'api.mistral.ai/', providerName: 'mistral' },\n { pattern: 'api.fireworks.ai/', providerName: 'fireworks' },\n { pattern: 'api.perplexity.ai/', providerName: 'perplexity' },\n { pattern: 'api.deepseek.com/', providerName: 'deepseek' },\n { pattern: 'openrouter.ai/api/', providerName: 'openrouter' },\n // Azure OpenAI: https://{resource}.openai.azure.com/openai/deployments/{model}/chat/completions\n { pattern: 'openai.azure.com/openai/deployments/', providerName: 'azure_openai' },\n // Local Ollama (OpenAI-compatible mode)\n { pattern: 'localhost:11434/', providerName: 'ollama' },\n { pattern: '127.0.0.1:11434/', providerName: 'ollama' },\n];\n\n/** Protocol-level format: Anthropic Messages vs OpenAI Chat Completions. */\ntype ProviderFormat = 'anthropic' | 'openai';\n\n/** Detected provider info: wire format + label for the trace. */\ninterface DetectedProvider {\n format: ProviderFormat;\n name: string;\n}\n\n// ── Module state ──\n\nlet _originalFetch: typeof globalThis.fetch | null = null;\nlet _installed = false;\nlet _sessionId: string | null = null;\nlet _inFlight = false;\n\n// ── Environment variable reader (Node + Deno compatible) ──\n\nfunction readEnvVar(name: string): string | undefined {\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const g = globalThis as any;\n if (typeof g.Deno !== 'undefined' && g.Deno.env?.get) {\n return g.Deno.env.get(name) as string | undefined;\n }\n if (typeof g.process !== 'undefined' && g.process.env) {\n return g.process.env[name] as string | undefined;\n }\n /* eslint-enable @typescript-eslint/no-explicit-any */\n return undefined;\n}\n\n// ── Provider detection ──\n\nfunction detectProvider(url: string): DetectedProvider | null {\n if (url.includes(ANTHROPIC_PATTERN)) return { format: 'anthropic', name: 'anthropic' };\n if (url.includes(OPENAI_PATTERN)) return { format: 'openai', name: 'openai' };\n // OpenAI-compatible providers (Groq, Together, Mistral, Azure, Ollama, etc.)\n for (const { pattern, providerName } of OPENAI_COMPAT_PATTERNS) {\n if (url.includes(pattern)) return { format: 'openai', name: providerName };\n }\n return null;\n}\n\n// ── URL extraction from fetch input ──\n\nfunction resolveUrl(input: string | URL | Request): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.href;\n return input.url;\n}\n\n// ── Request body parsing ──\n\nfunction parseBodySync(init: RequestInit | undefined): Record<string, unknown> | null {\n if (!init?.body || typeof init.body !== 'string') return null;\n try {\n return JSON.parse(init.body) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n// ── Anthropic extraction helpers ──\n\nfunction extractLastUserMessage(messages: unknown[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i] as Record<string, unknown>;\n if (msg?.role !== 'user') continue;\n const content = msg.content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n for (const block of content) {\n const b = block as Record<string, unknown>;\n if (b?.type === 'text' && typeof b.text === 'string') return b.text;\n }\n }\n }\n return '';\n}\n\nfunction extractAnthropicTrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number,\n ttfbMs?: number,\n providerName: string = 'anthropic'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: providerName,\n },\n performance: { duration_ms: durationMs, ttfb_ms: ttfbMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response content blocks\n const contentBlocks = (resBody.content ?? []) as Array<Record<string, unknown>>;\n const toolCalls: ToolCall[] = [];\n\n for (const block of contentBlocks) {\n if (block.type === 'text' && typeof block.text === 'string' && !payload.response?.text) {\n payload.response = { ...payload.response, text: block.text };\n }\n if (block.type === 'tool_use') {\n toolCalls.push({\n name: (block.name as string) ?? 'unknown',\n arguments: (block.input as Record<string, unknown>) ?? undefined,\n });\n }\n }\n\n if (toolCalls.length > 0) payload.tool_calls = toolCalls;\n\n // Finish reason\n if (typeof resBody.stop_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: resBody.stop_reason };\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const inputTokens = usage.input_tokens ?? 0;\n const outputTokens = usage.output_tokens ?? 0;\n const total = inputTokens + outputTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { ...payload.performance, duration_ms: durationMs };\n if (inputTokens) payload.performance.tokens_input = inputTokens;\n if (outputTokens) payload.performance.tokens_output = outputTokens;\n }\n\n return payload;\n}\n\n// ── OpenAI extraction helpers ──\n\nfunction extractOpenAITrace(\n reqBody: Record<string, unknown>,\n resBody: Record<string, unknown>,\n durationMs: number,\n ttfbMs?: number,\n providerName: string = 'openai'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n\n // Extract system and user messages\n let systemMsg = '';\n let userMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n }\n }\n userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: {\n name: (resBody.model as string) ?? (reqBody.model as string) ?? 'unknown',\n provider: providerName,\n },\n performance: { duration_ms: durationMs, ttfb_ms: ttfbMs },\n };\n\n // Prompt\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Response\n const choices = (resBody.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === 'string') {\n payload.response = { ...payload.response, text: message.content };\n }\n\n // Tool calls\n const rawToolCalls = message.tool_calls as Array<Record<string, unknown>> | undefined;\n if (rawToolCalls && rawToolCalls.length > 0) {\n payload.tool_calls = rawToolCalls.map((tc) => {\n const fn = tc.function as Record<string, unknown> | undefined;\n const entry: ToolCall = { name: (fn?.name as string) ?? 'unknown' };\n if (typeof fn?.arguments === 'string') {\n try {\n entry.arguments = JSON.parse(fn.arguments);\n } catch {\n entry.arguments = { raw: fn.arguments };\n }\n }\n return entry;\n });\n }\n }\n\n if (typeof choice.finish_reason === 'string') {\n payload.response = { ...payload.response, finish_reason: choice.finish_reason };\n }\n }\n\n // Token usage\n const usage = resBody.usage as Record<string, number> | undefined;\n if (usage) {\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const total = usage.total_tokens ?? promptTokens + completionTokens;\n if (total > 0) {\n payload.response = { ...payload.response, tokens_used: total };\n }\n payload.performance = { ...payload.performance, duration_ms: durationMs };\n if (promptTokens) payload.performance.tokens_input = promptTokens;\n if (completionTokens) payload.performance.tokens_output = completionTokens;\n }\n\n return payload;\n}\n\n// ── SSE Streaming state ──\n\ninterface AnthropicSSEState {\n model: string;\n inputTokens: number;\n outputTokens: number;\n stopReason: string;\n textContent: string;\n toolCalls: ToolCall[];\n currentToolName: string;\n currentToolArgs: string;\n done: boolean;\n /** Timestamp when the first content token was received (streaming). */\n firstTokenAt: number | null;\n}\n\ninterface OpenAISSEState {\n model: string;\n textContent: string;\n toolCallMap: Map<number, { name: string; argumentsRaw: string }>;\n finishReason: string;\n done: boolean;\n /** Timestamp when the first content token was received (streaming). */\n firstTokenAt: number | null;\n}\n\nfunction makeAnthropicState(): AnthropicSSEState {\n return {\n model: '',\n inputTokens: 0,\n outputTokens: 0,\n stopReason: '',\n textContent: '',\n toolCalls: [],\n currentToolName: '',\n currentToolArgs: '',\n done: false,\n firstTokenAt: null,\n };\n}\n\nfunction makeOpenAIState(): OpenAISSEState {\n return {\n model: '',\n textContent: '',\n toolCallMap: new Map(),\n finishReason: '',\n done: false,\n firstTokenAt: null,\n };\n}\n\n// ── SSE line processors ──\n\nfunction processAnthropicSSE(line: string, state: AnthropicSSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n switch (event.type) {\n case 'message_start': {\n const msg = event.message as Record<string, unknown> | undefined;\n if (msg) {\n if (typeof msg.model === 'string') state.model = msg.model;\n const usage = msg.usage as Record<string, number> | undefined;\n if (usage?.input_tokens) state.inputTokens = usage.input_tokens;\n }\n break;\n }\n case 'content_block_start': {\n const block = event.content_block as Record<string, unknown> | undefined;\n if (block?.type === 'tool_use') {\n state.currentToolName = (block.name as string) ?? 'unknown';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'content_block_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && typeof delta.text === 'string') {\n if (state.firstTokenAt === null) state.firstTokenAt = Date.now();\n state.textContent += delta.text;\n }\n if (delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') {\n state.currentToolArgs += delta.partial_json;\n }\n break;\n }\n case 'content_block_stop': {\n if (state.currentToolName) {\n const tc: ToolCall = { name: state.currentToolName };\n if (state.currentToolArgs) {\n try {\n tc.arguments = JSON.parse(state.currentToolArgs);\n } catch {\n tc.arguments = { raw: state.currentToolArgs };\n }\n }\n state.toolCalls.push(tc);\n state.currentToolName = '';\n state.currentToolArgs = '';\n }\n break;\n }\n case 'message_delta': {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (typeof delta?.stop_reason === 'string') state.stopReason = delta.stop_reason;\n const usage = event.usage as Record<string, number> | undefined;\n if (usage?.output_tokens) state.outputTokens = usage.output_tokens;\n break;\n }\n case 'message_stop':\n state.done = true;\n break;\n }\n } catch {\n // Ignore malformed SSE lines\n }\n}\n\nfunction processOpenAISSE(line: string, state: OpenAISSEState): void {\n if (!line.startsWith('data: ')) return;\n const json = line.slice(6).trim();\n if (json === '[DONE]') {\n state.done = true;\n return;\n }\n try {\n const event = JSON.parse(json) as Record<string, unknown>;\n if (typeof event.model === 'string' && !state.model) {\n state.model = event.model;\n }\n const choices = (event.choices ?? []) as Array<Record<string, unknown>>;\n if (choices.length > 0) {\n const choice = choices[0];\n const delta = choice.delta as Record<string, unknown> | undefined;\n if (delta) {\n if (typeof delta.content === 'string') {\n if (state.firstTokenAt === null && delta.content.length > 0)\n state.firstTokenAt = Date.now();\n state.textContent += delta.content;\n }\n if (typeof choice.finish_reason === 'string') {\n state.finishReason = choice.finish_reason;\n }\n // Tool call deltas\n const toolCallDeltas = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCallDeltas) {\n for (const tcd of toolCallDeltas) {\n const idx = (tcd.index as number) ?? 0;\n const existing = state.toolCallMap.get(idx);\n const fn = tcd.function as Record<string, unknown> | undefined;\n if (!existing) {\n state.toolCallMap.set(idx, {\n name: (fn?.name as string) ?? '',\n argumentsRaw: (fn?.arguments as string) ?? '',\n });\n } else {\n if (fn?.name) existing.name += fn.name as string;\n if (fn?.arguments) existing.argumentsRaw += fn.arguments as string;\n }\n }\n }\n }\n }\n } catch {\n // Ignore malformed SSE chunks\n }\n}\n\n// ── Build trace from streaming state ──\n\nfunction buildAnthropicStreamTrace(\n reqBody: Record<string, unknown>,\n state: AnthropicSSEState,\n durationMs: number,\n ttfbMs?: number,\n timeToFirstTokenMs?: number,\n streamingDurationMs?: number,\n providerName: string = 'anthropic'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as unknown[];\n const userMsg = extractLastUserMessage(messages);\n const systemMsg = typeof reqBody.system === 'string' ? reqBody.system : '';\n const total = state.inputTokens + state.outputTokens;\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: providerName },\n response: {\n text: state.textContent,\n finish_reason: state.stopReason || undefined,\n tokens_used: total > 0 ? total : undefined,\n },\n performance: {\n duration_ms: durationMs,\n ttfb_ms: ttfbMs,\n time_to_first_token_ms: timeToFirstTokenMs,\n streaming_duration_ms: streamingDurationMs,\n tokens_input: state.inputTokens || undefined,\n tokens_output: state.outputTokens || undefined,\n },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n if (state.toolCalls.length > 0) payload.tool_calls = state.toolCalls;\n\n return payload;\n}\n\nfunction buildOpenAIStreamTrace(\n reqBody: Record<string, unknown>,\n state: OpenAISSEState,\n durationMs: number,\n ttfbMs?: number,\n timeToFirstTokenMs?: number,\n streamingDurationMs?: number,\n providerName: string = 'openai'\n): TracePayload {\n const messages = (reqBody.messages ?? []) as Array<Record<string, unknown>>;\n let systemMsg = '';\n for (const msg of messages) {\n if (msg.role === 'system' && typeof msg.content === 'string') {\n systemMsg = msg.content;\n break;\n }\n }\n const userMsg = extractLastUserMessage(messages);\n\n const payload: TracePayload = {\n input: { user_message: userMsg },\n model: { name: state.model || (reqBody.model as string) || 'unknown', provider: providerName },\n response: {\n text: state.textContent || undefined,\n finish_reason: state.finishReason || undefined,\n },\n performance: {\n duration_ms: durationMs,\n ttfb_ms: ttfbMs,\n time_to_first_token_ms: timeToFirstTokenMs,\n streaming_duration_ms: streamingDurationMs,\n },\n };\n\n const prompt: TracePayload['prompt'] = {};\n if (systemMsg) prompt.system = systemMsg;\n if (userMsg) prompt.user = userMsg;\n if (prompt.system || prompt.user) payload.prompt = prompt;\n\n // Assemble tool calls\n if (state.toolCallMap.size > 0) {\n payload.tool_calls = Array.from(state.toolCallMap.values()).map((tc) => {\n const entry: ToolCall = { name: tc.name };\n if (tc.argumentsRaw) {\n try {\n entry.arguments = JSON.parse(tc.argumentsRaw);\n } catch {\n entry.arguments = { raw: tc.argumentsRaw };\n }\n }\n return entry;\n });\n }\n\n return payload;\n}\n\n// ── Stream wrapping ──\n\nfunction wrapStreamingResponse(\n response: Response,\n provider: DetectedProvider,\n reqBody: Record<string, unknown>,\n startTime: number,\n ttfbMs: number\n): Response {\n const body = response.body;\n if (!body) return response;\n\n const decoder = new TextDecoder();\n const state = provider.format === 'anthropic' ? makeAnthropicState() : makeOpenAIState();\n let buffer = '';\n\n const transformedStream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n // Pass through unchanged\n controller.enqueue(value);\n\n // Parse SSE lines\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (provider.format === 'anthropic') {\n processAnthropicSSE(trimmed, state as AnthropicSSEState);\n } else {\n processOpenAISSE(trimmed, state as OpenAISSEState);\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim()) {\n if (provider.format === 'anthropic') {\n processAnthropicSSE(buffer.trim(), state as AnthropicSSEState);\n } else {\n processOpenAISSE(buffer.trim(), state as OpenAISSEState);\n }\n }\n\n // Fire trace on stream end\n state.done = true;\n try {\n const endTime = Date.now();\n const durationMs = endTime - startTime;\n const firstTokenAt = state.firstTokenAt;\n const timeToFirstTokenMs = firstTokenAt ? firstTokenAt - startTime : undefined;\n const streamingDurationMs = firstTokenAt ? endTime - firstTokenAt : undefined;\n\n const tracePayload =\n provider.format === 'anthropic'\n ? buildAnthropicStreamTrace(\n reqBody,\n state as AnthropicSSEState,\n durationMs,\n ttfbMs,\n timeToFirstTokenMs,\n streamingDurationMs,\n provider.name\n )\n : buildOpenAIStreamTrace(\n reqBody,\n state as OpenAISSEState,\n durationMs,\n ttfbMs,\n timeToFirstTokenMs,\n streamingDurationMs,\n provider.name\n );\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture break the stream\n }\n\n controller.close();\n } catch (err) {\n controller.error(err);\n } finally {\n reader.releaseLock();\n }\n },\n });\n\n return new Response(transformedStream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n// ── Patched fetch ──\n\nasync function patchedFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {\n // Prevent recursion when TraceBuffer flushes\n if (_inFlight) {\n return _originalFetch!(input, init);\n }\n\n const url = resolveUrl(input);\n const provider = detectProvider(url);\n\n // Pass through non-LLM calls\n if (!provider) {\n return _originalFetch!(input, init);\n }\n\n _inFlight = true;\n const startTime = Date.now();\n let reqBody: Record<string, unknown> | null = null;\n\n try {\n reqBody = parseBodySync(init);\n } catch {\n // Can't parse request — still make the call\n }\n\n try {\n const response = await _originalFetch!(input, init);\n const ttfbMs = Date.now() - startTime;\n\n // Check if streaming\n const isStreaming = reqBody?.stream === true;\n\n if (isStreaming && reqBody) {\n try {\n return wrapStreamingResponse(response, provider, reqBody, startTime, ttfbMs);\n } catch {\n return response;\n }\n }\n\n // Non-streaming: clone and parse\n if (reqBody) {\n try {\n const clone = response.clone();\n const resBody = (await clone.json()) as Record<string, unknown>;\n const durationMs = Date.now() - startTime;\n\n const tracePayload =\n provider.format === 'anthropic'\n ? extractAnthropicTrace(reqBody, resBody, durationMs, ttfbMs, provider.name)\n : extractOpenAITrace(reqBody, resBody, durationMs, ttfbMs, provider.name);\n\n const hasToolCalls = (tracePayload.tool_calls?.length ?? 0) > 0;\n const traceType: TraceType = hasToolCalls ? 'tool_use' : 'chat';\n\n if (isInitialized()) {\n captureTrace(tracePayload, {\n sessionId: _sessionId ?? undefined,\n traceType,\n });\n }\n } catch {\n // Never let trace capture fail the request\n }\n }\n\n return response;\n } finally {\n _inFlight = false;\n }\n}\n\n// ── Public API ──\n\n/**\n * Install the fetch auto-instrumentation.\n * Reads STELVARA_API_KEY and STELVARA_AGENT_ID from environment variables.\n * Safe to call multiple times — idempotent.\n */\nexport function install(options?: { apiKey?: string; agentId?: string; endpoint?: string }): void {\n if (_installed) return;\n\n const apiKey = options?.apiKey ?? readEnvVar('STELVARA_API_KEY');\n const agentId = options?.agentId ?? readEnvVar('STELVARA_AGENT_ID');\n\n if (!isInitialized() && apiKey && agentId) {\n init({ apiKey, agentId, endpoint: options?.endpoint });\n }\n\n _originalFetch = globalThis.fetch;\n globalThis.fetch = patchedFetch as typeof globalThis.fetch;\n _installed = true;\n}\n\n/**\n * Remove the fetch patch and restore original fetch.\n */\nexport function uninstall(): void {\n if (!_installed || !_originalFetch) return;\n globalThis.fetch = _originalFetch;\n _originalFetch = null;\n _installed = false;\n}\n\n/**\n * Set a session ID for all auto-captured traces.\n * Call with null to clear.\n */\nexport function setSessionId(id: string | null): void {\n _sessionId = id;\n}\n\n/** @internal — exposed for testing only */\nexport function _resetForTesting(): void {\n if (_installed) uninstall();\n _sessionId = null;\n}\n\n// ── Side-effect: auto-install on import ──\ninstall();\n"],"mappings":"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","names":["init"]}
package/dist/index.d.cts CHANGED
@@ -33,6 +33,12 @@ interface TraceDecision {
33
33
  }
34
34
  interface TracePerformance {
35
35
  duration_ms?: number;
36
+ /** Time from fetch start until the HTTP response headers arrive (first byte). */
37
+ ttfb_ms?: number;
38
+ /** Time from fetch start until the first content token is emitted (streaming only). */
39
+ time_to_first_token_ms?: number;
40
+ /** Time from first content token to stream end (streaming only). */
41
+ streaming_duration_ms?: number;
36
42
  tokens_input?: number;
37
43
  tokens_output?: number;
38
44
  }
package/dist/index.d.ts CHANGED
@@ -33,6 +33,12 @@ interface TraceDecision {
33
33
  }
34
34
  interface TracePerformance {
35
35
  duration_ms?: number;
36
+ /** Time from fetch start until the HTTP response headers arrive (first byte). */
37
+ ttfb_ms?: number;
38
+ /** Time from fetch start until the first content token is emitted (streaming only). */
39
+ time_to_first_token_ms?: number;
40
+ /** Time from first content token to stream end (streaming only). */
41
+ streaming_duration_ms?: number;
36
42
  tokens_input?: number;
37
43
  tokens_output?: number;
38
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crevanta/stelvara-sdk",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "TypeScript SDK for Stelvara — AI Behavior Control Plane",
5
5
  "license": "MIT",
6
6
  "type": "module",