modelrig 0.11.1 → 0.12.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 (182) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/adapters/anthropic.d.ts +4 -4
  3. package/dist/adapters/anthropic.d.ts.map +1 -1
  4. package/dist/adapters/anthropic.js +8 -1
  5. package/dist/adapters/deepseek.d.ts +2 -4
  6. package/dist/adapters/deepseek.d.ts.map +1 -1
  7. package/dist/adapters/deepseek.js +9 -5
  8. package/dist/adapters/gemini.d.ts +4 -1
  9. package/dist/adapters/gemini.d.ts.map +1 -1
  10. package/dist/adapters/gemini.js +16 -1
  11. package/dist/adapters/grok.d.ts +2 -4
  12. package/dist/adapters/grok.d.ts.map +1 -1
  13. package/dist/adapters/grok.js +8 -4
  14. package/dist/adapters/history-mapping.d.ts +126 -0
  15. package/dist/adapters/history-mapping.d.ts.map +1 -0
  16. package/dist/adapters/history-mapping.js +230 -0
  17. package/dist/adapters/openai-compat.d.ts +2 -4
  18. package/dist/adapters/openai-compat.d.ts.map +1 -1
  19. package/dist/adapters/openai-compat.js +9 -5
  20. package/dist/adapters/openai.d.ts +2 -4
  21. package/dist/adapters/openai.d.ts.map +1 -1
  22. package/dist/adapters/openai.js +8 -4
  23. package/dist/adapters/responses-api.d.ts.map +1 -1
  24. package/dist/adapters/responses-api.js +9 -4
  25. package/dist/artifacts/artifact.d.ts.map +1 -1
  26. package/dist/artifacts/artifact.js +79 -0
  27. package/dist/artifacts/conversation.d.ts +128 -0
  28. package/dist/artifacts/conversation.d.ts.map +1 -0
  29. package/dist/artifacts/conversation.js +172 -0
  30. package/dist/artifacts/index.d.ts +1 -1
  31. package/dist/artifacts/index.d.ts.map +1 -1
  32. package/dist/artifacts/media.d.ts +46 -0
  33. package/dist/artifacts/media.d.ts.map +1 -0
  34. package/dist/artifacts/media.js +88 -0
  35. package/dist/artifacts/run-context.d.ts +61 -2
  36. package/dist/artifacts/run-context.d.ts.map +1 -1
  37. package/dist/artifacts/run-context.js +183 -12
  38. package/dist/artifacts/scrub.d.ts +3 -0
  39. package/dist/artifacts/scrub.d.ts.map +1 -1
  40. package/dist/bakeoff/engine.d.ts.map +1 -1
  41. package/dist/bakeoff/engine.js +34 -7
  42. package/dist/bakeoff/eval-cases.d.ts +5 -0
  43. package/dist/bakeoff/eval-cases.d.ts.map +1 -1
  44. package/dist/bakeoff/eval-cases.js +10 -1
  45. package/dist/bakeoff/replay.d.ts +15 -0
  46. package/dist/bakeoff/replay.d.ts.map +1 -1
  47. package/dist/bakeoff/replay.js +19 -1
  48. package/dist/bakeoff/report.d.ts.map +1 -1
  49. package/dist/bakeoff/report.js +9 -2
  50. package/dist/bakeoff/turn-replay.d.ts +93 -0
  51. package/dist/bakeoff/turn-replay.d.ts.map +1 -0
  52. package/dist/bakeoff/turn-replay.js +150 -0
  53. package/dist/bakeoff/types.d.ts +12 -0
  54. package/dist/bakeoff/types.d.ts.map +1 -1
  55. package/dist/feedback/grade.d.ts +7 -2
  56. package/dist/feedback/grade.d.ts.map +1 -1
  57. package/dist/feedback/grade.js +14 -0
  58. package/dist/feedback/outcome.d.ts +103 -0
  59. package/dist/feedback/outcome.d.ts.map +1 -0
  60. package/dist/feedback/outcome.js +175 -0
  61. package/dist/gateway/history-bridge.d.ts +32 -0
  62. package/dist/gateway/history-bridge.d.ts.map +1 -0
  63. package/dist/gateway/history-bridge.js +64 -0
  64. package/dist/gateway/serve-client.d.ts.map +1 -1
  65. package/dist/gateway/serve-client.js +6 -0
  66. package/dist/index.d.ts +44 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +106 -6
  69. package/dist/init/observe.d.ts.map +1 -1
  70. package/dist/init/observe.js +3 -3
  71. package/dist/registry/build.d.ts +4 -0
  72. package/dist/registry/build.d.ts.map +1 -1
  73. package/dist/registry/generation-models.d.ts +68 -0
  74. package/dist/registry/generation-models.d.ts.map +1 -0
  75. package/dist/registry/generation-models.js +168 -0
  76. package/dist/registry/model-kind.d.ts +34 -0
  77. package/dist/registry/model-kind.d.ts.map +1 -0
  78. package/dist/registry/model-kind.js +40 -0
  79. package/dist/registry/pricing-shape.d.ts +29 -1
  80. package/dist/registry/pricing-shape.d.ts.map +1 -1
  81. package/dist/registry/pricing-shape.js +9 -0
  82. package/dist/registry/pricing.d.ts +26 -0
  83. package/dist/registry/pricing.d.ts.map +1 -1
  84. package/dist/registry/pricing.js +29 -0
  85. package/dist/registry/rows.d.ts.map +1 -1
  86. package/dist/registry/rows.js +3 -1
  87. package/dist/registry/unit-pricing.d.ts +32 -0
  88. package/dist/registry/unit-pricing.d.ts.map +1 -0
  89. package/dist/registry/unit-pricing.js +62 -0
  90. package/dist/routes/input-loader.d.ts +38 -0
  91. package/dist/routes/input-loader.d.ts.map +1 -0
  92. package/dist/routes/input-loader.js +80 -0
  93. package/dist/routes/loader.d.ts.map +1 -1
  94. package/dist/routes/loader.js +3 -0
  95. package/dist/routes/resolved-bundle.d.ts +2 -0
  96. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  97. package/dist/routes/resolved-bundle.js +3 -0
  98. package/dist/run/cost.d.ts +28 -0
  99. package/dist/run/cost.d.ts.map +1 -1
  100. package/dist/run/cost.js +74 -0
  101. package/dist/run/dispatch.d.ts +4 -1
  102. package/dist/run/dispatch.d.ts.map +1 -1
  103. package/dist/run/dispatch.js +3 -0
  104. package/dist/run/limits.d.ts +88 -0
  105. package/dist/run/limits.d.ts.map +1 -0
  106. package/dist/run/limits.js +155 -0
  107. package/dist/run/messages.d.ts +199 -0
  108. package/dist/run/messages.d.ts.map +1 -0
  109. package/dist/run/messages.js +430 -0
  110. package/dist/run/raw-finalize.d.ts +5 -2
  111. package/dist/run/raw-finalize.d.ts.map +1 -1
  112. package/dist/run/raw-finalize.js +8 -5
  113. package/dist/run/raw-request.d.ts.map +1 -1
  114. package/dist/run/raw-request.js +2 -1
  115. package/dist/run/raw.d.ts +10 -4
  116. package/dist/run/raw.d.ts.map +1 -1
  117. package/dist/run/raw.js +11 -3
  118. package/dist/run/record-attempt.d.ts.map +1 -1
  119. package/dist/run/record-attempt.js +16 -6
  120. package/dist/run/record-call.d.ts +13 -2
  121. package/dist/run/record-call.d.ts.map +1 -1
  122. package/dist/run/record-call.js +50 -7
  123. package/dist/run/run.d.ts.map +1 -1
  124. package/dist/run/run.js +12 -8
  125. package/dist/telemetry/attempt-meta.d.ts +49 -1
  126. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  127. package/dist/telemetry/attempt-meta.js +45 -1
  128. package/dist/telemetry/exporter.d.ts.map +1 -1
  129. package/dist/telemetry/exporter.js +2 -1
  130. package/dist/telemetry/grade-mappers.d.ts +1 -1
  131. package/dist/telemetry/grade-mappers.d.ts.map +1 -1
  132. package/dist/telemetry/grade-mappers.js +7 -1
  133. package/dist/telemetry/grade-task.d.ts +49 -0
  134. package/dist/telemetry/grade-task.d.ts.map +1 -0
  135. package/dist/telemetry/grade-task.js +85 -0
  136. package/dist/telemetry/grades-sql.d.ts +16 -1
  137. package/dist/telemetry/grades-sql.d.ts.map +1 -1
  138. package/dist/telemetry/grades-sql.js +64 -4
  139. package/dist/telemetry/ingest-protocol.d.ts +24 -1
  140. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  141. package/dist/telemetry/ingest-protocol.js +72 -43
  142. package/dist/telemetry/otel-content.d.ts +31 -0
  143. package/dist/telemetry/otel-content.d.ts.map +1 -0
  144. package/dist/telemetry/otel-content.js +106 -0
  145. package/dist/telemetry/otel-hierarchy.d.ts +57 -0
  146. package/dist/telemetry/otel-hierarchy.d.ts.map +1 -0
  147. package/dist/telemetry/otel-hierarchy.js +155 -0
  148. package/dist/telemetry/otel-normalize.d.ts +15 -1
  149. package/dist/telemetry/otel-normalize.d.ts.map +1 -1
  150. package/dist/telemetry/otel-normalize.js +24 -39
  151. package/dist/telemetry/sinks.d.ts +4 -1
  152. package/dist/telemetry/sinks.d.ts.map +1 -1
  153. package/dist/telemetry/sinks.js +53 -34
  154. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  155. package/dist/telemetry/store-migrations.js +8 -0
  156. package/dist/telemetry/store.d.ts +9 -0
  157. package/dist/telemetry/store.d.ts.map +1 -1
  158. package/dist/telemetry/store.js +10 -0
  159. package/dist/telemetry/uniform-rows.d.ts +43 -0
  160. package/dist/telemetry/uniform-rows.d.ts.map +1 -0
  161. package/dist/telemetry/uniform-rows.js +139 -0
  162. package/dist/types/failures-teaching.d.ts.map +1 -1
  163. package/dist/types/failures-teaching.js +11 -0
  164. package/dist/types/failures.d.ts +1 -1
  165. package/dist/types/failures.d.ts.map +1 -1
  166. package/dist/types/failures.js +2 -0
  167. package/dist/types/provider.d.ts +12 -0
  168. package/dist/types/provider.d.ts.map +1 -1
  169. package/dist/types/route.d.ts +13 -0
  170. package/dist/types/route.d.ts.map +1 -1
  171. package/dist/types/telemetry.d.ts +14 -1
  172. package/dist/types/telemetry.d.ts.map +1 -1
  173. package/docs/agent-skill/SKILL.md +16 -3
  174. package/docs/how-it-fits.md +3 -3
  175. package/docs/instrumentation.md +15 -6
  176. package/docs/llms.txt +55 -7
  177. package/docs/mcp-oracle.md +15 -4
  178. package/docs/quickstart.md +6 -5
  179. package/package.json +4 -2
  180. package/registry/registry.json +456 -0
  181. package/templates/creative-approval.json +81 -0
  182. package/templates/support-conversation.json +82 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * OTLP-in — the trace HIERARCHY half of the normalizer, extracted from
