modelrig 0.11.0 → 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 (199) hide show
  1. package/CHANGELOG.md +82 -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/cli.d.ts.map +1 -1
  56. package/dist/cli.js +1 -0
  57. package/dist/config.d.ts +18 -0
  58. package/dist/config.d.ts.map +1 -1
  59. package/dist/config.js +23 -3
  60. package/dist/doctor-preflight.d.ts.map +1 -1
  61. package/dist/doctor-preflight.js +6 -1
  62. package/dist/feedback/grade.d.ts +7 -2
  63. package/dist/feedback/grade.d.ts.map +1 -1
  64. package/dist/feedback/grade.js +14 -0
  65. package/dist/feedback/outcome.d.ts +103 -0
  66. package/dist/feedback/outcome.d.ts.map +1 -0
  67. package/dist/feedback/outcome.js +175 -0
  68. package/dist/gateway/history-bridge.d.ts +32 -0
  69. package/dist/gateway/history-bridge.d.ts.map +1 -0
  70. package/dist/gateway/history-bridge.js +64 -0
  71. package/dist/gateway/serve-client.d.ts.map +1 -1
  72. package/dist/gateway/serve-client.js +6 -0
  73. package/dist/index.d.ts +44 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +110 -9
  76. package/dist/init/init-cli.d.ts.map +1 -1
  77. package/dist/init/init-cli.js +21 -4
  78. package/dist/init/observe-cli.js +1 -1
  79. package/dist/init/observe.d.ts +7 -1
  80. package/dist/init/observe.d.ts.map +1 -1
  81. package/dist/init/observe.js +74 -22
  82. package/dist/init/starter.d.ts +36 -0
  83. package/dist/init/starter.d.ts.map +1 -0
  84. package/dist/init/starter.js +85 -0
  85. package/dist/registry/build.d.ts +4 -0
  86. package/dist/registry/build.d.ts.map +1 -1
  87. package/dist/registry/generation-models.d.ts +68 -0
  88. package/dist/registry/generation-models.d.ts.map +1 -0
  89. package/dist/registry/generation-models.js +168 -0
  90. package/dist/registry/model-kind.d.ts +34 -0
  91. package/dist/registry/model-kind.d.ts.map +1 -0
  92. package/dist/registry/model-kind.js +40 -0
  93. package/dist/registry/pricing-shape.d.ts +29 -1
  94. package/dist/registry/pricing-shape.d.ts.map +1 -1
  95. package/dist/registry/pricing-shape.js +9 -0
  96. package/dist/registry/pricing.d.ts +26 -0
  97. package/dist/registry/pricing.d.ts.map +1 -1
  98. package/dist/registry/pricing.js +29 -0
  99. package/dist/registry/rows.d.ts.map +1 -1
  100. package/dist/registry/rows.js +3 -1
  101. package/dist/registry/unit-pricing.d.ts +32 -0
  102. package/dist/registry/unit-pricing.d.ts.map +1 -0
  103. package/dist/registry/unit-pricing.js +62 -0
  104. package/dist/routes/input-loader.d.ts +38 -0
  105. package/dist/routes/input-loader.d.ts.map +1 -0
  106. package/dist/routes/input-loader.js +80 -0
  107. package/dist/routes/loader.d.ts.map +1 -1
  108. package/dist/routes/loader.js +3 -0
  109. package/dist/routes/resolved-bundle.d.ts +2 -0
  110. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  111. package/dist/routes/resolved-bundle.js +3 -0
  112. package/dist/run/cost.d.ts +28 -0
  113. package/dist/run/cost.d.ts.map +1 -1
  114. package/dist/run/cost.js +74 -0
  115. package/dist/run/dispatch.d.ts +4 -1
  116. package/dist/run/dispatch.d.ts.map +1 -1
  117. package/dist/run/dispatch.js +3 -0
  118. package/dist/run/limits.d.ts +88 -0
  119. package/dist/run/limits.d.ts.map +1 -0
  120. package/dist/run/limits.js +155 -0
  121. package/dist/run/messages.d.ts +199 -0
  122. package/dist/run/messages.d.ts.map +1 -0
  123. package/dist/run/messages.js +430 -0
  124. package/dist/run/raw-finalize.d.ts +5 -2
  125. package/dist/run/raw-finalize.d.ts.map +1 -1
  126. package/dist/run/raw-finalize.js +8 -5
  127. package/dist/run/raw-request.d.ts.map +1 -1
  128. package/dist/run/raw-request.js +2 -1
  129. package/dist/run/raw.d.ts +10 -4
  130. package/dist/run/raw.d.ts.map +1 -1
  131. package/dist/run/raw.js +11 -3
  132. package/dist/run/rawonly.d.ts +29 -5
  133. package/dist/run/rawonly.d.ts.map +1 -1
  134. package/dist/run/rawonly.js +54 -19
  135. package/dist/run/record-attempt.d.ts.map +1 -1
  136. package/dist/run/record-attempt.js +16 -6
  137. package/dist/run/record-call.d.ts +13 -2
  138. package/dist/run/record-call.d.ts.map +1 -1
  139. package/dist/run/record-call.js +50 -7
  140. package/dist/run/run.d.ts.map +1 -1
  141. package/dist/run/run.js +12 -8
  142. package/dist/telemetry/attempt-meta.d.ts +49 -1
  143. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  144. package/dist/telemetry/attempt-meta.js +45 -1
  145. package/dist/telemetry/exporter.d.ts.map +1 -1
  146. package/dist/telemetry/exporter.js +2 -1
  147. package/dist/telemetry/grade-mappers.d.ts +1 -1
  148. package/dist/telemetry/grade-mappers.d.ts.map +1 -1
  149. package/dist/telemetry/grade-mappers.js +7 -1
  150. package/dist/telemetry/grade-task.d.ts +49 -0
  151. package/dist/telemetry/grade-task.d.ts.map +1 -0
  152. package/dist/telemetry/grade-task.js +85 -0
  153. package/dist/telemetry/grades-sql.d.ts +16 -1
  154. package/dist/telemetry/grades-sql.d.ts.map +1 -1
  155. package/dist/telemetry/grades-sql.js +64 -4
  156. package/dist/telemetry/ingest-protocol.d.ts +24 -1
  157. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  158. package/dist/telemetry/ingest-protocol.js +72 -43
  159. package/dist/telemetry/otel-content.d.ts +31 -0
  160. package/dist/telemetry/otel-content.d.ts.map +1 -0
  161. package/dist/telemetry/otel-content.js +106 -0
  162. package/dist/telemetry/otel-hierarchy.d.ts +57 -0
  163. package/dist/telemetry/otel-hierarchy.d.ts.map +1 -0
  164. package/dist/telemetry/otel-hierarchy.js +155 -0
  165. package/dist/telemetry/otel-normalize.d.ts +15 -1
  166. package/dist/telemetry/otel-normalize.d.ts.map +1 -1
  167. package/dist/telemetry/otel-normalize.js +24 -39
  168. package/dist/telemetry/sinks.d.ts +4 -1
  169. package/dist/telemetry/sinks.d.ts.map +1 -1
  170. package/dist/telemetry/sinks.js +53 -34
  171. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  172. package/dist/telemetry/store-migrations.js +8 -0
  173. package/dist/telemetry/store.d.ts +9 -0
  174. package/dist/telemetry/store.d.ts.map +1 -1
  175. package/dist/telemetry/store.js +10 -0
  176. package/dist/telemetry/uniform-rows.d.ts +43 -0
  177. package/dist/telemetry/uniform-rows.d.ts.map +1 -0
  178. package/dist/telemetry/uniform-rows.js +139 -0
  179. package/dist/types/failures-teaching.d.ts.map +1 -1
  180. package/dist/types/failures-teaching.js +11 -0
  181. package/dist/types/failures.d.ts +1 -1
  182. package/dist/types/failures.d.ts.map +1 -1
  183. package/dist/types/failures.js +2 -0
  184. package/dist/types/provider.d.ts +12 -0
  185. package/dist/types/provider.d.ts.map +1 -1
  186. package/dist/types/route.d.ts +13 -0
  187. package/dist/types/route.d.ts.map +1 -1
  188. package/dist/types/telemetry.d.ts +14 -1
  189. package/dist/types/telemetry.d.ts.map +1 -1
  190. package/docs/agent-skill/SKILL.md +122 -12
  191. package/docs/how-it-fits.md +4 -4
  192. package/docs/instrumentation.md +148 -44
  193. package/docs/llms.txt +143 -13
  194. package/docs/mcp-oracle.md +15 -4
  195. package/docs/quickstart.md +129 -16
  196. package/package.json +4 -2
  197. package/registry/registry.json +456 -0
  198. package/templates/creative-approval.json +81 -0
  199. package/templates/support-conversation.json +82 -0
