@botpress/evals 1.19.0-beta.9 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/client.d.ts +21 -9
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +104 -75
  4. package/dist/client.js.map +4 -4
  5. package/dist/definition.d.ts +9 -2
  6. package/dist/definition.d.ts.map +1 -1
  7. package/dist/definition.js.map +2 -2
  8. package/dist/graders/index.js +21 -74
  9. package/dist/graders/index.js.map +2 -2
  10. package/dist/graders/llm.d.ts +12 -12
  11. package/dist/graders/llm.d.ts.map +1 -1
  12. package/dist/graders/llm.js +30 -75
  13. package/dist/graders/llm.js.map +2 -2
  14. package/dist/graders/response.js +16 -65
  15. package/dist/graders/response.js.map +2 -2
  16. package/dist/index.d.ts +3 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +10 -0
  19. package/dist/index.js.map +3 -3
  20. package/dist/loader.js +1 -39
  21. package/dist/loader.js.map +3 -3
  22. package/dist/manifest.d.ts +13 -0
  23. package/dist/manifest.d.ts.map +1 -0
  24. package/dist/runner.d.ts +5 -1
  25. package/dist/runner.d.ts.map +1 -1
  26. package/dist/runner.js +318 -227
  27. package/dist/runner.js.map +4 -4
  28. package/dist/spans/index.d.ts +5 -0
  29. package/dist/spans/index.d.ts.map +1 -0
  30. package/dist/spans/index.js +687 -0
  31. package/dist/spans/index.js.map +7 -0
  32. package/dist/spans/local-span-source.d.ts +19 -0
  33. package/dist/spans/local-span-source.d.ts.map +1 -0
  34. package/dist/spans/local-span-source.js +505 -0
  35. package/dist/spans/local-span-source.js.map +7 -0
  36. package/dist/spans/span-source.d.ts +40 -0
  37. package/dist/spans/span-source.d.ts.map +1 -0
  38. package/dist/spans/span-source.js +1 -0
  39. package/dist/{sse-collector.d.ts → spans/sse-collector.d.ts} +44 -5
  40. package/dist/spans/sse-collector.d.ts.map +1 -0
  41. package/dist/{sse-collector.js → spans/sse-collector.js} +98 -59
  42. package/dist/spans/sse-collector.js.map +7 -0
  43. package/dist/spans/trace.d.ts.map +1 -0
  44. package/dist/spans/trace.js.map +7 -0
  45. package/dist/spans/vortex-span-source.d.ts +42 -0
  46. package/dist/spans/vortex-span-source.d.ts.map +1 -0
  47. package/dist/spans/vortex-span-source.js +184 -0
  48. package/dist/spans/vortex-span-source.js.map +7 -0
  49. package/dist/stores/eval-definition-loader.d.ts +4 -0
  50. package/dist/stores/eval-definition-loader.d.ts.map +1 -0
  51. package/dist/stores/eval-definition-loader.js +104 -0
  52. package/dist/stores/eval-definition-loader.js.map +7 -0
  53. package/dist/stores/eval-store.d.ts +61 -0
  54. package/dist/stores/eval-store.d.ts.map +1 -0
  55. package/dist/stores/eval-store.js +1 -0
  56. package/dist/stores/eval-store.js.map +7 -0
  57. package/dist/stores/index.d.ts +8 -0
  58. package/dist/stores/index.d.ts.map +1 -0
  59. package/dist/stores/index.js +1118 -0
  60. package/dist/stores/index.js.map +7 -0
  61. package/dist/stores/local-eval-store.d.ts +66 -0
  62. package/dist/stores/local-eval-store.d.ts.map +1 -0
  63. package/dist/stores/local-eval-store.js +557 -0
  64. package/dist/stores/local-eval-store.js.map +7 -0
  65. package/dist/stores/vortex-eval-store-entry.d.ts +6 -0
  66. package/dist/stores/vortex-eval-store-entry.d.ts.map +1 -0
  67. package/dist/stores/vortex-eval-store-entry.js +664 -0
  68. package/dist/stores/vortex-eval-store-entry.js.map +7 -0
  69. package/dist/stores/vortex-eval-store.d.ts +78 -0
  70. package/dist/stores/vortex-eval-store.d.ts.map +1 -0
  71. package/dist/stores/vortex-eval-store.js +562 -0
  72. package/dist/stores/vortex-eval-store.js.map +7 -0
  73. package/dist/transformer.d.ts +2 -2
  74. package/dist/transformer.d.ts.map +1 -1
  75. package/dist/transformer.js +1 -1
  76. package/dist/transformer.js.map +2 -2
  77. package/dist/types.d.ts +38 -33
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/types.js.map +2 -2
  80. package/package.json +66 -6
  81. package/dist/sse-collector.d.ts.map +0 -1
  82. package/dist/sse-collector.js.map +0 -7
  83. package/dist/trace.d.ts.map +0 -1
  84. /package/dist/{trace.js.map → spans/span-source.js.map} +0 -0
  85. /package/dist/{trace.d.ts → spans/trace.d.ts} +0 -0
  86. /package/dist/{trace.js → spans/trace.js} +0 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/errors.ts", "../../src/spans/sse-collector.ts", "../../src/spans/local-span-source.ts"],