3
+ * otel-normalize.ts (ARC-γ: the normalizer sat at 662 LOC, near the 700 cap).
4
+ * PURE: span identity → run/step identity, and step ordering within a run.
5
+ * No attribute is read here and no content ever reaches this module.
6
+ */
7
+ import type { WireStep } from "./ingest-protocol";
8
+ /** ARC-6: the classified operation kept in the attested envelope — the five
9
+ * classes OTLP-in v1 uses (turn numbering + tool counts), or `span` for
10
+ * anything else (honest: never guessed into a class). */
11
+ export type OperationClass = "generation" | "tool" | "agent" | "retriever" | "embedding" | "span";
12
+ /** Map the normalizer's raw operation string to its class. */
13
+ export declare function operationClassOf(operation: string): OperationClass;
14
+ /** span name → step_key: trimmed, whitespace-collapsed, length-capped, never
15
+ * empty (the step PK forbids it). */
16
+ export declare function toStepKey(name: unknown): string;
17
+ /** 32-hex trace id → canonical UUID string (runs.id is uuid; 16 bytes ≡ a UUID).
18
+ * A trace id that is not 16 bytes gets a deterministic UUID derived from it. */
19
+ export declare function traceIdToRunUuid(traceHex: string): string;
20
+ /** The structural identity of the span a pending step came from (ARC-6). */
21
+ export interface SpanShape {
22
+ readonly id: string;
23
+ readonly parent: string | null;
24
+ /** The normalizer's raw operation (`invoke_agent`, `chat`, …). */
25
+ readonly operation: string;
26
+ readonly operationClass: OperationClass;
27
+ }
28
+ /** One normalized span awaiting its attempt number. */
29
+ export interface PendingStep {
30
+ readonly step: WireStep;
31
+ readonly stepKey: string;
32
+ readonly startMs: number;
33
+ readonly span?: SpanShape;
34
+ }
35
+ /**
36
+ * Assign attempt_no per (run, step_key): repeated same-named spans become
37
+ * attempts in start-time (then span-id) order — deterministic within a trace.
38
+ * v0 limitation: a trace split across batches is not deduped (no seen-store);
39
+ * one trace = one batch is the common, correct case.
40
+ */
41
+ export declare function assignAttempts(pending: readonly PendingStep[]): WireStep[];
42
+ /**
43
+ * ARC-6 turn numbering for ONE trace that is a conversation (it carries
44
+ * `gen_ai.conversation.id` and has `invoke_agent` spans). Each TOP-LEVEL
45
+ * `invoke_agent` span (no `invoke_agent` ancestor), in start order, is turn n;
46
+ * every generation span under it (any depth) becomes step `turn:<n>.<k>` in
47
+ * start order — ARC-α's step-key convention exactly (`turnStepKey`; a repeated
48
+ * key would read as a retry). The turn number and, on the agent span, the
49
+ * count of tool spans in that turn ride the attested envelope (`turn`,
50
+ * `turn_tool_calls`, `turn_generations`). Spans outside every turn are left
51
+ * as they were. Returns the rewritten list and the number of turns.
52
+ */
53
+ export declare function numberTurns(pending: readonly PendingStep[]): {
54
+ pending: PendingStep[];
55
+ turns: number;
56
+ };
57
+ //# sourceMappingURL=otel-hierarchy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel-hierarchy.d.ts","sourceRoot":"","sources":["../../src/telemetry/otel-hierarchy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;yDAEyD;AACzD,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;AAuBlG,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAElE;AAID;qCACqC;AACrC,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI/C;AAED;gFACgF;AAChF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,QAAQ,EAAE,CAmB1E;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAgDtG"}
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ /**
3
+ * OTLP-in — the trace HIERARCHY half of the normalizer, extracted from
4
+ * otel-normalize.ts (ARC-γ: the normalizer sat at 662 LOC, near the 700 cap).
5
+ * PURE: span identity → run/step identity, and step ordering within a run.
6
+ * No attribute is read here and no content ever reaches this module.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.operationClassOf = operationClassOf;
10
+ exports.toStepKey = toStepKey;
11
+ exports.traceIdToRunUuid = traceIdToRunUuid;
12
+ exports.assignAttempts = assignAttempts;
13
+ exports.numberTurns = numberTurns;
14
+ const node_crypto_1 = require("node:crypto");
15
+ const conversation_1 = require("../artifacts/conversation");
16
+ const OPERATION_CLASSES = {
17
+ // gen_ai.operation.name (semconv) + the normalizer's model-presence fallback
18
+ chat: "generation",
19
+ text_completion: "generation",
20
+ generate_content: "generation",
21
+ completion: "generation",
22
+ // openinference.span.kind (lower-cased by the normalizer)
23
+ llm: "generation",
24
+ generation: "generation",
25
+ execute_tool: "tool",
26
+ tool: "tool",
27
+ invoke_agent: "agent",
28
+ create_agent: "agent",
29
+ agent_step: "agent",
30
+ agent: "agent",
31
+ retriever: "retriever",
32
+ retrieval: "retriever",
33
+ embeddings: "embedding",
34
+ embedding: "embedding",
35
+ };
36
+ /** Map the normalizer's raw operation string to its class. */
37
+ function operationClassOf(operation) {
38
+ return OPERATION_CLASSES[operation] ?? "span";
39
+ }
40
+ const WS_RE = /\s+/g;
41
+ /** span name → step_key: trimmed, whitespace-collapsed, length-capped, never
42
+ * empty (the step PK forbids it). */
43
+ function toStepKey(name) {
44
+ const raw = typeof name === "string" ? name.trim().replace(WS_RE, " ") : "";
45
+ if (raw === "")
46
+ return "span";
47
+ return raw.slice(0, 200);
48
+ }
49
+ /** 32-hex trace id → canonical UUID string (runs.id is uuid; 16 bytes ≡ a UUID).
50
+ * A trace id that is not 16 bytes gets a deterministic UUID derived from it. */
51
+ function traceIdToRunUuid(traceHex) {
52
+ const hex = traceHex.length === 32 ? traceHex : (0, node_crypto_1.createHash)("sha256").update(traceHex).digest("hex").slice(0, 32);
53
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
54
+ }
55
+ /**
56
+ * Assign attempt_no per (run, step_key): repeated same-named spans become
57
+ * attempts in start-time (then span-id) order — deterministic within a trace.
58
+ * v0 limitation: a trace split across batches is not deduped (no seen-store);
59
+ * one trace = one batch is the common, correct case.
60
+ */
61
+ function assignAttempts(pending) {
62
+ const out = [];
63
+ const perKey = new Map();
64
+ for (const p of pending) {
65
+ const list = perKey.get(p.stepKey) ?? [];
66
+ list.push({ step: p.step, startMs: p.startMs });
67
+ perKey.set(p.stepKey, list);
68
+ }
69
+ for (const list of perKey.values()) {
70
+ list.sort((a, b) => a.startMs !== b.startMs
71
+ ? a.startMs - b.startMs
72
+ : (a.step.span_id ?? "") < (b.step.span_id ?? "")
73
+ ? -1
74
+ : 1);
75
+ list.forEach(({ step }, i) => out.push({ ...step, attempt_no: i + 1 }));
76
+ }
77
+ return out;
78
+ }
79
+ /** Parent-chain walk bound (a malformed trace can carry a cycle). */
80
+ const MAX_DEPTH = 64;
81
+ function byStart(a, b) {
82
+ if (a.startMs !== b.startMs)
83
+ return a.startMs - b.startMs;
84
+ return (a.span?.id ?? "") < (b.span?.id ?? "") ? -1 : 1;
85
+ }
86
+ /**
87
+ * ARC-6 turn numbering for ONE trace that is a conversation (it carries
88
+ * `gen_ai.conversation.id` and has `invoke_agent` spans). Each TOP-LEVEL
89
+ * `invoke_agent` span (no `invoke_agent` ancestor), in start order, is turn n;
90
+ * every generation span under it (any depth) becomes step `turn:<n>.<k>` in
91
+ * start order — ARC-α's step-key convention exactly (`turnStepKey`; a repeated
92
+ * key would read as a retry). The turn number and, on the agent span, the
93
+ * count of tool spans in that turn ride the attested envelope (`turn`,
94
+ * `turn_tool_calls`, `turn_generations`). Spans outside every turn are left
95
+ * as they were. Returns the rewritten list and the number of turns.
96
+ */
97
+ function numberTurns(pending) {
98
+ const byId = new Map();
99
+ for (const p of pending)
100
+ if (p.span !== undefined)
101
+ byId.set(p.span.id, p);
102
+ const isAgent = (p) => p?.span?.operation === "invoke_agent";
103
+ /** The top-level invoke_agent ancestor of a span (itself when it is one), or null. */
104
+ const topAgentOf = (p) => {
105
+ let top = isAgent(p) ? p : null;
106
+ let cur = p.span?.parent != null ? byId.get(p.span.parent) : undefined;
107
+ const seen = new Set([p.span?.id ?? ""]);
108
+ for (let d = 0; cur !== undefined && d < MAX_DEPTH; d++) {
109
+ const id = cur.span?.id ?? "";
110
+ if (seen.has(id))
111
+ break;
112
+ seen.add(id);
113
+ if (isAgent(cur))
114
+ top = cur;
115
+ cur = cur.span?.parent != null ? byId.get(cur.span.parent) : undefined;
116
+ }
117
+ return top;
118
+ };
119
+ const agents = pending.filter((p) => isAgent(p) && topAgentOf(p) === p).sort(byStart);
120
+ if (agents.length === 0)
121
+ return { pending: [...pending], turns: 0 };
122
+ const turnOf = new Map(agents.map((a, i) => [a, i + 1]));
123
+ const generations = new Map();
124
+ const tools = new Map();
125
+ const turnBySpan = new Map();
126
+ for (const p of pending) {
127
+ const top = topAgentOf(p);
128
+ const n = top !== null ? turnOf.get(top) : undefined;
129
+ if (n === undefined)
130
+ continue;
131
+ turnBySpan.set(p, n);
132
+ if (p.span?.operationClass === "generation")
133
+ generations.set(n, [...(generations.get(n) ?? []), p]);
134
+ if (p.span?.operationClass === "tool")
135
+ tools.set(n, (tools.get(n) ?? 0) + 1);
136
+ }
137
+ const keyOf = new Map();
138
+ for (const [n, list] of generations)
139
+ list.sort(byStart).forEach((p, i) => keyOf.set(p, (0, conversation_1.turnStepKey)(n, i + 1)));
140
+ const out = pending.map((p) => {
141
+ const n = turnBySpan.get(p);
142
+ if (n === undefined)
143
+ return p;
144
+ const env = (typeof p.step.attested === "object" && p.step.attested !== null ? p.step.attested : {});
145
+ const isTop = turnOf.has(p);
146
+ const attested = {
147
+ ...env,
148
+ turn: n,
149
+ ...(isTop ? { turn_tool_calls: tools.get(n) ?? 0, turn_generations: generations.get(n)?.length ?? 0 } : {}),
150
+ };
151
+ const stepKey = keyOf.get(p) ?? p.stepKey;
152
+ return { ...p, stepKey, step: { ...p.step, step_key: stepKey, attested } };
153
+ });
154
+ return { pending: out, turns: agents.length };
155
+ }
@@ -23,6 +23,7 @@
23
23
  * pattern per §5.3 — implemented fresh here, no bodies copied).