@@ -0,0 +1,430 @@
1
+ "use strict";
2
+ /**
3
+ * The messages-native lane — ARC-β (agent-archetypes spec §4, builder-prompt-02
4
+ * §1.1). A provider-neutral conversation HISTORY that every adapter maps to its
5
+ * native shape (adapters/history-mapping.ts), so a chat turn or a tool loop can
6
+ * be routed, captured and replayed on a cheaper model.
7
+ *
8
+ * Design rules (binding):
9
+ * - NO `system` role in history. The system prompt stays
10
+ * `AdapterRequest.systemPrompt`, rendered by the route template exactly as
11
+ * today. A `system` message in caller input is rejected with a teaching error.
12
+ * - Tool calls reuse the existing normalized {@link ToolCall} (OpenAI
13
+ * `tool_calls` shape) — there is no second tool-call vocabulary.
14
+ * - `history` absent ⇒ every adapter takes today's code path, byte-identical
15
+ * (pinned by adapters/one-turn-parity.test.ts).
16
+ *
17
+ * NAMING NOTE: the spec calls the type `ChatMessage`, but `ChatMessage` is
18
+ * already a public export (the gateway's OpenAI wire message,
19
+ * gateway/http-protocol.ts). To keep that export stable the lane's type is
20
+ * exported as {@link HistoryMessage} (it is what `AdapterRequest.history` holds).
21
+ *
22
+ * Pure module: types, validation, canonicalization and the capture codec. No I/O.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.HISTORY_INCOMPATIBLE_PREFIX = exports.HISTORY_SKIPPED_TAG = exports.MESSAGES_CAPTURE_KIND = exports.MessagesInputError = exports.MESSAGES_DOCS_ANCHOR = void 0;
26
+ exports.historyProblems = historyProblems;
27
+ exports.assertValidHistory = assertValidHistory;
28
+ exports.contentText = contentText;
29
+ exports.historyChars = historyChars;
30
+ exports.canonicalize = canonicalize;
31
+ exports.renderForCapture = renderForCapture;
32
+ exports.parseCaptureInput = parseCaptureInput;
33
+ exports.captureInputFor = captureInputFor;
34
+ exports.routedHistory = routedHistory;
35
+ exports.withSkipTag = withSkipTag;
36
+ exports.messagesUserTurn = messagesUserTurn;
37
+ exports.historyIncompatibility = historyIncompatibility;
38
+ exports.isGemini3Model = isGemini3Model;
39
+ exports.historyIncompatibilityCode = historyIncompatibilityCode;
40
+ exports.rawHistory = rawHistory;
41
+ exports.userSideForHash = userSideForHash;
42
+ const loader_1 = require("../routes/loader");
43
+ const failures_1 = require("../types/failures");
44
+ const input_loader_1 = require("../routes/input-loader");
45
+ const template_1 = require("../routes/template");
46
+ /** Where every messages-lane teaching message points. */
47
+ exports.MESSAGES_DOCS_ANCHOR = "messages-lane.md";
48
+ /** A caller-contract breach in a `messages` input. Subclasses RouteConfigError
49
+ * so every surface that already maps config errors (the gateway's 422, the CLI)
50
+ * treats it the same way: thrown pre-dispatch, nothing billed, no telemetry. */
51
+ class MessagesInputError extends loader_1.RouteConfigError {
52
+ constructor(problems) {
53
+ super("invalid messages", { problems });
54
+ this.name = "MessagesInputError";
55
+ }
56
+ }
57
+ exports.MessagesInputError = MessagesInputError;
58
+ function isRecord(v) {
59
+ return typeof v === "object" && v !== null && !Array.isArray(v);
60
+ }
61
+ function contentProblems(content, at, problems) {
62
+ if (typeof content === "string")
63
+ return;
64
+ if (!Array.isArray(content)) {
65
+ problems.push(`${at}.content must be a string or an array of {type: "text", text} parts`);
66
+ return;
67
+ }
68
+ content.forEach((p, j) => {
69
+ if (!isRecord(p) || p.type !== "text" || typeof p.text !== "string") {
70
+ const kind = isRecord(p) && typeof p.type === "string" ? `"${p.type}" ` : "";
71
+ problems.push(`${at}.content[${j}] ${kind}is not a text part — only {type: "text", text: string} parts are supported ` +
72
+ "(image parts arrive with the image-input lane)");
73
+ }
74
+ });
75
+ }
76
+ function isJsonObjectString(s) {
77
+ try {
78
+ return isRecord(JSON.parse(s));
79
+ }
80
+ catch {
81
+ return false;
82
+ }
83
+ }
84
+ function toolCallProblems(calls, at, problems) {
85
+ if (!Array.isArray(calls)) {
86
+ problems.push(`${at}.toolCalls must be an array of tool calls (the shape meta.toolCalls returned)`);
87
+ return [];
88
+ }
89
+ const ids = [];
90
+ calls.forEach((c, j) => {
91
+ const fn = isRecord(c) ? c.function : undefined;
92
+ if (!isRecord(c) ||
93
+ typeof c.id !== "string" ||
94
+ c.id === "" ||
95
+ c.type !== "function" ||
96
+ !isRecord(fn) ||
97
+ typeof fn.name !== "string" ||
98
+ typeof fn.arguments !== "string") {
99
+ problems.push(`${at}.toolCalls[${j}] must be {id, type: "function", function: {name, arguments: <JSON string>}}`);
100
+ return;
101
+ }
102
+ if (!isJsonObjectString(fn.arguments)) {
103
+ problems.push(`${at}.toolCalls[${j}].function.arguments must be a JSON object string (e.g. "{\\"q\\":\\"acme\\"}")`);
104
+ return;
105
+ }
106
+ ids.push(c.id);
107
+ });
108
+ return ids;
109
+ }
110
+ /**
111
+ * Validate a caller-supplied history. Returns the list of teaching problems
112
+ * (empty ⇒ valid). Rules: a non-empty array; roles user | assistant | tool only
113
+ * (a `system` message is rejected, pointing at the route template /
114
+ * `systemPrompt`); `toolCalls` only on assistant messages; every tool message
115
+ * answers a PRIOR assistant tool call by id; the last message is `user` or
116
+ * `tool` (the model answers it).
117
+ */
118
+ function historyProblems(messages) {
119
+ const problems = [];
120
+ if (!Array.isArray(messages))
121
+ return ["messages must be an array of {role, content} messages"];
122
+ if (messages.length === 0) {
123
+ return ["messages is empty — a conversation needs at least one user message for the model to answer"];
124
+ }
125
+ // Tool-call PAIRING (QA M2 — every provider enforces it): the tool messages that
126
+ // immediately follow an assistant turn with toolCalls answer EXACTLY that turn's
127
+ // ids — none missing, none extra, none twice — before any other turn. Ids are
128
+ // scoped to ONE assistant turn and its results (R2-3): hosts that emit `call_0`
129
+ // on every turn are valid. One problem per real fault (no overlapping echoes).
130
+ const everIssued = new Set();
131
+ const reportedUnanswered = new Set();
132
+ let windowAnswered = new Set();
133
+ let pending = null;
134
+ const closePending = (before) => {
135
+ if (pending !== null && pending.ids.size > 0) {
136
+ problems.push(`${pending.at} issued tool call(s) ${[...pending.ids].map((id) => `"${id}"`).join(", ")} that are not answered ` +
137
+ `before ${before} — every tool call needs its tool result immediately after the assistant turn`);
138
+ for (const id of pending.ids)
139
+ reportedUnanswered.add(id);
140
+ }
141
+ pending = null;
142
+ };
143
+ messages.forEach((m, i) => {
144
+ const at = `messages[${i}]`;
145
+ if (!isRecord(m)) {
146
+ problems.push(`${at} must be an object {role, content}`);
147
+ return;
148
+ }
149
+ if (m.role === "system") {
150
+ problems.push(`send your system prompt via the route template, not as a system message — ${at} has role "system". ` +
151
+ "With rig.run it belongs in the route's system template; with runRaw pass it as `systemPrompt`; " +
152
+ "history holds only user, assistant and tool turns");
153
+ return;
154
+ }
155
+ if (m.role === "user" || m.role === "assistant") {
156
+ closePending(at);
157
+ contentProblems(m.content, at, problems);
158
+ if (m.toolCalls !== undefined) {
159
+ if (m.role === "user") {
160
+ problems.push(`${at}.toolCalls is only allowed on an assistant message`);
161
+ }
162
+ else {
163
+ const ids = new Set();
164
+ for (const id of toolCallProblems(m.toolCalls, at, problems)) {
165
+ if (ids.has(id))
166
+ problems.push(`${at} issues tool call id "${id}" more than once in the same turn`);
167
+ ids.add(id);
168
+ everIssued.add(id);
169
+ reportedUnanswered.delete(id);
170
+ }
171
+ windowAnswered = new Set();
172
+ if (ids.size > 0)
173
+ pending = { at, ids };
174
+ }
175
+ }
176
+ return;
177
+ }
178
+ if (m.role === "tool") {
179
+ if (typeof m.content !== "string")
180
+ problems.push(`${at}.content must be a string (the tool's result)`);
181
+ const id = m.toolCallId;
182
+ if (typeof id !== "string" || id === "") {
183
+ problems.push(`${at}.toolCallId must name the tool call this result answers`);
184
+ }
185
+ else if (pending !== null && pending.ids.has(id)) {
186
+ pending.ids.delete(id);
187
+ windowAnswered.add(id);
188
+ }
189
+ else if (windowAnswered.has(id)) {
190
+ problems.push(`${at} answers tool call "${id}" a second time — each tool call takes exactly one tool result`);
191
+ }
192
+ else if (reportedUnanswered.has(id)) {
193
+ // already reported once, as the unanswered call — no second message
194
+ }
195
+ else if (everIssued.has(id)) {
196
+ problems.push(`${at} answers tool call "${id}" out of place — a tool result must immediately follow the assistant turn ` +
197
+ "that issued it (no user or assistant turn in between)");
198
+ }
199
+ else {
200
+ problems.push(`${at} answers tool call "${id}", but no earlier assistant message issued it — ` +
201
+ "include the assistant turn with its toolCalls before the tool result");
202
+ }
203
+ return;
204
+ }
205
+ problems.push(`${at}.role ${JSON.stringify(m.role)} is not one of user | assistant | tool`);
206
+ });
207
+ closePending("the end of the conversation");
208
+ const last = messages[messages.length - 1];
209
+ if (isRecord(last) && last.role !== "user" && last.role !== "tool" && last.role !== "system") {
210
+ problems.push(`the last message is "${String(last.role)}" — a conversation must end with a user or tool message for the model to answer`);
211
+ }
212
+ return problems;
213
+ }
214
+ /** Throw a {@link MessagesInputError} when `messages` is not a valid history;
215
+ * otherwise return it typed. */
216
+ function assertValidHistory(messages) {
217
+ const problems = historyProblems(messages);
218
+ if (problems.length > 0)
219
+ throw new MessagesInputError(problems);
220
+ return messages;
221
+ }
222
+ /** The text of a content value (parts concatenated in order, no separator —
223
+ * the same joining OpenAI applies to multi-part text content). */
224
+ function contentText(content) {
225
+ return typeof content === "string" ? content : content.map((p) => p.text).join("");
226
+ }
227
+ /** Total characters across every message's text and tool-call arguments — the
228
+ * history's contribution to the envelope's pre-dispatch input estimate. */
229
+ function historyChars(history) {
230
+ if (history === undefined)
231
+ return 0;
232
+ let n = 0;
233
+ for (const m of history) {
234
+ n += m.role === "tool" ? m.content.length : contentText(m.content).length;
235
+ if (m.role === "assistant")
236
+ for (const c of m.toolCalls ?? [])
237
+ n += c.function.name.length + c.function.arguments.length;
238
+ }
239
+ return n;
240
+ }
241
+ /** Deterministic JSON (keys sorted, arrays in order). */
242
+ function stable(value) {
243
+ if (value === null || typeof value !== "object")
244
+ return JSON.stringify(value) ?? "null";
245
+ if (Array.isArray(value))
246
+ return `[${value.map(stable).join(",")}]`;
247
+ const rec = value;
248
+ return `{${Object.keys(rec)
249
+ .filter((k) => rec[k] !== undefined)
250
+ .sort()
251
+ .map((k) => `${JSON.stringify(k)}:${stable(rec[k])}`)
252
+ .join(",")}}`;
253
+ }
254
+ /**
255
+ * Canonical text of a history, for hashing and cache-prefix reasoning: one
256
+ * sorted-key JSON line per message. PREFIX-STABLE by construction —
257
+ * `canonicalize(h.slice(0, n))` is a string prefix of `canonicalize(h)` — so a
258
+ * conversation that grows by appending turns keeps every earlier turn's bytes
259
+ * (the property provider prefix caches key on).
260
+ */
261
+ function canonicalize(history) {
262
+ return history.map((m) => `${stable(m)}\n`).join("");
263
+ }
264
+ // ── Capture codec (spec §4: one JSON capture per turn) ───────────────────────
265
+ /** The marker that tells a capture row's `variablesJson` holds a messages call. */
266
+ exports.MESSAGES_CAPTURE_KIND = "modelrig.messages";
267
+ /** Render a messages call's replay input for the capture row (JSON). */
268
+ function renderForCapture(history, variables = {}, tools = {}) {
269
+ const capture = {
270
+ kind: exports.MESSAGES_CAPTURE_KIND,
271
+ v: 1,
272
+ variables,
273
+ history,
274
+ ...(tools.tools !== undefined ? { tools: tools.tools } : {}),
275
+ ...(tools.toolChoice !== undefined ? { toolChoice: tools.toolChoice } : {}),
276
+ };
277
+ return JSON.stringify(capture);
278
+ }
279
+ function parseCaptureInput(variablesJson) {
280
+ const parsed = JSON.parse(variablesJson);
281
+ if (isRecord(parsed) && parsed.kind === exports.MESSAGES_CAPTURE_KIND && Array.isArray(parsed.history)) {
282
+ const variables = isRecord(parsed.variables) ? parsed.variables : {};
283
+ return {
284
+ input: variables,
285
+ messages: assertValidHistory(parsed.history),
286
+ ...(Array.isArray(parsed.tools) ? { tools: parsed.tools } : {}),
287
+ ...(parsed.toolChoice !== undefined ? { toolChoice: parsed.toolChoice } : {}),
288
+ };
289
+ }
290
+ return { input: parsed };
291
+ }
292
+ /** The capture-row input for a run: the variables alone (legacy, byte-identical)
293
+ * or the messages envelope. Returned as a value for `JSON.stringify` by the
294
+ * capture writer. */
295
+ function captureInputFor(input, messages, tools = {}) {
296
+ return messages === undefined ? input : JSON.parse(renderForCapture(messages, input, tools));
297
+ }
298
+ function routedHistory(route, opts) {
299
+ if (route.inputKind !== "messages") {
300
+ if (opts.messages === undefined)
301
+ return { history: undefined, route, skipTag: {} };
302
+ throw new loader_1.RouteConfigError(`route "${route.route}" does not accept messages — declare \`input: { kind: messages }\` in its bundle to ` +
303
+ `serve a conversation, or pass the content as \`input\` variables (see ${exports.MESSAGES_DOCS_ANCHOR})`);
304
+ }
305
+ const conflict = (0, input_loader_1.messagesGuardConflict)({
306
+ maskDeclared: route.mask !== undefined,
307
+ screenDeclared: route.screen !== undefined,
308
+ repairDeclared: route.repair !== undefined || (route.variants ?? []).some((v) => v.repair !== undefined),
309
+ });
310
+ if (conflict !== null)
311
+ throw new loader_1.RouteConfigError(`route "${route.route}": ${conflict}`);
312
+ if (opts.messages === undefined) {
313
+ throw new MessagesInputError([
314
+ `route "${route.route}" declares input: messages — pass the conversation as \`messages\` (the system prompt still comes from the route template)`,
315
+ ]);
316
+ }
317
+ const history = assertValidHistory(opts.messages);
318
+ return { history, ...compatibleCandidates(route, history, opts.tools) };
319
+ }
320
+ /** The `history_skipped` tag key (the precedent is `zdr_enforced`). */
321
+ exports.HISTORY_SKIPPED_TAG = "history_skipped";
322
+ /** Merge the skip tag onto the run's tags; the SAME object when there is none. */
323
+ function withSkipTag(tags, skipTag) {
324
+ return Object.keys(skipTag).length === 0 ? tags : { ...tags, ...skipTag };
325
+ }
326
+ /**
327
+ * Ruling R2-2: a candidate that cannot take this history natively is SKIPPED
328
+ * before the ladder (no dispatch, no row), and the run proceeds on the
329
+ * compatible candidates in their declared order. The skips ride every row of
330
+ * the call as the `history_skipped` tag. Only when NO candidate is compatible
331
+ * does the run fail closed — the terminal `invariant_violation`, listing each
332
+ * candidate's reason (no new FailureClass).
333
+ */
334
+ function compatibleCandidates(route, history, tools) {
335
+ const skipped = [];
336
+ const kept = route.candidates.filter((c) => {
337
+ const why = historyIncompatibilityCode(c.provider, c.model, history, tools);
338
+ if (why !== null)
339
+ skipped.push({ c, why });
340
+ return why === null;
341
+ });
342
+ if (skipped.length === 0)
343
+ return { route, skipTag: {} };
344
+ if (kept.length === 0) {
345
+ const only = route.candidates.length === 1 ? route.candidates[0] : undefined;
346
+ throw new failures_1.RigFailureError({
347
+ class: "invariant_violation",
348
+ message: `${exports.HISTORY_INCOMPATIBLE_PREFIX}: no candidate of route "${route.route}" can take this conversation natively — ` +
349
+ skipped.map((s) => s.why.message.slice(exports.HISTORY_INCOMPATIBLE_PREFIX.length + 2)).join("; "),
350
+ fixHint: "add a candidate that accepts tool-loop history (and pass the loop's `tools` on every turn)",
351
+ ...(only !== undefined ? { provider: only.provider, model: only.model } : {}),
352
+ });
353
+ }
354
+ return {
355
+ route: Object.freeze({ ...route, candidates: Object.freeze(kept) }),
356
+ skipTag: { [exports.HISTORY_SKIPPED_TAG]: skipped.map((s) => `${s.c.provider}/${s.c.model}=${s.why.code}`).join(",") },
357
+ };
358
+ }
359
+ /** A messages route's final user instruction: its rendered `prompt.user` when
360
+ * declared, else nothing (the history's last turn IS the prompt — the fixed
361
+ * single-turn trigger is never appended to a conversation). */
362
+ function messagesUserTurn(route, ctx) {
363
+ return route.userTemplate === undefined ? "" : (0, template_1.renderTemplate)(route.userTemplate, ctx);
364
+ }
365
+ // ── Provider acceptance (architect ruling M3(b), 2026-10-24) ─────────────────
366
+ /** Prefix of every "this provider cannot take this history natively" refusal —
367
+ * the stable marker turn replay keys on to mark a case INCOMPARABLE for a
368
+ * candidate (bakeoff/turn-replay.ts) rather than score it as a failure. */
369
+ exports.HISTORY_INCOMPATIBLE_PREFIX = "history not accepted natively";
370
+ const hasToolBlocks = (h) => h.some((m) => m.role === "tool" || (m.role === "assistant" && (m.toolCalls ?? []).length > 0));
371
+ /**
372
+ * Why `provider/model` cannot be sent this history natively, or null. Fail-closed
373
+ * PRE-DISPATCH (no provider call) on two documented provider rules:
374
+ * - Anthropic rejects `tool_use` / `tool_result` blocks unless the request
375
+ * declares `tools`;
376
+ * - Gemini 3 models require thought signatures on earlier `functionCall` parts,
377
+ * which ModelRig neither captures nor invents (no placeholder signatures).
378
+ * Every other provider/model proceeds.
379
+ */
380
+ function historyIncompatibility(provider, model, history, declaredTools) {
381
+ return historyIncompatibilityCode(provider, model, history, declaredTools)?.message ?? null;
382
+ }
383
+ /** R2-4: `models/gemini-3…` and case variants normalize to `gemini-3…`. An
384
+ * unregistered alias (e.g. `gemini-pro-latest`) does not match and falls
385
+ * through to the provider's own 400 — documented, never guessed. */
386
+ function isGemini3Model(model) {
387
+ return /^gemini-3/.test(model.trim().toLowerCase().replace(/^models\//, ""));
388
+ }
389
+ function historyIncompatibilityCode(provider, model, history, declaredTools) {
390
+ if (history === undefined || !hasToolBlocks(history))
391
+ return null;
392
+ if (provider === "anthropic" && (declaredTools === undefined || declaredTools.length === 0)) {
393
+ return {
394
+ code: "tools_required",
395
+ message: `${exports.HISTORY_INCOMPATIBLE_PREFIX}: anthropic/${model} rejects tool_use / tool_result blocks in the history unless ` +
396
+ "the request declares `tools`, and this request declared none — pass the same `tools` on every turn of the tool loop",
397
+ };
398
+ }
399
+ if (provider === "gemini" && isGemini3Model(model) && history.some((m) => m.role === "assistant" && (m.toolCalls ?? []).length > 0)) {
400
+ return {
401
+ code: "thought_signatures",
402
+ message: `${exports.HISTORY_INCOMPATIBLE_PREFIX}: gemini/${model} requires thought signatures on the function calls in the history, ` +
403
+ "which ModelRig does not capture or invent — serve tool-loop turns with a non-Gemini-3 model (Gemini 2.x and other providers accept them)",
404
+ };
405
+ }
406
+ return null;
407
+ }
408
+ /** The raw lane's history (runRaw / runRawStream), or `undefined` for the
409
+ * single-turn call. `userPrompt` is optional only when `messages` is given;
410
+ * neither is a teaching error. A history the target provider cannot take
411
+ * natively is refused here, pre-dispatch (no provider call, no telemetry row). */
412
+ function rawHistory(input) {
413
+ if (input.messages === undefined) {
414
+ if (input.userPrompt === undefined) {
415
+ throw new MessagesInputError(["runRaw needs a `userPrompt` or a `messages` conversation — neither was given"]);
416
+ }
417
+ return undefined;
418
+ }
419
+ const history = assertValidHistory(input.messages);
420
+ const incompatible = historyIncompatibility(input.provider, input.model, history, input.tools);
421
+ if (incompatible !== null)
422
+ throw new MessagesInputError([incompatible]);
423
+ return history;
424
+ }
425
+ /** The user side of the rendered request for the attempt envelope's
426
+ * input_hash / size (WI-1): the canonical history followed by the final user
427
+ * instruction; exactly `userPrompt` when there is no history (byte-identical). */
428
+ function userSideForHash(userPrompt, history) {
429
+ return history === undefined ? userPrompt : `${canonicalize(history)}${userPrompt}`;
430
+ }
@@ -9,7 +9,7 @@
9
9
  * recorder — NO route/registry/capability-gate dependency.
10
10
  */
11
11
  import { type ActiveRunContext } from "../artifacts/run-context";
12
- import type { AdapterResult, AdapterUsage, ProviderId, ServiceTier } from "../types/provider";
12
+ import type { AdapterResult, AdapterUsage, ProviderId, ServiceTier, ToolCall } from "../types/provider";
13
13
  import type { RunResult, RunTags } from "../types/route";
14
14
  import type { AttemptMetaV1 } from "../telemetry/attempt-meta";
15
15
  import type { TelemetryRow, TelemetryStore } from "../types/telemetry";
@@ -80,7 +80,10 @@ export declare function prepareRawStep(deps: Pick<RawRunDeps, "manifest">, input
80
80
  * ground-truth step — parity with the routed lane's record-attempt. No-op when
81
81
  * no run context was captured at entry. Fail-open is handled inside
82
82
  * `emitGroundTruthStep` (buffer errors are swallowed). */
83
- export declare function emitRawStep(step: RawStepContext | null, task: string, inferenceId: string, failureClass: string | null): void;
83
+ export declare function emitRawStep(step: RawStepContext | null, task: string, inferenceId: string, failureClass: string | null, limitInputs?: {
84
+ readonly costUsd?: number;
85
+ readonly toolCalls?: readonly ToolCall[];
86
+ }): void;
84
87
  /**
85
88
  * Turn an `AdapterResult` into a RunResult, recording exactly one telemetry row.
86
89
  * Shared by the buffered and streaming paths so cost/telemetry are byte-identical.
@@ -1 +1 @@
1
- {"version":3,"file":"raw-finalize.d.ts","sourceRoot":"","sources":["../../src/run/raw-finalize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"raw-finalize.d.ts","sourceRoot":"","sources":["../../src/run/raw-finalize.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ exports.recordRawRow = recordRawRow;
15
15
  exports.prepareRawStep = prepareRawStep;
16
16
  exports.emitRawStep = emitRawStep;
17
17
  exports.finalizeRaw = finalizeRaw;
18
+ const messages_1 = require("./messages");
18
19
  const node_crypto_1 = require("node:crypto");
19
20
  const cost_1 = require("./cost");
20
21
  const raw_defaults_1 = require("./raw-defaults");
@@ -112,7 +113,7 @@ function prepareRawStep(deps, input) {
112
113
  * ground-truth step — parity with the routed lane's record-attempt. No-op when
113
114
  * no run context was captured at entry. Fail-open is handled inside
114
115
  * `emitGroundTruthStep` (buffer errors are swallowed). */
115
- function emitRawStep(step, task, inferenceId, failureClass) {
116
+ function emitRawStep(step, task, inferenceId, failureClass, limitInputs) {
116
117
  if (step === null)
117
118
  return;
118
119
  step.runCtx.emitGroundTruthStep({
@@ -122,6 +123,8 @@ function emitRawStep(step, task, inferenceId, failureClass) {
122
123
  startedAt: step.startedAtIso,
123
124
  endedAt: new Date().toISOString(),
124
125
  failureClass,
126
+ ...(limitInputs?.costUsd !== undefined ? { costUsd: limitInputs.costUsd } : {}),
127
+ ...(limitInputs?.toolCalls !== undefined ? { toolCalls: limitInputs.toolCalls.map((t) => ({ name: t.function.name, arguments: t.function.arguments })) } : {}),
125
128
  });
126
129
  }
127
130
  /**
@@ -171,7 +174,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
171
174
  const declaredToolNames = input.tools?.map((t) => t.function.name);
172
175
  const rawMeta = (usage) => (0, attempt_meta_1.summarizeAttemptMeta)({
173
176
  ...(result.meta !== undefined ? { adapterMeta: result.meta } : {}),
174
- renderedRequest: (0, attempt_meta_1.renderRequestForHash)({ system: input.systemPrompt, user: input.userPrompt, schema }),
177
+ renderedRequest: (0, attempt_meta_1.renderRequestForHash)({ system: input.systemPrompt, user: (0, messages_1.userSideForHash)(input.userPrompt ?? "", input.messages), schema }),
175
178
  outputText: result.ok ? result.text : null,
176
179
  ...(declaredToolNames !== undefined ? { declaredToolNames } : {}),
177
180
  ...(schemaSha !== undefined ? { schemaSha } : {}),
@@ -182,7 +185,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
182
185
  // G1: mirror this raw inference into a ground-truth step (parity with the
183
186
  // routed lane's record-attempt). task = tags.step ?? provider/model (spec F1).
184
187
  // Fail-open is handled inside emitGroundTruthStep (buffer errors are swallowed).
185
- const emitStep = (inferenceId, failureClass) => emitRawStep(step, input.tags?.step ?? rawModelId, inferenceId, failureClass);
188
+ const emitStep = (inferenceId, failureClass, limitInputs) => emitRawStep(step, input.tags?.step ?? rawModelId, inferenceId, failureClass, limitInputs);
186
189
  if (!result.ok) {
187
190
  let costUsd = 0;
188
191
  let pricingMissing = false;
@@ -208,7 +211,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
208
211
  meta: rawMeta(result.usage),
209
212
  ...(input.cache !== undefined ? { cacheKey: input.cache.key } : {}),
210
213
  });
211
- emitStep(failId, result.failure.class);
214
+ emitStep(failId, result.failure.class, { costUsd });
212
215
  // R1: a raw-lane timeout is the #245 footgun — attach the tier-aware teaching
213
216
  // hint (the customer is looking at THIS error). Any other failure surfaces
214
217
  // unchanged. Overrides any adapter-set hint: the tier + budget is the fix.
@@ -245,7 +248,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
245
248
  meta: rawMeta(result.usage),
246
249
  ...(input.cache !== undefined ? { cacheKey: input.cache.key } : {}),
247
250
  });
248
- emitStep(id, schemaOutcome.ok ? null : "content_invalid");
251
+ emitStep(id, schemaOutcome.ok ? null : "content_invalid", { costUsd, toolCalls: toolCalls ?? [] });
249
252
  if (!schemaOutcome.ok)
250
253
  throw new failures_1.RigFailureError((0, raw_schema_1.rawSchemaMismatchFailure)(input, schemaOutcome.errors));
251
254
  // NWS N3: a NATIVE-grounded success writes one extra, legible `rung:"search"`
@@ -1 +1 @@
1
- {"version":3,"file":"raw-request.d.ts","sourceRoot":"","sources":["../../src/run/raw-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,cAAc,CA0C3F"}
1
+ {"version":3,"file":"raw-request.d.ts","sourceRoot":"","sources":["../../src/run/raw-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,cAAc,CA2C3F"}
@@ -39,7 +39,8 @@ function buildRawRequest(candidate, input) {
39
39
  return {
40
40
  candidate,
41
41
  systemPrompt: input.systemPrompt,
42
- userPrompt: input.userPrompt,
42
+ userPrompt: input.userPrompt ?? "", // ARC-β: optional only alongside `messages` (rawHistory guards)
43
+ ...(input.messages !== undefined ? { history: input.messages } : {}), // ARC-β; absent ⇒ no key
43
44
  // R2 (issue #6): a declared `responseSchema` rides the adapters' existing
44
45
  // NATIVE schema path (outputSchema) — gemini responseJsonSchema, openai/grok
45
46
  // strict json_schema, anthropic forced tool. Absent ⇒ null, byte-identical:
package/dist/run/raw.d.ts CHANGED
@@ -16,9 +16,9 @@
16
16
  * touches an adapter or the brand.
17
17
  *
18
18
  * KNOWN LIMITATIONS (flagged for the architect, not hidden):
19
- * - The adapter contract is single `systemPrompt` + `userPrompt`, not a
20
- * `messages[]` array, so a multi-turn transcript is flattened by the caller
21
- * (see `modelrig-server`'s `deriveRawPrompts`).
19
+ * - ARC-β: a multi-turn transcript rides `messages` (→ `AdapterRequest.history`,
20
+ * mapped per provider); the single-turn `systemPrompt` + `userPrompt` path is
21
+ * unchanged (byte-identical, adapters/one-turn-parity.test.ts).
22
22
  * - CE-6: the raw lane now forwards `temperature`/`topP` (via
23
23
  * `AdapterRequest.sampling`) when the caller supplies them; absent, they are
24
24
  * still dropped and the dispatch is byte-identical to pre-CE-6. `max_tokens`
@@ -52,6 +52,7 @@ import type { ProviderId, ReasoningParams, ServiceTier, ToolChoice, ToolDefiniti
52
52
  import type { RunResult, RunTags } from "../types/route";
53
53
  import type { RunStreamHandle } from "./stream";
54
54
  import type { TelemetryStore } from "../types/telemetry";
55
+ import type { HistoryMessage } from "./messages";
55
56
  export { assertRawKnobsSupported } from "./raw-knobs";
56
57
  export { buildRawRequest } from "./raw-request";
57
58
  /** Raw-lane request. `systemPrompt`/`userPrompt` are already flattened from the
@@ -65,7 +66,12 @@ export interface RawRunInput {
65
66
  * in memory for this one call, then dropped (never logged, never stored). */
66
67
  readonly apiKey: string;
67
68
  readonly systemPrompt: string;
68
- readonly userPrompt: string;
69
+ /** The single-turn user prompt. ARC-β: optional when `messages` is given (then a
70
+ * non-empty value is appended as a final user message); one of them is required. */
71
+ readonly userPrompt?: string;
72
+ /** ARC-β messages lane — the conversation (user / assistant+toolCalls / tool);
73
+ * `systemPrompt` stays the system prompt. Absent ⇒ byte-identical single turn. */
74
+ readonly messages?: readonly HistoryMessage[];
69
75
  readonly tags?: RunTags;
70
76
  /** G1 (spec §3): names the ground-truth step this raw call records inside an
71
77
  * active `run.start` context — the SAME field the routed lane exposes, so a
@@ -1 +1 @@
1
- {"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/run/raw.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAgBH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAIV,UAAU,EACV,eAAe,EACf,WAAW,EACX,UAAU,EACV,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;;;sCAGsC;AACtC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;iFAC6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;iFAK6E;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;6EAGyE;IACzE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC;;;;;;;0BAOsB;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE;;;;;;;iDAO6C;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IACjD;;;;mFAI+E;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC;;;;qFAIiF;IACjF,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC;IACrC;;;;;;;;sEAQkE;IAClE,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC;IACxC;;;;;;;;;;;;;4DAawD;IACxD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC;;+EAE2E;IAC3E,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC;;sEAEkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACxC;AAsCD;wDACwD;AACxD,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAYrF;AAcD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,eAAe,CAiDlF"}
1
+ {"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/run/raw.ts"],"names":[],"mappings":"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"}
package/dist/run/raw.js CHANGED
@@ -17,9 +17,9 @@
17
17
  * touches an adapter or the brand.
18
18
  *
19
19
  * KNOWN LIMITATIONS (flagged for the architect, not hidden):
20
- * - The adapter contract is single `systemPrompt` + `userPrompt`, not a
21
- * `messages[]` array, so a multi-turn transcript is flattened by the caller
22
- * (see `modelrig-server`'s `deriveRawPrompts`).
20
+ * - ARC-β: a multi-turn transcript rides `messages` (→ `AdapterRequest.history`,
21
+ * mapped per provider); the single-turn `systemPrompt` + `userPrompt` path is
22
+ * unchanged (byte-identical, adapters/one-turn-parity.test.ts).
23
23
  * - CE-6: the raw lane now forwards `temperature`/`topP` (via
24
24
  * `AdapterRequest.sampling`) when the caller supplies them; absent, they are
25
25
  * still dropped and the dispatch is byte-identical to pre-CE-6. `max_tokens`
@@ -66,6 +66,8 @@ const resolve_1 = require("./resolve");
66
66
  const raw_knobs_1 = require("./raw-knobs");
67
67
  const raw_request_1 = require("./raw-request");
68
68
  const raw_finalize_1 = require("./raw-finalize");
69
+ const run_context_1 = require("../artifacts/run-context"); // ARC-5
70
+ const messages_1 = require("./messages");
69
71
  var raw_knobs_2 = require("./raw-knobs");
70
72
  Object.defineProperty(exports, "assertRawKnobsSupported", { enumerable: true, get: function () { return raw_knobs_2.assertRawKnobsSupported; } });
71
73
  var raw_request_2 = require("./raw-request");
@@ -110,9 +112,11 @@ async function runRaw(deps, input) {
110
112
  // CE-2 F1/F3: fail closed on an unsupported declared directive BEFORE building
111
113
  // an adapter — no meter, no provider call, no telemetry row.
112
114
  (0, raw_knobs_1.assertRawKnobsSupported)(input);
115
+ (0, messages_1.rawHistory)(input); // ARC-β: validate messages / require a prompt, pre-dispatch
113
116
  // G1: bind the ambient run context at ENTRY (ALS boundary) so a raw call inside
114
117
  // a run.start records a ground-truth step (spec §4). Null ⇒ uninstrumented.
115
118
  const step = (0, raw_finalize_1.prepareRawStep)(deps, input);
119
+ (0, run_context_1.enforceRunLimits)(); // ARC-5: pre-dispatch run-limit check (throws RunLimitExceeded; no-op without limits)
116
120
  const adapter = buildRawAdapter(input.provider, input.apiKey);
117
121
  const candidate = (0, resolve_1.mintRawCandidate)(input.provider, input.model);
118
122
  const started = Date.now();
@@ -143,6 +147,7 @@ function runRawStream(deps, input) {
143
147
  // boundary — NOT inside the async generator below (which defers to drain time,
144
148
  // possibly on a different async context, where getActiveRunContext would miss).
145
149
  const step = (0, raw_finalize_1.prepareRawStep)(deps, input);
150
+ const limitRefusal = (0, run_context_1.runLimitRefusal)(); // ARC-5: checked at the ALS entry, thrown through the handle below
146
151
  let settle;
147
152
  let fail;
148
153
  const result = new Promise((res, rej) => {
@@ -158,6 +163,9 @@ function runRawStream(deps, input) {
158
163
  // adapter/dispatch — surfaced through the handle (result rejects, the
159
164
  // events iterator throws), so no meter, no provider call, no telemetry row.
160
165
  (0, raw_knobs_1.assertRawKnobsSupported)(input);
166
+ (0, messages_1.rawHistory)(input); // ARC-β: validate messages / require a prompt, pre-dispatch
167
+ if (limitRefusal !== null)
168
+ throw limitRefusal; // ARC-5
161
169
  const adapter = buildRawAdapter(input.provider, input.apiKey);
162
170
  const candidate = (0, resolve_1.mintRawCandidate)(input.provider, input.model);
163
171
  const req = (0, raw_request_1.buildRawRequest)(candidate, input);