@tangle-network/agent-eval 0.221.7 → 0.221.8
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/CHANGELOG.md +64 -0
- package/dist/contract/index.js +1 -1
- package/dist/diagnosis/index.js +1 -1
- package/dist/openapi.json +1 -1
- package/dist/{otel-spans-CdN909k1.js → otel-spans-64hmQqON.js} +2 -2
- package/dist/{otel-spans-CdN909k1.js.map → otel-spans-64hmQqON.js.map} +1 -1
- package/dist/{task-failure-attributes-BMsblaDh.js → task-failure-attributes-oIK3BhCR.js} +6 -2
- package/dist/task-failure-attributes-oIK3BhCR.js.map +1 -0
- package/dist/traces.js +1 -1
- package/docs/concepts.md +1 -0
- package/package.json +1 -1
- package/dist/task-failure-attributes-BMsblaDh.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,70 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.221.8] — 2026-10-25
|
|
8
|
+
|
|
9
|
+
- test(adopt): retain failed browser child outcomes
|
|
10
|
+
- docs: leave changelog preparation to the release workflow
|
|
11
|
+
- chore(release): 0.221.7
|
|
12
|
+
- feat(multishot): conversation legs report the USD they spent
|
|
13
|
+
- chore(release): 0.221.6
|
|
14
|
+
- docs(traces): bound the incomplete usage coverage claim
|
|
15
|
+
- feat: own judge-panel calibration, multishot conversation legs and served-panel identity
|
|
16
|
+
- test(traces): typecheck OTLP usage integration proof
|
|
17
|
+
- fix(traces): retain incomplete OTLP token usage
|
|
18
|
+
- chore(release): 0.221.5
|
|
19
|
+
- ci: retain private adopter failure evidence (#1106)
|
|
20
|
+
- test(process): isolate process-tree fixture environment
|
|
21
|
+
- fix(adopt): invoke each resolved Playwright CLI (#1096)
|
|
22
|
+
- test(systemone): close disposable provider connections
|
|
23
|
+
- chore(release): 0.221.4
|
|
24
|
+
- test(adoption): inherit the maintained integration deadline
|
|
25
|
+
- fix(adoption): scope guard allowance to disposable Git fixtures
|
|
26
|
+
- fix(analyst): carry root-cause target into failure-mode rubric
|
|
27
|
+
- ci: check locally and in publication paths
|
|
28
|
+
- test(adopt): give real-git fixtures their own commit identity (#1101)
|
|
29
|
+
- chore(release): 0.221.3 (#1099)
|
|
30
|
+
- fix(analyst): repin the benchmark implementation digest after #1097 (#1098)
|
|
31
|
+
- fix(trace-analyst): build OTLP stores from bytes, not one joined string
|
|
32
|
+
- fix(adopt): prepare caller-locked Chromium before gates
|
|
33
|
+
- test(adopt): retain admitted identity in real Git fixtures
|
|
34
|
+
- fix(adopt): retain the sparse caller entrypoint
|
|
35
|
+
- fix(adopt): settle pushed proposal reads against remote refs
|
|
36
|
+
- chore(release): 0.221.2
|
|
37
|
+
- Return plain analyst findings from decision replay
|
|
38
|
+
- chore(release): 0.221.1 (#1091)
|
|
39
|
+
- fix(deps): pin zod 4.6.6 (GHSA-mrhx-c7xc-m3w5) (#1088)
|
|
40
|
+
- Give the disposable adoption fixture its own Git author
|
|
41
|
+
- Fix real adoption drift fixture under the declared package manager
|
|
42
|
+
- Capture supporting trace events in the original native decision
|
|
43
|
+
- test: inherit the integration default for behavioral questions
|
|
44
|
+
- test: set the integration default from the observed loaded tail
|
|
45
|
+
- chore(release): 0.221.0
|
|
46
|
+
- test: own the measured integration deadline in Vitest config
|
|
47
|
+
- test: await observed integration completion across tool paths
|
|
48
|
+
- feat: preserve finding composition and assigned arm populations
|
|
49
|
+
- test: await decision-point HTTP and ledger completion
|
|
50
|
+
- test: await durable readiness in filesystem integration cases
|
|
51
|
+
- test(process): wait for argv child readiness before abort
|
|
52
|
+
- feat(analyst): add bounded calibrated trace question observations
|
|
53
|
+
- fix(analyst): reject surrounding whitespace in caller gold ids
|
|
54
|
+
- test(analyst): reproduce whitespace source-id bypass
|
|
55
|
+
- test(optimizer): retire fake CLI metering harness
|
|
56
|
+
- test(analyst): isolate recorded gold admission scenarios
|
|
57
|
+
- fix(systemone): validate decision boundaries before routing
|
|
58
|
+
- ci: pin the eval publication dispatcher body
|
|
59
|
+
- fix(analyst): reserve excluded source ids across caller gold
|
|
60
|
+
- fix(analyst): validate root provenance before gold admission
|
|
61
|
+
- fix(analyst): preserve actor boundaries in AgentRx calibration gold
|
|
62
|
+
- Preserve manual drafts observed while recording failed adoption (#1076)
|
|
63
|
+
- Recover updater-owned failure drafts while preserving manual holds (#1075)
|
|
64
|
+
- feat(systemone): persist calibrated decision points and outcomes
|
|
65
|
+
- ci: move eval verification and Python publishing to owned runners
|
|
66
|
+
- chore(deps): bump opentelemetry-api in /clients/python (#1068)
|
|
67
|
+
- fix(adopt): quote optional workflow flags
|
|
68
|
+
- fix(adopt): preserve shallow history and caller-owned dependency references
|
|
69
|
+
- fix(adopt): reconcile effective workspace pins and preserve proposal history
|
|
70
|
+
|
|
7
71
|
## [0.221.7] — 2026-10-25
|
|
8
72
|
|
|
9
73
|
- feat(multishot): conversation legs report the USD they spent
|
package/dist/contract/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { t as createChatClient } from "../chat-client-BNATB3rJ.js";
|
|
|
19
19
|
import { n as InMemoryOutcomeStore, t as FileSystemOutcomeStore } from "../outcome-store-DUcMj-3D.js";
|
|
20
20
|
import { c as composeGate, l as externalTextOptimizationMethod, n as compareOptimizationMethods, r as gepaOptimizationMethod, s as heldOutGate, t as skillOptOptimizationMethod } from "../skillopt-optimization-method-sB_-yi1G.js";
|
|
21
21
|
import { n as paretoPolicy, r as paretoSignificanceGate, t as buildEvidenceVector } from "../promotion-policy-6A8osGr_.js";
|
|
22
|
-
import { t as fromOtelSpans } from "../otel-spans-
|
|
22
|
+
import { t as fromOtelSpans } from "../otel-spans-64hmQqON.js";
|
|
23
23
|
import { createHash } from "node:crypto";
|
|
24
24
|
import { agentCandidateBenchmarkSuiteSchema, agentCandidateBenchmarkTaskSchema, agentCandidateBundleSchema, agentCandidateEvaluationPolicySchema, agentCandidateExperimentSchema, agentImprovementMeasuredComparisonSchema, agentProfileImprovementExperimentSchema, agentProfileImprovementMeasuredComparisonSchema, agentProfileImprovementRunCellSchema, agentProfileImprovementRunReceiptSchema, agentProfileImprovementSuiteInputsSchema, agentProfileImprovementSuiteSchema, agentProfileImprovementTaskSchema, candidateExecutionEvidenceSchema, canonicalCandidateDigest, canonicalCandidateJson, numbersApproximatelyEqual, omitTopLevelDigest } from "@tangle-network/agent-interface";
|
|
25
25
|
import { createReadStream } from "node:fs";
|
package/dist/diagnosis/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { t as packageVersion } from "../package-version-D7lQHt_-.js";
|
|
|
4
4
|
import { r as classifyFailureReason } from "../failure-taxonomy-Bx5oWqaW.js";
|
|
5
5
|
import { c as runPrimeExchange, n as buildPrimePrompt, o as primeProtocolSha256 } from "../prime-protocol-BFgGrEMP.js";
|
|
6
6
|
import { t as nodeHttpPrimeBridgeTransport } from "../prime-bridge-transport-Z3cpKGoV.js";
|
|
7
|
-
import { t as fromOtelSpans } from "../otel-spans-
|
|
7
|
+
import { t as fromOtelSpans } from "../otel-spans-64hmQqON.js";
|
|
8
8
|
import { createHash, createHmac, randomBytes } from "node:crypto";
|
|
9
9
|
import { MODEL_ATTR_KEYS, SPAN_KINDS, TOOL_NAME_ATTR_KEYS, resolveSpanKind, validateTraceSpans } from "@tangle-network/agent-trace-contract";
|
|
10
10
|
//#region src/diagnosis/first-failure.ts
|
package/dist/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "@tangle-network/agent-eval — wire protocol",
|
|
5
|
-
"version": "0.221.
|
|
5
|
+
"version": "0.221.8",
|
|
6
6
|
"description": "HTTP and stdio RPC interface to agent-eval. The TypeScript runtime is the source of truth; this spec is the contract that cross-language clients (Python, Rust, Go) generate from.\n\nWire-protocol version: 1.1.0. Bumps on breaking changes to request/response schemas.",
|
|
7
7
|
"contact": {
|
|
8
8
|
"name": "Tangle Network",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { s as ValidationError } from "./errors-BcOs5MKW.js";
|
|
2
2
|
import { LLM_MODEL_ATTR_KEYS, SPAN_KIND_ATTR_KEYS as SPAN_KIND_ATTR_KEYS$1 } from "./trace-attributes.js";
|
|
3
3
|
import "./otlp-attributes-D9ptWaIJ.js";
|
|
4
|
-
import { i as summarizeTraceErrors, n as recordAggregateMeasurements, r as summarizeExecutionMeasurements, t as readTaskFailureLabels } from "./task-failure-attributes-
|
|
4
|
+
import { i as summarizeTraceErrors, n as recordAggregateMeasurements, r as summarizeExecutionMeasurements, t as readTaskFailureLabels } from "./task-failure-attributes-oIK3BhCR.js";
|
|
5
5
|
import { resolveSpanKind } from "@tangle-network/agent-trace-contract";
|
|
6
6
|
//#region src/contract/intake/otel-spans.ts
|
|
7
7
|
/**
|
|
@@ -266,4 +266,4 @@ function collectNumericAttrs(spans) {
|
|
|
266
266
|
//#endregion
|
|
267
267
|
export { fromOtelSpans as t };
|
|
268
268
|
|
|
269
|
-
//# sourceMappingURL=otel-spans-
|
|
269
|
+
//# sourceMappingURL=otel-spans-64hmQqON.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otel-spans-CdN909k1.js","names":["SPAN_KIND_ATTR_KEYS"],"sources":["../src/contract/intake/otel-spans.ts"],"sourcesContent":["/**\n * # `intake/otel-spans` — OTel `TraceSpanEvent[]` → `RunRecord[]`.\n *\n * Turns an existing observability stream into the substrate-canonical\n * `RunRecord` shape so consumers with logs but no eval discipline can\n * call `analyzeRuns()` against their production traffic immediately.\n *\n * Pivot rule: spans are grouped by `tangle.runId` (the same attribute the\n * hosted-tier wire format uses) or, when absent, by `traceId`. One group\n * becomes one `RunRecord`. The root span (no `parentSpanId`) supplies:\n *\n * - `runId` (the group key)\n * - `wallMs` from `endTimeUnixNano - startTimeUnixNano`\n * - `model` from `gen_ai.request.model` / `llm.model` / `tangle.model`\n * - task failure class and detail from explicit `tangle.task.*` attributes\n * - cost from `cost.usd` / `gen_ai.usage.cost_usd` / `tangle.cost.usd`\n * - token usage from model-call input, output, cache-read, and cache-write\n * attributes without double-counting aggregate parent spans\n * - task quality from an explicit `scoreForRun` callback or a designated\n * evaluation attribute on a root / `EVALUATOR` span; `outcome.raw`\n * collects every numeric attribute without promoting it to task quality.\n *\n * Errored tool, model, and child-agent spans contribute to execution-error\n * counts. Root process, guardrail, evaluator, propagated parent, and unknown\n * errors retain separate counters. Only one failed root can set\n * `RunRecord.terminalOutcome` and `RunRecord.terminalFailureReason`; a child\n * error cannot become a task failure.\n */\n\nimport { resolveSpanKind } from '@tangle-network/agent-trace-contract'\nimport { ValidationError } from '../../errors'\nimport type { TraceSpanEvent } from '../../hosted/types'\nimport type {\n JudgeScoresRecord,\n RunOutcome,\n RunRecord,\n RunSplitTag,\n RunTerminalOutcome,\n} from '../../run-record'\nimport { summarizeTraceErrors, type TraceErrorRole } from '../../trace/error-classification'\nimport {\n recordAggregateMeasurements,\n summarizeExecutionMeasurements,\n} from '../../trace/execution-measurements'\nimport { LLM_MODEL_ATTR_KEYS, SPAN_KIND_ATTR_KEYS } from '../../trace/otlp-attributes'\nimport { readTaskFailureLabels } from '../../trace/task-failure-attributes'\n\nconst TASK_SCORE_ATTR_KEYS = [\n 'gen_ai.evaluation.score.value',\n 'tangle.task.score',\n 'eval.score',\n 'tangle.score',\n] as const\nconst MODEL_KEYS = ['tangle.model', ...LLM_MODEL_ATTR_KEYS, 'model']\nconst PROMPT_HASH_KEYS = ['tangle.prompt_hash', 'prompt.hash']\nconst CONFIG_HASH_KEYS = ['tangle.config_hash', 'config.hash']\n\nexport interface FromOtelSpansOptions {\n spans: TraceSpanEvent[]\n /** Default split tag for synthesized records. Defaults to `'holdout'`. */\n defaultSplit?: RunSplitTag\n /** Default `experimentId` when not present on any span. */\n experimentId?: string\n /**\n * Explicit task-quality score for a logical run. The callback receives\n * spans in deterministic time/id order. Its value must agree with any\n * designated score attributes present on root or `EVALUATOR` spans.\n */\n scoreForRun?: (runId: string, spans: readonly TraceSpanEvent[]) => number | undefined\n}\n\nexport function fromOtelSpans(opts: FromOtelSpansOptions): RunRecord[] {\n const { spans, defaultSplit = 'holdout', experimentId = 'otel-corpus' } = opts\n const grouped = groupSpans(spans)\n\n const runs: RunRecord[] = []\n for (const [groupKey, groupSpans] of grouped) {\n const root = findRoot(groupSpans)\n if (!root) continue\n const measurements = summarizeExecutionMeasurements(\n groupSpans.map((span) => ({\n id: span.spanId,\n ...(span.parentSpanId ? { parentId: span.parentSpanId } : {}),\n attributes: span.attributes,\n kind: errorRoleForSpan(span),\n })),\n )\n const callSpanIds = new Set(measurements.callSpanIds)\n const callSpans = groupSpans.filter((span) => callSpanIds.has(span.spanId))\n const wallMs = unixNanoDurationMs(root.startTimeUnixNano, root.endTimeUnixNano)\n const model =\n readAttrString(callSpans, MODEL_KEYS) ??\n readAttrString(groupSpans, MODEL_KEYS) ??\n 'unknown@unknown'\n const capturedCost =\n (measurements.cost.complete ? measurements.cost.value : undefined) ??\n measurements.aggregate?.costUsd\n const costUsd = capturedCost ?? null\n const scenarioId = readConsistentScenarioId(groupKey, groupSpans) ?? groupKey\n const promptHash = readAttrString(groupSpans, PROMPT_HASH_KEYS) ?? 'sha256:unknown'\n const configHash = readAttrString(groupSpans, CONFIG_HASH_KEYS) ?? 'sha256:unknown'\n const score = resolveTaskScore(groupKey, groupSpans, opts.scoreForRun)\n const taskFailure = readTaskFailureLabels(\n groupSpans.filter((span) => !span.parentSpanId && isTerminalRootCandidate(span)),\n `fromOtelSpans: run '${groupKey}'`,\n )\n\n const rawNumeric = collectNumericAttrs(groupSpans)\n const errorSummary = summarizeTraceErrors(\n groupSpans.map((span) => ({\n id: spanIdentity(span),\n ...(span.parentSpanId ? { parentId: parentIdentity(span) } : {}),\n role: errorRoleForSpan(span),\n error: span.status?.code === 'ERROR',\n processRoot: !span.parentSpanId && isTerminalRootCandidate(span),\n })),\n )\n rawNumeric.error_span_count = errorSummary.total\n rawNumeric.execution_error_count = errorSummary.execution\n rawNumeric.process_error_count = errorSummary.process\n rawNumeric.guardrail_error_count = errorSummary.guardrail\n rawNumeric.judge_error_count = errorSummary.evaluation\n rawNumeric.propagated_error_count = errorSummary.propagated\n rawNumeric.unclassified_error_count = errorSummary.unclassified\n rawNumeric.llm_span_count = measurements.modelCallCount\n if (measurements.cost.value !== undefined && !measurements.cost.complete) {\n rawNumeric.partial_observed_cost_usd = measurements.cost.value\n }\n recordAggregateMeasurements(rawNumeric, measurements.aggregate)\n\n const judgeScores: JudgeScoresRecord | undefined =\n score !== undefined\n ? {\n perJudge: { 'otel-derived': { score } },\n perDimMean: { score },\n composite: score,\n }\n : undefined\n\n const terminalOutcome = terminalOutcomeFromRoots(groupSpans)\n const failedRoot =\n terminalOutcome === 'failed'\n ? groupSpans.find(\n (span) =>\n !span.parentSpanId && isTerminalRootCandidate(span) && span.status?.code === 'ERROR',\n )\n : undefined\n const outcome: RunOutcome = {\n raw: rawNumeric,\n ...(judgeScores ? { judgeScores } : {}),\n }\n if (score !== undefined) {\n if (defaultSplit === 'holdout') outcome.holdoutScore = score\n else outcome.searchScore = score\n }\n\n runs.push({\n runId: groupKey,\n experimentId,\n candidateId: (root.attributes['tangle.candidateId'] as string | undefined) ?? 'otel-default',\n seed: 0,\n model,\n promptHash,\n configHash,\n commitSha: (root.attributes['tangle.commit_sha'] as string | undefined) ?? 'unknown',\n wallMs,\n costUsd,\n costProvenance:\n capturedCost === undefined\n ? { kind: 'uncaptured', usd: null }\n : { kind: 'observed', usd: capturedCost },\n tokenUsage: measurements.tokenUsage,\n terminalOutcome,\n ...(failedRoot\n ? { terminalFailureReason: failedRoot.status?.message ?? failedRoot.name }\n : {}),\n outcome,\n ...taskFailure,\n splitTag: defaultSplit,\n scenarioId,\n })\n }\n return runs\n}\n\nfunction terminalOutcomeFromRoots(spans: TraceSpanEvent[]): RunTerminalOutcome {\n const roots = spans.filter((span) => !span.parentSpanId && isTerminalRootCandidate(span))\n if (roots.length !== 1) return 'unknown'\n if (roots[0]!.status?.code === 'OK') return 'succeeded'\n if (roots[0]!.status?.code === 'ERROR') return 'failed'\n return 'unknown'\n}\n\nfunction isTerminalRootCandidate(span: TraceSpanEvent): boolean {\n const role = errorRoleForSpan(span)\n if (role === 'LLM' || role === 'TOOL' || role === 'GUARDRAIL' || role === 'EVALUATOR') {\n return false\n }\n return true\n}\n\nfunction readSpanKind(span: TraceSpanEvent): string | undefined {\n return readAttrString([span], [...SPAN_KIND_ATTR_KEYS, 'span.kind'])?.toUpperCase()\n}\n\nfunction errorRoleForSpan(span: TraceSpanEvent): TraceErrorRole {\n return resolveSpanKind({\n kind: readSpanKind(span),\n name: span.name,\n attributes: span.attributes,\n })\n}\n\nfunction spanIdentity(span: TraceSpanEvent): string {\n return `${span.traceId}:${span.spanId}`\n}\n\nfunction parentIdentity(span: TraceSpanEvent): string {\n return `${span.traceId}:${span.parentSpanId}`\n}\n\n// ── Internal helpers ────────────────────────────────────────────────\n\nfunction groupSpans(spans: TraceSpanEvent[]): Map<string, TraceSpanEvent[]> {\n const m = new Map<string, TraceSpanEvent[]>()\n for (const span of spans) {\n const key = (span['tangle.runId'] as string | undefined) ?? span.traceId\n const list = m.get(key) ?? []\n list.push(span)\n m.set(key, list)\n }\n return m\n}\n\nfunction findRoot(group: TraceSpanEvent[]): TraceSpanEvent | undefined {\n const structuralRoots = group.filter((span) => !span.parentSpanId)\n const terminalRoots = structuralRoots.filter(isTerminalRootCandidate)\n const pool =\n terminalRoots.length > 0 ? terminalRoots : structuralRoots.length > 0 ? structuralRoots : group\n return orderSpans(pool)[0]\n}\n\nfunction readAttrString(spans: TraceSpanEvent[], keys: string[]): string | undefined {\n for (const span of spans) {\n for (const key of keys) {\n const v = span.attributes[key]\n if (typeof v === 'string' && v.length > 0) return v\n }\n }\n return undefined\n}\n\nfunction readConsistentScenarioId(\n runId: string,\n spans: readonly TraceSpanEvent[],\n): string | undefined {\n const values = new Set<string>()\n for (const span of spans) {\n const topLevel = span['tangle.scenarioId']\n if (typeof topLevel === 'string' && topLevel.length > 0) values.add(topLevel)\n const attribute = span.attributes['tangle.scenarioId']\n if (typeof attribute === 'string' && attribute.length > 0) values.add(attribute)\n }\n if (values.size > 1) {\n throw new ValidationError(\n `fromOtelSpans: conflicting scenario ids for run '${runId}': ${[...values].sort().join(', ')}`,\n )\n }\n return values.values().next().value\n}\n\ninterface TaskScoreSource {\n label: string\n value: number\n}\n\nfunction resolveTaskScore(\n runId: string,\n spans: TraceSpanEvent[],\n scoreForRun: FromOtelSpansOptions['scoreForRun'],\n): number | undefined {\n const orderedSpans = orderSpans(spans)\n const sources: TaskScoreSource[] = []\n\n if (scoreForRun) {\n const supplied: unknown = scoreForRun(runId, orderedSpans)\n if (supplied !== undefined) {\n if (typeof supplied !== 'number' || !Number.isFinite(supplied)) {\n throw new ValidationError(\n `fromOtelSpans: scoreForRun returned a non-finite number for run '${runId}'`,\n )\n }\n sources.push({ label: 'scoreForRun', value: supplied })\n }\n }\n\n for (const span of orderedSpans) {\n const role = errorRoleForSpan(span)\n if (span.parentSpanId && role !== 'EVALUATOR') continue\n if (role === 'EVALUATOR' && span.status?.code === 'ERROR') continue\n for (const key of TASK_SCORE_ATTR_KEYS) {\n if (!Object.hasOwn(span.attributes, key)) continue\n sources.push({\n label: `span '${span.spanId}' attribute '${key}'`,\n value: parseTaskScoreAttribute(runId, span.spanId, key, span.attributes[key]),\n })\n }\n }\n\n if (sources.length === 0) return undefined\n sources.sort((left, right) => left.label.localeCompare(right.label))\n const score = sources[0]!.value\n if (sources.some((source) => source.value !== score)) {\n const details = sources.map((source) => `${source.label}=${source.value}`).join(', ')\n throw new ValidationError(\n `fromOtelSpans: conflicting task-quality scores for run '${runId}': ${details}`,\n )\n }\n return score\n}\n\nfunction parseTaskScoreAttribute(\n runId: string,\n spanId: string,\n key: string,\n value: unknown,\n): number {\n const source = `span '${spanId}' attribute '${key}'`\n if (typeof value === 'string') {\n if (value.trim().length === 0) {\n throw new ValidationError(\n `fromOtelSpans: ${source} is blank for run '${runId}'; task quality must be finite`,\n )\n }\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n } else if (typeof value === 'number' && Number.isFinite(value)) {\n return value\n }\n throw new ValidationError(\n `fromOtelSpans: ${source} is not a finite task-quality score for run '${runId}'`,\n )\n}\n\nfunction orderSpans(spans: readonly TraceSpanEvent[]): TraceSpanEvent[] {\n return [...spans].sort(\n (left, right) =>\n compareUnixNano(left.startTimeUnixNano, right.startTimeUnixNano) ||\n left.spanId.localeCompare(right.spanId),\n )\n}\n\nfunction parseUnixNano(value: string): bigint {\n try {\n return BigInt(value)\n } catch {\n throw new ValidationError(`fromOtelSpans: invalid Unix nanosecond timestamp '${value}'`)\n }\n}\n\nfunction compareUnixNano(left: string, right: string): number {\n const leftValue = parseUnixNano(left)\n const rightValue = parseUnixNano(right)\n return leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0\n}\n\nfunction unixNanoDurationMs(start: string, end: string): number {\n const delta = parseUnixNano(end) - parseUnixNano(start)\n if (delta <= 0n) return 0\n const wholeMs = delta / 1_000_000n\n const fractionalMs = delta % 1_000_000n\n const value = Number(wholeMs) + Number(fractionalMs) / 1_000_000\n if (!Number.isSafeInteger(Number(wholeMs))) {\n throw new ValidationError('fromOtelSpans: span duration exceeds the safe millisecond range')\n }\n return value\n}\n\nfunction collectNumericAttrs(spans: TraceSpanEvent[]): Record<string, number> {\n const raw: Record<string, number> = {}\n for (const span of spans) {\n for (const [k, v] of Object.entries(span.attributes)) {\n if (typeof v === 'number' && Number.isFinite(v)) raw[k] = v\n }\n }\n return raw\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"otel-spans-64hmQqON.js","names":["SPAN_KIND_ATTR_KEYS"],"sources":["../src/contract/intake/otel-spans.ts"],"sourcesContent":["/**\n * # `intake/otel-spans` — OTel `TraceSpanEvent[]` → `RunRecord[]`.\n *\n * Turns an existing observability stream into the substrate-canonical\n * `RunRecord` shape so consumers with logs but no eval discipline can\n * call `analyzeRuns()` against their production traffic immediately.\n *\n * Pivot rule: spans are grouped by `tangle.runId` (the same attribute the\n * hosted-tier wire format uses) or, when absent, by `traceId`. One group\n * becomes one `RunRecord`. The root span (no `parentSpanId`) supplies:\n *\n * - `runId` (the group key)\n * - `wallMs` from `endTimeUnixNano - startTimeUnixNano`\n * - `model` from `gen_ai.request.model` / `llm.model` / `tangle.model`\n * - task failure class and detail from explicit `tangle.task.*` attributes\n * - cost from `cost.usd` / `gen_ai.usage.cost_usd` / `tangle.cost.usd`\n * - token usage from model-call input, output, cache-read, and cache-write\n * attributes without double-counting aggregate parent spans\n * - task quality from an explicit `scoreForRun` callback or a designated\n * evaluation attribute on a root / `EVALUATOR` span; `outcome.raw`\n * collects every numeric attribute without promoting it to task quality.\n *\n * Errored tool, model, and child-agent spans contribute to execution-error\n * counts. Root process, guardrail, evaluator, propagated parent, and unknown\n * errors retain separate counters. Only one failed root can set\n * `RunRecord.terminalOutcome` and `RunRecord.terminalFailureReason`; a child\n * error cannot become a task failure.\n */\n\nimport { resolveSpanKind } from '@tangle-network/agent-trace-contract'\nimport { ValidationError } from '../../errors'\nimport type { TraceSpanEvent } from '../../hosted/types'\nimport type {\n JudgeScoresRecord,\n RunOutcome,\n RunRecord,\n RunSplitTag,\n RunTerminalOutcome,\n} from '../../run-record'\nimport { summarizeTraceErrors, type TraceErrorRole } from '../../trace/error-classification'\nimport {\n recordAggregateMeasurements,\n summarizeExecutionMeasurements,\n} from '../../trace/execution-measurements'\nimport { LLM_MODEL_ATTR_KEYS, SPAN_KIND_ATTR_KEYS } from '../../trace/otlp-attributes'\nimport { readTaskFailureLabels } from '../../trace/task-failure-attributes'\n\nconst TASK_SCORE_ATTR_KEYS = [\n 'gen_ai.evaluation.score.value',\n 'tangle.task.score',\n 'eval.score',\n 'tangle.score',\n] as const\nconst MODEL_KEYS = ['tangle.model', ...LLM_MODEL_ATTR_KEYS, 'model']\nconst PROMPT_HASH_KEYS = ['tangle.prompt_hash', 'prompt.hash']\nconst CONFIG_HASH_KEYS = ['tangle.config_hash', 'config.hash']\n\nexport interface FromOtelSpansOptions {\n spans: TraceSpanEvent[]\n /** Default split tag for synthesized records. Defaults to `'holdout'`. */\n defaultSplit?: RunSplitTag\n /** Default `experimentId` when not present on any span. */\n experimentId?: string\n /**\n * Explicit task-quality score for a logical run. The callback receives\n * spans in deterministic time/id order. Its value must agree with any\n * designated score attributes present on root or `EVALUATOR` spans.\n */\n scoreForRun?: (runId: string, spans: readonly TraceSpanEvent[]) => number | undefined\n}\n\nexport function fromOtelSpans(opts: FromOtelSpansOptions): RunRecord[] {\n const { spans, defaultSplit = 'holdout', experimentId = 'otel-corpus' } = opts\n const grouped = groupSpans(spans)\n\n const runs: RunRecord[] = []\n for (const [groupKey, groupSpans] of grouped) {\n const root = findRoot(groupSpans)\n if (!root) continue\n const measurements = summarizeExecutionMeasurements(\n groupSpans.map((span) => ({\n id: span.spanId,\n ...(span.parentSpanId ? { parentId: span.parentSpanId } : {}),\n attributes: span.attributes,\n kind: errorRoleForSpan(span),\n })),\n )\n const callSpanIds = new Set(measurements.callSpanIds)\n const callSpans = groupSpans.filter((span) => callSpanIds.has(span.spanId))\n const wallMs = unixNanoDurationMs(root.startTimeUnixNano, root.endTimeUnixNano)\n const model =\n readAttrString(callSpans, MODEL_KEYS) ??\n readAttrString(groupSpans, MODEL_KEYS) ??\n 'unknown@unknown'\n const capturedCost =\n (measurements.cost.complete ? measurements.cost.value : undefined) ??\n measurements.aggregate?.costUsd\n const costUsd = capturedCost ?? null\n const scenarioId = readConsistentScenarioId(groupKey, groupSpans) ?? groupKey\n const promptHash = readAttrString(groupSpans, PROMPT_HASH_KEYS) ?? 'sha256:unknown'\n const configHash = readAttrString(groupSpans, CONFIG_HASH_KEYS) ?? 'sha256:unknown'\n const score = resolveTaskScore(groupKey, groupSpans, opts.scoreForRun)\n const taskFailure = readTaskFailureLabels(\n groupSpans.filter((span) => !span.parentSpanId && isTerminalRootCandidate(span)),\n `fromOtelSpans: run '${groupKey}'`,\n )\n\n const rawNumeric = collectNumericAttrs(groupSpans)\n const errorSummary = summarizeTraceErrors(\n groupSpans.map((span) => ({\n id: spanIdentity(span),\n ...(span.parentSpanId ? { parentId: parentIdentity(span) } : {}),\n role: errorRoleForSpan(span),\n error: span.status?.code === 'ERROR',\n processRoot: !span.parentSpanId && isTerminalRootCandidate(span),\n })),\n )\n rawNumeric.error_span_count = errorSummary.total\n rawNumeric.execution_error_count = errorSummary.execution\n rawNumeric.process_error_count = errorSummary.process\n rawNumeric.guardrail_error_count = errorSummary.guardrail\n rawNumeric.judge_error_count = errorSummary.evaluation\n rawNumeric.propagated_error_count = errorSummary.propagated\n rawNumeric.unclassified_error_count = errorSummary.unclassified\n rawNumeric.llm_span_count = measurements.modelCallCount\n if (measurements.cost.value !== undefined && !measurements.cost.complete) {\n rawNumeric.partial_observed_cost_usd = measurements.cost.value\n }\n recordAggregateMeasurements(rawNumeric, measurements.aggregate)\n\n const judgeScores: JudgeScoresRecord | undefined =\n score !== undefined\n ? {\n perJudge: { 'otel-derived': { score } },\n perDimMean: { score },\n composite: score,\n }\n : undefined\n\n const terminalOutcome = terminalOutcomeFromRoots(groupSpans)\n const failedRoot =\n terminalOutcome === 'failed'\n ? groupSpans.find(\n (span) =>\n !span.parentSpanId && isTerminalRootCandidate(span) && span.status?.code === 'ERROR',\n )\n : undefined\n const outcome: RunOutcome = {\n raw: rawNumeric,\n ...(judgeScores ? { judgeScores } : {}),\n }\n if (score !== undefined) {\n if (defaultSplit === 'holdout') outcome.holdoutScore = score\n else outcome.searchScore = score\n }\n\n runs.push({\n runId: groupKey,\n experimentId,\n candidateId: (root.attributes['tangle.candidateId'] as string | undefined) ?? 'otel-default',\n seed: 0,\n model,\n promptHash,\n configHash,\n commitSha: (root.attributes['tangle.commit_sha'] as string | undefined) ?? 'unknown',\n wallMs,\n costUsd,\n costProvenance:\n capturedCost === undefined\n ? { kind: 'uncaptured', usd: null }\n : { kind: 'observed', usd: capturedCost },\n tokenUsage: measurements.tokenUsage,\n terminalOutcome,\n ...(failedRoot\n ? { terminalFailureReason: failedRoot.status?.message ?? failedRoot.name }\n : {}),\n outcome,\n ...taskFailure,\n splitTag: defaultSplit,\n scenarioId,\n })\n }\n return runs\n}\n\nfunction terminalOutcomeFromRoots(spans: TraceSpanEvent[]): RunTerminalOutcome {\n const roots = spans.filter((span) => !span.parentSpanId && isTerminalRootCandidate(span))\n if (roots.length !== 1) return 'unknown'\n if (roots[0]!.status?.code === 'OK') return 'succeeded'\n if (roots[0]!.status?.code === 'ERROR') return 'failed'\n return 'unknown'\n}\n\nfunction isTerminalRootCandidate(span: TraceSpanEvent): boolean {\n const role = errorRoleForSpan(span)\n if (role === 'LLM' || role === 'TOOL' || role === 'GUARDRAIL' || role === 'EVALUATOR') {\n return false\n }\n return true\n}\n\nfunction readSpanKind(span: TraceSpanEvent): string | undefined {\n return readAttrString([span], [...SPAN_KIND_ATTR_KEYS, 'span.kind'])?.toUpperCase()\n}\n\nfunction errorRoleForSpan(span: TraceSpanEvent): TraceErrorRole {\n return resolveSpanKind({\n kind: readSpanKind(span),\n name: span.name,\n attributes: span.attributes,\n })\n}\n\nfunction spanIdentity(span: TraceSpanEvent): string {\n return `${span.traceId}:${span.spanId}`\n}\n\nfunction parentIdentity(span: TraceSpanEvent): string {\n return `${span.traceId}:${span.parentSpanId}`\n}\n\n// ── Internal helpers ────────────────────────────────────────────────\n\nfunction groupSpans(spans: TraceSpanEvent[]): Map<string, TraceSpanEvent[]> {\n const m = new Map<string, TraceSpanEvent[]>()\n for (const span of spans) {\n const key = (span['tangle.runId'] as string | undefined) ?? span.traceId\n const list = m.get(key) ?? []\n list.push(span)\n m.set(key, list)\n }\n return m\n}\n\nfunction findRoot(group: TraceSpanEvent[]): TraceSpanEvent | undefined {\n const structuralRoots = group.filter((span) => !span.parentSpanId)\n const terminalRoots = structuralRoots.filter(isTerminalRootCandidate)\n const pool =\n terminalRoots.length > 0 ? terminalRoots : structuralRoots.length > 0 ? structuralRoots : group\n return orderSpans(pool)[0]\n}\n\nfunction readAttrString(spans: TraceSpanEvent[], keys: string[]): string | undefined {\n for (const span of spans) {\n for (const key of keys) {\n const v = span.attributes[key]\n if (typeof v === 'string' && v.length > 0) return v\n }\n }\n return undefined\n}\n\nfunction readConsistentScenarioId(\n runId: string,\n spans: readonly TraceSpanEvent[],\n): string | undefined {\n const values = new Set<string>()\n for (const span of spans) {\n const topLevel = span['tangle.scenarioId']\n if (typeof topLevel === 'string' && topLevel.length > 0) values.add(topLevel)\n const attribute = span.attributes['tangle.scenarioId']\n if (typeof attribute === 'string' && attribute.length > 0) values.add(attribute)\n }\n if (values.size > 1) {\n throw new ValidationError(\n `fromOtelSpans: conflicting scenario ids for run '${runId}': ${[...values].sort().join(', ')}`,\n )\n }\n return values.values().next().value\n}\n\ninterface TaskScoreSource {\n label: string\n value: number\n}\n\nfunction resolveTaskScore(\n runId: string,\n spans: TraceSpanEvent[],\n scoreForRun: FromOtelSpansOptions['scoreForRun'],\n): number | undefined {\n const orderedSpans = orderSpans(spans)\n const sources: TaskScoreSource[] = []\n\n if (scoreForRun) {\n const supplied: unknown = scoreForRun(runId, orderedSpans)\n if (supplied !== undefined) {\n if (typeof supplied !== 'number' || !Number.isFinite(supplied)) {\n throw new ValidationError(\n `fromOtelSpans: scoreForRun returned a non-finite number for run '${runId}'`,\n )\n }\n sources.push({ label: 'scoreForRun', value: supplied })\n }\n }\n\n for (const span of orderedSpans) {\n const role = errorRoleForSpan(span)\n if (span.parentSpanId && role !== 'EVALUATOR') continue\n if (role === 'EVALUATOR' && span.status?.code === 'ERROR') continue\n for (const key of TASK_SCORE_ATTR_KEYS) {\n if (!Object.hasOwn(span.attributes, key)) continue\n sources.push({\n label: `span '${span.spanId}' attribute '${key}'`,\n value: parseTaskScoreAttribute(runId, span.spanId, key, span.attributes[key]),\n })\n }\n }\n\n if (sources.length === 0) return undefined\n sources.sort((left, right) => left.label.localeCompare(right.label))\n const score = sources[0]!.value\n if (sources.some((source) => source.value !== score)) {\n const details = sources.map((source) => `${source.label}=${source.value}`).join(', ')\n throw new ValidationError(\n `fromOtelSpans: conflicting task-quality scores for run '${runId}': ${details}`,\n )\n }\n return score\n}\n\nfunction parseTaskScoreAttribute(\n runId: string,\n spanId: string,\n key: string,\n value: unknown,\n): number {\n const source = `span '${spanId}' attribute '${key}'`\n if (typeof value === 'string') {\n if (value.trim().length === 0) {\n throw new ValidationError(\n `fromOtelSpans: ${source} is blank for run '${runId}'; task quality must be finite`,\n )\n }\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n } else if (typeof value === 'number' && Number.isFinite(value)) {\n return value\n }\n throw new ValidationError(\n `fromOtelSpans: ${source} is not a finite task-quality score for run '${runId}'`,\n )\n}\n\nfunction orderSpans(spans: readonly TraceSpanEvent[]): TraceSpanEvent[] {\n return [...spans].sort(\n (left, right) =>\n compareUnixNano(left.startTimeUnixNano, right.startTimeUnixNano) ||\n left.spanId.localeCompare(right.spanId),\n )\n}\n\nfunction parseUnixNano(value: string): bigint {\n try {\n return BigInt(value)\n } catch {\n throw new ValidationError(`fromOtelSpans: invalid Unix nanosecond timestamp '${value}'`)\n }\n}\n\nfunction compareUnixNano(left: string, right: string): number {\n const leftValue = parseUnixNano(left)\n const rightValue = parseUnixNano(right)\n return leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0\n}\n\nfunction unixNanoDurationMs(start: string, end: string): number {\n const delta = parseUnixNano(end) - parseUnixNano(start)\n if (delta <= 0n) return 0\n const wholeMs = delta / 1_000_000n\n const fractionalMs = delta % 1_000_000n\n const value = Number(wholeMs) + Number(fractionalMs) / 1_000_000\n if (!Number.isSafeInteger(Number(wholeMs))) {\n throw new ValidationError('fromOtelSpans: span duration exceeds the safe millisecond range')\n }\n return value\n}\n\nfunction collectNumericAttrs(spans: TraceSpanEvent[]): Record<string, number> {\n const raw: Record<string, number> = {}\n for (const span of spans) {\n for (const [k, v] of Object.entries(span.attributes)) {\n if (typeof v === 'number' && Number.isFinite(v)) raw[k] = v\n }\n }\n return raw\n}\n"],"mappings":"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"}
|
|
@@ -95,6 +95,7 @@ function summarizeExecutionMeasurements(source) {
|
|
|
95
95
|
const output = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_OUTPUT_TOKEN_ATTR_KEYS, LLM_REASONING_TOKEN_ATTR_KEYS);
|
|
96
96
|
const cached = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_CACHED_TOKEN_ATTR_KEYS);
|
|
97
97
|
const cacheWrite = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_CACHE_WRITE_TOKEN_ATTR_KEYS);
|
|
98
|
+
const tokensUnknown = calls.length > 0 && (!input.complete || !output.complete || calls.some((call) => call.attributes["tangle.supervise.tokens_known"] === false));
|
|
98
99
|
const aggregate = summarizeAggregateMeasurements(spans, byId, /* @__PURE__ */ new Set([...aggregateIds, ...untypedRunCostIds]));
|
|
99
100
|
const reasoningValue = reasoning.value ?? aggregate?.tokenUsage.reasoning;
|
|
100
101
|
const cachedValue = cached.value ?? aggregate?.tokenUsage.cached;
|
|
@@ -103,6 +104,7 @@ function summarizeExecutionMeasurements(source) {
|
|
|
103
104
|
tokenUsage: {
|
|
104
105
|
input: input.value ?? aggregate?.tokenUsage.input ?? 0,
|
|
105
106
|
output: Math.max(output.value ?? aggregate?.tokenUsage.output ?? 0, reasoningValue ?? 0),
|
|
107
|
+
...tokensUnknown ? { tokensKnown: false } : {},
|
|
106
108
|
...reasoningValue !== void 0 ? { reasoning: reasoningValue } : {},
|
|
107
109
|
...cachedValue !== void 0 ? { cached: cachedValue } : {},
|
|
108
110
|
...cacheWriteValue !== void 0 ? { cacheWrite: cacheWriteValue } : {}
|
|
@@ -159,9 +161,11 @@ function reconcileMeasurement(calls, byId, aggregateSourceIds, keys, fallbackKey
|
|
|
159
161
|
const selected = /* @__PURE__ */ new Map();
|
|
160
162
|
const callIds = new Set(calls.map((call) => call.id));
|
|
161
163
|
let reportingCalls = 0;
|
|
164
|
+
let complete = calls.length > 0;
|
|
162
165
|
for (const call of calls) {
|
|
163
166
|
const primary = nearestMeasurement(call, byId, callIds, aggregateSourceIds, keys);
|
|
164
167
|
const fallback = fallbackKeys ? nearestMeasurement(call, byId, callIds, aggregateSourceIds, fallbackKeys) : void 0;
|
|
168
|
+
if (!primary) complete = false;
|
|
165
169
|
const source = primary && fallback && primary.span.id === fallback.span.id ? {
|
|
166
170
|
span: primary.span,
|
|
167
171
|
value: Math.max(primary.value, fallback.value)
|
|
@@ -178,7 +182,7 @@ function reconcileMeasurement(calls, byId, aggregateSourceIds, keys, fallbackKey
|
|
|
178
182
|
return {
|
|
179
183
|
...selected.size > 0 ? { value: [...selected.values()].reduce((total, value) => total + value, 0) } : {},
|
|
180
184
|
reportingCalls,
|
|
181
|
-
complete:
|
|
185
|
+
complete: complete && reportingCalls === calls.length
|
|
182
186
|
};
|
|
183
187
|
}
|
|
184
188
|
function nearestMeasurement(call, byId, callIds, aggregateSourceIds, keys) {
|
|
@@ -317,4 +321,4 @@ function readConsistentRootString(roots, key, context) {
|
|
|
317
321
|
//#endregion
|
|
318
322
|
export { summarizeTraceErrors as i, recordAggregateMeasurements as n, summarizeExecutionMeasurements as r, readTaskFailureLabels as t };
|
|
319
323
|
|
|
320
|
-
//# sourceMappingURL=task-failure-attributes-
|
|
324
|
+
//# sourceMappingURL=task-failure-attributes-oIK3BhCR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-failure-attributes-oIK3BhCR.js","names":[],"sources":["../src/trace/error-classification.ts","../src/trace/execution-measurements.ts","../src/trace/task-failure-attributes.ts"],"sourcesContent":["import type { SpanKind } from '@tangle-network/agent-trace-contract'\n\nexport type TraceErrorRole = SpanKind\n\ninterface TraceErrorSignal {\n id: string\n parentId?: string\n role: TraceErrorRole\n error: boolean\n processRoot: boolean\n}\n\ninterface TraceErrorSummary {\n total: number\n execution: number\n process: number\n guardrail: number\n evaluation: number\n propagated: number\n unclassified: number\n}\n\n/**\n * Classify errored spans without counting a propagated parent status as a\n * second execution failure.\n */\nexport function summarizeTraceErrors(signals: readonly TraceErrorSignal[]): TraceErrorSummary {\n const byId = new Map<string, TraceErrorSignal>()\n for (const signal of signals) {\n if (byId.has(signal.id)) {\n throw new Error(`summarizeTraceErrors: duplicate span id '${signal.id}'`)\n }\n byId.set(signal.id, signal)\n }\n\n const propagated = new Set<string>()\n for (const signal of signals) {\n if (!signal.error) continue\n const visited = new Set<string>()\n let parentId = signal.parentId\n while (parentId && !visited.has(parentId)) {\n visited.add(parentId)\n const parent = byId.get(parentId)\n if (!parent) break\n if (parent.error) propagated.add(parent.id)\n parentId = parent.parentId\n }\n }\n\n const summary: TraceErrorSummary = {\n total: 0,\n execution: 0,\n process: 0,\n guardrail: 0,\n evaluation: 0,\n propagated: 0,\n unclassified: 0,\n }\n\n for (const signal of signals) {\n if (!signal.error) continue\n summary.total += 1\n if (signal.role === 'GUARDRAIL') {\n summary.guardrail += 1\n } else if (signal.role === 'EVALUATOR') {\n summary.evaluation += 1\n } else if (signal.processRoot) {\n summary.process += 1\n } else if (propagated.has(signal.id)) {\n summary.propagated += 1\n } else if (signal.role !== 'UNKNOWN') {\n summary.execution += 1\n } else {\n summary.unclassified += 1\n }\n }\n\n return summary\n}\n","import { isModelCallKind, type SpanKind } from '@tangle-network/agent-trace-contract'\nimport type { RunTokenUsage } from '../run-record'\nimport {\n LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,\n LLM_CACHED_TOKEN_ATTR_KEYS,\n LLM_COST_ATTR_KEYS,\n LLM_INPUT_TOKEN_ATTR_KEYS,\n LLM_OUTPUT_TOKEN_ATTR_KEYS,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n RUN_COST_ATTR_KEYS,\n} from './otlp-attributes'\n\ninterface ExecutionMeasurementSpan {\n id: string\n parentId?: string\n attributes: Record<string, unknown>\n /**\n * The contract kind, declared or inferred. `LLM`, `EMBEDDING` and `RERANKER`\n * ({@link isModelCallKind}) are each a leaf model call. Any other known kind\n * (an agent, a workflow step, a tool) is an aggregate whose tokens total the\n * calls beneath it. `UNKNOWN` is neither: its tokens count only when no model\n * call beneath it reports the same field.\n */\n kind: SpanKind\n}\n\ninterface MeasuredSpan extends ExecutionMeasurementSpan {\n modelCall: boolean\n aggregate: boolean\n}\n\nexport interface MeasurementCoverage {\n value?: number\n reportingCalls: number\n complete: boolean\n}\n\ninterface ExecutionMeasurements {\n tokenUsage: RunTokenUsage\n modelCallCount: number\n callSpanIds: string[]\n cost: MeasurementCoverage\n aggregate?: {\n tokenUsage: RunTokenUsage\n costUsd?: number\n }\n}\n\nconst TOKEN_MEASUREMENT_KEY_GROUPS = [\n LLM_INPUT_TOKEN_ATTR_KEYS,\n LLM_OUTPUT_TOKEN_ATTR_KEYS,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n LLM_CACHED_TOKEN_ATTR_KEYS,\n LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,\n] as const\n\nconst EXECUTION_MEASUREMENT_KEY_GROUPS = [\n ...TOKEN_MEASUREMENT_KEY_GROUPS,\n LLM_COST_ATTR_KEYS,\n] as const\n\ninterface RetainedCallSummary {\n callCount: number\n measurements: Array<{\n total: number\n reportingCalls: number\n }>\n}\n\n/**\n * Reconcile execution measurements across nested telemetry wrappers.\n * A measured parent is used only when a descendant call does not report the\n * same field, so aggregate wrappers neither duplicate complete child data nor\n * erase complementary parent fields.\n */\nexport function summarizeExecutionMeasurements(\n source: readonly ExecutionMeasurementSpan[],\n): ExecutionMeasurements {\n const spans: MeasuredSpan[] = source.map((span) => ({\n ...span,\n // EMBEDDING and RERANKER are leaf model calls too, billed like LLM — only a\n // wrapper kind (AGENT/CHAIN/TOOL/RETRIEVER/GUARDRAIL/EVALUATOR/PROMPT) is an\n // aggregate whose totals restate a descendant's.\n modelCall: isModelCallKind(span.kind),\n aggregate: !isModelCallKind(span.kind) && span.kind !== 'UNKNOWN',\n }))\n const byId = new Map<string, MeasuredSpan>()\n for (const span of spans) {\n if (byId.has(span.id)) {\n throw new Error(`summarizeExecutionMeasurements: duplicate span id \"${span.id}\"`)\n }\n byId.set(span.id, span)\n }\n const tokenMeasurementKeys = TOKEN_MEASUREMENT_KEY_GROUPS.flat()\n const candidates = spans.filter(\n (span) =>\n span.modelCall ||\n (!span.aggregate && readNumber(span.attributes, tokenMeasurementKeys) !== undefined),\n )\n const candidateIds = new Set(candidates.map((span) => span.id))\n const candidateChildren = new Map<string, MeasuredSpan[]>()\n for (const candidate of candidates) {\n const parentId = nearestCandidateParent(candidate, byId, candidateIds)\n if (!parentId) continue\n const children = candidateChildren.get(parentId) ?? []\n children.push(candidate)\n candidateChildren.set(parentId, children)\n }\n const aggregateIds = classifyAggregateSpans(candidates, candidateChildren)\n const untypedRunCostIds = new Set(\n spans\n .filter(\n (span) =>\n !span.modelCall &&\n !span.aggregate &&\n !candidateIds.has(span.id) &&\n readNumber(span.attributes, RUN_COST_ATTR_KEYS) !== undefined,\n )\n .map((span) => span.id),\n )\n const aggregateSourceIds = new Set(\n spans\n .filter(\n (span) => span.aggregate || aggregateIds.has(span.id) || untypedRunCostIds.has(span.id),\n )\n .map((span) => span.id),\n )\n\n const calls = candidates.filter((span) => !aggregateIds.has(span.id))\n const input = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_INPUT_TOKEN_ATTR_KEYS)\n const reasoning = reconcileMeasurement(\n calls,\n byId,\n aggregateSourceIds,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n )\n const output = reconcileMeasurement(\n calls,\n byId,\n aggregateSourceIds,\n LLM_OUTPUT_TOKEN_ATTR_KEYS,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n )\n const cached = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_CACHED_TOKEN_ATTR_KEYS)\n const cacheWrite = reconcileMeasurement(\n calls,\n byId,\n aggregateSourceIds,\n LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,\n )\n // A parent total can supply a subtotal without proving that an explicitly\n // unmeasured call reported its usage.\n const tokensUnknown =\n calls.length > 0 &&\n (!input.complete ||\n !output.complete ||\n calls.some((call) => call.attributes['tangle.supervise.tokens_known'] === false))\n const aggregate = summarizeAggregateMeasurements(\n spans,\n byId,\n new Set([...aggregateIds, ...untypedRunCostIds]),\n )\n\n // A trace whose only token-bearing span is an aggregate (a run or tool\n // span that reports totals but has no llm_call events beneath it) has no\n // model-call candidates, so `input`/`output`/etc. above are all undefined.\n // Fall back to the aggregate measurement's own field before defaulting to\n // 0, so \"no model call reported this\" does not read the same as \"measured\n // zero\". When neither reports a field, it really is 0/unset.\n const reasoningValue = reasoning.value ?? aggregate?.tokenUsage.reasoning\n const cachedValue = cached.value ?? aggregate?.tokenUsage.cached\n const cacheWriteValue = cacheWrite.value ?? aggregate?.tokenUsage.cacheWrite\n\n return {\n tokenUsage: {\n input: input.value ?? aggregate?.tokenUsage.input ?? 0,\n output: Math.max(output.value ?? aggregate?.tokenUsage.output ?? 0, reasoningValue ?? 0),\n ...(tokensUnknown ? { tokensKnown: false } : {}),\n ...(reasoningValue !== undefined ? { reasoning: reasoningValue } : {}),\n ...(cachedValue !== undefined ? { cached: cachedValue } : {}),\n ...(cacheWriteValue !== undefined ? { cacheWrite: cacheWriteValue } : {}),\n },\n modelCallCount: calls.length,\n callSpanIds: calls.map((span) => span.id),\n cost: reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_COST_ATTR_KEYS),\n ...(aggregate ? { aggregate } : {}),\n }\n}\n\nexport function recordAggregateMeasurements(\n raw: Record<string, number>,\n aggregate: ExecutionMeasurements['aggregate'],\n): void {\n if (!aggregate) return\n raw.aggregate_prompt_tokens = aggregate.tokenUsage.input\n raw.aggregate_completion_tokens = aggregate.tokenUsage.output\n if (aggregate.tokenUsage.reasoning !== undefined)\n raw.aggregate_reasoning_tokens = aggregate.tokenUsage.reasoning\n if (aggregate.tokenUsage.cached !== undefined)\n raw.aggregate_cached_tokens = aggregate.tokenUsage.cached\n if (aggregate.tokenUsage.cacheWrite !== undefined)\n raw.aggregate_cache_write_tokens = aggregate.tokenUsage.cacheWrite\n if (aggregate.costUsd !== undefined) raw.aggregate_cost_usd = aggregate.costUsd\n}\n\nfunction summarizeAggregateMeasurements(\n spans: MeasuredSpan[],\n byId: Map<string, MeasuredSpan>,\n aggregateIds: Set<string>,\n): ExecutionMeasurements['aggregate'] {\n const aggregates = spans.filter((span) => span.aggregate || aggregateIds.has(span.id))\n const input = reconcileTopLevelMeasurement(aggregates, byId, LLM_INPUT_TOKEN_ATTR_KEYS)\n const output = reconcileTopLevelMeasurement(aggregates, byId, LLM_OUTPUT_TOKEN_ATTR_KEYS)\n const reasoning = reconcileTopLevelMeasurement(aggregates, byId, LLM_REASONING_TOKEN_ATTR_KEYS)\n const cached = reconcileTopLevelMeasurement(aggregates, byId, LLM_CACHED_TOKEN_ATTR_KEYS)\n const cacheWrite = reconcileTopLevelMeasurement(aggregates, byId, LLM_CACHE_WRITE_TOKEN_ATTR_KEYS)\n const costUsd = reconcileTopLevelMeasurement(aggregates, byId, LLM_COST_ATTR_KEYS)\n if (\n input === undefined &&\n output === undefined &&\n reasoning === undefined &&\n cached === undefined &&\n cacheWrite === undefined &&\n costUsd === undefined\n )\n return undefined\n return {\n tokenUsage: {\n input: input ?? 0,\n output: output ?? reasoning ?? 0,\n ...(reasoning !== undefined ? { reasoning } : {}),\n ...(cached !== undefined ? { cached } : {}),\n ...(cacheWrite !== undefined ? { cacheWrite } : {}),\n },\n ...(costUsd !== undefined ? { costUsd } : {}),\n }\n}\n\nfunction reconcileTopLevelMeasurement(\n spans: MeasuredSpan[],\n byId: Map<string, MeasuredSpan>,\n keys: readonly string[],\n): number | undefined {\n const selected = new Map<string, number>()\n for (const span of spans) {\n const value = readNumber(span.attributes, keys)\n if (value !== undefined) selected.set(span.id, value)\n }\n for (const spanId of [...selected.keys()]) {\n const span = byId.get(spanId)\n if (!span) continue\n if (ancestorIds(span, byId).some((ancestorId) => selected.has(ancestorId))) {\n selected.delete(spanId)\n }\n }\n return selected.size > 0\n ? [...selected.values()].reduce((total, value) => total + value, 0)\n : undefined\n}\n\nfunction reconcileMeasurement(\n calls: MeasuredSpan[],\n byId: Map<string, MeasuredSpan>,\n aggregateSourceIds: Set<string>,\n keys: readonly string[],\n fallbackKeys?: readonly string[],\n): MeasurementCoverage {\n const selected = new Map<string, number>()\n const callIds = new Set(calls.map((call) => call.id))\n let reportingCalls = 0\n let complete = calls.length > 0\n\n for (const call of calls) {\n const primary = nearestMeasurement(call, byId, callIds, aggregateSourceIds, keys)\n const fallback = fallbackKeys\n ? nearestMeasurement(call, byId, callIds, aggregateSourceIds, fallbackKeys)\n : undefined\n // A fallback retains a known lower bound, such as reasoning tokens when\n // total output is absent; it cannot establish complete coverage.\n if (!primary) complete = false\n const source =\n primary && fallback && primary.span.id === fallback.span.id\n ? { span: primary.span, value: Math.max(primary.value, fallback.value) }\n : (primary ?? fallback)\n if (!source) continue\n reportingCalls += 1\n selected.set(source.span.id, source.value)\n }\n\n for (const spanId of [...selected.keys()]) {\n const span = byId.get(spanId)\n if (!span) continue\n if (\n ancestorIds(span, byId).some(\n (ancestorId) => selected.has(ancestorId) && !callIds.has(ancestorId),\n )\n ) {\n selected.delete(spanId)\n }\n }\n\n return {\n ...(selected.size > 0\n ? { value: [...selected.values()].reduce((total, value) => total + value, 0) }\n : {}),\n reportingCalls,\n complete: complete && reportingCalls === calls.length,\n }\n}\n\nfunction nearestMeasurement(\n call: MeasuredSpan,\n byId: Map<string, MeasuredSpan>,\n callIds: Set<string>,\n aggregateSourceIds: Set<string>,\n keys: readonly string[],\n): { span: MeasuredSpan; value: number } | undefined {\n let current: MeasuredSpan | undefined = call\n const seen = new Set<string>()\n while (current && !seen.has(current.id)) {\n seen.add(current.id)\n const value = readNumber(current.attributes, keys)\n if (\n value !== undefined &&\n (current.id === call.id || (!callIds.has(current.id) && aggregateSourceIds.has(current.id)))\n ) {\n return { span: current, value }\n }\n current = current.parentId ? byId.get(current.parentId) : undefined\n }\n return undefined\n}\n\nfunction classifyAggregateSpans(\n candidates: MeasuredSpan[],\n childrenById: Map<string, MeasuredSpan[]>,\n): Set<string> {\n const aggregateIds = new Set<string>()\n const summaries = new Map<string, RetainedCallSummary>()\n const visiting = new Set<string>()\n\n const visit = (span: MeasuredSpan): RetainedCallSummary => {\n const cached = summaries.get(span.id)\n if (cached) return cached\n if (visiting.has(span.id)) return emptyRetainedCallSummary()\n visiting.add(span.id)\n\n const descendants = emptyRetainedCallSummary()\n for (const child of childrenById.get(span.id) ?? []) {\n const childDescendants = visit(child)\n if (!aggregateIds.has(child.id)) addRetainedCall(descendants, child)\n addRetainedCallSummary(descendants, childDescendants)\n }\n\n if (\n descendants.callCount > 0 &&\n (!span.modelCall || hasCompatibleDescendantMeasurements(span, descendants))\n ) {\n aggregateIds.add(span.id)\n }\n\n visiting.delete(span.id)\n summaries.set(span.id, descendants)\n return descendants\n }\n\n for (const candidate of candidates) visit(candidate)\n return aggregateIds\n}\n\nfunction emptyRetainedCallSummary(): RetainedCallSummary {\n return {\n callCount: 0,\n measurements: EXECUTION_MEASUREMENT_KEY_GROUPS.map(() => ({\n total: 0,\n reportingCalls: 0,\n })),\n }\n}\n\nfunction addRetainedCall(summary: RetainedCallSummary, span: MeasuredSpan): void {\n summary.callCount += 1\n for (let index = 0; index < EXECUTION_MEASUREMENT_KEY_GROUPS.length; index += 1) {\n const value = readNumber(span.attributes, EXECUTION_MEASUREMENT_KEY_GROUPS[index]!)\n if (value === undefined) continue\n const measurement = summary.measurements[index]!\n measurement.total += value\n measurement.reportingCalls += 1\n }\n}\n\nfunction addRetainedCallSummary(target: RetainedCallSummary, source: RetainedCallSummary): void {\n target.callCount += source.callCount\n for (let index = 0; index < target.measurements.length; index += 1) {\n const measurement = target.measurements[index]!\n const sourceMeasurement = source.measurements[index]!\n measurement.total += sourceMeasurement.total\n measurement.reportingCalls += sourceMeasurement.reportingCalls\n }\n}\n\nfunction hasCompatibleDescendantMeasurements(\n span: MeasuredSpan,\n descendants: RetainedCallSummary,\n): boolean {\n let parentMeasurements = 0\n let descendantMeasurements = 0\n for (let index = 0; index < EXECUTION_MEASUREMENT_KEY_GROUPS.length; index += 1) {\n const keys = EXECUTION_MEASUREMENT_KEY_GROUPS[index]!\n const parentValue = readNumber(span.attributes, keys)\n const measurement = descendants.measurements[index]!\n if (parentValue !== undefined) parentMeasurements += 1\n if (measurement.reportingCalls > 0) descendantMeasurements += 1\n if (parentValue === undefined || measurement.reportingCalls === 0) continue\n if (measurement.reportingCalls !== descendants.callCount) continue\n if (Math.abs(parentValue - measurement.total) > 1e-12) return false\n }\n return parentMeasurements === 0 || descendantMeasurements > 0\n}\n\nfunction ancestorIds(span: MeasuredSpan, byId: Map<string, MeasuredSpan>): string[] {\n const ids: string[] = []\n const seen = new Set<string>()\n let parentId = span.parentId\n while (parentId && !seen.has(parentId)) {\n ids.push(parentId)\n seen.add(parentId)\n parentId = byId.get(parentId)?.parentId\n }\n return ids\n}\n\nfunction nearestCandidateParent(\n span: MeasuredSpan,\n byId: Map<string, MeasuredSpan>,\n candidateIds: Set<string>,\n): string | undefined {\n const seen = new Set<string>()\n let parentId = span.parentId\n while (parentId && !seen.has(parentId)) {\n if (candidateIds.has(parentId)) return parentId\n seen.add(parentId)\n parentId = byId.get(parentId)?.parentId\n }\n return undefined\n}\n\nfunction readNumber(\n attributes: Record<string, unknown>,\n keys: readonly string[],\n): number | undefined {\n for (const key of keys) {\n const value = attributes[key]\n const parsed =\n typeof value === 'number'\n ? value\n : typeof value === 'string' && value.length > 0\n ? Number(value)\n : Number.NaN\n if (Number.isFinite(parsed) && parsed >= 0) return parsed\n }\n return undefined\n}\n","import { ValidationError } from '../errors'\nimport { FAILURE_CLASSES, type FailureClass } from './schema'\n\nconst TASK_FAILURE_CLASS_ATTR = 'tangle.task.failure_class'\nconst TASK_FAILURE_MODE_ATTR = 'tangle.task.failure_mode'\n\ninterface AttributeCarrier {\n attributes: Record<string, unknown>\n}\n\ntype TaskFailureLabels =\n | { failureClass?: undefined; failureMode?: undefined }\n | { failureClass: 'success'; failureMode?: undefined }\n | { failureClass: Exclude<FailureClass, 'success'>; failureMode?: string }\n\nexport function readTaskFailureLabels(\n roots: readonly AttributeCarrier[],\n context: string,\n): TaskFailureLabels {\n const failureClass = readConsistentRootString(roots, TASK_FAILURE_CLASS_ATTR, context)\n const failureMode = readConsistentRootString(roots, TASK_FAILURE_MODE_ATTR, context)\n\n if (failureClass !== undefined && !FAILURE_CLASSES.includes(failureClass as FailureClass)) {\n throw new ValidationError(\n `${context}: ${TASK_FAILURE_CLASS_ATTR} must be one of ${FAILURE_CLASSES.join(', ')}`,\n )\n }\n if (failureMode !== undefined && (failureClass === undefined || failureClass === 'success')) {\n throw new ValidationError(\n `${context}: ${TASK_FAILURE_MODE_ATTR} requires a non-success ${TASK_FAILURE_CLASS_ATTR}`,\n )\n }\n\n if (failureClass === undefined) return {}\n if (failureClass === 'success') return { failureClass }\n return {\n failureClass: failureClass as Exclude<FailureClass, 'success'>,\n ...(failureMode ? { failureMode } : {}),\n }\n}\n\nfunction readConsistentRootString(\n roots: readonly AttributeCarrier[],\n key: string,\n context: string,\n): string | undefined {\n const values = new Set<string>()\n for (const root of roots) {\n if (!Object.hasOwn(root.attributes, key)) continue\n const value = root.attributes[key]\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new ValidationError(`${context}: ${key} must be a non-empty string`)\n }\n values.add(value)\n }\n\n if (values.size > 1) {\n throw new ValidationError(\n `${context}: conflicting ${key} values: ${[...values].sort().join(', ')}`,\n )\n }\n return values.values().next().value\n}\n"],"mappings":"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"}
|
package/dist/traces.js
CHANGED
|
@@ -12,7 +12,7 @@ import { n as assertRunCaptured, t as RunIntegrityError } from "./integrity-W4CA
|
|
|
12
12
|
import { n as traceSpanKindToOpenInferenceKind, t as applyToolSpanOtlpAttributes } from "./otlp-attributes-D9ptWaIJ.js";
|
|
13
13
|
import { n as InMemoryTraceStore, t as FileSystemTraceStore } from "./store-BhNuLdJw.js";
|
|
14
14
|
import { a as spanRecordsToTraceAnalysisStore, i as otlpTextToTraceAnalysisStore, r as OtlpFileTraceStore } from "./store-otlp-CFf27E8_.js";
|
|
15
|
-
import { i as summarizeTraceErrors, n as recordAggregateMeasurements, r as summarizeExecutionMeasurements, t as readTaskFailureLabels } from "./task-failure-attributes-
|
|
15
|
+
import { i as summarizeTraceErrors, n as recordAggregateMeasurements, r as summarizeExecutionMeasurements, t as readTaskFailureLabels } from "./task-failure-attributes-oIK3BhCR.js";
|
|
16
16
|
import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
import { resolveSpanKind } from "@tangle-network/agent-trace-contract";
|
package/docs/concepts.md
CHANGED
|
@@ -63,6 +63,7 @@ Use the profile improvement functions from `/contract` when a host owns immutabl
|
|
|
63
63
|
`runAgentProfileImprovementExperiment()` asks the host to execute every frozen baseline/candidate cell and requires one complete receipt per execution.
|
|
64
64
|
`measuredComparisonFromAgentProfileImprovementExperiment()` recomputes scores, uncertainty, cost, latency, and the release decision from those receipts.
|
|
65
65
|
`RunTokenUsage.tokensKnown: false` marks incomplete provider usage; its counts are known subtotals, and token-efficiency ratios stay absent.
|
|
66
|
+
The OTLP run-record readers also set this flag when reconciled model-call input or output coverage is incomplete or a call explicitly reports unknown usage. Existing parent-aggregate inheritance still applies to unflagged calls. A failed call remains counted and measured zero remains zero. An explicit unknown-call marker remains incomplete when a parent aggregate supplies numbers; reasoning-only output remains a subtotal.
|
|
66
67
|
|
|
67
68
|
This API never activates a candidate or runs an agent itself.
|
|
68
69
|
The host owns authorization, billing, task isolation, profile materialization, execution, and durable evidence.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-eval",
|
|
3
|
-
"version": "0.221.
|
|
3
|
+
"version": "0.221.8",
|
|
4
4
|
"description": "Evaluate and improve AI agents from runs, traces, judges, and feedback. Compare candidates, cluster failures, measure lift, and gate releases.",
|
|
5
5
|
"homepage": "https://github.com/tangle-network/agent-eval#readme",
|
|
6
6
|
"repository": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"task-failure-attributes-BMsblaDh.js","names":[],"sources":["../src/trace/error-classification.ts","../src/trace/execution-measurements.ts","../src/trace/task-failure-attributes.ts"],"sourcesContent":["import type { SpanKind } from '@tangle-network/agent-trace-contract'\n\nexport type TraceErrorRole = SpanKind\n\ninterface TraceErrorSignal {\n id: string\n parentId?: string\n role: TraceErrorRole\n error: boolean\n processRoot: boolean\n}\n\ninterface TraceErrorSummary {\n total: number\n execution: number\n process: number\n guardrail: number\n evaluation: number\n propagated: number\n unclassified: number\n}\n\n/**\n * Classify errored spans without counting a propagated parent status as a\n * second execution failure.\n */\nexport function summarizeTraceErrors(signals: readonly TraceErrorSignal[]): TraceErrorSummary {\n const byId = new Map<string, TraceErrorSignal>()\n for (const signal of signals) {\n if (byId.has(signal.id)) {\n throw new Error(`summarizeTraceErrors: duplicate span id '${signal.id}'`)\n }\n byId.set(signal.id, signal)\n }\n\n const propagated = new Set<string>()\n for (const signal of signals) {\n if (!signal.error) continue\n const visited = new Set<string>()\n let parentId = signal.parentId\n while (parentId && !visited.has(parentId)) {\n visited.add(parentId)\n const parent = byId.get(parentId)\n if (!parent) break\n if (parent.error) propagated.add(parent.id)\n parentId = parent.parentId\n }\n }\n\n const summary: TraceErrorSummary = {\n total: 0,\n execution: 0,\n process: 0,\n guardrail: 0,\n evaluation: 0,\n propagated: 0,\n unclassified: 0,\n }\n\n for (const signal of signals) {\n if (!signal.error) continue\n summary.total += 1\n if (signal.role === 'GUARDRAIL') {\n summary.guardrail += 1\n } else if (signal.role === 'EVALUATOR') {\n summary.evaluation += 1\n } else if (signal.processRoot) {\n summary.process += 1\n } else if (propagated.has(signal.id)) {\n summary.propagated += 1\n } else if (signal.role !== 'UNKNOWN') {\n summary.execution += 1\n } else {\n summary.unclassified += 1\n }\n }\n\n return summary\n}\n","import { isModelCallKind, type SpanKind } from '@tangle-network/agent-trace-contract'\nimport type { RunTokenUsage } from '../run-record'\nimport {\n LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,\n LLM_CACHED_TOKEN_ATTR_KEYS,\n LLM_COST_ATTR_KEYS,\n LLM_INPUT_TOKEN_ATTR_KEYS,\n LLM_OUTPUT_TOKEN_ATTR_KEYS,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n RUN_COST_ATTR_KEYS,\n} from './otlp-attributes'\n\ninterface ExecutionMeasurementSpan {\n id: string\n parentId?: string\n attributes: Record<string, unknown>\n /**\n * The contract kind, declared or inferred. `LLM`, `EMBEDDING` and `RERANKER`\n * ({@link isModelCallKind}) are each a leaf model call. Any other known kind\n * (an agent, a workflow step, a tool) is an aggregate whose tokens total the\n * calls beneath it. `UNKNOWN` is neither: its tokens count only when no model\n * call beneath it reports the same field.\n */\n kind: SpanKind\n}\n\ninterface MeasuredSpan extends ExecutionMeasurementSpan {\n modelCall: boolean\n aggregate: boolean\n}\n\nexport interface MeasurementCoverage {\n value?: number\n reportingCalls: number\n complete: boolean\n}\n\ninterface ExecutionMeasurements {\n tokenUsage: RunTokenUsage\n modelCallCount: number\n callSpanIds: string[]\n cost: MeasurementCoverage\n aggregate?: {\n tokenUsage: RunTokenUsage\n costUsd?: number\n }\n}\n\nconst TOKEN_MEASUREMENT_KEY_GROUPS = [\n LLM_INPUT_TOKEN_ATTR_KEYS,\n LLM_OUTPUT_TOKEN_ATTR_KEYS,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n LLM_CACHED_TOKEN_ATTR_KEYS,\n LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,\n] as const\n\nconst EXECUTION_MEASUREMENT_KEY_GROUPS = [\n ...TOKEN_MEASUREMENT_KEY_GROUPS,\n LLM_COST_ATTR_KEYS,\n] as const\n\ninterface RetainedCallSummary {\n callCount: number\n measurements: Array<{\n total: number\n reportingCalls: number\n }>\n}\n\n/**\n * Reconcile execution measurements across nested telemetry wrappers.\n * A measured parent is used only when a descendant call does not report the\n * same field, so aggregate wrappers neither duplicate complete child data nor\n * erase complementary parent fields.\n */\nexport function summarizeExecutionMeasurements(\n source: readonly ExecutionMeasurementSpan[],\n): ExecutionMeasurements {\n const spans: MeasuredSpan[] = source.map((span) => ({\n ...span,\n // EMBEDDING and RERANKER are leaf model calls too, billed like LLM — only a\n // wrapper kind (AGENT/CHAIN/TOOL/RETRIEVER/GUARDRAIL/EVALUATOR/PROMPT) is an\n // aggregate whose totals restate a descendant's.\n modelCall: isModelCallKind(span.kind),\n aggregate: !isModelCallKind(span.kind) && span.kind !== 'UNKNOWN',\n }))\n const byId = new Map<string, MeasuredSpan>()\n for (const span of spans) {\n if (byId.has(span.id)) {\n throw new Error(`summarizeExecutionMeasurements: duplicate span id \"${span.id}\"`)\n }\n byId.set(span.id, span)\n }\n const tokenMeasurementKeys = TOKEN_MEASUREMENT_KEY_GROUPS.flat()\n const candidates = spans.filter(\n (span) =>\n span.modelCall ||\n (!span.aggregate && readNumber(span.attributes, tokenMeasurementKeys) !== undefined),\n )\n const candidateIds = new Set(candidates.map((span) => span.id))\n const candidateChildren = new Map<string, MeasuredSpan[]>()\n for (const candidate of candidates) {\n const parentId = nearestCandidateParent(candidate, byId, candidateIds)\n if (!parentId) continue\n const children = candidateChildren.get(parentId) ?? []\n children.push(candidate)\n candidateChildren.set(parentId, children)\n }\n const aggregateIds = classifyAggregateSpans(candidates, candidateChildren)\n const untypedRunCostIds = new Set(\n spans\n .filter(\n (span) =>\n !span.modelCall &&\n !span.aggregate &&\n !candidateIds.has(span.id) &&\n readNumber(span.attributes, RUN_COST_ATTR_KEYS) !== undefined,\n )\n .map((span) => span.id),\n )\n const aggregateSourceIds = new Set(\n spans\n .filter(\n (span) => span.aggregate || aggregateIds.has(span.id) || untypedRunCostIds.has(span.id),\n )\n .map((span) => span.id),\n )\n\n const calls = candidates.filter((span) => !aggregateIds.has(span.id))\n const input = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_INPUT_TOKEN_ATTR_KEYS)\n const reasoning = reconcileMeasurement(\n calls,\n byId,\n aggregateSourceIds,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n )\n const output = reconcileMeasurement(\n calls,\n byId,\n aggregateSourceIds,\n LLM_OUTPUT_TOKEN_ATTR_KEYS,\n LLM_REASONING_TOKEN_ATTR_KEYS,\n )\n const cached = reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_CACHED_TOKEN_ATTR_KEYS)\n const cacheWrite = reconcileMeasurement(\n calls,\n byId,\n aggregateSourceIds,\n LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,\n )\n const aggregate = summarizeAggregateMeasurements(\n spans,\n byId,\n new Set([...aggregateIds, ...untypedRunCostIds]),\n )\n\n // A trace whose only token-bearing span is an aggregate (a run or tool\n // span that reports totals but has no llm_call events beneath it) has no\n // model-call candidates, so `input`/`output`/etc. above are all undefined.\n // Fall back to the aggregate measurement's own field before defaulting to\n // 0, so \"no model call reported this\" does not read the same as \"measured\n // zero\". When neither reports a field, it really is 0/unset.\n const reasoningValue = reasoning.value ?? aggregate?.tokenUsage.reasoning\n const cachedValue = cached.value ?? aggregate?.tokenUsage.cached\n const cacheWriteValue = cacheWrite.value ?? aggregate?.tokenUsage.cacheWrite\n\n return {\n tokenUsage: {\n input: input.value ?? aggregate?.tokenUsage.input ?? 0,\n output: Math.max(output.value ?? aggregate?.tokenUsage.output ?? 0, reasoningValue ?? 0),\n ...(reasoningValue !== undefined ? { reasoning: reasoningValue } : {}),\n ...(cachedValue !== undefined ? { cached: cachedValue } : {}),\n ...(cacheWriteValue !== undefined ? { cacheWrite: cacheWriteValue } : {}),\n },\n modelCallCount: calls.length,\n callSpanIds: calls.map((span) => span.id),\n cost: reconcileMeasurement(calls, byId, aggregateSourceIds, LLM_COST_ATTR_KEYS),\n ...(aggregate ? { aggregate } : {}),\n }\n}\n\nexport function recordAggregateMeasurements(\n raw: Record<string, number>,\n aggregate: ExecutionMeasurements['aggregate'],\n): void {\n if (!aggregate) return\n raw.aggregate_prompt_tokens = aggregate.tokenUsage.input\n raw.aggregate_completion_tokens = aggregate.tokenUsage.output\n if (aggregate.tokenUsage.reasoning !== undefined)\n raw.aggregate_reasoning_tokens = aggregate.tokenUsage.reasoning\n if (aggregate.tokenUsage.cached !== undefined)\n raw.aggregate_cached_tokens = aggregate.tokenUsage.cached\n if (aggregate.tokenUsage.cacheWrite !== undefined)\n raw.aggregate_cache_write_tokens = aggregate.tokenUsage.cacheWrite\n if (aggregate.costUsd !== undefined) raw.aggregate_cost_usd = aggregate.costUsd\n}\n\nfunction summarizeAggregateMeasurements(\n spans: MeasuredSpan[],\n byId: Map<string, MeasuredSpan>,\n aggregateIds: Set<string>,\n): ExecutionMeasurements['aggregate'] {\n const aggregates = spans.filter((span) => span.aggregate || aggregateIds.has(span.id))\n const input = reconcileTopLevelMeasurement(aggregates, byId, LLM_INPUT_TOKEN_ATTR_KEYS)\n const output = reconcileTopLevelMeasurement(aggregates, byId, LLM_OUTPUT_TOKEN_ATTR_KEYS)\n const reasoning = reconcileTopLevelMeasurement(aggregates, byId, LLM_REASONING_TOKEN_ATTR_KEYS)\n const cached = reconcileTopLevelMeasurement(aggregates, byId, LLM_CACHED_TOKEN_ATTR_KEYS)\n const cacheWrite = reconcileTopLevelMeasurement(aggregates, byId, LLM_CACHE_WRITE_TOKEN_ATTR_KEYS)\n const costUsd = reconcileTopLevelMeasurement(aggregates, byId, LLM_COST_ATTR_KEYS)\n if (\n input === undefined &&\n output === undefined &&\n reasoning === undefined &&\n cached === undefined &&\n cacheWrite === undefined &&\n costUsd === undefined\n )\n return undefined\n return {\n tokenUsage: {\n input: input ?? 0,\n output: output ?? reasoning ?? 0,\n ...(reasoning !== undefined ? { reasoning } : {}),\n ...(cached !== undefined ? { cached } : {}),\n ...(cacheWrite !== undefined ? { cacheWrite } : {}),\n },\n ...(costUsd !== undefined ? { costUsd } : {}),\n }\n}\n\nfunction reconcileTopLevelMeasurement(\n spans: MeasuredSpan[],\n byId: Map<string, MeasuredSpan>,\n keys: readonly string[],\n): number | undefined {\n const selected = new Map<string, number>()\n for (const span of spans) {\n const value = readNumber(span.attributes, keys)\n if (value !== undefined) selected.set(span.id, value)\n }\n for (const spanId of [...selected.keys()]) {\n const span = byId.get(spanId)\n if (!span) continue\n if (ancestorIds(span, byId).some((ancestorId) => selected.has(ancestorId))) {\n selected.delete(spanId)\n }\n }\n return selected.size > 0\n ? [...selected.values()].reduce((total, value) => total + value, 0)\n : undefined\n}\n\nfunction reconcileMeasurement(\n calls: MeasuredSpan[],\n byId: Map<string, MeasuredSpan>,\n aggregateSourceIds: Set<string>,\n keys: readonly string[],\n fallbackKeys?: readonly string[],\n): MeasurementCoverage {\n const selected = new Map<string, number>()\n const callIds = new Set(calls.map((call) => call.id))\n let reportingCalls = 0\n\n for (const call of calls) {\n const primary = nearestMeasurement(call, byId, callIds, aggregateSourceIds, keys)\n const fallback = fallbackKeys\n ? nearestMeasurement(call, byId, callIds, aggregateSourceIds, fallbackKeys)\n : undefined\n const source =\n primary && fallback && primary.span.id === fallback.span.id\n ? { span: primary.span, value: Math.max(primary.value, fallback.value) }\n : (primary ?? fallback)\n if (!source) continue\n reportingCalls += 1\n selected.set(source.span.id, source.value)\n }\n\n for (const spanId of [...selected.keys()]) {\n const span = byId.get(spanId)\n if (!span) continue\n if (\n ancestorIds(span, byId).some(\n (ancestorId) => selected.has(ancestorId) && !callIds.has(ancestorId),\n )\n ) {\n selected.delete(spanId)\n }\n }\n\n return {\n ...(selected.size > 0\n ? { value: [...selected.values()].reduce((total, value) => total + value, 0) }\n : {}),\n reportingCalls,\n complete: calls.length > 0 && reportingCalls === calls.length,\n }\n}\n\nfunction nearestMeasurement(\n call: MeasuredSpan,\n byId: Map<string, MeasuredSpan>,\n callIds: Set<string>,\n aggregateSourceIds: Set<string>,\n keys: readonly string[],\n): { span: MeasuredSpan; value: number } | undefined {\n let current: MeasuredSpan | undefined = call\n const seen = new Set<string>()\n while (current && !seen.has(current.id)) {\n seen.add(current.id)\n const value = readNumber(current.attributes, keys)\n if (\n value !== undefined &&\n (current.id === call.id || (!callIds.has(current.id) && aggregateSourceIds.has(current.id)))\n ) {\n return { span: current, value }\n }\n current = current.parentId ? byId.get(current.parentId) : undefined\n }\n return undefined\n}\n\nfunction classifyAggregateSpans(\n candidates: MeasuredSpan[],\n childrenById: Map<string, MeasuredSpan[]>,\n): Set<string> {\n const aggregateIds = new Set<string>()\n const summaries = new Map<string, RetainedCallSummary>()\n const visiting = new Set<string>()\n\n const visit = (span: MeasuredSpan): RetainedCallSummary => {\n const cached = summaries.get(span.id)\n if (cached) return cached\n if (visiting.has(span.id)) return emptyRetainedCallSummary()\n visiting.add(span.id)\n\n const descendants = emptyRetainedCallSummary()\n for (const child of childrenById.get(span.id) ?? []) {\n const childDescendants = visit(child)\n if (!aggregateIds.has(child.id)) addRetainedCall(descendants, child)\n addRetainedCallSummary(descendants, childDescendants)\n }\n\n if (\n descendants.callCount > 0 &&\n (!span.modelCall || hasCompatibleDescendantMeasurements(span, descendants))\n ) {\n aggregateIds.add(span.id)\n }\n\n visiting.delete(span.id)\n summaries.set(span.id, descendants)\n return descendants\n }\n\n for (const candidate of candidates) visit(candidate)\n return aggregateIds\n}\n\nfunction emptyRetainedCallSummary(): RetainedCallSummary {\n return {\n callCount: 0,\n measurements: EXECUTION_MEASUREMENT_KEY_GROUPS.map(() => ({\n total: 0,\n reportingCalls: 0,\n })),\n }\n}\n\nfunction addRetainedCall(summary: RetainedCallSummary, span: MeasuredSpan): void {\n summary.callCount += 1\n for (let index = 0; index < EXECUTION_MEASUREMENT_KEY_GROUPS.length; index += 1) {\n const value = readNumber(span.attributes, EXECUTION_MEASUREMENT_KEY_GROUPS[index]!)\n if (value === undefined) continue\n const measurement = summary.measurements[index]!\n measurement.total += value\n measurement.reportingCalls += 1\n }\n}\n\nfunction addRetainedCallSummary(target: RetainedCallSummary, source: RetainedCallSummary): void {\n target.callCount += source.callCount\n for (let index = 0; index < target.measurements.length; index += 1) {\n const measurement = target.measurements[index]!\n const sourceMeasurement = source.measurements[index]!\n measurement.total += sourceMeasurement.total\n measurement.reportingCalls += sourceMeasurement.reportingCalls\n }\n}\n\nfunction hasCompatibleDescendantMeasurements(\n span: MeasuredSpan,\n descendants: RetainedCallSummary,\n): boolean {\n let parentMeasurements = 0\n let descendantMeasurements = 0\n for (let index = 0; index < EXECUTION_MEASUREMENT_KEY_GROUPS.length; index += 1) {\n const keys = EXECUTION_MEASUREMENT_KEY_GROUPS[index]!\n const parentValue = readNumber(span.attributes, keys)\n const measurement = descendants.measurements[index]!\n if (parentValue !== undefined) parentMeasurements += 1\n if (measurement.reportingCalls > 0) descendantMeasurements += 1\n if (parentValue === undefined || measurement.reportingCalls === 0) continue\n if (measurement.reportingCalls !== descendants.callCount) continue\n if (Math.abs(parentValue - measurement.total) > 1e-12) return false\n }\n return parentMeasurements === 0 || descendantMeasurements > 0\n}\n\nfunction ancestorIds(span: MeasuredSpan, byId: Map<string, MeasuredSpan>): string[] {\n const ids: string[] = []\n const seen = new Set<string>()\n let parentId = span.parentId\n while (parentId && !seen.has(parentId)) {\n ids.push(parentId)\n seen.add(parentId)\n parentId = byId.get(parentId)?.parentId\n }\n return ids\n}\n\nfunction nearestCandidateParent(\n span: MeasuredSpan,\n byId: Map<string, MeasuredSpan>,\n candidateIds: Set<string>,\n): string | undefined {\n const seen = new Set<string>()\n let parentId = span.parentId\n while (parentId && !seen.has(parentId)) {\n if (candidateIds.has(parentId)) return parentId\n seen.add(parentId)\n parentId = byId.get(parentId)?.parentId\n }\n return undefined\n}\n\nfunction readNumber(\n attributes: Record<string, unknown>,\n keys: readonly string[],\n): number | undefined {\n for (const key of keys) {\n const value = attributes[key]\n const parsed =\n typeof value === 'number'\n ? value\n : typeof value === 'string' && value.length > 0\n ? Number(value)\n : Number.NaN\n if (Number.isFinite(parsed) && parsed >= 0) return parsed\n }\n return undefined\n}\n","import { ValidationError } from '../errors'\nimport { FAILURE_CLASSES, type FailureClass } from './schema'\n\nconst TASK_FAILURE_CLASS_ATTR = 'tangle.task.failure_class'\nconst TASK_FAILURE_MODE_ATTR = 'tangle.task.failure_mode'\n\ninterface AttributeCarrier {\n attributes: Record<string, unknown>\n}\n\ntype TaskFailureLabels =\n | { failureClass?: undefined; failureMode?: undefined }\n | { failureClass: 'success'; failureMode?: undefined }\n | { failureClass: Exclude<FailureClass, 'success'>; failureMode?: string }\n\nexport function readTaskFailureLabels(\n roots: readonly AttributeCarrier[],\n context: string,\n): TaskFailureLabels {\n const failureClass = readConsistentRootString(roots, TASK_FAILURE_CLASS_ATTR, context)\n const failureMode = readConsistentRootString(roots, TASK_FAILURE_MODE_ATTR, context)\n\n if (failureClass !== undefined && !FAILURE_CLASSES.includes(failureClass as FailureClass)) {\n throw new ValidationError(\n `${context}: ${TASK_FAILURE_CLASS_ATTR} must be one of ${FAILURE_CLASSES.join(', ')}`,\n )\n }\n if (failureMode !== undefined && (failureClass === undefined || failureClass === 'success')) {\n throw new ValidationError(\n `${context}: ${TASK_FAILURE_MODE_ATTR} requires a non-success ${TASK_FAILURE_CLASS_ATTR}`,\n )\n }\n\n if (failureClass === undefined) return {}\n if (failureClass === 'success') return { failureClass }\n return {\n failureClass: failureClass as Exclude<FailureClass, 'success'>,\n ...(failureMode ? { failureMode } : {}),\n }\n}\n\nfunction readConsistentRootString(\n roots: readonly AttributeCarrier[],\n key: string,\n context: string,\n): string | undefined {\n const values = new Set<string>()\n for (const root of roots) {\n if (!Object.hasOwn(root.attributes, key)) continue\n const value = root.attributes[key]\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new ValidationError(`${context}: ${key} must be a non-empty string`)\n }\n values.add(value)\n }\n\n if (values.size > 1) {\n throw new ValidationError(\n `${context}: conflicting ${key} values: ${[...values].sort().join(', ')}`,\n )\n }\n return values.values().next().value\n}\n"],"mappings":";;;;;;;;;;AA0BA,SAAgB,qBAAqB,SAAyD;CAC5F,MAAM,uBAAO,IAAI,IAA8B;CAC/C,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,KAAK,IAAI,OAAO,EAAE,GACpB,MAAM,IAAI,MAAM,4CAA4C,OAAO,GAAG,EAAE;EAE1E,KAAK,IAAI,OAAO,IAAI,MAAM;CAC5B;CAEA,MAAM,6BAAa,IAAI,IAAY;CACnC,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,0BAAU,IAAI,IAAY;EAChC,IAAI,WAAW,OAAO;EACtB,OAAO,YAAY,CAAC,QAAQ,IAAI,QAAQ,GAAG;GACzC,QAAQ,IAAI,QAAQ;GACpB,MAAM,SAAS,KAAK,IAAI,QAAQ;GAChC,IAAI,CAAC,QAAQ;GACb,IAAI,OAAO,OAAO,WAAW,IAAI,OAAO,EAAE;GAC1C,WAAW,OAAO;EACpB;CACF;CAEA,MAAM,UAA6B;EACjC,OAAO;EACP,WAAW;EACX,SAAS;EACT,WAAW;EACX,YAAY;EACZ,YAAY;EACZ,cAAc;CAChB;CAEA,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,CAAC,OAAO,OAAO;EACnB,QAAQ,SAAS;EACjB,IAAI,OAAO,SAAS,aAClB,QAAQ,aAAa;OAChB,IAAI,OAAO,SAAS,aACzB,QAAQ,cAAc;OACjB,IAAI,OAAO,aAChB,QAAQ,WAAW;OACd,IAAI,WAAW,IAAI,OAAO,EAAE,GACjC,QAAQ,cAAc;OACjB,IAAI,OAAO,SAAS,WACzB,QAAQ,aAAa;OAErB,QAAQ,gBAAgB;CAE5B;CAEA,OAAO;AACT;;;AC9BA,MAAM,+BAA+B;CACnC;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,mCAAmC,CACvC,GAAG,8BACH,kBACF;;;;;;;AAgBA,SAAgB,+BACd,QACuB;CACvB,MAAM,QAAwB,OAAO,KAAK,UAAU;EAClD,GAAG;EAIH,WAAW,gBAAgB,KAAK,IAAI;EACpC,WAAW,CAAC,gBAAgB,KAAK,IAAI,KAAK,KAAK,SAAS;CAC1D,EAAE;CACF,MAAM,uBAAO,IAAI,IAA0B;CAC3C,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,IAAI,KAAK,EAAE,GAClB,MAAM,IAAI,MAAM,sDAAsD,KAAK,GAAG,EAAE;EAElF,KAAK,IAAI,KAAK,IAAI,IAAI;CACxB;CACA,MAAM,uBAAuB,6BAA6B,KAAK;CAC/D,MAAM,aAAa,MAAM,QACtB,SACC,KAAK,aACJ,CAAC,KAAK,aAAa,WAAW,KAAK,YAAY,oBAAoB,MAAM,KAAA,CAC9E;CACA,MAAM,eAAe,IAAI,IAAI,WAAW,KAAK,SAAS,KAAK,EAAE,CAAC;CAC9D,MAAM,oCAAoB,IAAI,IAA4B;CAC1D,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,WAAW,uBAAuB,WAAW,MAAM,YAAY;EACrE,IAAI,CAAC,UAAU;EACf,MAAM,WAAW,kBAAkB,IAAI,QAAQ,KAAK,CAAC;EACrD,SAAS,KAAK,SAAS;EACvB,kBAAkB,IAAI,UAAU,QAAQ;CAC1C;CACA,MAAM,eAAe,uBAAuB,YAAY,iBAAiB;CACzE,MAAM,oBAAoB,IAAI,IAC5B,MACG,QACE,SACC,CAAC,KAAK,aACN,CAAC,KAAK,aACN,CAAC,aAAa,IAAI,KAAK,EAAE,KACzB,WAAW,KAAK,YAAY,kBAAkB,MAAM,KAAA,CACxD,CAAC,CACA,KAAK,SAAS,KAAK,EAAE,CAC1B;CACA,MAAM,qBAAqB,IAAI,IAC7B,MACG,QACE,SAAS,KAAK,aAAa,aAAa,IAAI,KAAK,EAAE,KAAK,kBAAkB,IAAI,KAAK,EAAE,CACxF,CAAC,CACA,KAAK,SAAS,KAAK,EAAE,CAC1B;CAEA,MAAM,QAAQ,WAAW,QAAQ,SAAS,CAAC,aAAa,IAAI,KAAK,EAAE,CAAC;CACpE,MAAM,QAAQ,qBAAqB,OAAO,MAAM,oBAAoB,yBAAyB;CAC7F,MAAM,YAAY,qBAChB,OACA,MACA,oBACA,6BACF;CACA,MAAM,SAAS,qBACb,OACA,MACA,oBACA,4BACA,6BACF;CACA,MAAM,SAAS,qBAAqB,OAAO,MAAM,oBAAoB,0BAA0B;CAC/F,MAAM,aAAa,qBACjB,OACA,MACA,oBACA,+BACF;CACA,MAAM,YAAY,+BAChB,OACA,sBACA,IAAI,IAAI,CAAC,GAAG,cAAc,GAAG,iBAAiB,CAAC,CACjD;CAQA,MAAM,iBAAiB,UAAU,SAAS,WAAW,WAAW;CAChE,MAAM,cAAc,OAAO,SAAS,WAAW,WAAW;CAC1D,MAAM,kBAAkB,WAAW,SAAS,WAAW,WAAW;CAElE,OAAO;EACL,YAAY;GACV,OAAO,MAAM,SAAS,WAAW,WAAW,SAAS;GACrD,QAAQ,KAAK,IAAI,OAAO,SAAS,WAAW,WAAW,UAAU,GAAG,kBAAkB,CAAC;GACvF,GAAI,mBAAmB,KAAA,IAAY,EAAE,WAAW,eAAe,IAAI,CAAC;GACpE,GAAI,gBAAgB,KAAA,IAAY,EAAE,QAAQ,YAAY,IAAI,CAAC;GAC3D,GAAI,oBAAoB,KAAA,IAAY,EAAE,YAAY,gBAAgB,IAAI,CAAC;EACzE;EACA,gBAAgB,MAAM;EACtB,aAAa,MAAM,KAAK,SAAS,KAAK,EAAE;EACxC,MAAM,qBAAqB,OAAO,MAAM,oBAAoB,kBAAkB;EAC9E,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CACnC;AACF;AAEA,SAAgB,4BACd,KACA,WACM;CACN,IAAI,CAAC,WAAW;CAChB,IAAI,0BAA0B,UAAU,WAAW;CACnD,IAAI,8BAA8B,UAAU,WAAW;CACvD,IAAI,UAAU,WAAW,cAAc,KAAA,GACrC,IAAI,6BAA6B,UAAU,WAAW;CACxD,IAAI,UAAU,WAAW,WAAW,KAAA,GAClC,IAAI,0BAA0B,UAAU,WAAW;CACrD,IAAI,UAAU,WAAW,eAAe,KAAA,GACtC,IAAI,+BAA+B,UAAU,WAAW;CAC1D,IAAI,UAAU,YAAY,KAAA,GAAW,IAAI,qBAAqB,UAAU;AAC1E;AAEA,SAAS,+BACP,OACA,MACA,cACoC;CACpC,MAAM,aAAa,MAAM,QAAQ,SAAS,KAAK,aAAa,aAAa,IAAI,KAAK,EAAE,CAAC;CACrF,MAAM,QAAQ,6BAA6B,YAAY,MAAM,yBAAyB;CACtF,MAAM,SAAS,6BAA6B,YAAY,MAAM,0BAA0B;CACxF,MAAM,YAAY,6BAA6B,YAAY,MAAM,6BAA6B;CAC9F,MAAM,SAAS,6BAA6B,YAAY,MAAM,0BAA0B;CACxF,MAAM,aAAa,6BAA6B,YAAY,MAAM,+BAA+B;CACjG,MAAM,UAAU,6BAA6B,YAAY,MAAM,kBAAkB;CACjF,IACE,UAAU,KAAA,KACV,WAAW,KAAA,KACX,cAAc,KAAA,KACd,WAAW,KAAA,KACX,eAAe,KAAA,KACf,YAAY,KAAA,GAEZ,OAAO,KAAA;CACT,OAAO;EACL,YAAY;GACV,OAAO,SAAS;GAChB,QAAQ,UAAU,aAAa;GAC/B,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;GAC/C,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;GACzC,GAAI,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,CAAC;EACnD;EACA,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;CAC7C;AACF;AAEA,SAAS,6BACP,OACA,MACA,MACoB;CACpB,MAAM,2BAAW,IAAI,IAAoB;CACzC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,WAAW,KAAK,YAAY,IAAI;EAC9C,IAAI,UAAU,KAAA,GAAW,SAAS,IAAI,KAAK,IAAI,KAAK;CACtD;CACA,KAAK,MAAM,UAAU,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG;EACzC,MAAM,OAAO,KAAK,IAAI,MAAM;EAC5B,IAAI,CAAC,MAAM;EACX,IAAI,YAAY,MAAM,IAAI,CAAC,CAAC,MAAM,eAAe,SAAS,IAAI,UAAU,CAAC,GACvE,SAAS,OAAO,MAAM;CAE1B;CACA,OAAO,SAAS,OAAO,IACnB,CAAC,GAAG,SAAS,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,UAAU,QAAQ,OAAO,CAAC,IAChE,KAAA;AACN;AAEA,SAAS,qBACP,OACA,MACA,oBACA,MACA,cACqB;CACrB,MAAM,2BAAW,IAAI,IAAoB;CACzC,MAAM,UAAU,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,EAAE,CAAC;CACpD,IAAI,iBAAiB;CAErB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,mBAAmB,MAAM,MAAM,SAAS,oBAAoB,IAAI;EAChF,MAAM,WAAW,eACb,mBAAmB,MAAM,MAAM,SAAS,oBAAoB,YAAY,IACxE,KAAA;EACJ,MAAM,SACJ,WAAW,YAAY,QAAQ,KAAK,OAAO,SAAS,KAAK,KACrD;GAAE,MAAM,QAAQ;GAAM,OAAO,KAAK,IAAI,QAAQ,OAAO,SAAS,KAAK;EAAE,IACpE,WAAW;EAClB,IAAI,CAAC,QAAQ;EACb,kBAAkB;EAClB,SAAS,IAAI,OAAO,KAAK,IAAI,OAAO,KAAK;CAC3C;CAEA,KAAK,MAAM,UAAU,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG;EACzC,MAAM,OAAO,KAAK,IAAI,MAAM;EAC5B,IAAI,CAAC,MAAM;EACX,IACE,YAAY,MAAM,IAAI,CAAC,CAAC,MACrB,eAAe,SAAS,IAAI,UAAU,KAAK,CAAC,QAAQ,IAAI,UAAU,CACrE,GAEA,SAAS,OAAO,MAAM;CAE1B;CAEA,OAAO;EACL,GAAI,SAAS,OAAO,IAChB,EAAE,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,UAAU,QAAQ,OAAO,CAAC,EAAE,IAC3E,CAAC;EACL;EACA,UAAU,MAAM,SAAS,KAAK,mBAAmB,MAAM;CACzD;AACF;AAEA,SAAS,mBACP,MACA,MACA,SACA,oBACA,MACmD;CACnD,IAAI,UAAoC;CACxC,MAAM,uBAAO,IAAI,IAAY;CAC7B,OAAO,WAAW,CAAC,KAAK,IAAI,QAAQ,EAAE,GAAG;EACvC,KAAK,IAAI,QAAQ,EAAE;EACnB,MAAM,QAAQ,WAAW,QAAQ,YAAY,IAAI;EACjD,IACE,UAAU,KAAA,MACT,QAAQ,OAAO,KAAK,MAAO,CAAC,QAAQ,IAAI,QAAQ,EAAE,KAAK,mBAAmB,IAAI,QAAQ,EAAE,IAEzF,OAAO;GAAE,MAAM;GAAS;EAAM;EAEhC,UAAU,QAAQ,WAAW,KAAK,IAAI,QAAQ,QAAQ,IAAI,KAAA;CAC5D;AAEF;AAEA,SAAS,uBACP,YACA,cACa;CACb,MAAM,+BAAe,IAAI,IAAY;CACrC,MAAM,4BAAY,IAAI,IAAiC;CACvD,MAAM,2BAAW,IAAI,IAAY;CAEjC,MAAM,SAAS,SAA4C;EACzD,MAAM,SAAS,UAAU,IAAI,KAAK,EAAE;EACpC,IAAI,QAAQ,OAAO;EACnB,IAAI,SAAS,IAAI,KAAK,EAAE,GAAG,OAAO,yBAAyB;EAC3D,SAAS,IAAI,KAAK,EAAE;EAEpB,MAAM,cAAc,yBAAyB;EAC7C,KAAK,MAAM,SAAS,aAAa,IAAI,KAAK,EAAE,KAAK,CAAC,GAAG;GACnD,MAAM,mBAAmB,MAAM,KAAK;GACpC,IAAI,CAAC,aAAa,IAAI,MAAM,EAAE,GAAG,gBAAgB,aAAa,KAAK;GACnE,uBAAuB,aAAa,gBAAgB;EACtD;EAEA,IACE,YAAY,YAAY,MACvB,CAAC,KAAK,aAAa,oCAAoC,MAAM,WAAW,IAEzE,aAAa,IAAI,KAAK,EAAE;EAG1B,SAAS,OAAO,KAAK,EAAE;EACvB,UAAU,IAAI,KAAK,IAAI,WAAW;EAClC,OAAO;CACT;CAEA,KAAK,MAAM,aAAa,YAAY,MAAM,SAAS;CACnD,OAAO;AACT;AAEA,SAAS,2BAAgD;CACvD,OAAO;EACL,WAAW;EACX,cAAc,iCAAiC,WAAW;GACxD,OAAO;GACP,gBAAgB;EAClB,EAAE;CACJ;AACF;AAEA,SAAS,gBAAgB,SAA8B,MAA0B;CAC/E,QAAQ,aAAa;CACrB,KAAK,IAAI,QAAQ,GAAG,QAAQ,iCAAiC,QAAQ,SAAS,GAAG;EAC/E,MAAM,QAAQ,WAAW,KAAK,YAAY,iCAAiC,MAAO;EAClF,IAAI,UAAU,KAAA,GAAW;EACzB,MAAM,cAAc,QAAQ,aAAa;EACzC,YAAY,SAAS;EACrB,YAAY,kBAAkB;CAChC;AACF;AAEA,SAAS,uBAAuB,QAA6B,QAAmC;CAC9F,OAAO,aAAa,OAAO;CAC3B,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,aAAa,QAAQ,SAAS,GAAG;EAClE,MAAM,cAAc,OAAO,aAAa;EACxC,MAAM,oBAAoB,OAAO,aAAa;EAC9C,YAAY,SAAS,kBAAkB;EACvC,YAAY,kBAAkB,kBAAkB;CAClD;AACF;AAEA,SAAS,oCACP,MACA,aACS;CACT,IAAI,qBAAqB;CACzB,IAAI,yBAAyB;CAC7B,KAAK,IAAI,QAAQ,GAAG,QAAQ,iCAAiC,QAAQ,SAAS,GAAG;EAC/E,MAAM,OAAO,iCAAiC;EAC9C,MAAM,cAAc,WAAW,KAAK,YAAY,IAAI;EACpD,MAAM,cAAc,YAAY,aAAa;EAC7C,IAAI,gBAAgB,KAAA,GAAW,sBAAsB;EACrD,IAAI,YAAY,iBAAiB,GAAG,0BAA0B;EAC9D,IAAI,gBAAgB,KAAA,KAAa,YAAY,mBAAmB,GAAG;EACnE,IAAI,YAAY,mBAAmB,YAAY,WAAW;EAC1D,IAAI,KAAK,IAAI,cAAc,YAAY,KAAK,IAAI,OAAO,OAAO;CAChE;CACA,OAAO,uBAAuB,KAAK,yBAAyB;AAC9D;AAEA,SAAS,YAAY,MAAoB,MAA2C;CAClF,MAAM,MAAgB,CAAC;CACvB,MAAM,uBAAO,IAAI,IAAY;CAC7B,IAAI,WAAW,KAAK;CACpB,OAAO,YAAY,CAAC,KAAK,IAAI,QAAQ,GAAG;EACtC,IAAI,KAAK,QAAQ;EACjB,KAAK,IAAI,QAAQ;EACjB,WAAW,KAAK,IAAI,QAAQ,CAAC,EAAE;CACjC;CACA,OAAO;AACT;AAEA,SAAS,uBACP,MACA,MACA,cACoB;CACpB,MAAM,uBAAO,IAAI,IAAY;CAC7B,IAAI,WAAW,KAAK;CACpB,OAAO,YAAY,CAAC,KAAK,IAAI,QAAQ,GAAG;EACtC,IAAI,aAAa,IAAI,QAAQ,GAAG,OAAO;EACvC,KAAK,IAAI,QAAQ;EACjB,WAAW,KAAK,IAAI,QAAQ,CAAC,EAAE;CACjC;AAEF;AAEA,SAAS,WACP,YACA,MACoB;CACpB,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,WAAW;EACzB,MAAM,SACJ,OAAO,UAAU,WACb,QACA,OAAO,UAAU,YAAY,MAAM,SAAS,IAC1C,OAAO,KAAK,IACZ;EACR,IAAI,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG,OAAO;CACrD;AAEF;;;AC/bA,MAAM,0BAA0B;AAChC,MAAM,yBAAyB;AAW/B,SAAgB,sBACd,OACA,SACmB;CACnB,MAAM,eAAe,yBAAyB,OAAO,yBAAyB,OAAO;CACrF,MAAM,cAAc,yBAAyB,OAAO,wBAAwB,OAAO;CAEnF,IAAI,iBAAiB,KAAA,KAAa,CAAC,gBAAgB,SAAS,YAA4B,GACtF,MAAM,IAAI,gBACR,GAAG,QAAQ,IAAI,wBAAwB,kBAAkB,gBAAgB,KAAK,IAAI,GACpF;CAEF,IAAI,gBAAgB,KAAA,MAAc,iBAAiB,KAAA,KAAa,iBAAiB,YAC/E,MAAM,IAAI,gBACR,GAAG,QAAQ,IAAI,uBAAuB,0BAA0B,yBAClE;CAGF,IAAI,iBAAiB,KAAA,GAAW,OAAO,CAAC;CACxC,IAAI,iBAAiB,WAAW,OAAO,EAAE,aAAa;CACtD,OAAO;EACS;EACd,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;CACvC;AACF;AAEA,SAAS,yBACP,OACA,KACA,SACoB;CACpB,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,OAAO,OAAO,KAAK,YAAY,GAAG,GAAG;EAC1C,MAAM,QAAQ,KAAK,WAAW;EAC9B,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,WAAW,GACvD,MAAM,IAAI,gBAAgB,GAAG,QAAQ,IAAI,IAAI,4BAA4B;EAE3E,OAAO,IAAI,KAAK;CAClB;CAEA,IAAI,OAAO,OAAO,GAChB,MAAM,IAAI,gBACR,GAAG,QAAQ,gBAAgB,IAAI,WAAW,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,GACxE;CAEF,OAAO,OAAO,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;AAChC"}
|