24
24
  */
25
25
  import type { WireRun, WireStep } from "./ingest-protocol";
26
+ import { type OperationClass } from "./otel-hierarchy";
26
27
  export interface AttestedUsage {
27
28
  /** Non-cached input tokens (cache buckets subtracted, floored 0). */
28
29
  readonly input?: number;
@@ -41,8 +42,21 @@ export interface AttestedEnvelope {
41
42
  readonly model: string | null;
42
43
  /** Resolved provider — honest, ending at "unknown", NEVER guessed. */
43
44
  readonly provider: string;
44
- /** Classified operation (generation/tool/agent/retriever/embedding/span). */
45
+ /** The classifier's raw operation (`invoke_agent`, `chat`, `retriever`, …). */
45
46
  readonly operation: string;
47
+ /** ARC-6: the operation's class — generation | tool | agent | retriever |
48
+ * embedding, or `span` (unclassified). Additive. */
49
+ readonly operation_class?: OperationClass;
50
+ /** ARC-6: the parent span id (lowercase hex), or null for a root span.
51
+ * Kept for turn numbering + per-turn tool counts only — never rendered as a
52
+ * tree (spec §9). Additive. */
53
+ readonly parent_span_id?: string | null;
54
+ /** ARC-6: in a conversation trace, the turn this span belongs to. */
55
+ readonly turn?: number;
56
+ /** ARC-6: on a turn's top-level `invoke_agent` span — tool / generation
57
+ * spans counted in that turn. */
58
+ readonly turn_tool_calls?: number;
59
+ readonly turn_generations?: number;
46
60
  /** De-double-counted usage; omitted entirely on agent-aggregate spans. */
47
61
  readonly usage?: AttestedUsage;
48
62
  /** The emitting instrumentation scope (name + optional version). */
@@ -1 +1 @@
1
- {"version":3,"file":"otel-normalize.d.ts","sourceRoot":"","sources":["../../src/telemetry/otel-normalize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"otel-normalize.d.ts","sourceRoot":"","sources":["../../src/telemetry/otel-normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA8E,KAAK,cAAc,EAAoB,MAAM,kBAAkB,CAAC;AAIrJ,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;wDACoD;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,cAAc,CAAC;IAC1C;;mCAE+B;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,qEAAqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;qCACiC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC5E;AAMD,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC9C,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAChD;AAYD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;CAC/B;AAmUD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,SAAa,GAAG,eAAe,CAyJtF;AA+BD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAoB3D"}
@@ -26,7 +26,7 @@
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.normalizeOtlpTraces = normalizeOtlpTraces;
28
28
  exports.looksLikeLogsPayload = looksLikeLogsPayload;
29
- const node_crypto_1 = require("node:crypto");
29
+ const otel_hierarchy_1 = require("./otel-hierarchy");
30
30
  // ── value coercion (learnings §1.2, ported fresh) ────────────────────────────
31
31
  /** OTLP AnyValue → plain JS. Numeric strings coerce EXCEPT NaN/±Infinity which
32
32
  * stay as their string form (invalid JSON must never enter the envelope). int64
@@ -296,22 +296,6 @@ function classify(map) {
296
296
  return "chat";
297
297
  return "span";
298
298
  }
299
- // ── structure helpers ────────────────────────────────────────────────────────
300
- const WS_RE = /\s+/g;
301
- /** span name → step_key: trimmed, whitespace-collapsed, length-capped, never
302
- * empty (the step PK forbids it). */
303
- function toStepKey(name) {
304
- const raw = typeof name === "string" ? name.trim().replace(WS_RE, " ") : "";
305
- if (raw === "")
306
- return "span";
307
- return raw.slice(0, 200);
308
- }
309
- /** 32-hex trace id → canonical UUID string (runs.id is uuid; 16 bytes ≡ a UUID).
310
- * A trace id that is not 16 bytes gets a deterministic UUID derived from it. */
311
- function traceIdToRunUuid(traceHex) {
312
- const hex = traceHex.length === 32 ? traceHex : (0, node_crypto_1.createHash)("sha256").update(traceHex).digest("hex").slice(0, 32);
313
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
314
- }
315
299
  function bump(drops, reason, by = 1) {
316
300
  drops[reason] = (drops[reason] ?? 0) + by;
317
301
  }
@@ -374,7 +358,7 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
374
358
  }
375
359
  const { map, keys } = attrMap(span.attributes);
376
360
  const timing = resolveTiming(span, nowMs, drops);
377
- const runId = traceIdToRunUuid(traceHex);
361
+ const runId = (0, otel_hierarchy_1.traceIdToRunUuid)(traceHex);
378
362
  // ── envelope (identity/structure/usage ONLY; NO content) ──
379
363
  const operation = classify(map);
380
364
  const isAgentAggregate = AGENT_OPERATIONS.has(operation);
@@ -383,10 +367,14 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
383
367
  ? "unknown"
384
368
  : (firstAttr(map, PROVIDER_CHAIN) ?? "unknown");
385
369
  const usage = isAgentAggregate ? undefined : extractUsage(map);
370
+ const parentHex = toHexId(span.parentSpanId ?? span.parent_span_id, 8);
371
+ const operationClass = (0, otel_hierarchy_1.operationClassOf)(operation);
386
372
  const envelope = {
387
373
  model,
388
374
  provider,
389
375
  operation,
376
+ operation_class: operationClass,
377
+ parent_span_id: parentHex,
390
378
  ...(usage !== undefined ? { usage } : {}),
391
379
  scope: { name: scopeName, version: scopeVersion },
392
380
  };
@@ -397,7 +385,7 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
397
385
  const failureClass = statusCode === 2 ? "attested_error" : null;
398
386
  const step = {
399
387
  run_id: runId,
400
- step_key: toStepKey(span.name),
388
+ step_key: (0, otel_hierarchy_1.toStepKey)(span.name),
401
389
  attempt_no: 1, // assigned per (trace, step_key) below
402
390
  task: null,
403
391
  provenance: "attested",
@@ -413,6 +401,13 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
413
401
  const acc = runs.get(runId);
414
402
  const endMs = timing.endedAt !== null ? Date.parse(timing.endedAt) : timing.startMs;
415
403
  const episodeKey = firstAttr(map, EPISODE_CHAIN) ?? null;
404
+ const conversation = firstAttr(map, ["gen_ai.conversation.id"]) !== undefined;
405
+ const pendingStep = {
406
+ step,
407
+ stepKey: step.step_key,
408
+ startMs: timing.startMs,
409
+ span: { id: spanHex, parent: parentHex, operation, operationClass },
410
+ };
416
411
  if (acc === undefined) {
417
412
  runs.set(runId, {
418
413
  runId,
@@ -423,7 +418,8 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
423
418
  minStartMs: timing.startMs,
424
419
  maxEndMs: endMs,
425
420
  anyError: failureClass !== null,
426
- pending: [{ step, stepKey: step.step_key, startMs: timing.startMs }],
421
+ conversation,
422
+ pending: [pendingStep],
427
423
  });
428
424
  }
429
425
  else {
@@ -432,13 +428,17 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
432
428
  acc.anyError = acc.anyError || failureClass !== null;
433
429
  if (acc.episodeKey === null && episodeKey !== null)
434
430
  acc.episodeKey = episodeKey;
435
- acc.pending.push({ step, stepKey: step.step_key, startMs: timing.startMs });
431
+ acc.conversation = acc.conversation || conversation;
432
+ acc.pending.push(pendingStep);
436
433
  }
437
434
  }
438
435
  }
439
436
  }
440
437
  const runRows = [];
441
438
  for (const acc of runs.values()) {
439
+ // ARC-6: a conversation trace (gen_ai.conversation.id + invoke_agent spans)
440
+ // numbers its generations into ARC-α turn steps and marks the run.
441
+ const turned = acc.conversation ? (0, otel_hierarchy_1.numberTurns)(acc.pending) : { pending: acc.pending, turns: 0 };
442
442
  runRows.push({
443
443
  id: acc.runId,
444
444
  pipeline: acc.pipeline,
@@ -449,25 +449,10 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
449
449
  ended_at: new Date(acc.maxEndMs).toISOString(),
450
450
  // The trace has completed (we only see finished spans); status is honest.
451
451
  status: acc.anyError ? "failed" : "succeeded",
452
+ ...(turned.turns > 0 ? { meta: { v: 1, trigger: "conversation" } } : {}),
452
453
  });
453
- // Assign attempt_no per (run, step_key): repeated same-named spans become
454
- // attempts in start-time (then span-id) order — deterministic within a
455
- // trace. v0 limitation: a trace split across batches is not deduped (no
456
- // seen-store); one trace = one batch is the common, correct case.
457
- const perKey = new Map();
458
- for (const p of acc.pending) {
459
- const list = perKey.get(p.stepKey) ?? [];
460
- list.push({ step: p.step, startMs: p.startMs });
461
- perKey.set(p.stepKey, list);
462
- }
463
- for (const list of perKey.values()) {
464
- list.sort((a, b) => a.startMs !== b.startMs
465
- ? a.startMs - b.startMs
466
- : (a.step.span_id ?? "") < (b.step.span_id ?? "")
467
- ? -1
468
- : 1);
469
- list.forEach(({ step }, i) => steps.push({ ...step, attempt_no: i + 1 }));
470
- }
454
+ // attempt_no per (run, step_key) — the hierarchy module (otel-hierarchy.ts).
455
+ steps.push(...(0, otel_hierarchy_1.assignAttempts)(turned.pending));
471
456
  }
472
457
  return { runs: runRows, steps, drops, spanCount, scopeNames: [...scopeNames] };
473
458
  }