4
+ "sourcesContent": ["import { AdkError } from '@botpress/analytics'\n\n/**\n * Stable error codes for the eval engine. Typed as a literal union so a typo'd\n * code is a compile error and PostHog issue-slicing stays consistent.\n * Conventions per docs/ERROR-HANDLING.md.\n */\nexport const EVAL_ERROR_CODES = [\n 'EVAL_LOAD_FAILED',\n 'EVAL_FILE_EMPTY',\n 'EVAL_DUPLICATE_NAME',\n 'EVAL_SEED_NO_CONVERSATION',\n 'EVAL_NO_CONVERSATION_ID',\n 'EVAL_TURN_CONFIG_INVALID',\n 'SSE_CONNECT_FAILED',\n 'SSE_NO_BODY',\n 'CHAT_CLIENT_MISSING',\n 'CHAT_NOT_CONNECTED',\n 'CHAT_INTEGRATION_MISSING',\n 'CHAT_CHANNEL_UNBOUND',\n] as const\n\nexport type EvalErrorCode = (typeof EVAL_ERROR_CODES)[number]\n\n/**\n * Typed error for the eval engine. Named EvalRunnerError (not EvalError) to\n * avoid shadowing the JS built-in `EvalError`.\n */\nexport class EvalRunnerError extends AdkError<EvalErrorCode> {}\n", "/**\n * SSECollector \u2014 connects to the ADK dev server's SSE trace stream,\n * accumulates spans, and provides promise-based wait methods for\n * turn completion and workflow signals.\n *\n * Uses fetch() + async iteration with manual SSE line parsing\n * (Bun does not have native EventSource).\n */\n\nimport type { Span } from './trace'\nimport { EvalRunnerError } from '../errors'\n\ntype SSEFilter = { conversationId: string } | { workflowId: string }\n\ninterface WaitOptions {\n timeout: number\n /**\n * Optional abort signal \u2014 when fired, the wait rejects immediately rather\n * than burning the full timeout. Used by the runner so Stop unblocks\n * pending workflow assertions (otherwise a single negative-result wait\n * can hold the suite hostage for `idleTimeout` per assertion).\n */\n abortSignal?: AbortSignal\n}\n\ninterface WorkflowWaitOptions extends WaitOptions {\n signal: 'entered' | 'completed'\n}\n\ninterface TurnWaitOptions extends WaitOptions {\n /**\n * Which handler span names may complete this turn. A user-message turn\n * completes via `handler.conversation` only \u2014 async noise such as a\n * workflow callback closes a silent `handler.event` span, and counting it\n * releases the runner early, whose next message then interrupts the\n * still-running handler. An event-driven turn completes via\n * `handler.event`. Omitted = any turn-handler span (legacy behavior).\n */\n acceptSpanNames?: ReadonlySet<string>\n /**\n * Quiet window after the handler closes before the turn is considered\n * settled (default 100 ms). The runner raises it so async follow-through\n * the handler scheduled \u2014 a tool-started workflow run and its callback,\n * which only execute AFTER the handler closes \u2014 finishes before the next\n * message is sent. A message sent into that window can be absorbed by the\n * follow-through's transcript save and silently skipped as already\n * processed.\n */\n settleQuietMs?: number\n /** Hard cap on the settle phase (default 2 s; the runner raises it with settleQuietMs). */\n settleMaxMs?: number\n}\n\n/**\n * Unique key for span deduplication.\n */\nfunction spanKey(span: Span): string {\n return `${span.id.trace}:${span.id.span}`\n}\n\n/**\n * Type-safe accessor for span.data fields.\n */\nfunction spanData(span: Span): Record<string, unknown> {\n return span.data && typeof span.data === 'object' ? (span.data as Record<string, unknown>) : {}\n}\n\n/**\n * Span names that mark a Conversation handler reaching the end of a turn.\n *\n * A turn driven by a user message emits `handler.conversation`; a turn driven\n * by an event (a Conversation's `events:` subscription \u2014 e.g. greeting on a\n * conversation-start signal) emits `handler.event`. Both close exactly once\n * per handler invocation, so either is a valid \"turn complete\" signal.\n */\nconst TURN_HANDLER_SPAN_NAMES = new Set(['handler.conversation', 'handler.event'])\n\nfunction isTurnHandlerSpan(span: Span): boolean {\n return TURN_HANDLER_SPAN_NAMES.has(span.name)\n}\n\nexport class SSECollector {\n private devServerUrl: string\n private headers: Record<string, string>\n\n /** All spans accumulated since connect(), keyed by spanKey for dedup. */\n private spanMap = new Map<string, Span>()\n\n /** Index into spanMap insertion order marking where the current turn started. */\n private turnStartKeys = new Set<string>()\n\n /** Handler span IDs seen before the current turn (for startTurn filtering). */\n private seenHandlerKeys = new Set<string>()\n\n /** Callbacks waiting for span updates. */\n private waiters: Array<(span: Span) => void> = []\n\n /** AbortController for the active SSE connection. */\n private abortController: AbortController | null = null\n\n /** Whether the background loop is running. */\n private connected = false\n\n /** Error from the SSE stream if it died unexpectedly. */\n private streamError: Error | null = null\n\n /** Passive counters used only to explain timeout failures. */\n private updateEventCount = 0\n private keepaliveEventCount = 0\n private lastEvent: { name: string; at: number } | null = null\n\n /** Set while a settle phase is in flight; called by disconnect() to short-circuit. */\n private _cancelSettle: (() => void) | null = null\n\n constructor(devServerUrl: string, headers: Record<string, string> = {}) {\n this.devServerUrl = devServerUrl\n this.headers = headers\n }\n\n /**\n * Open a streaming SSE connection filtered by conversationId or workflowId.\n * Starts a background loop that parses SSE events and accumulates spans.\n */\n async connect(filter: SSEFilter): Promise<void> {\n const attributeName = 'conversationId' in filter ? 'conversationId' : 'workflowId'\n const attributeValue = 'conversationId' in filter ? filter.conversationId : filter.workflowId\n\n const url = `${this.devServerUrl}/api/traces/stream?attributeName=${encodeURIComponent(attributeName)}&attributeValue=${encodeURIComponent(attributeValue)}`\n\n this.abortController = new AbortController()\n const myController = this.abortController\n\n const response = await fetch(url, {\n headers: { ...this.headers, Accept: 'text/event-stream' },\n signal: myController.signal,\n })\n\n if (!response.ok) {\n throw new EvalRunnerError({\n code: 'SSE_CONNECT_FAILED',\n message: `SSE connection failed: ${response.status} ${response.statusText}`,\n expected: true,\n suggestion: 'Make sure the dev server is running (`adk dev`) and reachable.',\n })\n }\n\n if (!response.body) {\n throw new EvalRunnerError({ code: 'SSE_NO_BODY', message: 'SSE response has no body' })\n }\n\n this.connected = true\n\n // Start background parsing loop\n this._consumeStream(response.body).catch((err) => {\n // Ignore the deliberate abort from repoint(): only the current stream's failure counts.\n if (this.connected && this.abortController === myController) {\n this.streamError = err instanceof Error ? err : new Error(String(err))\n }\n })\n\n // Wait for the initial snapshot to arrive before returning.\n // This ensures that connect() resolves only after the snapshot\n // has been processed so callers can immediately check spans.\n await this._waitForSnapshot()\n }\n\n /**\n * Re-point at a new conversation, keeping `spanMap` and `seenHandlerKeys` (so\n * the earlier conversation stays visible and its completed handler can't\n * satisfy the next turn). Resets the snapshot latch so connect() awaits the\n * new conversation's snapshot.\n */\n async repoint(filter: SSEFilter): Promise<void> {\n this.connected = false\n this.abortController?.abort()\n this.abortController = null\n this._cancelSettle?.()\n this._cancelSettle = null\n this.waiters = []\n this.snapshotReceived = false\n this.snapshotResolvers = []\n // Clear any prior stream's error so a later timeout reports the new\n // conversation's state, not a dead connection we intentionally closed.\n this.streamError = null\n await this.connect(filter)\n }\n\n /**\n * Mark the beginning of a new turn.\n * Records which turn-handler span IDs (handler.conversation / handler.event)\n * have been seen so far, so waitForTurnComplete() only resolves on NEW\n * handler completions.\n */\n startTurn(): void {\n // Snapshot the current set of span keys as the turn boundary\n this.turnStartKeys = new Set(this.spanMap.keys())\n\n // Record all turn-handler spans seen so far\n for (const [key, span] of this.spanMap) {\n if (isTurnHandlerSpan(span)) {\n this.seenHandlerKeys.add(key)\n }\n }\n }\n\n private static readonly SETTLE_MS = 100\n\n /**\n * Wait for a turn-handler span (handler.conversation or handler.event) to\n * reach terminal status (ok or error).\n * Only considers spans NOT seen before the last startTurn() call.\n *\n * After the handler closes, waits for a quiet period (no new spans for SETTLE_MS)\n * to ensure all child spans (tool calls, messages, state saves) have arrived.\n * The runtime's HttpSpanExporter fires POSTs without awaiting them, so child\n * spans may arrive slightly after the handler close event.\n */\n async waitForTurnComplete(opts: TurnWaitOptions): Promise<void> {\n const accept = opts.acceptSpanNames\n const settle = { quietMs: opts.settleQuietMs, maxMs: opts.settleMaxMs }\n // Check if a completed handler already exists (e.g. from snapshot)\n if (this._hasNewCompletedHandler(accept)) {\n return this._settleAfterHandler(settle)\n }\n if (opts.abortSignal?.aborted) {\n throw new Error('waitForTurnComplete aborted')\n }\n\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n const expected = accept ? [...accept].join(' or ') : 'handler.conversation or handler.event'\n const reason = this.streamError\n ? `SSE stream died: ${this.streamError.message}`\n : `no ${expected} span completed`\n reject(\n new Error(\n `waitForTurnComplete timed out after ${opts.timeout}ms \u2014 ${reason}; diagnostics: ${this._formatTurnDiagnostics()}`\n )\n )\n }, opts.timeout)\n\n const onAbort = () => {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n reject(new Error('waitForTurnComplete aborted'))\n }\n opts.abortSignal?.addEventListener('abort', onAbort, { once: true })\n\n const waiter = (_span: Span) => {\n if (this._hasNewCompletedHandler(accept)) {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n this._settleAfterHandler(settle).then(resolve, reject)\n }\n }\n\n this.waiters.push(waiter)\n })\n }\n\n private static readonly MAX_SETTLE_MS = 2_000\n\n /**\n * After the handler span closes, wait for spans to stop arriving.\n * Resolves when no new spans arrive for SETTLE_MS, or after MAX_SETTLE_MS hard cap.\n *\n * Exposes a cancellation hook via _cancelSettle so disconnect() can short-\n * circuit an in-flight settle phase and avoid leaking timers for up to 2s.\n */\n private _settleAfterHandler(settle?: { quietMs?: number; maxMs?: number }): Promise<void> {\n const quietMs = settle?.quietMs ?? SSECollector.SETTLE_MS\n const maxMs = settle?.maxMs ?? SSECollector.MAX_SETTLE_MS\n return new Promise<void>((resolve) => {\n let resolved = false\n const done = () => {\n if (resolved) return\n resolved = true\n clearTimeout(hardCap)\n clearTimeout(settleTimer)\n this._removeWaiter(settleWaiter)\n this._cancelSettle = null\n resolve()\n }\n\n const hardCap = setTimeout(done, maxMs)\n\n let settleTimer = setTimeout(done, quietMs)\n\n const settleWaiter = () => {\n if (resolved) return\n clearTimeout(settleTimer)\n settleTimer = setTimeout(done, quietMs)\n }\n\n this.waiters.push(settleWaiter)\n this._cancelSettle = done\n })\n }\n\n /**\n * Wait for a handler.workflow span with matching workflow name.\n * - signal 'entered': resolves when the span appears (any status)\n * - signal 'completed': resolves when the span has status !== 'running'\n */\n async waitForWorkflow(name: string, opts: WorkflowWaitOptions): Promise<void> {\n // Check if condition is already met\n if (this._matchesWorkflowCondition(name, opts.signal)) {\n return\n }\n if (opts.abortSignal?.aborted) {\n throw new Error(`waitForWorkflow(\"${name}\", { signal: \"${opts.signal}\" }) aborted`)\n }\n\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n const reason = this.streamError ? `SSE stream died: ${this.streamError.message}` : 'span not found'\n reject(\n new Error(\n `waitForWorkflow(\"${name}\", { signal: \"${opts.signal}\" }) timed out after ${opts.timeout}ms \u2014 ${reason}`\n )\n )\n }, opts.timeout)\n\n const onAbort = () => {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n reject(new Error(`waitForWorkflow(\"${name}\", { signal: \"${opts.signal}\" }) aborted`))\n }\n opts.abortSignal?.addEventListener('abort', onAbort, { once: true })\n\n const waiter = (_span: Span) => {\n if (this._matchesWorkflowCondition(name, opts.signal)) {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n resolve()\n }\n }\n\n this.waiters.push(waiter)\n })\n }\n\n /**\n * Return spans accumulated since the last startTurn() call.\n */\n getTurnSpans(): Span[] {\n const result: Span[] = []\n for (const [key, span] of this.spanMap) {\n if (!this.turnStartKeys.has(key)) {\n result.push(span)\n }\n }\n return result\n }\n\n /**\n * Return all spans accumulated since connect().\n */\n getAllSpans(): Span[] {\n return Array.from(this.spanMap.values())\n }\n\n /**\n * Close the SSE connection and clean up.\n */\n disconnect(): void {\n this.connected = false\n this.abortController?.abort()\n this.abortController = null\n // Short-circuit an in-flight settle phase so its hardCap / settleTimer\n // don't fire after disconnect.\n this._cancelSettle?.()\n this._cancelSettle = null\n // Waiters hold setTimeout references that will reject on their own;\n // clearing the array just prevents further notifications.\n this.waiters = []\n this.snapshotResolvers = []\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Whether we have received the initial snapshot. */\n private snapshotReceived = false\n private snapshotResolvers: Array<() => void> = []\n\n private static readonly SNAPSHOT_TIMEOUT_MS = 10_000\n\n /**\n * Returns a promise that resolves once the snapshot event has been processed,\n * or rejects after a timeout to prevent connect() from hanging indefinitely.\n */\n private _waitForSnapshot(): Promise<void> {\n if (this.snapshotReceived) {\n return Promise.resolve()\n }\n return new Promise<void>((resolve, reject) => {\n let settled = false\n const resolver = () => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n resolve()\n }\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n const idx = this.snapshotResolvers.indexOf(resolver)\n if (idx !== -1) this.snapshotResolvers.splice(idx, 1)\n reject(\n new Error(\n `SSE snapshot not received within ${SSECollector.SNAPSHOT_TIMEOUT_MS}ms \u2014 is the dev server healthy?`\n )\n )\n }, SSECollector.SNAPSHOT_TIMEOUT_MS)\n this.snapshotResolvers.push(resolver)\n })\n }\n\n /**\n * Consume the ReadableStream body, parse SSE lines, and dispatch events.\n */\n private async _consumeStream(body: ReadableStream<Uint8Array>): Promise<void> {\n const decoder = new TextDecoder()\n let buffer = ''\n\n const reader = body.getReader()\n\n try {\n while (this.connected) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n\n // Split on double-newline boundaries to get complete SSE events\n const parts = buffer.split('\\n\\n')\n // The last part may be incomplete \u2014 keep it in the buffer\n buffer = parts.pop() ?? ''\n\n for (const part of parts) {\n this._parseSSEBlock(part)\n }\n }\n } finally {\n reader.releaseLock()\n }\n }\n\n /**\n * Parse a single SSE event block (text between \\n\\n boundaries).\n * Extracts event name and data payload.\n */\n private _parseSSEBlock(block: string): void {\n const lines = block.split('\\n')\n let eventName = ''\n let dataLine = ''\n\n for (const line of lines) {\n if (line.startsWith('event:')) {\n eventName = line.slice('event:'.length).trim()\n } else if (line.startsWith('data:')) {\n const value = line.slice('data:'.length).trim()\n dataLine = dataLine ? `${dataLine}\\n${value}` : value\n }\n // id: and retry: lines are ignored\n }\n\n if (!eventName || !dataLine) {\n // Could be a retry: line or empty block \u2014 skip\n if (!this.snapshotReceived && block.includes('retry:')) {\n // The retry line comes before the snapshot; don't resolve yet\n }\n return\n }\n\n try {\n const payload = JSON.parse(dataLine)\n this._handleEvent(eventName, payload)\n } catch {\n // Malformed JSON \u2014 skip\n }\n }\n\n /**\n * Dispatch a parsed SSE event to the appropriate handler.\n */\n private _handleEvent(eventName: string, payload: unknown): void {\n this._recordEvent(eventName)\n\n if (eventName === 'snapshot') {\n const data = payload as { spans: Span[] }\n const spans = Array.isArray(data.spans) ? data.spans : []\n for (const span of spans) {\n this._upsertSpan(span)\n }\n this.snapshotReceived = true\n for (const resolve of this.snapshotResolvers) {\n resolve()\n }\n this.snapshotResolvers = []\n } else if (eventName === 'update') {\n const data = payload as { span: Span }\n if (data.span) {\n this._upsertSpan(data.span)\n this._notifyWaiters(data.span)\n }\n }\n // keepalive events are intentionally ignored\n }\n\n private _recordEvent(eventName: string): void {\n this.lastEvent = { name: eventName, at: Date.now() }\n\n if (eventName === 'update') {\n this.updateEventCount++\n } else if (eventName === 'keepalive') {\n this.keepaliveEventCount++\n }\n }\n\n private _formatTurnDiagnostics(): string {\n const turnSpans = this.getTurnSpans()\n const spanNames = new Set<string>()\n const handlerStatuses = new Set<string>()\n\n for (const span of turnSpans) {\n spanNames.add(span.name)\n // Tracks both handler.conversation and handler.event so an event-driven\n // turn that times out still surfaces its handler status.\n if (isTurnHandlerSpan(span)) {\n handlerStatuses.add(span.status)\n }\n }\n\n const lastEvent = this.lastEvent ? `${this.lastEvent.name} ${Date.now() - this.lastEvent.at}ms ago` : 'none'\n\n return [\n `snapshotReceived=${this.snapshotReceived}`,\n `totalUpdates=${this.updateEventCount}`,\n `totalKeepalives=${this.keepaliveEventCount}`,\n `lastEvent=${lastEvent}`,\n `totalSpans=${this.spanMap.size}`,\n `turnSpans=${turnSpans.length}`,\n `turnSpanNames=${this._formatSet(spanNames)}`,\n `turnHandlerStatuses=${this._formatSet(handlerStatuses)}`,\n ].join(', ')\n }\n\n private _formatSet(values: Set<string>, limit = 12): string {\n if (values.size === 0) {\n return 'none'\n }\n\n const items = Array.from(values)\n const suffix = items.length > limit ? `|+${items.length - limit} more` : ''\n return `${items.slice(0, limit).join('|')}${suffix}`\n }\n\n /**\n * Insert or replace a span in the map (dedup by trace+span ID).\n */\n private _upsertSpan(span: Span): void {\n this.spanMap.set(spanKey(span), span)\n }\n\n /**\n * Notify all waiters that a new span was received.\n */\n private _notifyWaiters(span: Span): void {\n // Copy the array since waiters may remove themselves during iteration\n const current = [...this.waiters]\n for (const waiter of current) {\n waiter(span)\n }\n }\n\n /**\n * Remove a waiter callback from the list.\n */\n private _removeWaiter(fn: (span: Span) => void): void {\n const idx = this.waiters.indexOf(fn)\n if (idx !== -1) {\n this.waiters.splice(idx, 1)\n }\n }\n\n /**\n * Check if there is a turn-handler span (handler.conversation / handler.event) that:\n * 1. Was NOT seen before startTurn()\n * 2. Has terminal status (ok or error)\n *\n * Marks the matched key as consumed so the same handler span cannot\n * satisfy both the current turn's settle phase AND the next turn's\n * waitForTurnComplete check (which would otherwise resolve instantly\n * against stale data instead of waiting for the new turn's handler).\n */\n private _hasNewCompletedHandler(acceptSpanNames?: ReadonlySet<string>): boolean {\n for (const [key, span] of this.spanMap) {\n if (\n isTurnHandlerSpan(span) &&\n (!acceptSpanNames || acceptSpanNames.has(span.name)) &&\n !this.seenHandlerKeys.has(key) &&\n (span.status === 'ok' || span.status === 'error')\n ) {\n this.seenHandlerKeys.add(key)\n return true\n }\n }\n return false\n }\n\n /**\n * Check if a handler.workflow span matches the given condition.\n */\n private _matchesWorkflowCondition(name: string, signal: 'entered' | 'completed'): boolean {\n for (const span of this.spanMap.values()) {\n if (span.name !== 'handler.workflow') continue\n\n const d = spanData(span)\n const wfName = d['workflow.name'] || d['workflowName']\n if (wfName !== name) continue\n\n if (signal === 'entered') {\n // Any status means the workflow was entered\n return true\n }\n\n if (signal === 'completed') {\n return span.status === 'ok' || span.status === 'error'\n }\n }\n return false\n }\n}\n", "import type { SpanSource, TurnWaitOptions, WaitOptions, WorkflowWaitOptions } from './span-source'\nimport type { Span } from './trace'\nimport { SSECollector } from './sse-collector'\n\nexport class LocalSpanSource implements SpanSource {\n private collector: SSECollector\n\n constructor(devServerUrl: string, headers: Record<string, string> = {}) {\n this.collector = new SSECollector(devServerUrl, headers)\n }\n\n async connect(filter: { conversationId: string }): Promise<void> {\n await this.collector.connect(filter)\n }\n\n async repoint(filter: { conversationId: string }): Promise<void> {\n await this.collector.repoint(filter)\n }\n\n startTurn(): void {\n this.collector.startTurn()\n }\n\n async waitForTurnComplete(opts: TurnWaitOptions): Promise<void> {\n await this.collector.waitForTurnComplete(opts)\n }\n\n async waitForWorkflow(name: string, opts: WorkflowWaitOptions): Promise<void> {\n await this.collector.waitForWorkflow(name, opts)\n }\n\n getTurnSpans(): Span[] {\n return this.collector.getTurnSpans()\n }\n\n getAllSpans(): Span[] {\n return this.collector.getAllSpans()\n }\n\n disconnect(): void {\n this.collector.disconnect()\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,40 @@
1
+ import type { Span } from './trace';
2
+ export interface WaitOptions {
3
+ timeout: number;
4
+ abortSignal?: AbortSignal;
5
+ }
6
+ export interface WorkflowWaitOptions extends WaitOptions {
7
+ signal: 'entered' | 'completed';
8
+ }
9
+ export interface TurnWaitOptions extends WaitOptions {
10
+ /**
11
+ * Which handler span names may complete this turn (e.g. a user-message turn
12
+ * completes via `handler.conversation`, an event-driven turn via
13
+ * `handler.event`). Omitted = any turn-handler span.
14
+ */
15
+ acceptSpanNames?: ReadonlySet<string>;
16
+ /** Quiet window after the handler closes before the turn counts as settled. */
17
+ settleQuietMs?: number;
18
+ /** Hard cap on the settle phase. */
19
+ settleMaxMs?: number;
20
+ }
21
+ export interface SpanSource {
22
+ connect(filter: {
23
+ conversationId: string;
24
+ }): Promise<void>;
25
+ /**
26
+ * Re-point the stream at a new conversation, keeping spans already
27
+ * accumulated (so outcome assertions still see the earlier conversation and
28
+ * its completed handler can't satisfy the next turn).
29
+ */
30
+ repoint(filter: {
31
+ conversationId: string;
32
+ }): Promise<void>;
33
+ startTurn(): void;
34
+ waitForTurnComplete(opts: TurnWaitOptions): Promise<void>;
35
+ waitForWorkflow(name: string, opts: WorkflowWaitOptions): Promise<void>;
36
+ getTurnSpans(): Span[];
37
+ getAllSpans(): Span[];
38
+ disconnect(): void;
39
+ }
40
+ //# sourceMappingURL=span-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span-source.d.ts","sourceRoot":"","sources":["../../src/spans/span-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEnC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,MAAM,EAAE,SAAS,GAAG,WAAW,CAAA;CAChC;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD;;;;OAIG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,MAAM,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1D;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1D,SAAS,IAAI,IAAI,CAAA;IACjB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvE,YAAY,IAAI,IAAI,EAAE,CAAA;IACtB,WAAW,IAAI,IAAI,EAAE,CAAA;IACrB,UAAU,IAAI,IAAI,CAAA;CACnB"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=span-source.js.map
@@ -25,6 +25,29 @@ interface WaitOptions {
25
25
  interface WorkflowWaitOptions extends WaitOptions {
26
26
  signal: 'entered' | 'completed';
27
27
  }
28
+ interface TurnWaitOptions extends WaitOptions {
29
+ /**
30
+ * Which handler span names may complete this turn. A user-message turn
31
+ * completes via `handler.conversation` only — async noise such as a
32
+ * workflow callback closes a silent `handler.event` span, and counting it
33
+ * releases the runner early, whose next message then interrupts the
34
+ * still-running handler. An event-driven turn completes via
35
+ * `handler.event`. Omitted = any turn-handler span (legacy behavior).
36
+ */
37
+ acceptSpanNames?: ReadonlySet<string>;
38
+ /**
39
+ * Quiet window after the handler closes before the turn is considered
40
+ * settled (default 100 ms). The runner raises it so async follow-through
41
+ * the handler scheduled — a tool-started workflow run and its callback,
42
+ * which only execute AFTER the handler closes — finishes before the next
43
+ * message is sent. A message sent into that window can be absorbed by the
44
+ * follow-through's transcript save and silently skipped as already
45
+ * processed.
46
+ */
47
+ settleQuietMs?: number;
48
+ /** Hard cap on the settle phase (default 2 s; the runner raises it with settleQuietMs). */
49
+ settleMaxMs?: number;
50
+ }
28
51
  export declare class SSECollector {
29
52
  private devServerUrl;
30
53
  private headers;
@@ -42,6 +65,10 @@ export declare class SSECollector {
42
65
  private connected;
43
66
  /** Error from the SSE stream if it died unexpectedly. */
44
67
  private streamError;
68
+ /** Passive counters used only to explain timeout failures. */
69
+ private updateEventCount;
70
+ private keepaliveEventCount;
71
+ private lastEvent;
45
72
  /** Set while a settle phase is in flight; called by disconnect() to short-circuit. */
46
73
  private _cancelSettle;
47
74
  constructor(devServerUrl: string, headers?: Record<string, string>);
@@ -50,15 +77,24 @@ export declare class SSECollector {
50
77
  * Starts a background loop that parses SSE events and accumulates spans.
51
78
  */
52
79
  connect(filter: SSEFilter): Promise<void>;
80
+ /**
81
+ * Re-point at a new conversation, keeping `spanMap` and `seenHandlerKeys` (so
82
+ * the earlier conversation stays visible and its completed handler can't
83
+ * satisfy the next turn). Resets the snapshot latch so connect() awaits the
84
+ * new conversation's snapshot.
85
+ */
86
+ repoint(filter: SSEFilter): Promise<void>;
53
87
  /**
54
88
  * Mark the beginning of a new turn.
55
- * Records which handler.conversation span IDs have been seen so far,
56
- * so waitForTurnComplete() only resolves on NEW handler completions.
89
+ * Records which turn-handler span IDs (handler.conversation / handler.event)
90
+ * have been seen so far, so waitForTurnComplete() only resolves on NEW
91
+ * handler completions.
57
92
  */
58
93
  startTurn(): void;
59
94
  private static readonly SETTLE_MS;
60
95
  /**
61
- * Wait for a handler.conversation span to reach terminal status (ok or error).
96
+ * Wait for a turn-handler span (handler.conversation or handler.event) to
97
+ * reach terminal status (ok or error).
62
98
  * Only considers spans NOT seen before the last startTurn() call.
63
99
  *
64
100
  * After the handler closes, waits for a quiet period (no new spans for SETTLE_MS)
@@ -66,7 +102,7 @@ export declare class SSECollector {
66
102
  * The runtime's HttpSpanExporter fires POSTs without awaiting them, so child
67
103
  * spans may arrive slightly after the handler close event.
68
104
  */
69
- waitForTurnComplete(opts: WaitOptions): Promise<void>;
105
+ waitForTurnComplete(opts: TurnWaitOptions): Promise<void>;
70
106
  private static readonly MAX_SETTLE_MS;
71
107
  /**
72
108
  * After the handler span closes, wait for spans to stop arriving.
@@ -116,6 +152,9 @@ export declare class SSECollector {
116
152
  * Dispatch a parsed SSE event to the appropriate handler.
117
153
  */
118
154
  private _handleEvent;
155
+ private _recordEvent;
156
+ private _formatTurnDiagnostics;
157
+ private _formatSet;
119
158
  /**
120
159
  * Insert or replace a span in the map (dedup by trace+span ID).
121
160
  */
@@ -129,7 +168,7 @@ export declare class SSECollector {
129
168
  */
130
169
  private _removeWaiter;
131
170
  /**
132
- * Check if there is a handler.conversation span that:
171
+ * Check if there is a turn-handler span (handler.conversation / handler.event) that:
133
172
  * 1. Was NOT seen before startTurn()
134
173
  * 2. Has terminal status (ok or error)
135
174
  *
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse-collector.d.ts","sourceRoot":"","sources":["../../src/spans/sse-collector.ts"],"names":[],"mappings":"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"}
@@ -1,53 +1,19 @@
1
- // ../analytics/src/errors.ts
2
- var AdkError = class extends Error {
3
- /**
4
- * Marker for cross-module-instance checks — `instanceof` breaks when a
5
- * package is duplicated in the dependency graph.
6
- * @internal
7
- */
8
- static __IS_ADK_BASE_ERROR = true;
9
- /**
10
- * Stable SCREAMING_SNAKE_CASE identifier, e.g. 'BOT_NOT_FOUND'. Subclasses
11
- * pin `Code` to a literal union (`class EvalRunnerError extends
12
- * AdkError<EvalErrorCode>`) so typos are compile errors and `code` narrows
13
- * in switches.
14
- */
15
- code;
16
- /**
17
- * True for user/environment conditions the developer can fix themselves
18
- * (validation, auth, connectivity). False for internal bugs. Defaults to
19
- * false so unclassified errors surface as bugs until someone triages them.
20
- */
21
- expected;
22
- /** Structured context. Sanitized before any telemetry leaves the machine. */
23
- details;
24
- /** Actionable next step shown to the user, e.g. "Run 'adk login' first." */
25
- suggestion;
26
- constructor(opts) {
27
- super(opts.message, opts.cause !== void 0 ? { cause: opts.cause } : void 0);
28
- this.name = this.constructor.name;
29
- this.code = opts.code;
30
- this.expected = opts.expected ?? false;
31
- if (opts.details !== void 0) {
32
- this.details = opts.details;
33
- }
34
- if (opts.suggestion !== void 0) {
35
- this.suggestion = opts.suggestion;
36
- }
37
- }
38
- };
39
-
40
1
  // src/errors.ts
2
+ import { AdkError } from "@botpress/analytics";
41
3
  var EvalRunnerError = class extends AdkError {
42
4
  };
43
5
 
44
- // src/sse-collector.ts
6
+ // src/spans/sse-collector.ts
45
7
  function spanKey(span) {
46
8
  return `${span.id.trace}:${span.id.span}`;
47
9
  }
48
10
  function spanData(span) {
49
11
  return span.data && typeof span.data === "object" ? span.data : {};
50
12
  }
13
+ var TURN_HANDLER_SPAN_NAMES = /* @__PURE__ */ new Set(["handler.conversation", "handler.event"]);
14
+ function isTurnHandlerSpan(span) {
15
+ return TURN_HANDLER_SPAN_NAMES.has(span.name);
16
+ }
51
17
  var SSECollector = class _SSECollector {
52
18
  devServerUrl;
53
19
  headers;
@@ -65,6 +31,10 @@ var SSECollector = class _SSECollector {
65
31
  connected = false;
66
32
  /** Error from the SSE stream if it died unexpectedly. */
67
33
  streamError = null;
34
+ /** Passive counters used only to explain timeout failures. */
35
+ updateEventCount = 0;
36
+ keepaliveEventCount = 0;
37
+ lastEvent = null;
68
38
  /** Set while a settle phase is in flight; called by disconnect() to short-circuit. */
69
39
  _cancelSettle = null;
70
40
  constructor(devServerUrl, headers = {}) {
@@ -80,9 +50,10 @@ var SSECollector = class _SSECollector {
80
50
  const attributeValue = "conversationId" in filter ? filter.conversationId : filter.workflowId;
81
51
  const url = `${this.devServerUrl}/api/traces/stream?attributeName=${encodeURIComponent(attributeName)}&attributeValue=${encodeURIComponent(attributeValue)}`;
82
52
  this.abortController = new AbortController();
53
+ const myController = this.abortController;
83
54
  const response = await fetch(url, {
84
55
  headers: { ...this.headers, Accept: "text/event-stream" },
85
- signal: this.abortController.signal
56
+ signal: myController.signal
86
57
  });
87
58
  if (!response.ok) {
88
59
  throw new EvalRunnerError({
@@ -97,28 +68,48 @@ var SSECollector = class _SSECollector {
97
68
  }
98
69
  this.connected = true;
99
70
  this._consumeStream(response.body).catch((err) => {
100
- if (this.connected) {
71
+ if (this.connected && this.abortController === myController) {
101
72
  this.streamError = err instanceof Error ? err : new Error(String(err));
102
73
  }
103
74
  });
104
75
  await this._waitForSnapshot();
105
76
  }
77
+ /**
78
+ * Re-point at a new conversation, keeping `spanMap` and `seenHandlerKeys` (so
79
+ * the earlier conversation stays visible and its completed handler can't
80
+ * satisfy the next turn). Resets the snapshot latch so connect() awaits the
81
+ * new conversation's snapshot.
82
+ */
83
+ async repoint(filter) {
84
+ this.connected = false;
85
+ this.abortController?.abort();
86
+ this.abortController = null;
87
+ this._cancelSettle?.();
88
+ this._cancelSettle = null;
89
+ this.waiters = [];
90
+ this.snapshotReceived = false;
91
+ this.snapshotResolvers = [];
92
+ this.streamError = null;
93
+ await this.connect(filter);
94
+ }
106
95
  /**
107
96
  * Mark the beginning of a new turn.
108
- * Records which handler.conversation span IDs have been seen so far,
109
- * so waitForTurnComplete() only resolves on NEW handler completions.
97
+ * Records which turn-handler span IDs (handler.conversation / handler.event)
98
+ * have been seen so far, so waitForTurnComplete() only resolves on NEW
99
+ * handler completions.
110
100
  */
111
101
  startTurn() {
112
102
  this.turnStartKeys = new Set(this.spanMap.keys());
113
103
  for (const [key, span] of this.spanMap) {
114
- if (span.name === "handler.conversation") {
104
+ if (isTurnHandlerSpan(span)) {
115
105
  this.seenHandlerKeys.add(key);
116
106
  }
117
107
  }
118
108
  }
119
109
  static SETTLE_MS = 100;
120
110
  /**
121
- * Wait for a handler.conversation span to reach terminal status (ok or error).
111
+ * Wait for a turn-handler span (handler.conversation or handler.event) to
112
+ * reach terminal status (ok or error).
122
113
  * Only considers spans NOT seen before the last startTurn() call.
123
114
  *
124
115
  * After the handler closes, waits for a quiet period (no new spans for SETTLE_MS)
@@ -127,8 +118,10 @@ var SSECollector = class _SSECollector {
127
118
  * spans may arrive slightly after the handler close event.
128
119
  */
129
120
  async waitForTurnComplete(opts) {
130
- if (this._hasNewCompletedHandler()) {
131
- return this._settleAfterHandler();
121
+ const accept = opts.acceptSpanNames;
122
+ const settle = { quietMs: opts.settleQuietMs, maxMs: opts.settleMaxMs };
123
+ if (this._hasNewCompletedHandler(accept)) {
124
+ return this._settleAfterHandler(settle);
132
125
  }
133
126
  if (opts.abortSignal?.aborted) {
134
127
  throw new Error("waitForTurnComplete aborted");
@@ -137,8 +130,13 @@ var SSECollector = class _SSECollector {
137
130
  const timer = setTimeout(() => {
138
131
  this._removeWaiter(waiter);
139
132
  opts.abortSignal?.removeEventListener("abort", onAbort);
140
- const reason = this.streamError ? `SSE stream died: ${this.streamError.message}` : "no handler.conversation span completed";
141
- reject(new Error(`waitForTurnComplete timed out after ${opts.timeout}ms \u2014 ${reason}`));
133
+ const expected = accept ? [...accept].join(" or ") : "handler.conversation or handler.event";
134
+ const reason = this.streamError ? `SSE stream died: ${this.streamError.message}` : `no ${expected} span completed`;
135
+ reject(
136
+ new Error(
137
+ `waitForTurnComplete timed out after ${opts.timeout}ms \u2014 ${reason}; diagnostics: ${this._formatTurnDiagnostics()}`
138
+ )
139
+ );
142
140
  }, opts.timeout);
143
141
  const onAbort = () => {
144
142
  clearTimeout(timer);
@@ -147,11 +145,11 @@ var SSECollector = class _SSECollector {
147
145
  };
148
146
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
149
147
  const waiter = (_span) => {
150
- if (this._hasNewCompletedHandler()) {
148
+ if (this._hasNewCompletedHandler(accept)) {
151
149
  clearTimeout(timer);
152
150
  this._removeWaiter(waiter);
153
151
  opts.abortSignal?.removeEventListener("abort", onAbort);
154
- this._settleAfterHandler().then(resolve, reject);
152
+ this._settleAfterHandler(settle).then(resolve, reject);
155
153
  }
156
154
  };
157
155
  this.waiters.push(waiter);
@@ -165,7 +163,9 @@ var SSECollector = class _SSECollector {
165
163
  * Exposes a cancellation hook via _cancelSettle so disconnect() can short-
166
164
  * circuit an in-flight settle phase and avoid leaking timers for up to 2s.
167
165
  */
168
- _settleAfterHandler() {
166
+ _settleAfterHandler(settle) {
167
+ const quietMs = settle?.quietMs ?? _SSECollector.SETTLE_MS;
168
+ const maxMs = settle?.maxMs ?? _SSECollector.MAX_SETTLE_MS;
169
169
  return new Promise((resolve) => {
170
170
  let resolved = false;
171
171
  const done = () => {
@@ -177,12 +177,12 @@ var SSECollector = class _SSECollector {
177
177
  this._cancelSettle = null;
178
178
  resolve();
179
179
  };
180
- const hardCap = setTimeout(done, _SSECollector.MAX_SETTLE_MS);
181
- let settleTimer = setTimeout(done, _SSECollector.SETTLE_MS);
180
+ const hardCap = setTimeout(done, maxMs);
181
+ let settleTimer = setTimeout(done, quietMs);
182
182
  const settleWaiter = () => {
183
183
  if (resolved) return;
184
184
  clearTimeout(settleTimer);
185
- settleTimer = setTimeout(done, _SSECollector.SETTLE_MS);
185
+ settleTimer = setTimeout(done, quietMs);
186
186
  };
187
187
  this.waiters.push(settleWaiter);
188
188
  this._cancelSettle = done;
@@ -347,6 +347,7 @@ ${value}` : value;
347
347
  * Dispatch a parsed SSE event to the appropriate handler.
348
348
  */
349
349
  _handleEvent(eventName, payload) {
350
+ this._recordEvent(eventName);
350
351
  if (eventName === "snapshot") {
351
352
  const data = payload;
352
353
  const spans = Array.isArray(data.spans) ? data.spans : [];
@@ -366,6 +367,44 @@ ${value}` : value;
366
367
  }
367
368
  }
368
369
  }
370
+ _recordEvent(eventName) {
371
+ this.lastEvent = { name: eventName, at: Date.now() };
372
+ if (eventName === "update") {
373
+ this.updateEventCount++;
374
+ } else if (eventName === "keepalive") {
375
+ this.keepaliveEventCount++;
376
+ }
377
+ }
378
+ _formatTurnDiagnostics() {
379
+ const turnSpans = this.getTurnSpans();
380
+ const spanNames = /* @__PURE__ */ new Set();
381
+ const handlerStatuses = /* @__PURE__ */ new Set();
382
+ for (const span of turnSpans) {
383
+ spanNames.add(span.name);
384
+ if (isTurnHandlerSpan(span)) {
385
+ handlerStatuses.add(span.status);
386
+ }
387
+ }
388
+ const lastEvent = this.lastEvent ? `${this.lastEvent.name} ${Date.now() - this.lastEvent.at}ms ago` : "none";
389
+ return [
390
+ `snapshotReceived=${this.snapshotReceived}`,
391
+ `totalUpdates=${this.updateEventCount}`,
392
+ `totalKeepalives=${this.keepaliveEventCount}`,
393
+ `lastEvent=${lastEvent}`,
394
+ `totalSpans=${this.spanMap.size}`,
395
+ `turnSpans=${turnSpans.length}`,
396
+ `turnSpanNames=${this._formatSet(spanNames)}`,
397
+ `turnHandlerStatuses=${this._formatSet(handlerStatuses)}`
398
+ ].join(", ");
399
+ }
400
+ _formatSet(values, limit = 12) {
401
+ if (values.size === 0) {
402
+ return "none";
403
+ }
404
+ const items = Array.from(values);
405
+ const suffix = items.length > limit ? `|+${items.length - limit} more` : "";
406
+ return `${items.slice(0, limit).join("|")}${suffix}`;
407
+ }
369
408
  /**
370
409
  * Insert or replace a span in the map (dedup by trace+span ID).
371
410
  */
@@ -391,7 +430,7 @@ ${value}` : value;
391
430
  }
392
431
  }
393
432
  /**
394
- * Check if there is a handler.conversation span that:
433
+ * Check if there is a turn-handler span (handler.conversation / handler.event) that:
395
434
  * 1. Was NOT seen before startTurn()
396
435
  * 2. Has terminal status (ok or error)
397
436
  *
@@ -400,9 +439,9 @@ ${value}` : value;
400
439
  * waitForTurnComplete check (which would otherwise resolve instantly
401
440
  * against stale data instead of waiting for the new turn's handler).
402
441
  */
403
- _hasNewCompletedHandler() {
442
+ _hasNewCompletedHandler(acceptSpanNames) {
404
443
  for (const [key, span] of this.spanMap) {
405
- if (span.name === "handler.conversation" && !this.seenHandlerKeys.has(key) && (span.status === "ok" || span.status === "error")) {
444
+ if (isTurnHandlerSpan(span) && (!acceptSpanNames || acceptSpanNames.has(span.name)) && !this.seenHandlerKeys.has(key) && (span.status === "ok" || span.status === "error")) {
406
445
  this.seenHandlerKeys.add(key);
407
446
  return true;
408
447
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/errors.ts", "../../src/spans/sse-collector.ts"],
4
+ "sourcesContent": ["import { AdkError } from '@botpress/analytics'\n\n/**\n * Stable error codes for the eval engine. Typed as a literal union so a typo'd\n * code is a compile error and PostHog issue-slicing stays consistent.\n * Conventions per docs/ERROR-HANDLING.md.\n */\nexport const EVAL_ERROR_CODES = [\n 'EVAL_LOAD_FAILED',\n 'EVAL_FILE_EMPTY',\n 'EVAL_DUPLICATE_NAME',\n 'EVAL_SEED_NO_CONVERSATION',\n 'EVAL_NO_CONVERSATION_ID',\n 'EVAL_TURN_CONFIG_INVALID',\n 'SSE_CONNECT_FAILED',\n 'SSE_NO_BODY',\n 'CHAT_CLIENT_MISSING',\n 'CHAT_NOT_CONNECTED',\n 'CHAT_INTEGRATION_MISSING',\n 'CHAT_CHANNEL_UNBOUND',\n] as const\n\nexport type EvalErrorCode = (typeof EVAL_ERROR_CODES)[number]\n\n/**\n * Typed error for the eval engine. Named EvalRunnerError (not EvalError) to\n * avoid shadowing the JS built-in `EvalError`.\n */\nexport class EvalRunnerError extends AdkError<EvalErrorCode> {}\n", "/**\n * SSECollector \u2014 connects to the ADK dev server's SSE trace stream,\n * accumulates spans, and provides promise-based wait methods for\n * turn completion and workflow signals.\n *\n * Uses fetch() + async iteration with manual SSE line parsing\n * (Bun does not have native EventSource).\n */\n\nimport type { Span } from './trace'\nimport { EvalRunnerError } from '../errors'\n\ntype SSEFilter = { conversationId: string } | { workflowId: string }\n\ninterface WaitOptions {\n timeout: number\n /**\n * Optional abort signal \u2014 when fired, the wait rejects immediately rather\n * than burning the full timeout. Used by the runner so Stop unblocks\n * pending workflow assertions (otherwise a single negative-result wait\n * can hold the suite hostage for `idleTimeout` per assertion).\n */\n abortSignal?: AbortSignal\n}\n\ninterface WorkflowWaitOptions extends WaitOptions {\n signal: 'entered' | 'completed'\n}\n\ninterface TurnWaitOptions extends WaitOptions {\n /**\n * Which handler span names may complete this turn. A user-message turn\n * completes via `handler.conversation` only \u2014 async noise such as a\n * workflow callback closes a silent `handler.event` span, and counting it\n * releases the runner early, whose next message then interrupts the\n * still-running handler. An event-driven turn completes via\n * `handler.event`. Omitted = any turn-handler span (legacy behavior).\n */\n acceptSpanNames?: ReadonlySet<string>\n /**\n * Quiet window after the handler closes before the turn is considered\n * settled (default 100 ms). The runner raises it so async follow-through\n * the handler scheduled \u2014 a tool-started workflow run and its callback,\n * which only execute AFTER the handler closes \u2014 finishes before the next\n * message is sent. A message sent into that window can be absorbed by the\n * follow-through's transcript save and silently skipped as already\n * processed.\n */\n settleQuietMs?: number\n /** Hard cap on the settle phase (default 2 s; the runner raises it with settleQuietMs). */\n settleMaxMs?: number\n}\n\n/**\n * Unique key for span deduplication.\n */\nfunction spanKey(span: Span): string {\n return `${span.id.trace}:${span.id.span}`\n}\n\n/**\n * Type-safe accessor for span.data fields.\n */\nfunction spanData(span: Span): Record<string, unknown> {\n return span.data && typeof span.data === 'object' ? (span.data as Record<string, unknown>) : {}\n}\n\n/**\n * Span names that mark a Conversation handler reaching the end of a turn.\n *\n * A turn driven by a user message emits `handler.conversation`; a turn driven\n * by an event (a Conversation's `events:` subscription \u2014 e.g. greeting on a\n * conversation-start signal) emits `handler.event`. Both close exactly once\n * per handler invocation, so either is a valid \"turn complete\" signal.\n */\nconst TURN_HANDLER_SPAN_NAMES = new Set(['handler.conversation', 'handler.event'])\n\nfunction isTurnHandlerSpan(span: Span): boolean {\n return TURN_HANDLER_SPAN_NAMES.has(span.name)\n}\n\nexport class SSECollector {\n private devServerUrl: string\n private headers: Record<string, string>\n\n /** All spans accumulated since connect(), keyed by spanKey for dedup. */\n private spanMap = new Map<string, Span>()\n\n /** Index into spanMap insertion order marking where the current turn started. */\n private turnStartKeys = new Set<string>()\n\n /** Handler span IDs seen before the current turn (for startTurn filtering). */\n private seenHandlerKeys = new Set<string>()\n\n /** Callbacks waiting for span updates. */\n private waiters: Array<(span: Span) => void> = []\n\n /** AbortController for the active SSE connection. */\n private abortController: AbortController | null = null\n\n /** Whether the background loop is running. */\n private connected = false\n\n /** Error from the SSE stream if it died unexpectedly. */\n private streamError: Error | null = null\n\n /** Passive counters used only to explain timeout failures. */\n private updateEventCount = 0\n private keepaliveEventCount = 0\n private lastEvent: { name: string; at: number } | null = null\n\n /** Set while a settle phase is in flight; called by disconnect() to short-circuit. */\n private _cancelSettle: (() => void) | null = null\n\n constructor(devServerUrl: string, headers: Record<string, string> = {}) {\n this.devServerUrl = devServerUrl\n this.headers = headers\n }\n\n /**\n * Open a streaming SSE connection filtered by conversationId or workflowId.\n * Starts a background loop that parses SSE events and accumulates spans.\n */\n async connect(filter: SSEFilter): Promise<void> {\n const attributeName = 'conversationId' in filter ? 'conversationId' : 'workflowId'\n const attributeValue = 'conversationId' in filter ? filter.conversationId : filter.workflowId\n\n const url = `${this.devServerUrl}/api/traces/stream?attributeName=${encodeURIComponent(attributeName)}&attributeValue=${encodeURIComponent(attributeValue)}`\n\n this.abortController = new AbortController()\n const myController = this.abortController\n\n const response = await fetch(url, {\n headers: { ...this.headers, Accept: 'text/event-stream' },\n signal: myController.signal,\n })\n\n if (!response.ok) {\n throw new EvalRunnerError({\n code: 'SSE_CONNECT_FAILED',\n message: `SSE connection failed: ${response.status} ${response.statusText}`,\n expected: true,\n suggestion: 'Make sure the dev server is running (`adk dev`) and reachable.',\n })\n }\n\n if (!response.body) {\n throw new EvalRunnerError({ code: 'SSE_NO_BODY', message: 'SSE response has no body' })\n }\n\n this.connected = true\n\n // Start background parsing loop\n this._consumeStream(response.body).catch((err) => {\n // Ignore the deliberate abort from repoint(): only the current stream's failure counts.\n if (this.connected && this.abortController === myController) {\n this.streamError = err instanceof Error ? err : new Error(String(err))\n }\n })\n\n // Wait for the initial snapshot to arrive before returning.\n // This ensures that connect() resolves only after the snapshot\n // has been processed so callers can immediately check spans.\n await this._waitForSnapshot()\n }\n\n /**\n * Re-point at a new conversation, keeping `spanMap` and `seenHandlerKeys` (so\n * the earlier conversation stays visible and its completed handler can't\n * satisfy the next turn). Resets the snapshot latch so connect() awaits the\n * new conversation's snapshot.\n */\n async repoint(filter: SSEFilter): Promise<void> {\n this.connected = false\n this.abortController?.abort()\n this.abortController = null\n this._cancelSettle?.()\n this._cancelSettle = null\n this.waiters = []\n this.snapshotReceived = false\n this.snapshotResolvers = []\n // Clear any prior stream's error so a later timeout reports the new\n // conversation's state, not a dead connection we intentionally closed.\n this.streamError = null\n await this.connect(filter)\n }\n\n /**\n * Mark the beginning of a new turn.\n * Records which turn-handler span IDs (handler.conversation / handler.event)\n * have been seen so far, so waitForTurnComplete() only resolves on NEW\n * handler completions.\n */\n startTurn(): void {\n // Snapshot the current set of span keys as the turn boundary\n this.turnStartKeys = new Set(this.spanMap.keys())\n\n // Record all turn-handler spans seen so far\n for (const [key, span] of this.spanMap) {\n if (isTurnHandlerSpan(span)) {\n this.seenHandlerKeys.add(key)\n }\n }\n }\n\n private static readonly SETTLE_MS = 100\n\n /**\n * Wait for a turn-handler span (handler.conversation or handler.event) to\n * reach terminal status (ok or error).\n * Only considers spans NOT seen before the last startTurn() call.\n *\n * After the handler closes, waits for a quiet period (no new spans for SETTLE_MS)\n * to ensure all child spans (tool calls, messages, state saves) have arrived.\n * The runtime's HttpSpanExporter fires POSTs without awaiting them, so child\n * spans may arrive slightly after the handler close event.\n */\n async waitForTurnComplete(opts: TurnWaitOptions): Promise<void> {\n const accept = opts.acceptSpanNames\n const settle = { quietMs: opts.settleQuietMs, maxMs: opts.settleMaxMs }\n // Check if a completed handler already exists (e.g. from snapshot)\n if (this._hasNewCompletedHandler(accept)) {\n return this._settleAfterHandler(settle)\n }\n if (opts.abortSignal?.aborted) {\n throw new Error('waitForTurnComplete aborted')\n }\n\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n const expected = accept ? [...accept].join(' or ') : 'handler.conversation or handler.event'\n const reason = this.streamError\n ? `SSE stream died: ${this.streamError.message}`\n : `no ${expected} span completed`\n reject(\n new Error(\n `waitForTurnComplete timed out after ${opts.timeout}ms \u2014 ${reason}; diagnostics: ${this._formatTurnDiagnostics()}`\n )\n )\n }, opts.timeout)\n\n const onAbort = () => {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n reject(new Error('waitForTurnComplete aborted'))\n }\n opts.abortSignal?.addEventListener('abort', onAbort, { once: true })\n\n const waiter = (_span: Span) => {\n if (this._hasNewCompletedHandler(accept)) {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n this._settleAfterHandler(settle).then(resolve, reject)\n }\n }\n\n this.waiters.push(waiter)\n })\n }\n\n private static readonly MAX_SETTLE_MS = 2_000\n\n /**\n * After the handler span closes, wait for spans to stop arriving.\n * Resolves when no new spans arrive for SETTLE_MS, or after MAX_SETTLE_MS hard cap.\n *\n * Exposes a cancellation hook via _cancelSettle so disconnect() can short-\n * circuit an in-flight settle phase and avoid leaking timers for up to 2s.\n */\n private _settleAfterHandler(settle?: { quietMs?: number; maxMs?: number }): Promise<void> {\n const quietMs = settle?.quietMs ?? SSECollector.SETTLE_MS\n const maxMs = settle?.maxMs ?? SSECollector.MAX_SETTLE_MS\n return new Promise<void>((resolve) => {\n let resolved = false\n const done = () => {\n if (resolved) return\n resolved = true\n clearTimeout(hardCap)\n clearTimeout(settleTimer)\n this._removeWaiter(settleWaiter)\n this._cancelSettle = null\n resolve()\n }\n\n const hardCap = setTimeout(done, maxMs)\n\n let settleTimer = setTimeout(done, quietMs)\n\n const settleWaiter = () => {\n if (resolved) return\n clearTimeout(settleTimer)\n settleTimer = setTimeout(done, quietMs)\n }\n\n this.waiters.push(settleWaiter)\n this._cancelSettle = done\n })\n }\n\n /**\n * Wait for a handler.workflow span with matching workflow name.\n * - signal 'entered': resolves when the span appears (any status)\n * - signal 'completed': resolves when the span has status !== 'running'\n */\n async waitForWorkflow(name: string, opts: WorkflowWaitOptions): Promise<void> {\n // Check if condition is already met\n if (this._matchesWorkflowCondition(name, opts.signal)) {\n return\n }\n if (opts.abortSignal?.aborted) {\n throw new Error(`waitForWorkflow(\"${name}\", { signal: \"${opts.signal}\" }) aborted`)\n }\n\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n const reason = this.streamError ? `SSE stream died: ${this.streamError.message}` : 'span not found'\n reject(\n new Error(\n `waitForWorkflow(\"${name}\", { signal: \"${opts.signal}\" }) timed out after ${opts.timeout}ms \u2014 ${reason}`\n )\n )\n }, opts.timeout)\n\n const onAbort = () => {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n reject(new Error(`waitForWorkflow(\"${name}\", { signal: \"${opts.signal}\" }) aborted`))\n }\n opts.abortSignal?.addEventListener('abort', onAbort, { once: true })\n\n const waiter = (_span: Span) => {\n if (this._matchesWorkflowCondition(name, opts.signal)) {\n clearTimeout(timer)\n this._removeWaiter(waiter)\n opts.abortSignal?.removeEventListener('abort', onAbort)\n resolve()\n }\n }\n\n this.waiters.push(waiter)\n })\n }\n\n /**\n * Return spans accumulated since the last startTurn() call.\n */\n getTurnSpans(): Span[] {\n const result: Span[] = []\n for (const [key, span] of this.spanMap) {\n if (!this.turnStartKeys.has(key)) {\n result.push(span)\n }\n }\n return result\n }\n\n /**\n * Return all spans accumulated since connect().\n */\n getAllSpans(): Span[] {\n return Array.from(this.spanMap.values())\n }\n\n /**\n * Close the SSE connection and clean up.\n */\n disconnect(): void {\n this.connected = false\n this.abortController?.abort()\n this.abortController = null\n // Short-circuit an in-flight settle phase so its hardCap / settleTimer\n // don't fire after disconnect.\n this._cancelSettle?.()\n this._cancelSettle = null\n // Waiters hold setTimeout references that will reject on their own;\n // clearing the array just prevents further notifications.\n this.waiters = []\n this.snapshotResolvers = []\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Whether we have received the initial snapshot. */\n private snapshotReceived = false\n private snapshotResolvers: Array<() => void> = []\n\n private static readonly SNAPSHOT_TIMEOUT_MS = 10_000\n\n /**\n * Returns a promise that resolves once the snapshot event has been processed,\n * or rejects after a timeout to prevent connect() from hanging indefinitely.\n */\n private _waitForSnapshot(): Promise<void> {\n if (this.snapshotReceived) {\n return Promise.resolve()\n }\n return new Promise<void>((resolve, reject) => {\n let settled = false\n const resolver = () => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n resolve()\n }\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n const idx = this.snapshotResolvers.indexOf(resolver)\n if (idx !== -1) this.snapshotResolvers.splice(idx, 1)\n reject(\n new Error(\n `SSE snapshot not received within ${SSECollector.SNAPSHOT_TIMEOUT_MS}ms \u2014 is the dev server healthy?`\n )\n )\n }, SSECollector.SNAPSHOT_TIMEOUT_MS)\n this.snapshotResolvers.push(resolver)\n })\n }\n\n /**\n * Consume the ReadableStream body, parse SSE lines, and dispatch events.\n */\n private async _consumeStream(body: ReadableStream<Uint8Array>): Promise<void> {\n const decoder = new TextDecoder()\n let buffer = ''\n\n const reader = body.getReader()\n\n try {\n while (this.connected) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n\n // Split on double-newline boundaries to get complete SSE events\n const parts = buffer.split('\\n\\n')\n // The last part may be incomplete \u2014 keep it in the buffer\n buffer = parts.pop() ?? ''\n\n for (const part of parts) {\n this._parseSSEBlock(part)\n }\n }\n } finally {\n reader.releaseLock()\n }\n }\n\n /**\n * Parse a single SSE event block (text between \\n\\n boundaries).\n * Extracts event name and data payload.\n */\n private _parseSSEBlock(block: string): void {\n const lines = block.split('\\n')\n let eventName = ''\n let dataLine = ''\n\n for (const line of lines) {\n if (line.startsWith('event:')) {\n eventName = line.slice('event:'.length).trim()\n } else if (line.startsWith('data:')) {\n const value = line.slice('data:'.length).trim()\n dataLine = dataLine ? `${dataLine}\\n${value}` : value\n }\n // id: and retry: lines are ignored\n }\n\n if (!eventName || !dataLine) {\n // Could be a retry: line or empty block \u2014 skip\n if (!this.snapshotReceived && block.includes('retry:')) {\n // The retry line comes before the snapshot; don't resolve yet\n }\n return\n }\n\n try {\n const payload = JSON.parse(dataLine)\n this._handleEvent(eventName, payload)\n } catch {\n // Malformed JSON \u2014 skip\n }\n }\n\n /**\n * Dispatch a parsed SSE event to the appropriate handler.\n */\n private _handleEvent(eventName: string, payload: unknown): void {\n this._recordEvent(eventName)\n\n if (eventName === 'snapshot') {\n const data = payload as { spans: Span[] }\n const spans = Array.isArray(data.spans) ? data.spans : []\n for (const span of spans) {\n this._upsertSpan(span)\n }\n this.snapshotReceived = true\n for (const resolve of this.snapshotResolvers) {\n resolve()\n }\n this.snapshotResolvers = []\n } else if (eventName === 'update') {\n const data = payload as { span: Span }\n if (data.span) {\n this._upsertSpan(data.span)\n this._notifyWaiters(data.span)\n }\n }\n // keepalive events are intentionally ignored\n }\n\n private _recordEvent(eventName: string): void {\n this.lastEvent = { name: eventName, at: Date.now() }\n\n if (eventName === 'update') {\n this.updateEventCount++\n } else if (eventName === 'keepalive') {\n this.keepaliveEventCount++\n }\n }\n\n private _formatTurnDiagnostics(): string {\n const turnSpans = this.getTurnSpans()\n const spanNames = new Set<string>()\n const handlerStatuses = new Set<string>()\n\n for (const span of turnSpans) {\n spanNames.add(span.name)\n // Tracks both handler.conversation and handler.event so an event-driven\n // turn that times out still surfaces its handler status.\n if (isTurnHandlerSpan(span)) {\n handlerStatuses.add(span.status)\n }\n }\n\n const lastEvent = this.lastEvent ? `${this.lastEvent.name} ${Date.now() - this.lastEvent.at}ms ago` : 'none'\n\n return [\n `snapshotReceived=${this.snapshotReceived}`,\n `totalUpdates=${this.updateEventCount}`,\n `totalKeepalives=${this.keepaliveEventCount}`,\n `lastEvent=${lastEvent}`,\n `totalSpans=${this.spanMap.size}`,\n `turnSpans=${turnSpans.length}`,\n `turnSpanNames=${this._formatSet(spanNames)}`,\n `turnHandlerStatuses=${this._formatSet(handlerStatuses)}`,\n ].join(', ')\n }\n\n private _formatSet(values: Set<string>, limit = 12): string {\n if (values.size === 0) {\n return 'none'\n }\n\n const items = Array.from(values)\n const suffix = items.length > limit ? `|+${items.length - limit} more` : ''\n return `${items.slice(0, limit).join('|')}${suffix}`\n }\n\n /**\n * Insert or replace a span in the map (dedup by trace+span ID).\n */\n private _upsertSpan(span: Span): void {\n this.spanMap.set(spanKey(span), span)\n }\n\n /**\n * Notify all waiters that a new span was received.\n */\n private _notifyWaiters(span: Span): void {\n // Copy the array since waiters may remove themselves during iteration\n const current = [...this.waiters]\n for (const waiter of current) {\n waiter(span)\n }\n }\n\n /**\n * Remove a waiter callback from the list.\n */\n private _removeWaiter(fn: (span: Span) => void): void {\n const idx = this.waiters.indexOf(fn)\n if (idx !== -1) {\n this.waiters.splice(idx, 1)\n }\n }\n\n /**\n * Check if there is a turn-handler span (handler.conversation / handler.event) that:\n * 1. Was NOT seen before startTurn()\n * 2. Has terminal status (ok or error)\n *\n * Marks the matched key as consumed so the same handler span cannot\n * satisfy both the current turn's settle phase AND the next turn's\n * waitForTurnComplete check (which would otherwise resolve instantly\n * against stale data instead of waiting for the new turn's handler).\n */\n private _hasNewCompletedHandler(acceptSpanNames?: ReadonlySet<string>): boolean {\n for (const [key, span] of this.spanMap) {\n if (\n isTurnHandlerSpan(span) &&\n (!acceptSpanNames || acceptSpanNames.has(span.name)) &&\n !this.seenHandlerKeys.has(key) &&\n (span.status === 'ok' || span.status === 'error')\n ) {\n this.seenHandlerKeys.add(key)\n return true\n }\n }\n return false\n }\n\n /**\n * Check if a handler.workflow span matches the given condition.\n */\n private _matchesWorkflowCondition(name: string, signal: 'entered' | 'completed'): boolean {\n for (const span of this.spanMap.values()) {\n if (span.name !== 'handler.workflow') continue\n\n const d = spanData(span)\n const wfName = d['workflow.name'] || d['workflowName']\n if (wfName !== name) continue\n\n if (signal === 'entered') {\n // Any status means the workflow was entered\n return true\n }\n\n if (signal === 'completed') {\n return span.status === 'ok' || span.status === 'error'\n }\n }\n return false\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/spans/trace.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,CAAA;AAEnD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,aAAa,GAAG,YAAY,CAAA;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE;QACR,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAA;AAEzD,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,UAAU,CAAA;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,IAAI,EAAE,QAAQ,CAAA;IACd,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,YAAY,CAAA;CACvB"}
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }