@tangle-network/agent-core 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/auth/browser.d.ts +1 -1
- package/dist/auth/browser.js +1 -2
- package/dist/auth/index.d.ts +1 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +4 -7
- package/dist/auth/index.js.map +1 -1
- package/dist/computer-use/index.d.ts +0 -6
- package/dist/computer-use/index.d.ts.map +1 -1
- package/dist/computer-use/index.js +3 -6
- package/dist/computer-use/index.js.map +1 -1
- package/dist/{errors-GuWACHTg.js → errors-B5KiSXnw.js} +2 -3
- package/dist/{errors-GuWACHTg.js.map → errors-B5KiSXnw.js.map} +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +1 -2
- package/dist/{events-U7kxNUpM.js → events-C2vdiRD_.js} +4 -7
- package/dist/{events-U7kxNUpM.js.map → events-C2vdiRD_.js.map} +1 -1
- package/dist/{index-68is3nof.d.ts → index-AEKS1kB8.d.ts} +1 -9
- package/dist/index-AEKS1kB8.d.ts.map +1 -0
- package/dist/{index-Dq6SfGaX.d.ts → index-CevHCod7.d.ts} +1 -9
- package/dist/index-CevHCod7.d.ts.map +1 -0
- package/dist/{index-CKDeaS7l.d.ts → index-DWPciD1S.d.ts} +2 -8
- package/dist/index-DWPciD1S.d.ts.map +1 -0
- package/dist/{index-BFYASYJk.d.ts → index-DwvNnL6X.d.ts} +1 -3
- package/dist/index-DwvNnL6X.d.ts.map +1 -0
- package/dist/{index-CuBvSsBf.d.ts → index-OCnM4OoP.d.ts} +1 -7
- package/dist/index-OCnM4OoP.d.ts.map +1 -0
- package/dist/{index-CQPGv8jg.d.ts → index-v0euJYbX.d.ts} +10 -12
- package/dist/index-v0euJYbX.d.ts.map +1 -0
- package/dist/index.d.ts +7 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -23
- package/dist/index.js.map +1 -1
- package/dist/interactions/index.d.ts +1 -3
- package/dist/interactions/index.d.ts.map +1 -1
- package/dist/interactions/index.js +4 -7
- package/dist/interactions/index.js.map +1 -1
- package/dist/interactions/mcp-server.d.ts +1 -3
- package/dist/interactions/mcp-server.d.ts.map +1 -1
- package/dist/interactions/mcp-server.js +2 -3
- package/dist/{lifecycle-BljLcnY5.js → lifecycle-wjO8lqUZ.js} +2 -3
- package/dist/{lifecycle-BljLcnY5.js.map → lifecycle-wjO8lqUZ.js.map} +1 -1
- package/dist/node/index.d.ts +0 -2
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -2
- package/dist/resilience/index.d.ts +1 -1
- package/dist/resilience/index.js +1 -2
- package/dist/{resilience-Cv5tqIDU.js → resilience-Bb1XDoKt.js} +5 -8
- package/dist/{resilience-Cv5tqIDU.js.map → resilience-Bb1XDoKt.js.map} +1 -1
- package/dist/sse/index.d.ts +123 -11
- package/dist/sse/index.d.ts.map +1 -1
- package/dist/sse/index.js +164 -37
- package/dist/sse/index.js.map +1 -1
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +1 -2
- package/dist/{telemetry--KwUEdTv.js → telemetry-CF4pOM9u.js} +5 -9
- package/dist/{telemetry--KwUEdTv.js.map → telemetry-CF4pOM9u.js.map} +1 -1
- package/dist/{tokens-browser-DCV3-WHc.d.ts → tokens-browser-CGLWUfZ7.d.ts} +5 -11
- package/dist/tokens-browser-CGLWUfZ7.d.ts.map +1 -0
- package/dist/{tools-DeZkIW3h.d.ts → tools-C3tUmLEu.d.ts} +1 -5
- package/dist/tools-C3tUmLEu.d.ts.map +1 -0
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +1 -2
- package/dist/{transport-DAfw3oL0.js → transport-BqEVpHWb.js} +5 -9
- package/dist/{transport-DAfw3oL0.js.map → transport-BqEVpHWb.js.map} +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -2
- package/dist/{types--u-Om3vE.js → types-BKMl10eN.js} +5 -8
- package/dist/{types--u-Om3vE.js.map → types-BKMl10eN.js.map} +1 -1
- package/dist/{types-DVN5qATk.js → types-UwiuyvMA.js} +2 -3
- package/dist/{types-DVN5qATk.js.map → types-UwiuyvMA.js.map} +1 -1
- package/package.json +9 -9
- package/dist/index-68is3nof.d.ts.map +0 -1
- package/dist/index-BFYASYJk.d.ts.map +0 -1
- package/dist/index-CKDeaS7l.d.ts.map +0 -1
- package/dist/index-CQPGv8jg.d.ts.map +0 -1
- package/dist/index-CuBvSsBf.d.ts.map +0 -1
- package/dist/index-Dq6SfGaX.d.ts.map +0 -1
- package/dist/tokens-browser-DCV3-WHc.d.ts.map +0 -1
- package/dist/tools-DeZkIW3h.d.ts.map +0 -1
package/dist/sse/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/sse/index.ts"],"sourcesContent":["/**\n * SSE Utilities\n *\n * Shared SSE parsing and stream usage extraction for the SDK.\n * Used by sdk-telemetry, sdk-memory, and other consumers.\n */\n\n/** Raw SSE event data from stream */\nexport interface SSEEventData {\n type: string;\n // Tool events\n toolName?: string;\n name?: string;\n callId?: string;\n input?: unknown;\n arguments?: unknown;\n output?: unknown;\n result?: unknown;\n durationMs?: number;\n // Error events\n error?: string;\n message?: string;\n code?: string;\n // Status events\n status?: string;\n // Token/LLM events\n tokens?: number;\n model?: string;\n // Usage fields (Anthropic format)\n inputTokens?: number;\n outputTokens?: number;\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n // Usage object (some providers wrap in usage field)\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n };\n // Generic\n [key: string]: unknown;\n}\n\n/**\n * Parse raw SSE data string into structured event data.\n *\n * Handles the standard SSE format:\n * ```\n * event: eventType\n * data: {\"json\": \"payload\"}\n * ```\n */\nexport function parseSSEData(rawData: string): SSEEventData | null {\n try {\n const lines = rawData.split(\"\\n\");\n let eventType: string | undefined;\n let data: string | undefined;\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith(\"data: \")) {\n data = line.slice(6);\n }\n }\n\n if (data) {\n const parsed = JSON.parse(data);\n return {\n type: eventType || parsed.type || \"unknown\",\n ...parsed,\n };\n }\n } catch {\n // Ignore parse errors (heartbeats, malformed events)\n }\n return null;\n}\n\n// ============================================================================\n// Stream Parsing (chunked SSE)\n// ============================================================================\n\nexport interface SSEParserOptions<T = unknown> {\n /** Log malformed data lines (default: false) */\n logMalformed?: boolean;\n /** Custom data transformer */\n transform?: (raw: string) => T | null;\n}\n\nexport interface ParsedSSEEvent<T = unknown> {\n data: T;\n rawData: string;\n eventId?: string;\n eventType?: string;\n}\n\n/**\n * Incremental SSE parser for chunked streams.\n * Feed decoded string chunks via push(); call flush() on stream end.\n */\nexport class SSEChunkParser<T = unknown> {\n private buffer = \"\";\n private currentEvent: { id?: string; event?: string; data?: string } = {};\n private options: SSEParserOptions<T>;\n\n constructor(options: SSEParserOptions<T> = {}) {\n this.options = options;\n }\n\n push(chunk: string): ParsedSSEEvent<T>[] {\n this.buffer += chunk;\n const lines = this.buffer.split(\"\\n\");\n this.buffer = lines.pop() ?? \"\";\n return this.processLines(lines);\n }\n\n flush(): ParsedSSEEvent<T>[] {\n const lines = this.buffer ? [this.buffer] : [];\n this.buffer = \"\";\n const events = this.processLines(lines);\n const finalEvent = this.parseCurrentEvent();\n if (finalEvent) {\n events.push(finalEvent);\n this.currentEvent = {};\n }\n return events;\n }\n\n private processLines(lines: string[]): ParsedSSEEvent<T>[] {\n const events: ParsedSSEEvent<T>[] = [];\n\n for (const rawLine of lines) {\n const line = rawLine.endsWith(\"\\r\") ? rawLine.slice(0, -1) : rawLine;\n\n if (line.startsWith(\":\")) {\n continue;\n }\n\n if (line === \"\") {\n const parsed = this.parseCurrentEvent();\n if (parsed) {\n events.push(parsed);\n }\n this.currentEvent = {};\n continue;\n }\n\n if (line.startsWith(\"id:\")) {\n this.currentEvent.id = line.slice(3).trim();\n continue;\n }\n\n if (line.startsWith(\"event:\")) {\n this.currentEvent.event = line.slice(6).trim();\n continue;\n }\n\n if (line.startsWith(\"data:\")) {\n let value = line.slice(5);\n if (value.startsWith(\" \")) {\n value = value.slice(1);\n }\n this.currentEvent.data =\n this.currentEvent.data !== undefined\n ? `${this.currentEvent.data}\\n${value}`\n : value;\n }\n }\n\n return events;\n }\n\n private parseCurrentEvent(): ParsedSSEEvent<T> | null {\n if (this.currentEvent.data === undefined) {\n return null;\n }\n\n const rawData = this.currentEvent.data.trim();\n if (!rawData) {\n return null;\n }\n\n let data: T;\n if (this.options.transform) {\n const transformed = this.options.transform(rawData);\n if (transformed === null) {\n if (this.options.logMalformed) {\n console.warn(\"SSE transform returned null:\", rawData.slice(0, 100));\n }\n return null;\n }\n data = transformed;\n } else {\n try {\n data = JSON.parse(rawData) as T;\n } catch {\n if (this.options.logMalformed) {\n console.warn(\"SSE parse failed:\", rawData.slice(0, 100));\n }\n data = rawData as unknown as T;\n }\n }\n\n return {\n data,\n rawData,\n eventId: this.currentEvent.id,\n eventType: this.currentEvent.event,\n };\n }\n}\n\n/**\n * Parse SSE stream into typed events via async iteration.\n */\nexport async function* parseSSEStream<T = unknown>(\n stream: ReadableStream<Uint8Array>,\n options: SSEParserOptions<T> = {},\n): AsyncGenerator<ParsedSSEEvent<T>> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n const parser = new SSEChunkParser<T>(options);\n let chunkCount = 0;\n let totalBytes = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n console.log(\n `[agent-core:sse] Stream reader done after ${chunkCount} chunks, ${totalBytes} bytes`,\n );\n break;\n }\n\n chunkCount++;\n totalBytes += value?.length ?? 0;\n const chunk = decoder.decode(value, { stream: true });\n\n // Log every chunk for debugging\n if (chunkCount <= 3 || chunkCount % 10 === 0) {\n console.log(\n `[agent-core:sse] Chunk ${chunkCount}: ${value?.length ?? 0} bytes, preview: ${chunk.slice(0, 100).replace(/\\n/g, \"\\\\n\")}`,\n );\n }\n\n for (const event of parser.push(chunk)) {\n yield event;\n }\n }\n\n for (const event of parser.flush()) {\n yield event;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Consume SSE stream with callbacks.\n */\nexport async function consumeSSEStream<T = unknown>(\n stream: ReadableStream<Uint8Array>,\n onEvent: (event: ParsedSSEEvent<T>) => void,\n onError: (error: Error) => void,\n options: SSEParserOptions<T> = {},\n): Promise<void> {\n let eventCount = 0;\n try {\n for await (const event of parseSSEStream<T>(stream, options)) {\n eventCount++;\n onEvent(event);\n }\n // Stream finished normally\n console.log(\n `[agent-core:sse] Stream ended normally, total events: ${eventCount}`,\n );\n } catch (error) {\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n const isAbortError =\n normalizedError.name === \"AbortError\" ||\n normalizedError.message.toLowerCase().includes(\"abort\");\n\n if (isAbortError) {\n console.log(\n `[agent-core:sse] Stream aborted normally after ${eventCount} events`,\n );\n onError(normalizedError);\n return;\n }\n\n console.error(\n `[agent-core:sse] Stream error after ${eventCount} events:`,\n normalizedError,\n );\n onError(normalizedError);\n }\n}\n\n// ============================================================================\n// Stream Usage Extraction\n// ============================================================================\n\n/** Token usage extracted from stream */\nexport interface StreamTokenUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n}\n\n/** Accumulated usage from a stream session */\nexport interface StreamUsageAccumulator {\n /** Total tokens from all events */\n usage: StreamTokenUsage;\n /** Number of LLM response events */\n responseCount: number;\n /** Number of tool calls */\n toolCallCount: number;\n /** Model ID if detected from events */\n modelId?: string;\n /** Start time of accumulation */\n startedAt: Date;\n /** Duration in ms (set on finalize) */\n durationMs?: number;\n}\n\n/** Event types that contain token information (Anthropic SDK format) */\nconst TOKEN_EVENTS = new Set([\"message.stop\", \"content_block_stop\"]);\n\n/** Event types for tool calls - now handled via message.part.updated */\nconst _TOOL_CALL_EVENTS = new Set<string>();\n\n/**\n * StreamUsageExtractor - Accumulates token usage from SSE events.\n *\n * Usage:\n * ```typescript\n * const extractor = createStreamUsageExtractor();\n *\n * // In your stream callback\n * for await (const event of stream) {\n * extractor.processEvent(event);\n * // ... handle event\n * }\n *\n * const usage = extractor.finalize();\n * billingService.recordUsage({ usage: usage.usage, ... });\n * ```\n */\nexport class StreamUsageExtractor {\n private accumulator: StreamUsageAccumulator;\n private finalized = false;\n\n constructor() {\n this.accumulator = {\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n },\n responseCount: 0,\n toolCallCount: 0,\n startedAt: new Date(),\n };\n }\n\n /**\n * Process a raw SSE string (event: xxx\\ndata: {...}).\n */\n processRawSSE(raw: string): void {\n const parsed = parseSSEData(raw);\n if (parsed) {\n this.processEvent(parsed);\n }\n }\n\n /**\n * Process a parsed SSE event object.\n */\n processEvent(event: SSEEventData | Record<string, unknown>): void {\n if (this.finalized) {\n throw new Error(\"StreamUsageExtractor already finalized\");\n }\n\n const eventType = (event as SSEEventData).type ?? event.type;\n\n // Handle message.part.updated (canonical event for all part types)\n if (eventType === \"message.part.updated\") {\n const part = (event as { part?: { type?: string; tool?: string } }).part;\n const partType = part?.type;\n\n // Text and reasoning parts count as LLM responses\n if (partType === \"text\" || partType === \"reasoning\") {\n this.extractTokens(event);\n this.accumulator.responseCount++;\n }\n\n // Tool parts count as tool calls\n if (partType === \"tool\" && part?.tool) {\n this.accumulator.toolCallCount++;\n }\n\n return;\n }\n\n // Extract token counts from Anthropic SDK format events\n if (TOKEN_EVENTS.has(eventType as string)) {\n this.extractTokens(event);\n this.accumulator.responseCount++;\n }\n\n // Extract model ID if present\n if (!this.accumulator.modelId && event.model) {\n this.accumulator.modelId = String(event.model);\n }\n\n // Handle usage summary events (some providers send final usage)\n if (eventType === \"usage\" || event.usage) {\n this.extractUsageSummary(event);\n }\n\n // Handle message_stop with usage (Anthropic format)\n if (eventType === \"message_stop\" && event.usage) {\n this.extractUsageSummary(event);\n }\n }\n\n /**\n * Finalize and return accumulated usage.\n * Sets duration and marks extractor as complete.\n */\n finalize(): StreamUsageAccumulator {\n if (this.finalized) {\n return this.accumulator;\n }\n\n this.finalized = true;\n this.accumulator.durationMs =\n Date.now() - this.accumulator.startedAt.getTime();\n\n return this.accumulator;\n }\n\n /**\n * Get current accumulated usage (without finalizing).\n */\n current(): Readonly<StreamUsageAccumulator> {\n return this.accumulator;\n }\n\n /**\n * Reset the extractor for reuse.\n */\n reset(): void {\n this.accumulator = {\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n },\n responseCount: 0,\n toolCallCount: 0,\n startedAt: new Date(),\n };\n this.finalized = false;\n }\n\n private extractTokens(event: Record<string, unknown>): void {\n // Direct token count (simple format)\n if (typeof event.tokens === \"number\") {\n this.accumulator.usage.outputTokens += event.tokens;\n }\n\n // Output tokens (explicit)\n if (typeof event.outputTokens === \"number\") {\n this.accumulator.usage.outputTokens += event.outputTokens;\n }\n if (typeof event.output_tokens === \"number\") {\n this.accumulator.usage.outputTokens += event.output_tokens;\n }\n\n // Input tokens (usually from final summary)\n if (typeof event.inputTokens === \"number\") {\n this.accumulator.usage.inputTokens = Math.max(\n this.accumulator.usage.inputTokens,\n event.inputTokens,\n );\n }\n if (typeof event.input_tokens === \"number\") {\n this.accumulator.usage.inputTokens = Math.max(\n this.accumulator.usage.inputTokens,\n event.input_tokens,\n );\n }\n\n // Cache tokens (Anthropic format)\n if (typeof event.cache_read_input_tokens === \"number\") {\n this.accumulator.usage.cacheReadTokens =\n (this.accumulator.usage.cacheReadTokens ?? 0) +\n event.cache_read_input_tokens;\n }\n if (typeof event.cache_creation_input_tokens === \"number\") {\n this.accumulator.usage.cacheWriteTokens =\n (this.accumulator.usage.cacheWriteTokens ?? 0) +\n event.cache_creation_input_tokens;\n }\n }\n\n private extractUsageSummary(event: Record<string, unknown>): void {\n const usage = (event.usage as Record<string, unknown>) ?? event;\n\n if (typeof usage.input_tokens === \"number\") {\n this.accumulator.usage.inputTokens = Math.max(\n this.accumulator.usage.inputTokens,\n usage.input_tokens,\n );\n }\n if (typeof usage.output_tokens === \"number\") {\n this.accumulator.usage.outputTokens = Math.max(\n this.accumulator.usage.outputTokens,\n usage.output_tokens,\n );\n }\n if (typeof usage.cache_read_input_tokens === \"number\") {\n this.accumulator.usage.cacheReadTokens = usage.cache_read_input_tokens;\n }\n if (typeof usage.cache_creation_input_tokens === \"number\") {\n this.accumulator.usage.cacheWriteTokens =\n usage.cache_creation_input_tokens;\n }\n }\n}\n\n/**\n * Create a new StreamUsageExtractor.\n */\nexport function createStreamUsageExtractor(): StreamUsageExtractor {\n return new StreamUsageExtractor();\n}\n\n/**\n * Create a callback wrapper that extracts usage while forwarding events.\n *\n * Usage:\n * ```typescript\n * const { callback, getUsage } = createUsageCallback((event) => {\n * // your event handler\n * });\n *\n * await orchestrator.executeAgentStream(projectRef, request, callback);\n * const usage = getUsage();\n * ```\n */\nexport function createUsageCallback<T extends Record<string, unknown>>(\n forward?: (event: T) => void,\n): {\n callback: (event: T) => void;\n getUsage: () => StreamUsageAccumulator;\n extractor: StreamUsageExtractor;\n} {\n const extractor = createStreamUsageExtractor();\n\n return {\n callback: (event: T) => {\n extractor.processEvent(event);\n forward?.(event);\n },\n getUsage: () => extractor.finalize(),\n extractor,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/sse/index.ts"],"sourcesContent":["/**\n * SSE Utilities\n *\n * The one Server-Sent Events wire-format implementation: frame parsing and\n * framing, plus the JSON event layer and stream usage extraction built on it.\n * Everything here is pure ECMAScript over `ReadableStream`/`TextDecoder`\n * globals and imports nothing, so Node, browser and Cloudflare Worker bundles\n * can take this subpath without pulling a dependency.\n *\n * Frame parser guarantees (WHATWG Server-Sent Events, §9.2 \"Interpreting an\n * event stream\"):\n * - Buffers frames that straddle chunk boundaries; `push()` emits only\n * complete frames and `flush()` dispatches a trailing frame whose\n * terminating blank line never arrived.\n * - Accepts LF and CRLF line terminators interchangeably. Bare CR is\n * intentionally not treated as a terminator: no known emitter uses\n * it, and supporting it would risk splitting payloads that\n * legitimately contain CR.\n * - Joins multi-line `data:` fields with \"\\n\" (§9.2.6 step 3).\n * - Skips comment lines (a leading \":\") — the heartbeat mechanism.\n * - Ignores `retry:` and unrecognized fields.\n * - Strips one leading U+FEFF (BOM) from the stream.\n * - Applies the `event:`/`id:` state of a frame only to that frame.\n *\n * The frame layer does no JSON parsing, reconnect/`Last-Event-ID` tracking or\n * event-type filtering; `SSEChunkParser` adds JSON payload handling on top.\n */\n\n/** Raw SSE event data from stream */\nexport interface SSEEventData {\n type: string;\n // Tool events\n toolName?: string;\n name?: string;\n callId?: string;\n input?: unknown;\n arguments?: unknown;\n output?: unknown;\n result?: unknown;\n durationMs?: number;\n // Error events\n error?: string;\n message?: string;\n code?: string;\n // Status events\n status?: string;\n // Token/LLM events\n tokens?: number;\n model?: string;\n // Usage fields (Anthropic format)\n inputTokens?: number;\n outputTokens?: number;\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n // Usage object (some providers wrap in usage field)\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n };\n // Generic\n [key: string]: unknown;\n}\n\n/**\n * Parse raw SSE data string into structured event data.\n *\n * Handles the standard SSE format:\n * ```\n * event: eventType\n * data: {\"json\": \"payload\"}\n * ```\n */\nexport function parseSSEData(rawData: string): SSEEventData | null {\n try {\n const lines = rawData.split(\"\\n\");\n let eventType: string | undefined;\n let data: string | undefined;\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith(\"data: \")) {\n data = line.slice(6);\n }\n }\n\n if (data) {\n const parsed = JSON.parse(data);\n return {\n type: eventType || parsed.type || \"unknown\",\n ...parsed,\n };\n }\n } catch {\n // Ignore parse errors (heartbeats, malformed events)\n }\n return null;\n}\n\n// ============================================================================\n// Frame Parsing and Framing\n// ============================================================================\n\n/**\n * One parsed SSE frame: the fields accumulated between blank lines.\n *\n * `event` and `id` share no memory with the chunk the frame arrived in, so a\n * consumer can keep them as long as it keeps its events. `data` can share\n * that memory: parse it, and keep what the parse returns.\n */\nexport interface SSEFrame {\n /** Value of the `event:` field; \"\" when the frame carries none. */\n event: string;\n /** `data:` field lines joined with \"\\n\". */\n data: string;\n /** Value of the `id:` field; undefined when the frame carries none. */\n id?: string;\n}\n\n/**\n * A copy of `value` that shares no memory with the string it was cut from.\n *\n * V8 can return a short substring of a long string as a view that keeps the\n * whole string alive. Consumers keep event names and ids as long as they keep\n * events, so a view would keep every chunk its frame arrived in alive.\n */\nfunction detached(value: string): string {\n return value === \"\" ? value : (JSON.parse(JSON.stringify(value)) as string);\n}\n\n/** Options for {@link streamSSEFrames}. */\nexport interface SSEStreamOptions {\n /**\n * Aborts the read loop. When the signal fires, the generator throws a\n * `DOMException` named \"AbortError\" so consumers can distinguish a\n * cancellation from a clean end-of-stream without inspecting signal\n * state.\n */\n signal?: AbortSignal;\n /**\n * The longest silence the stream may keep, read before each chunk read.\n * Any bytes reset it, comment heartbeats included, because a heartbeat is\n * the producer saying the connection is still live. A read that outlasts it\n * throws {@link SSEStreamSilentError}. `undefined` waits without bound, which\n * suits a producer that promises no heartbeat.\n */\n idleMs?: () => number | undefined;\n}\n\n/**\n * Thrown when a stream sends no bytes for longer than its `idleMs`. A\n * producer that heartbeats would have written in that time, so the\n * connection is dead even though it never closed: a half-open TCP connection\n * or a stalled proxy hold the socket open while nothing arrives.\n */\nexport class SSEStreamSilentError extends Error {\n override readonly name = \"SSEStreamSilentError\";\n readonly idleMs: number;\n constructor(idleMs: number) {\n super(`SSE stream sent no bytes for ${idleMs} ms`);\n this.idleMs = idleMs;\n }\n}\n\n/**\n * Incremental SSE frame parser. Feed decoded text via `push()`; call\n * `flush()` when the stream ends.\n */\nexport class SSEFrameParser {\n private buffer = \"\";\n private frameEvent = \"\";\n private frameId: string | undefined;\n private frameDataLines: string[] | null = null;\n private bomChecked = false;\n\n /**\n * Append a decoded text chunk and return every frame the chunk\n * completes. Frame state survives across calls: one frame can\n * straddle any number of chunks.\n */\n push(chunk: string): SSEFrame[] {\n let text = chunk;\n if (!this.bomChecked && text.length > 0) {\n this.bomChecked = true;\n // The spec's decode step strips one leading BOM. Without this the\n // first field line of a BOM'd stream is never recognized.\n if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);\n }\n this.buffer += text;\n const lines = this.buffer.split(\"\\n\");\n // The last element holds a line whose terminator has not arrived\n // yet; keep it for the next push.\n this.buffer = lines.pop() ?? \"\";\n return this.processLines(lines);\n }\n\n /**\n * End-of-stream: process the unterminated trailing line, then\n * dispatch a dangling frame that never got its blank line.\n * Well-behaved servers end frames with \"\\n\\n\", but some close early.\n */\n flush(): SSEFrame[] {\n const rest = this.buffer;\n this.buffer = \"\";\n const frames = rest.length > 0 ? this.processLines([rest]) : [];\n const dangling = this.dispatch();\n if (dangling) frames.push(dangling);\n return frames;\n }\n\n private processLines(lines: string[]): SSEFrame[] {\n const frames: SSEFrame[] = [];\n for (const rawLine of lines) {\n const line = rawLine.endsWith(\"\\r\") ? rawLine.slice(0, -1) : rawLine;\n if (line === \"\") {\n const frame = this.dispatch();\n if (frame) frames.push(frame);\n continue;\n }\n // Comment lines (heartbeats) carry no fields.\n if (line.startsWith(\":\")) continue;\n if (line.startsWith(\"data:\")) {\n // The spec removes one optional leading space from the value.\n let value = line.slice(5);\n if (value.startsWith(\" \")) value = value.slice(1);\n if (this.frameDataLines === null) this.frameDataLines = [];\n this.frameDataLines.push(value);\n continue;\n }\n if (line.startsWith(\"event:\")) {\n this.frameEvent = line.slice(6).trim();\n continue;\n }\n if (line.startsWith(\"id:\")) {\n this.frameId = line.slice(3).trim();\n }\n // `retry:` and unrecognized fields carry no event payload.\n }\n return frames;\n }\n\n /**\n * Dispatch the accumulated frame. A frame with no `data:` field (an\n * `event:`/`id:`-only frame, or a bare comment) produces nothing, and\n * its field state is consumed and reset either way.\n */\n private dispatch(): SSEFrame | null {\n const dataLines = this.frameDataLines;\n const frame: SSEFrame | null = dataLines\n ? {\n event: detached(this.frameEvent),\n data: dataLines.join(\"\\n\"),\n id: this.frameId ? detached(this.frameId) : undefined,\n }\n : null;\n this.frameEvent = \"\";\n this.frameId = undefined;\n this.frameDataLines = null;\n return frame;\n }\n}\n\n/**\n * Serialize one frame to its wire form. Multi-line `data` becomes one\n * `data:` line per source line so the payload survives the trip without\n * premature frame dispatch.\n */\nexport function encodeSSEFrame(frame: {\n data: string;\n event?: string;\n id?: string;\n}): string {\n let out = \"\";\n if (frame.event !== undefined) out += `event: ${frame.event}\\n`;\n if (frame.id !== undefined) out += `id: ${frame.id}\\n`;\n for (const line of frame.data.split(\"\\n\")) out += `data: ${line}\\n`;\n return `${out}\\n`;\n}\n\n/**\n * Read a streaming SSE response body into parsed frames.\n *\n * @throws `TypeError` when the body is missing.\n * @throws `DOMException` with `name === \"AbortError\"` on cancellation —\n * including the WHATWG-fetch variant where an aborted body resolves\n * `done: true` instead of rejecting the read.\n */\nexport async function* streamSSEFrames(\n body: ReadableStream<Uint8Array> | null | undefined,\n options?: SSEStreamOptions,\n): AsyncGenerator<SSEFrame> {\n if (!body) {\n throw new TypeError(\"SSE stream has no response body\");\n }\n const reader = body.getReader();\n const decoder = new TextDecoder();\n const parser = new SSEFrameParser();\n const { signal, idleMs } = options ?? {};\n const read = (): Promise<ReadableStreamReadResult<Uint8Array>> => {\n const limit = idleMs?.();\n if (limit === undefined) return reader.read();\n let timer: ReturnType<typeof setTimeout> | undefined;\n return Promise.race([\n reader.read(),\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new SSEStreamSilentError(limit)),\n limit,\n );\n }),\n ]).finally(() => clearTimeout(timer));\n };\n let completed = false;\n try {\n for (;;) {\n if (signal?.aborted) {\n throw new DOMException(\"SSE stream aborted\", \"AbortError\");\n }\n const { done, value } = await read();\n if (done) {\n if (signal?.aborted) {\n throw new DOMException(\"SSE stream aborted\", \"AbortError\");\n }\n break;\n }\n for (const frame of parser.push(\n decoder.decode(value, { stream: true }),\n )) {\n yield frame;\n }\n }\n // A final decode() flushes a partial multi-byte character left in\n // the decoder; flush() then dispatches a dangling frame.\n for (const frame of parser.push(decoder.decode())) yield frame;\n for (const frame of parser.flush()) yield frame;\n completed = true;\n } finally {\n // Returning early (a consumer that breaks on a terminal event)\n // closes the generator but does not by itself cancel a locked fetch\n // body. Cancel before releasing so the connection is not leaked\n // until the server times out.\n if (!completed) await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n}\n\n// ============================================================================\n// JSON Event Parsing (chunked SSE)\n// ============================================================================\n\nexport interface SSEParserOptions<T = unknown> {\n /** Log malformed data lines (default: false) */\n logMalformed?: boolean;\n /** Custom data transformer */\n transform?: (raw: string) => T | null;\n}\n\nexport interface ParsedSSEEvent<T = unknown> {\n data: T;\n rawData: string;\n eventId?: string;\n eventType?: string;\n}\n\n/**\n * Incremental JSON event parser for chunked streams, framed by\n * {@link SSEFrameParser}. Feed decoded string chunks via push(); call flush()\n * on stream end. Frames whose trimmed data is empty produce no event.\n */\nexport class SSEChunkParser<T = unknown> {\n private readonly frames = new SSEFrameParser();\n private readonly options: SSEParserOptions<T>;\n\n constructor(options: SSEParserOptions<T> = {}) {\n this.options = options;\n }\n\n push(chunk: string): ParsedSSEEvent<T>[] {\n return this.toEvents(this.frames.push(chunk));\n }\n\n flush(): ParsedSSEEvent<T>[] {\n return this.toEvents(this.frames.flush());\n }\n\n private toEvents(frames: SSEFrame[]): ParsedSSEEvent<T>[] {\n const events: ParsedSSEEvent<T>[] = [];\n for (const frame of frames) {\n const event = this.toEvent(frame);\n if (event) events.push(event);\n }\n return events;\n }\n\n private toEvent(frame: SSEFrame): ParsedSSEEvent<T> | null {\n const rawData = frame.data.trim();\n if (!rawData) {\n return null;\n }\n\n let data: T;\n if (this.options.transform) {\n const transformed = this.options.transform(rawData);\n if (transformed === null) {\n if (this.options.logMalformed) {\n console.warn(\"SSE transform returned null:\", rawData.slice(0, 100));\n }\n return null;\n }\n data = transformed;\n } else {\n try {\n data = JSON.parse(rawData) as T;\n } catch {\n if (this.options.logMalformed) {\n console.warn(\"SSE parse failed:\", rawData.slice(0, 100));\n }\n data = rawData as unknown as T;\n }\n }\n\n return {\n data,\n rawData,\n eventId: frame.id,\n eventType: frame.event || undefined,\n };\n }\n}\n\n/**\n * Parse SSE stream into typed events via async iteration.\n */\nexport async function* parseSSEStream<T = unknown>(\n stream: ReadableStream<Uint8Array>,\n options: SSEParserOptions<T> = {},\n): AsyncGenerator<ParsedSSEEvent<T>> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n const parser = new SSEChunkParser<T>(options);\n let chunkCount = 0;\n let totalBytes = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n console.log(\n `[agent-core:sse] Stream reader done after ${chunkCount} chunks, ${totalBytes} bytes`,\n );\n break;\n }\n\n chunkCount++;\n totalBytes += value?.length ?? 0;\n const chunk = decoder.decode(value, { stream: true });\n\n // Log every chunk for debugging\n if (chunkCount <= 3 || chunkCount % 10 === 0) {\n console.log(\n `[agent-core:sse] Chunk ${chunkCount}: ${value?.length ?? 0} bytes, preview: ${chunk.slice(0, 100).replace(/\\n/g, \"\\\\n\")}`,\n );\n }\n\n for (const event of parser.push(chunk)) {\n yield event;\n }\n }\n\n for (const event of parser.flush()) {\n yield event;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Consume SSE stream with callbacks.\n */\nexport async function consumeSSEStream<T = unknown>(\n stream: ReadableStream<Uint8Array>,\n onEvent: (event: ParsedSSEEvent<T>) => void,\n onError: (error: Error) => void,\n options: SSEParserOptions<T> = {},\n): Promise<void> {\n let eventCount = 0;\n try {\n for await (const event of parseSSEStream<T>(stream, options)) {\n eventCount++;\n onEvent(event);\n }\n // Stream finished normally\n console.log(\n `[agent-core:sse] Stream ended normally, total events: ${eventCount}`,\n );\n } catch (error) {\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n const isAbortError =\n normalizedError.name === \"AbortError\" ||\n normalizedError.message.toLowerCase().includes(\"abort\");\n\n if (isAbortError) {\n console.log(\n `[agent-core:sse] Stream aborted normally after ${eventCount} events`,\n );\n onError(normalizedError);\n return;\n }\n\n console.error(\n `[agent-core:sse] Stream error after ${eventCount} events:`,\n normalizedError,\n );\n onError(normalizedError);\n }\n}\n\n// ============================================================================\n// Stream Usage Extraction\n// ============================================================================\n\n/** Token usage extracted from stream */\nexport interface StreamTokenUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n}\n\n/** Accumulated usage from a stream session */\nexport interface StreamUsageAccumulator {\n /** Total tokens from all events */\n usage: StreamTokenUsage;\n /** Number of LLM response events */\n responseCount: number;\n /** Number of tool calls */\n toolCallCount: number;\n /** Model ID if detected from events */\n modelId?: string;\n /** Start time of accumulation */\n startedAt: Date;\n /** Duration in ms (set on finalize) */\n durationMs?: number;\n}\n\n/** Event types that contain token information (Anthropic SDK format) */\nconst TOKEN_EVENTS = new Set([\"message.stop\", \"content_block_stop\"]);\n\n/** Event types for tool calls - now handled via message.part.updated */\nconst _TOOL_CALL_EVENTS = new Set<string>();\n\n/**\n * StreamUsageExtractor - Accumulates token usage from SSE events.\n *\n * Usage:\n * ```typescript\n * const extractor = createStreamUsageExtractor();\n *\n * // In your stream callback\n * for await (const event of stream) {\n * extractor.processEvent(event);\n * // ... handle event\n * }\n *\n * const usage = extractor.finalize();\n * billingService.recordUsage({ usage: usage.usage, ... });\n * ```\n */\nexport class StreamUsageExtractor {\n private accumulator: StreamUsageAccumulator;\n private finalized = false;\n\n constructor() {\n this.accumulator = {\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n },\n responseCount: 0,\n toolCallCount: 0,\n startedAt: new Date(),\n };\n }\n\n /**\n * Process a raw SSE string (event: xxx\\ndata: {...}).\n */\n processRawSSE(raw: string): void {\n const parsed = parseSSEData(raw);\n if (parsed) {\n this.processEvent(parsed);\n }\n }\n\n /**\n * Process a parsed SSE event object.\n */\n processEvent(event: SSEEventData | Record<string, unknown>): void {\n if (this.finalized) {\n throw new Error(\"StreamUsageExtractor already finalized\");\n }\n\n const eventType = (event as SSEEventData).type ?? event.type;\n\n // Handle message.part.updated (canonical event for all part types)\n if (eventType === \"message.part.updated\") {\n const part = (event as { part?: { type?: string; tool?: string } }).part;\n const partType = part?.type;\n\n // Text and reasoning parts count as LLM responses\n if (partType === \"text\" || partType === \"reasoning\") {\n this.extractTokens(event);\n this.accumulator.responseCount++;\n }\n\n // Tool parts count as tool calls\n if (partType === \"tool\" && part?.tool) {\n this.accumulator.toolCallCount++;\n }\n\n return;\n }\n\n // Extract token counts from Anthropic SDK format events\n if (TOKEN_EVENTS.has(eventType as string)) {\n this.extractTokens(event);\n this.accumulator.responseCount++;\n }\n\n // Extract model ID if present\n if (!this.accumulator.modelId && event.model) {\n this.accumulator.modelId = String(event.model);\n }\n\n // Handle usage summary events (some providers send final usage)\n if (eventType === \"usage\" || event.usage) {\n this.extractUsageSummary(event);\n }\n\n // Handle message_stop with usage (Anthropic format)\n if (eventType === \"message_stop\" && event.usage) {\n this.extractUsageSummary(event);\n }\n }\n\n /**\n * Finalize and return accumulated usage.\n * Sets duration and marks extractor as complete.\n */\n finalize(): StreamUsageAccumulator {\n if (this.finalized) {\n return this.accumulator;\n }\n\n this.finalized = true;\n this.accumulator.durationMs =\n Date.now() - this.accumulator.startedAt.getTime();\n\n return this.accumulator;\n }\n\n /**\n * Get current accumulated usage (without finalizing).\n */\n current(): Readonly<StreamUsageAccumulator> {\n return this.accumulator;\n }\n\n /**\n * Reset the extractor for reuse.\n */\n reset(): void {\n this.accumulator = {\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n },\n responseCount: 0,\n toolCallCount: 0,\n startedAt: new Date(),\n };\n this.finalized = false;\n }\n\n private extractTokens(event: Record<string, unknown>): void {\n // Direct token count (simple format)\n if (typeof event.tokens === \"number\") {\n this.accumulator.usage.outputTokens += event.tokens;\n }\n\n // Output tokens (explicit)\n if (typeof event.outputTokens === \"number\") {\n this.accumulator.usage.outputTokens += event.outputTokens;\n }\n if (typeof event.output_tokens === \"number\") {\n this.accumulator.usage.outputTokens += event.output_tokens;\n }\n\n // Input tokens (usually from final summary)\n if (typeof event.inputTokens === \"number\") {\n this.accumulator.usage.inputTokens = Math.max(\n this.accumulator.usage.inputTokens,\n event.inputTokens,\n );\n }\n if (typeof event.input_tokens === \"number\") {\n this.accumulator.usage.inputTokens = Math.max(\n this.accumulator.usage.inputTokens,\n event.input_tokens,\n );\n }\n\n // Cache tokens (Anthropic format)\n if (typeof event.cache_read_input_tokens === \"number\") {\n this.accumulator.usage.cacheReadTokens =\n (this.accumulator.usage.cacheReadTokens ?? 0) +\n event.cache_read_input_tokens;\n }\n if (typeof event.cache_creation_input_tokens === \"number\") {\n this.accumulator.usage.cacheWriteTokens =\n (this.accumulator.usage.cacheWriteTokens ?? 0) +\n event.cache_creation_input_tokens;\n }\n }\n\n private extractUsageSummary(event: Record<string, unknown>): void {\n const usage = (event.usage as Record<string, unknown>) ?? event;\n\n if (typeof usage.input_tokens === \"number\") {\n this.accumulator.usage.inputTokens = Math.max(\n this.accumulator.usage.inputTokens,\n usage.input_tokens,\n );\n }\n if (typeof usage.output_tokens === \"number\") {\n this.accumulator.usage.outputTokens = Math.max(\n this.accumulator.usage.outputTokens,\n usage.output_tokens,\n );\n }\n if (typeof usage.cache_read_input_tokens === \"number\") {\n this.accumulator.usage.cacheReadTokens = usage.cache_read_input_tokens;\n }\n if (typeof usage.cache_creation_input_tokens === \"number\") {\n this.accumulator.usage.cacheWriteTokens =\n usage.cache_creation_input_tokens;\n }\n }\n}\n\n/**\n * Create a new StreamUsageExtractor.\n */\nexport function createStreamUsageExtractor(): StreamUsageExtractor {\n return new StreamUsageExtractor();\n}\n\n/**\n * Create a callback wrapper that extracts usage while forwarding events.\n *\n * Usage:\n * ```typescript\n * const { callback, getUsage } = createUsageCallback((event) => {\n * // your event handler\n * });\n *\n * await orchestrator.executeAgentStream(projectRef, request, callback);\n * const usage = getUsage();\n * ```\n */\nexport function createUsageCallback<T extends Record<string, unknown>>(\n forward?: (event: T) => void,\n): {\n callback: (event: T) => void;\n getUsage: () => StreamUsageAccumulator;\n extractor: StreamUsageExtractor;\n} {\n const extractor = createStreamUsageExtractor();\n\n return {\n callback: (event: T) => {\n extractor.processEvent(event);\n forward?.(event);\n },\n getUsage: () => extractor.finalize(),\n extractor,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as CONTENT_KEY_FIELD, A as TANGLE_SUBJECT_KEY, B as GEN_AI_MODEL_KEYS, C as RUN_KEY_ATTRS, D as TANGLE_RUN_ID_KEY, E as TANGLE_RUN_ID_CAMEL_KEY, F as TRIGGER_ID_ATTRS, G as GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, H as GEN_AI_OUTPUT_TOKEN_KEYS, I as TRIGGER_SOURCE_ATTRS, J as GEN_AI_USAGE_OUTPUT_TOKENS, K as GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, L as WORKFLOW_MARKER_ATTRS, M as TANGLE_TRIGGER_KIND_KEY, N as TANGLE_TRIGGER_SOURCE_KEY, O as TANGLE_RUN_KIND_KEY, P as TANGLE_WORKFLOW_ID_KEY, Q as CONTENT_KEYS, R as GEN_AI_CONVERSATION_ID, S as PARENT_RUN_KEY_ATTRS, T as SUBJECT_KEY_ATTRS, U as GEN_AI_REQUEST_MODEL, V as GEN_AI_OPERATION_NAME, W as GEN_AI_RESPONSE_MODEL, X as genAiUsageAttributes, Y as GenAiUsage, Z as CONTENT_COMPLETION_KEY, _ as promptTokens, a as TOKEN_USAGE_CACHE_READ_INCLUSIVE_KEYS, at as DeclaredIntentMatch, b as tokenCount, c as TOKEN_USAGE_COST_KEYS, ct as asContentField, d as TOKEN_USAGE_OUTPUT_KEYS, dt as declaredTaskText, et as CONTENT_PROMPT_KEY, f as TokenUsageCounts, ft as extractContent, g as firstUsageCostUsd, gt as resolveDeclaredIntent, h as firstTokenCount, ht as normalizeContentAttributes, i as TOKEN_USAGE_CACHE_READ_EXCLUSIVE_KEYS, it as ContentField, j as TANGLE_TRIGGER_ID_KEY, k as TANGLE_RUN_PARENT_ID_KEY, l as TOKEN_USAGE_DETAIL_KEYS, lt as asContentString, m as cacheHitRate, mt as logRecordContentBag, n as TOKEN_USAGE_CACHE_BAG_READ_KEYS, nt as CONTENT_TOOL_NAME_KEY, o as TOKEN_USAGE_CACHE_WRITE_EXCLUSIVE_KEYS, ot as INDEXED_CONTENT_KEY_LIKE_PATTERNS, p as addTokenUsage, pt as hasContent, q as GEN_AI_USAGE_INPUT_TOKENS, r as TOKEN_USAGE_CACHE_BAG_WRITE_KEYS, rt as CONTENT_TOOL_RESULT_KEY, s as TOKEN_USAGE_CACHE_WRITE_INCLUSIVE_KEYS, st as NormalizedContent, t as TOKEN_USAGE_CACHE_BAG_KEYS, tt as CONTENT_TOOL_ARGS_KEY, u as TOKEN_USAGE_INPUT_KEYS, ut as classifyAgent, v as readTokenCostUsd, w as RUN_KIND_ATTRS, x as tokenUsageSource, y as readTokenUsage, z as GEN_AI_INPUT_TOKEN_KEYS } from "../index-
|
|
1
|
+
import { $ as CONTENT_KEY_FIELD, A as TANGLE_SUBJECT_KEY, B as GEN_AI_MODEL_KEYS, C as RUN_KEY_ATTRS, D as TANGLE_RUN_ID_KEY, E as TANGLE_RUN_ID_CAMEL_KEY, F as TRIGGER_ID_ATTRS, G as GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, H as GEN_AI_OUTPUT_TOKEN_KEYS, I as TRIGGER_SOURCE_ATTRS, J as GEN_AI_USAGE_OUTPUT_TOKENS, K as GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, L as WORKFLOW_MARKER_ATTRS, M as TANGLE_TRIGGER_KIND_KEY, N as TANGLE_TRIGGER_SOURCE_KEY, O as TANGLE_RUN_KIND_KEY, P as TANGLE_WORKFLOW_ID_KEY, Q as CONTENT_KEYS, R as GEN_AI_CONVERSATION_ID, S as PARENT_RUN_KEY_ATTRS, T as SUBJECT_KEY_ATTRS, U as GEN_AI_REQUEST_MODEL, V as GEN_AI_OPERATION_NAME, W as GEN_AI_RESPONSE_MODEL, X as genAiUsageAttributes, Y as GenAiUsage, Z as CONTENT_COMPLETION_KEY, _ as promptTokens, a as TOKEN_USAGE_CACHE_READ_INCLUSIVE_KEYS, at as DeclaredIntentMatch, b as tokenCount, c as TOKEN_USAGE_COST_KEYS, ct as asContentField, d as TOKEN_USAGE_OUTPUT_KEYS, dt as declaredTaskText, et as CONTENT_PROMPT_KEY, f as TokenUsageCounts, ft as extractContent, g as firstUsageCostUsd, gt as resolveDeclaredIntent, h as firstTokenCount, ht as normalizeContentAttributes, i as TOKEN_USAGE_CACHE_READ_EXCLUSIVE_KEYS, it as ContentField, j as TANGLE_TRIGGER_ID_KEY, k as TANGLE_RUN_PARENT_ID_KEY, l as TOKEN_USAGE_DETAIL_KEYS, lt as asContentString, m as cacheHitRate, mt as logRecordContentBag, n as TOKEN_USAGE_CACHE_BAG_READ_KEYS, nt as CONTENT_TOOL_NAME_KEY, o as TOKEN_USAGE_CACHE_WRITE_EXCLUSIVE_KEYS, ot as INDEXED_CONTENT_KEY_LIKE_PATTERNS, p as addTokenUsage, pt as hasContent, q as GEN_AI_USAGE_INPUT_TOKENS, r as TOKEN_USAGE_CACHE_BAG_WRITE_KEYS, rt as CONTENT_TOOL_RESULT_KEY, s as TOKEN_USAGE_CACHE_WRITE_INCLUSIVE_KEYS, st as NormalizedContent, t as TOKEN_USAGE_CACHE_BAG_KEYS, tt as CONTENT_TOOL_ARGS_KEY, u as TOKEN_USAGE_INPUT_KEYS, ut as classifyAgent, v as readTokenCostUsd, w as RUN_KIND_ATTRS, x as tokenUsageSource, y as readTokenUsage, z as GEN_AI_INPUT_TOKEN_KEYS } from "../index-AEKS1kB8.js";
|
|
2
2
|
export { CONTENT_COMPLETION_KEY, CONTENT_KEYS, CONTENT_KEY_FIELD, CONTENT_PROMPT_KEY, CONTENT_TOOL_ARGS_KEY, CONTENT_TOOL_NAME_KEY, CONTENT_TOOL_RESULT_KEY, type ContentField, type DeclaredIntentMatch, GEN_AI_CONVERSATION_ID, GEN_AI_INPUT_TOKEN_KEYS, GEN_AI_MODEL_KEYS, GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_TOKEN_KEYS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, type GenAiUsage, INDEXED_CONTENT_KEY_LIKE_PATTERNS, type NormalizedContent, PARENT_RUN_KEY_ATTRS, RUN_KEY_ATTRS, RUN_KIND_ATTRS, SUBJECT_KEY_ATTRS, TANGLE_RUN_ID_CAMEL_KEY, TANGLE_RUN_ID_KEY, TANGLE_RUN_KIND_KEY, TANGLE_RUN_PARENT_ID_KEY, TANGLE_SUBJECT_KEY, TANGLE_TRIGGER_ID_KEY, TANGLE_TRIGGER_KIND_KEY, TANGLE_TRIGGER_SOURCE_KEY, TANGLE_WORKFLOW_ID_KEY, TOKEN_USAGE_CACHE_BAG_KEYS, TOKEN_USAGE_CACHE_BAG_READ_KEYS, TOKEN_USAGE_CACHE_BAG_WRITE_KEYS, TOKEN_USAGE_CACHE_READ_EXCLUSIVE_KEYS, TOKEN_USAGE_CACHE_READ_INCLUSIVE_KEYS, TOKEN_USAGE_CACHE_WRITE_EXCLUSIVE_KEYS, TOKEN_USAGE_CACHE_WRITE_INCLUSIVE_KEYS, TOKEN_USAGE_COST_KEYS, TOKEN_USAGE_DETAIL_KEYS, TOKEN_USAGE_INPUT_KEYS, TOKEN_USAGE_OUTPUT_KEYS, TRIGGER_ID_ATTRS, TRIGGER_SOURCE_ATTRS, type TokenUsageCounts, WORKFLOW_MARKER_ATTRS, addTokenUsage, asContentField, asContentString, cacheHitRate, classifyAgent, declaredTaskText, extractContent, firstTokenCount, firstUsageCostUsd, genAiUsageAttributes, hasContent, logRecordContentBag, normalizeContentAttributes, promptTokens, readTokenCostUsd, readTokenUsage, resolveDeclaredIntent, tokenCount, tokenUsageSource };
|
package/dist/telemetry/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { CONTENT_COMPLETION_KEY, CONTENT_KEYS, CONTENT_KEY_FIELD, CONTENT_PROMPT_KEY, CONTENT_TOOL_ARGS_KEY, CONTENT_TOOL_NAME_KEY, CONTENT_TOOL_RESULT_KEY, GEN_AI_CONVERSATION_ID, GEN_AI_INPUT_TOKEN_KEYS, GEN_AI_MODEL_KEYS, GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_TOKEN_KEYS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, INDEXED_CONTENT_KEY_LIKE_PATTERNS, PARENT_RUN_KEY_ATTRS, RUN_KEY_ATTRS, RUN_KIND_ATTRS, SUBJECT_KEY_ATTRS, TANGLE_RUN_ID_CAMEL_KEY, TANGLE_RUN_ID_KEY, TANGLE_RUN_KIND_KEY, TANGLE_RUN_PARENT_ID_KEY, TANGLE_SUBJECT_KEY, TANGLE_TRIGGER_ID_KEY, TANGLE_TRIGGER_KIND_KEY, TANGLE_TRIGGER_SOURCE_KEY, TANGLE_WORKFLOW_ID_KEY, TOKEN_USAGE_CACHE_BAG_KEYS, TOKEN_USAGE_CACHE_BAG_READ_KEYS, TOKEN_USAGE_CACHE_BAG_WRITE_KEYS, TOKEN_USAGE_CACHE_READ_EXCLUSIVE_KEYS, TOKEN_USAGE_CACHE_READ_INCLUSIVE_KEYS, TOKEN_USAGE_CACHE_WRITE_EXCLUSIVE_KEYS, TOKEN_USAGE_CACHE_WRITE_INCLUSIVE_KEYS, TOKEN_USAGE_COST_KEYS, TOKEN_USAGE_DETAIL_KEYS, TOKEN_USAGE_INPUT_KEYS, TOKEN_USAGE_OUTPUT_KEYS, TRIGGER_ID_ATTRS, TRIGGER_SOURCE_ATTRS, WORKFLOW_MARKER_ATTRS, addTokenUsage, asContentField, asContentString, cacheHitRate, classifyAgent, declaredTaskText, extractContent, firstTokenCount, firstUsageCostUsd, genAiUsageAttributes, hasContent, logRecordContentBag, normalizeContentAttributes, promptTokens, readTokenCostUsd, readTokenUsage, resolveDeclaredIntent, tokenCount, tokenUsageSource };
|
|
1
|
+
export { Y as CONTENT_COMPLETION_KEY, X as CONTENT_KEYS, Z as CONTENT_KEY_FIELD, Q as CONTENT_PROMPT_KEY, $ as CONTENT_TOOL_ARGS_KEY, et as CONTENT_TOOL_NAME_KEY, tt as CONTENT_TOOL_RESULT_KEY, L as GEN_AI_CONVERSATION_ID, R as GEN_AI_INPUT_TOKEN_KEYS, z as GEN_AI_MODEL_KEYS, B as GEN_AI_OPERATION_NAME, V as GEN_AI_OUTPUT_TOKEN_KEYS, H as GEN_AI_REQUEST_MODEL, U as GEN_AI_RESPONSE_MODEL, W as GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, G as GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, K as GEN_AI_USAGE_INPUT_TOKENS, q as GEN_AI_USAGE_OUTPUT_TOKENS, nt as INDEXED_CONTENT_KEY_LIKE_PATTERNS, x as PARENT_RUN_KEY_ATTRS, S as RUN_KEY_ATTRS, C as RUN_KIND_ATTRS, w as SUBJECT_KEY_ATTRS, T as TANGLE_RUN_ID_CAMEL_KEY, E as TANGLE_RUN_ID_KEY, D as TANGLE_RUN_KIND_KEY, O as TANGLE_RUN_PARENT_ID_KEY, k as TANGLE_SUBJECT_KEY, A as TANGLE_TRIGGER_ID_KEY, j as TANGLE_TRIGGER_KIND_KEY, M as TANGLE_TRIGGER_SOURCE_KEY, N as TANGLE_WORKFLOW_ID_KEY, t as TOKEN_USAGE_CACHE_BAG_KEYS, n as TOKEN_USAGE_CACHE_BAG_READ_KEYS, r as TOKEN_USAGE_CACHE_BAG_WRITE_KEYS, i as TOKEN_USAGE_CACHE_READ_EXCLUSIVE_KEYS, a as TOKEN_USAGE_CACHE_READ_INCLUSIVE_KEYS, o as TOKEN_USAGE_CACHE_WRITE_EXCLUSIVE_KEYS, s as TOKEN_USAGE_CACHE_WRITE_INCLUSIVE_KEYS, c as TOKEN_USAGE_COST_KEYS, l as TOKEN_USAGE_DETAIL_KEYS, u as TOKEN_USAGE_INPUT_KEYS, d as TOKEN_USAGE_OUTPUT_KEYS, P as TRIGGER_ID_ATTRS, F as TRIGGER_SOURCE_ATTRS, I as WORKFLOW_MARKER_ATTRS, f as addTokenUsage, rt as asContentField, it as asContentString, p as cacheHitRate, at as classifyAgent, ot as declaredTaskText, st as extractContent, m as firstTokenCount, h as firstUsageCostUsd, J as genAiUsageAttributes, ct as hasContent, lt as logRecordContentBag, ut as normalizeContentAttributes, g as promptTokens, _ as readTokenCostUsd, v as readTokenUsage, dt as resolveDeclaredIntent, y as tokenCount, b as tokenUsageSource } from "../telemetry-CF4pOM9u.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ATTR, INPUT_TOKEN_ATTR_KEYS, MODEL_ATTR_KEYS, OPERATION_NAME_ATTR, OUTPUT_TOKEN_ATTR_KEYS } from "@tangle-network/agent-trace-contract";
|
|
2
|
-
|
|
2
|
+
// src/telemetry/content-attributes.ts
|
|
3
3
|
/** Prompt / user message — the writer key OpenCode's user-turn export uses. */
|
|
4
4
|
const CONTENT_PROMPT_KEY = "gen_ai.prompt";
|
|
5
5
|
/** Model completion — the writer key OpenCode's assistant-text export uses. */
|
|
@@ -658,8 +658,7 @@ function declaredTaskText(content) {
|
|
|
658
658
|
}
|
|
659
659
|
return null;
|
|
660
660
|
}
|
|
661
|
-
|
|
662
|
-
//#region src/telemetry/genai-attributes.ts
|
|
661
|
+
// src/telemetry/genai-attributes.ts
|
|
663
662
|
/**
|
|
664
663
|
* OpenTelemetry GenAI semantic-convention span attributes — the vocabulary every
|
|
665
664
|
* trace producer in the stack emits and the intelligence ingest reads, so model /
|
|
@@ -722,8 +721,7 @@ function genAiUsageAttributes(usage) {
|
|
|
722
721
|
if (isNonNegativeFinite(usage.cacheWriteTokens)) attrs[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] = usage.cacheWriteTokens;
|
|
723
722
|
return attrs;
|
|
724
723
|
}
|
|
725
|
-
|
|
726
|
-
//#region src/telemetry/spine-attributes.ts
|
|
724
|
+
// src/telemetry/spine-attributes.ts
|
|
727
725
|
/**
|
|
728
726
|
* Spine run/subject/tree + trigger span-attribute vocabulary — the single set of
|
|
729
727
|
* `tangle.*` keys that name a run, its parent, its kind, its trigger, and the
|
|
@@ -808,8 +806,7 @@ const WORKFLOW_MARKER_ATTRS = Object.freeze([
|
|
|
808
806
|
"tangle.workflow.run.id",
|
|
809
807
|
"tangle.workflow.runId"
|
|
810
808
|
]);
|
|
811
|
-
|
|
812
|
-
//#region src/telemetry/token-usage.ts
|
|
809
|
+
// src/telemetry/token-usage.ts
|
|
813
810
|
/**
|
|
814
811
|
* Candidate field names for a producer-supplied `tokenUsage` bag on a
|
|
815
812
|
* `message.updated` trace event. Agents and SDK layers disagree on the
|
|
@@ -1077,7 +1074,6 @@ function cacheHitRate(usage) {
|
|
|
1077
1074
|
const total = promptTokens(usage);
|
|
1078
1075
|
return total === 0 ? 0 : (usage.cacheReadTokens ?? 0) / total;
|
|
1079
1076
|
}
|
|
1080
|
-
//#endregion
|
|
1081
1077
|
export { CONTENT_TOOL_ARGS_KEY as $, TANGLE_TRIGGER_ID_KEY as A, GEN_AI_OPERATION_NAME as B, RUN_KIND_ATTRS as C, TANGLE_RUN_KIND_KEY as D, TANGLE_RUN_ID_KEY as E, TRIGGER_SOURCE_ATTRS as F, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS as G, GEN_AI_REQUEST_MODEL as H, WORKFLOW_MARKER_ATTRS as I, genAiUsageAttributes as J, GEN_AI_USAGE_INPUT_TOKENS as K, GEN_AI_CONVERSATION_ID as L, TANGLE_TRIGGER_SOURCE_KEY as M, TANGLE_WORKFLOW_ID_KEY as N, TANGLE_RUN_PARENT_ID_KEY as O, TRIGGER_ID_ATTRS as P, CONTENT_PROMPT_KEY as Q, GEN_AI_INPUT_TOKEN_KEYS as R, RUN_KEY_ATTRS as S, TANGLE_RUN_ID_CAMEL_KEY as T, GEN_AI_RESPONSE_MODEL as U, GEN_AI_OUTPUT_TOKEN_KEYS as V, GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS as W, CONTENT_KEYS as X, CONTENT_COMPLETION_KEY as Y, CONTENT_KEY_FIELD as Z, readTokenCostUsd as _, TOKEN_USAGE_CACHE_READ_INCLUSIVE_KEYS as a, classifyAgent as at, tokenUsageSource as b, TOKEN_USAGE_COST_KEYS as c, hasContent as ct, TOKEN_USAGE_OUTPUT_KEYS as d, resolveDeclaredIntent as dt, CONTENT_TOOL_NAME_KEY as et, addTokenUsage as f, promptTokens as g, firstUsageCostUsd as h, TOKEN_USAGE_CACHE_READ_EXCLUSIVE_KEYS as i, asContentString as it, TANGLE_TRIGGER_KIND_KEY as j, TANGLE_SUBJECT_KEY as k, TOKEN_USAGE_DETAIL_KEYS as l, logRecordContentBag as lt, firstTokenCount as m, TOKEN_USAGE_CACHE_BAG_READ_KEYS as n, INDEXED_CONTENT_KEY_LIKE_PATTERNS as nt, TOKEN_USAGE_CACHE_WRITE_EXCLUSIVE_KEYS as o, declaredTaskText as ot, cacheHitRate as p, GEN_AI_USAGE_OUTPUT_TOKENS as q, TOKEN_USAGE_CACHE_BAG_WRITE_KEYS as r, asContentField as rt, TOKEN_USAGE_CACHE_WRITE_INCLUSIVE_KEYS as s, extractContent as st, TOKEN_USAGE_CACHE_BAG_KEYS as t, CONTENT_TOOL_RESULT_KEY as tt, TOKEN_USAGE_INPUT_KEYS as u, normalizeContentAttributes as ut, readTokenUsage as v, SUBJECT_KEY_ATTRS as w, PARENT_RUN_KEY_ATTRS as x, tokenCount as y, GEN_AI_MODEL_KEYS as z };
|
|
1082
1078
|
|
|
1083
|
-
//# sourceMappingURL=telemetry
|
|
1079
|
+
//# sourceMappingURL=telemetry-CF4pOM9u.js.map
|