@@ -57,7 +57,10 @@ export declare class SinkSendError extends Error {
57
57
  * poison-skip candidate). Derived once, from the status, so the classification
58
58
  * lives in exactly one place. */
59
59
  readonly retriable: boolean;
60
- constructor(message: string, status: number | undefined);
60
+ /** COV-1c: the server's 422 `reason_code` — a PostgREST/PG error CODE
61
+ * (`PGRST102`, `23514`) and nothing else, shape-checked; null when absent. */
62
+ readonly reasonCode: string | null;
63
+ constructor(message: string, status: number | undefined, reasonCode?: string | null);
61
64
  }
62
65
  export interface DirectSinkConfig {
63
66
  readonly supabaseUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"sinks.d.ts","sourceRoot":"","sources":["../../src/telemetry/sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAmBrE;6CAC6C;AAC7C,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED,MAAM,WAAW,aAAa;IAC5B;;yDAEqD;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAChC;;6EAEyE;IACzE,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;qCAEiC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS;CAMxD;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CA0CxE;AAMD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;4EACwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC;;;wDAGoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD;;oDAEgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAsElE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,eAAe,GAAG,kBAAkB,EAClD,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACzC,aAAa,CA2Bf"}
1
+ {"version":3,"file":"sinks.d.ts","sourceRoot":"","sources":["../../src/telemetry/sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAc,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAmBrE;6CAC6C;AAC7C,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED,MAAM,WAAW,aAAa;IAC5B;;yDAEqD;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAChC;;6EAEyE;IACzE,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;qCAEiC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;kFAC8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,GAAE,MAAM,GAAG,IAAW;CAO1F;AAcD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAkDxE;AAMD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;4EACwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC;;;wDAGoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD;;oDAEgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CA4ElE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,eAAe,GAAG,kBAAkB,EAClD,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACzC,aAAa,CA2Bf"}
@@ -66,55 +66,72 @@ function __resetApiSinkWarningsForTest() {
66
66
  * gate), so the body a failed write returns is logged nowhere.
67
67
  */
68
68
  class SinkSendError extends Error {
69
- constructor(message, status) {
69
+ constructor(message, status, reasonCode = null) {
70
70
  super(message);
71
71
  this.name = "SinkSendError";
72
72
  if (status !== undefined)
73
73
  this.status = status;
74
74
  this.retriable = status === undefined || status >= 500 || status === 429;
75
+ this.reasonCode = reasonCode;
75
76
  }
76
77
  }
77
78
  exports.SinkSendError = SinkSendError;
79
+ /** The 422 body's `reason_code`, iff it is code-shaped — never any other body text. */
80
+ async function reasonCodeOf(response) {
81
+ if (response.status !== 422)
82
+ return null;
83
+ const body = (await response.json().catch(() => null));
84
+ const code = body?.reason_code;
85
+ return typeof code === "string" && /^(PGRST\d{3}|[0-9A-Z]{5})$/.test(code) ? code : null;
86
+ }
78
87
  function createDirectSink(config) {
79
88
  const base = config.supabaseUrl.replace(/\/$/, "");
80
89
  const fetchFn = config.fetchFn ?? fetch;
81
90
  return {
82
91
  mode: "direct",
83
92
  async send(batch) {
84
- for (const write of (0, ingest_protocol_1.stampBatch)(batch, config.tenancy)) {
85
- let response;
86
- try {
87
- response = await fetchFn(
88
- // S5: the upsert names a real unique constraint. A single-argument
89
- // upsert targets the primary key and 23505s later.
90
- `${base}/rest/v1/${write.table}?on_conflict=${encodeURIComponent(write.onConflict)}`, {
91
- method: "POST",
92
- headers: {
93
- "content-type": "application/json",
94
- apikey: config.serviceRoleKey,
95
- authorization: `Bearer ${config.serviceRoleKey}`,
96
- prefer: write.merge
97
- ? "resolution=merge-duplicates,return=minimal"
98
- : "resolution=ignore-duplicates,return=minimal",
99
- },
100
- body: JSON.stringify(write.rows),
101
- });
102
- }
103
- catch {
104
- // Network-level throw — no status, so TRANSIENT (the exporter retries
105
- // forever). The raw error can carry the control-plane host, so it is
106
- // NOT echoed (C2); the table + "network error" is the whole message.
107
- throw new SinkSendError(`sink POST ${write.table} → network error`, undefined);
108
- }
109
- if (!response.ok) {
110
- // Status + table only — never the PostgREST body (it carries the
111
- // control-plane host and, on some failures, header fragments; C2).
112
- // The status is what the exporter classifies on (spec §2.3.1).
113
- throw new SinkSendError(`sink POST ${write.table} → ${response.status}`, response.status);
114
- }
115
- }
93
+ await sendWrites((0, ingest_protocol_1.stampBatch)(batch, config.tenancy));
94
+ // GRADE-RAW S2-1: step-task registration is best-effort and AFTER the
95
+ // batch landed — a refused registration never fails (or re-sends) it.
96
+ const registration = (0, ingest_protocol_1.stepTaskRegistration)(batch, config.tenancy);
97
+ if (registration !== null)
98
+ await sendWrites([registration]).catch(() => undefined);
116
99
  },
117
100
  };
101
+ async function sendWrites(writes) {
102
+ for (const write of writes) {
103
+ let response;
104
+ try {
105
+ response = await fetchFn(
106
+ // S5: the upsert names a real unique constraint. A single-argument
107
+ // upsert targets the primary key and 23505s later.
108
+ `${base}/rest/v1/${write.table}?on_conflict=${encodeURIComponent(write.onConflict)}`, {
109
+ method: "POST",
110
+ headers: {
111
+ "content-type": "application/json",
112
+ apikey: config.serviceRoleKey,
113
+ authorization: `Bearer ${config.serviceRoleKey}`,
114
+ prefer: write.merge
115
+ ? "resolution=merge-duplicates,return=minimal"
116
+ : "resolution=ignore-duplicates,return=minimal",
117
+ },
118
+ body: JSON.stringify(write.rows),
119
+ });
120
+ }
121
+ catch {
122
+ // Network-level throw — no status, so TRANSIENT (the exporter retries
123
+ // forever). The raw error can carry the control-plane host, so it is
124
+ // NOT echoed (C2); the table + "network error" is the whole message.
125
+ throw new SinkSendError(`sink POST ${write.table} → network error`, undefined);
126
+ }
127
+ if (!response.ok) {
128
+ // Status + table only — never the PostgREST body (it carries the
129
+ // control-plane host and, on some failures, header fragments; C2).
130
+ // The status is what the exporter classifies on (spec §2.3.1).
131
+ throw new SinkSendError(`sink POST ${write.table} → ${response.status}`, response.status);
132
+ }
133
+ }
134
+ }
118
135
  }
119
136
  /** Below this, gzip costs more than it saves. */
120
137
  const GZIP_MIN_BYTES = 1024;
@@ -166,7 +183,9 @@ function createApiSink(config) {
166
183
  // someone else's log aggregator. The status is the exporter's transient-
167
184
  // vs-poison signal: the server sends 422 for a deterministic control-plane
168
185
  // rejection, 503 for a transient one (spec §2.2/§2.3.1).
169
- throw new SinkSendError(`ingest POST → ${response.status}`, response.status);
186
+ // COV-1c: a 422 may name the PostgREST/PG code (shape-checked, code only).
187
+ const reasonCode = await reasonCodeOf(response);
188
+ throw new SinkSendError(`ingest POST → ${response.status}${reasonCode !== null ? ` (${reasonCode})` : ""}`, response.status, reasonCode);
170
189
  }
171
190
  // A good send clears the auth-failure latch, so a later key breakage
172
191
  // re-alerts instead of staying silent behind a once-per-process guard.
@@ -1 +1 @@
1
- {"version":3,"file":"store-migrations.d.ts","sourceRoot":"","sources":["../../src/telemetry/store-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C;sCACsC;AACtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAgM9D"}
1
+ {"version":3,"file":"store-migrations.d.ts","sourceRoot":"","sources":["../../src/telemetry/store-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C;sCACsC;AACtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAwM9D"}
@@ -206,4 +206,12 @@ function runStoreMigrations(db) {
206
206
  }
207
207
  db.pragma("user_version = 16");
208
208
  }
209
+ // v17 (ARC-3, mirrors 00089): `grades.kind` gains 'outcome' (table rebuild —
210
+ // SQLite cannot ALTER a CHECK) and the nullable `value` column. The rebuild
211
+ // copies each row's ROWID too, so the exporter's `:grades` rowid watermark is
212
+ // exactly as valid after the rung as before. See migrateGradesToV17.
213
+ if (db.pragma("user_version", { simple: true }) < 17) {
214
+ (0, grades_sql_1.migrateGradesToV17)(db);
215
+ db.pragma("user_version = 17");
216
+ }
209
217
  }
@@ -52,6 +52,15 @@ export interface ExportableTelemetryStore extends TelemetryStore {
52
52
  * non-null inference_id, or null (E4 chunk A: bake-off rehydration resolves an
53
53
  * eval case's source step to the inference that keys its captured input). */
54
54
  getStepInferenceId(runId: string, stepKey: string): string | null;
55
+ /** ARC-1 conversation re-entry: the newest `running` run for (pipeline,
56
+ * episode_key) in THIS local store, with its recorded step keys (the caller
57
+ * derives the turn counter from `turn:<n>.<k>` keys), or null. Local only —
58
+ * cross-process re-entry is out of scope (duplicate runs per episode are
59
+ * tolerated, 00031). */
60
+ findOpenRun?(pipeline: string, episodeKey: string): {
61
+ readonly row: RunRow;
62
+ readonly stepKeys: readonly string[];
63
+ } | null;
55
64
  listEnvelopes(): EnvelopeRow[];
56
65
  /** Local exporter watermark (per source instance), 0 when unset. */
57
66
  getExportWatermark(sourceId: string): number;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/telemetry/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,MAAM,EACN,OAAO,EACP,YAAY,EACZ,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAA2B,KAAK,cAAc,EAAuB,MAAM,UAAU,CAAC;AAU7F;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,qEAAqE;IACrE,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACpG;kEAC8D;IAC9D,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC;;wDAEoD;IACpD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;IAC5F;mEAC+D;IAC/D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iFAAiF;IACjF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,mEAAmE;IACnE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1F;;iFAE6E;IAC7E,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClE,aAAa,IAAI,WAAW,EAAE,CAAC;IAC/B,oEAAoE;IACpE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAAC;CAC3E;AAgSD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CA6P3E"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/telemetry/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,MAAM,EACN,OAAO,EACP,YAAY,EACZ,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAA2B,KAAK,cAAc,EAAuB,MAAM,UAAU,CAAC;AAU7F;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,qEAAqE;IACrE,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACpG;kEAC8D;IAC9D,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC;;wDAEoD;IACpD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;IAC5F;mEAC+D;IAC/D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iFAAiF;IACjF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,mEAAmE;IACnE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1F;;iFAE6E;IAC7E,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClE;;;;4BAIwB;IACxB,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1H,aAAa,IAAI,WAAW,EAAE,CAAC;IAC/B,oEAAoE;IACpE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAAC;CAC3E;AAgSD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAyQ3E"}
@@ -403,6 +403,16 @@ function openTelemetryStore(dbPath) {
403
403
  .all(afterRowid, limit);
404
404
  return rows.map((r) => ({ rowid: r.__rowid, row: toStepRow(r) }));
405
405
  },
406
+ findOpenRun(pipeline, episodeKey) {
407
+ const r = db
408
+ .prepare("SELECT * FROM runs WHERE pipeline = ? AND episode_key = ? AND status = 'running' " +
409
+ "ORDER BY started_at DESC LIMIT 1")
410
+ .get(pipeline, episodeKey);
411
+ if (r === undefined)
412
+ return null;
413
+ const keys = db.prepare("SELECT DISTINCT step_key FROM steps WHERE run_id = ?").all(r.id);
414
+ return { row: toRunRow(r), stepKeys: keys.map((k) => k.step_key) };
415
+ },
406
416
  getStepInferenceId(runId, stepKey) {
407
417
  const r = db
408
418
  .prepare("SELECT inference_id FROM steps WHERE run_id = ? AND step_key = ? AND inference_id IS NOT NULL " +