@alma-harness/providers 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,7 +78,7 @@ payload kept `opaque` for replay.
78
78
  |---|---|---|---|
79
79
  | declaration | `web_search_20250305` (the direct search; the agentic 2026-03-18 version drives `code_execution` and is another kind) with `max_uses`, `allowed_domains`, `blocked_domains` | `{ type: "web_search", filters: { allowed_domains } }` + `include: ["web_search_call.action.sources"]`; `blockedDomains` is REFUSED before the network; `maxUses` is the loop's to enforce | refused before the network |
80
80
  | on the stream | `server_tool_use` → call; `web_search_tool_result` → result (url, title, page age; an error code as `error`); `usage.server_tool_use.web_search_requests` | a completed `web_search_call` item → call (`input: action`) and result (the sources; `failed` as an error), counted | — |
81
- | replay | its own two blocks as `server_tool_use` + `web_search_tool_result` with the encrypted content; another provider's skipped | its own result's `opaque` as the item; the call block skipped | skipped |
81
+ | replay | its own two blocks as `server_tool_use` + `web_search_tool_result` with the encrypted content, only while the request declares the kind (a capped step replays the text and skips the search); another provider's skipped | its own result's `opaque` as the item, only while the request declares the kind; the call block skipped | skipped |
82
82
  | `pause_turn` | → the neutral `pause`: the loop re-sends | — | — |
83
83
 
84
84
  ## Arguments the wire cut
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ function toAnthropicParams(req) {
68
68
  max_tokens: req.maxTokens,
69
69
  stream: true,
70
70
  system: toSystem(req.system),
71
- messages: req.messages.flatMap((m) => toMessageParams(m, replay))
71
+ messages: req.messages.flatMap((m) => toMessageParams(m, replay, declaredKinds(req)))
72
72
  };
73
73
  if (reasoning !== void 0) {
74
74
  if (reasoning.effort === "none") {
@@ -122,12 +122,15 @@ function toSystem(blocks) {
122
122
  return param;
123
123
  });
124
124
  }
125
- function toMessageParams(msg, replayReasoning) {
125
+ function declaredKinds(req) {
126
+ return new Set((req.providerTools ?? []).map((t) => t.kind));
127
+ }
128
+ function toMessageParams(msg, replayReasoning, declared) {
126
129
  switch (msg.role) {
127
130
  case "user":
128
131
  return [{ role: "user", content: msg.blocks.map(toUserBlock) }];
129
132
  case "assistant": {
130
- const content = msg.blocks.flatMap((b) => toAssistantBlocks(b, replayReasoning));
133
+ const content = msg.blocks.flatMap((b) => toAssistantBlocks(b, replayReasoning, declared));
131
134
  return content.length === 0 ? [] : [{ role: "assistant", content }];
132
135
  }
133
136
  case "tool":
@@ -151,7 +154,7 @@ function toUserBlock(block) {
151
154
  throw new AnthropicTranslationError(`block type ${JSON.stringify(block.type)} is not valid in a user message`);
152
155
  }
153
156
  }
154
- function toAssistantBlocks(block, replayReasoning) {
157
+ function toAssistantBlocks(block, replayReasoning, declared) {
155
158
  switch (block.type) {
156
159
  case "text":
157
160
  return [{ type: "text", text: block.text }];
@@ -160,9 +163,9 @@ function toAssistantBlocks(block, replayReasoning) {
160
163
  case "reasoning":
161
164
  return block.provider === "anthropic" && replayReasoning ? [toThinkingParam(block)] : [];
162
165
  case "provider_tool_call":
163
- return block.provider === "anthropic" ? [{ type: "server_tool_use", id: block.id, name: block.name, input: block.input }] : [];
166
+ return block.provider === "anthropic" && declared.has(block.name) ? [{ type: "server_tool_use", id: block.id, name: block.name, input: block.input }] : [];
164
167
  case "provider_tool_result":
165
- return block.provider === "anthropic" ? [toWebSearchResultParam(block)] : [];
168
+ return block.provider === "anthropic" && declared.has(block.name) ? [toWebSearchResultParam(block)] : [];
166
169
  default:
167
170
  throw new AnthropicTranslationError(
168
171
  `block type ${JSON.stringify(block.type)} is not valid in an assistant message`
@@ -290,7 +293,7 @@ async function* translateStream(events) {
290
293
  pendingServer.delete(event.index);
291
294
  yield {
292
295
  type: "provider_tool_call",
293
- block: { type: "provider_tool_call", id: server.id, name: server.name, provider: "anthropic", input: server.json === "" ? {} : JSON.parse(server.json) }
296
+ block: { type: "provider_tool_call", id: server.id, name: server.name, provider: "anthropic", input: parseToolArguments(server.json).input }
294
297
  };
295
298
  }
296
299
  break;
@@ -490,7 +493,7 @@ function toOpenAIParams(req) {
490
493
  // Privacy-first (§3, §10): the Responses API stores responses server-side
491
494
  // by default; the harness never leaves conversation state at the provider.
492
495
  store: false,
493
- input: req.messages.flatMap((m) => toInputItems(m, replay))
496
+ input: req.messages.flatMap((m) => toInputItems(m, replay, new Set((req.providerTools ?? []).map((t) => t.kind))))
494
497
  };
495
498
  if (reasoning !== void 0) {
496
499
  params.reasoning = replay ? { effort: reasoning.effort, summary: "auto" } : { effort: "none" };
@@ -527,12 +530,12 @@ function toWebSearchTool(spec) {
527
530
  function toInstructions(blocks) {
528
531
  return blocks.map((b) => b.text).join("\n\n");
529
532
  }
530
- function toInputItems(msg, replayReasoning) {
533
+ function toInputItems(msg, replayReasoning, declared) {
531
534
  switch (msg.role) {
532
535
  case "user":
533
536
  return [{ role: "user", content: msg.blocks.map(toUserContentPart) }];
534
537
  case "assistant":
535
- return msg.blocks.flatMap((b) => toAssistantItems(b, replayReasoning));
538
+ return msg.blocks.flatMap((b) => toAssistantItems(b, replayReasoning, declared));
536
539
  case "tool":
537
540
  return msg.blocks.map(toFunctionCallOutput);
538
541
  }
@@ -554,7 +557,7 @@ function toUserContentPart(block) {
554
557
  );
555
558
  }
556
559
  }
557
- function toAssistantItems(block, replayReasoning) {
560
+ function toAssistantItems(block, replayReasoning, declared) {
558
561
  switch (block.type) {
559
562
  case "text":
560
563
  return [{ role: "assistant", content: block.text }];
@@ -572,7 +575,7 @@ function toAssistantItems(block, replayReasoning) {
572
575
  case "provider_tool_call":
573
576
  return [];
574
577
  case "provider_tool_result":
575
- return block.provider === "openai" ? [toWebSearchItem(block)] : [];
578
+ return block.provider === "openai" && declared.has(block.name) ? [toWebSearchItem(block)] : [];
576
579
  default:
577
580
  throw new OpenAITranslationError(
578
581
  `block type ${JSON.stringify(block.type)} is not valid in an assistant message`
@@ -790,12 +793,7 @@ function translateResponse(response) {
790
793
  break;
791
794
  case "function_call":
792
795
  sawToolCall = true;
793
- blocks.push({
794
- type: "tool_call",
795
- id: item.call_id,
796
- name: item.name,
797
- input: item.arguments === "" ? {} : JSON.parse(item.arguments)
798
- });
796
+ blocks.push({ type: "tool_call", id: item.call_id, name: item.name, input: parseToolArguments(item.arguments).input });
799
797
  break;
800
798
  case "reasoning": {
801
799
  if (typeof item.encrypted_content !== "string") break;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/anthropic/client.ts","../src/errors.ts","../src/anthropic/translate.ts","../src/arguments.ts","../src/anthropic/jobs.ts","../src/openai/client.ts","../src/openai/translate.ts","../src/openai/jobs.ts","../src/openrouter/client.ts","../src/openrouter/translate.ts","../src/index.ts"],"sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelClient, ModelEvent, ModelRequest } from \"@alma-harness/core\";\n\nimport { toProviderError } from \"../errors\";\nimport { toAnthropicParams, translateStream } from \"./translate\";\n\nexport interface AnthropicModelClientOptions {\n /** Omit to use the SDK's environment resolution (ANTHROPIC_API_KEY, …). */\n apiKey?: string;\n baseURL?: string;\n}\n\n/**\n * `ModelClient` adapter for the Anthropic Messages API — §6.2, spec 002.\n * A thin shell: request/stream translation lives in ./translate (pure);\n * this class only owns the SDK client. What the SDK throws leaves as a\n * `ProviderError` (spec: error-taxonomy); a user abort passes through.\n */\nexport class AnthropicModelClient implements ModelClient {\n readonly #client: Anthropic;\n\n constructor(opts: AnthropicModelClientOptions = {}) {\n const init: ConstructorParameters<typeof Anthropic>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new Anthropic(init);\n }\n\n stream(req: ModelRequest, opts?: { signal?: AbortSignal }): AsyncIterable<ModelEvent> {\n // Translation throws synchronously (wrong provider, unsupported blocks)\n // before any network activity — spec 002 criterion 5.\n const params = toAnthropicParams(req);\n return translateStream(this.#rawEvents(params, opts?.signal));\n }\n\n async *#rawEvents(\n params: Anthropic.MessageCreateParamsStreaming,\n signal?: AbortSignal,\n ): AsyncGenerator<Anthropic.RawMessageStreamEvent> {\n try {\n const stream = await this.#client.messages.create(\n params,\n signal !== undefined ? { signal } : undefined,\n );\n for await (const event of stream) yield event;\n } catch (err) {\n throw toProviderError(\"anthropic\", err);\n }\n }\n}\n","import { ProviderError, type ProviderFailureKind, type ProviderId } from \"@alma-harness/core\";\n\n/**\n * What the SDKs throw → one neutral class — spec: error-taxonomy. DUCK-TYPED\n * rather than `instanceof` over two SDKs' error trees: both expose `status`,\n * `message` and the response body on `error`, the OpenAI one `code` and the\n * Anthropic one `type`; and the gateway speaks through the OpenAI SDK to\n * upstreams that shape their bodies differently. One table over the fields\n * they share is what stays true across all three.\n */\n\ninterface SdkErrorShape {\n name?: unknown;\n status?: unknown;\n code?: unknown;\n type?: unknown;\n message?: unknown;\n error?: unknown;\n}\n\nconst CONTEXT_WINDOW = /context_length_exceeded|prompt is too long|context window|maximum context length|too many tokens|exceeds the context|input length/i;\nconst OVERLOADED = /overloaded|too many requests|capacity|server_busy/i;\n\n/** Every string the body or the error carries as a code or a type, lowercased and joined. */\nfunction hintsOf(e: SdkErrorShape): string {\n const nested = (typeof e.error === \"object\" && e.error !== null ? e.error : {}) as SdkErrorShape;\n const inner = (typeof nested.error === \"object\" && nested.error !== null ? nested.error : {}) as SdkErrorShape;\n return [e.code, e.type, nested.code, nested.type, inner.code, inner.type, inner.message]\n .filter((h): h is string => typeof h === \"string\")\n .join(\" \")\n .toLowerCase();\n}\n\n/**\n * Wraps anything but a user abort. The abort passes through untouched: the\n * loop already reads its own signal, and a wrapped abort would classify a\n * cancellation as a provider failure.\n */\nexport function toProviderError(provider: ProviderId, err: unknown): unknown {\n if (err instanceof ProviderError) return err;\n const e = (typeof err === \"object\" && err !== null ? err : {}) as SdkErrorShape;\n // The SDKs do not always set `name`; the constructor's is the fallback.\n const own = typeof e.name === \"string\" && e.name !== \"Error\" ? e.name : \"\";\n const name = own !== \"\" ? own : ((err as { constructor?: { name?: unknown } } | null)?.constructor?.name as string | undefined) ?? \"\";\n if (name === \"APIUserAbortError\" || name === \"AbortError\") return err;\n const message = err instanceof Error ? err.message : String(err);\n const status = typeof e.status === \"number\" ? e.status : undefined;\n const kind = classifyFailure(name, status, `${hintsOf(e)} ${message.toLowerCase()}`);\n return new ProviderError(provider, kind, message, { ...(status !== undefined ? { status } : {}), cause: err });\n}\n\n/** The table. Exported for the one caller that has a code and no thrown error: a failure the wire REPORTED. */\nexport function classifyFailure(name: string, status: number | undefined, hints: string): ProviderFailureKind {\n if (status === 429 || /rate_limit/.test(hints)) return hints.includes(\"insufficient_quota\") ? \"rejected\" : \"rate_limited\";\n if (status === 529 || status === 503 || ((status === undefined || status >= 500) && OVERLOADED.test(hints))) return \"overloaded\";\n if (status !== undefined && status >= 500) return \"unavailable\";\n if ((status === undefined || status === 400) && CONTEXT_WINDOW.test(hints)) return \"context_window\";\n if (status !== undefined && status >= 400) return \"rejected\";\n // No status: the SDK could not reach the wire, the wire reported a server\n // failure, or the wire sent something the translation could not read.\n if (/connection|timeout|timed out|fetch|network|socket|econn|enotfound|server_error|internal|unavailable/i.test(`${name} ${hints}`)) {\n return \"unavailable\";\n }\n return \"provider_drift\";\n}\n","import type Anthropic from \"@anthropic-ai/sdk\";\n\nimport {\n ProviderError,\n type Block,\n type ModelEvent,\n type ModelRequest,\n type Msg,\n type ProviderFailureKind,\n type ProviderToolCallBlock,\n type ProviderToolResultBlock,\n type ProviderToolSpec,\n type ReasoningBlock,\n type ReasoningEffort,\n type StopReason,\n type SystemBlock,\n type ToolSpec,\n type Usage,\n} from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\n\n/**\n * Pure translation between Alma's neutral vocabulary (§6.2) and the Anthropic\n * Messages API — spec 002. Everything here is side-effect-free so the wire\n * mapping is testable without a network.\n */\n\n/**\n * Raised when a request or stream cannot be represented faithfully. A\n * `ProviderError` since spec: error-taxonomy — `rejected` before the network\n * (the request as built cannot be sent), `provider_drift` mid-stream (the\n * wire sent something this adapter does not know).\n */\nexport class AnthropicTranslationError extends ProviderError {\n constructor(message: string, kind: ProviderFailureKind = \"rejected\") {\n super(\"anthropic\", kind, message);\n this.name = \"AnthropicTranslationError\";\n }\n}\n\nexport function toAnthropicParams(req: ModelRequest): Anthropic.MessageCreateParamsStreaming {\n if (req.model.provider !== \"anthropic\") {\n throw new AnthropicTranslationError(\n `AnthropicModelClient received a request for provider ${JSON.stringify(req.model.provider)}`,\n );\n }\n // Reasoning — spec: reasoning-blocks. Absent sends nothing (the provider's\n // default, whose thinking is opt-in); \"none\" disables explicitly; anything\n // else asks for adaptive thinking at the mapped effort. Its own blocks are\n // replayed only when thinking is on for THIS request.\n const reasoning = req.reasoning;\n const replay = reasoning !== undefined && reasoning.effort !== \"none\";\n const params: Anthropic.MessageCreateParamsStreaming = {\n model: req.model.id,\n max_tokens: req.maxTokens,\n stream: true,\n system: toSystem(req.system),\n messages: req.messages.flatMap((m) => toMessageParams(m, replay)),\n };\n if (reasoning !== undefined) {\n if (reasoning.effort === \"none\") {\n params.thinking = { type: \"disabled\" };\n } else {\n params.thinking = { type: \"adaptive\" };\n params.output_config = { effort: toAnthropicEffort(reasoning.effort as Exclude<ReasoningEffort, \"none\">) };\n }\n }\n // Service tier — spec: pricing-tiers. `priority` asks for priority capacity\n // when the account has it (\"auto\": standard otherwise, and the usage says\n // which served). Flex does not exist on this wire and batch is a job, not\n // a stream: both refused before the network, never downgraded in silence.\n switch (req.serviceTier) {\n case undefined:\n case \"standard\":\n break;\n case \"priority\":\n params.service_tier = \"auto\";\n break;\n default:\n throw new AnthropicTranslationError(\n `the Anthropic Messages API cannot serve the ${req.serviceTier} tier on a streaming request`,\n );\n }\n // Provider-executed tools ride in the same list as the registered ones (spec: provider-tools).\n const tools: Anthropic.ToolUnion[] = [...req.tools.map(toTool), ...(req.providerTools ?? []).map(toServerTool)];\n if (tools.length > 0) params.tools = tools;\n markConversationTail(params.messages);\n return params;\n}\n\n/** The neutral web search → the dated server tool; every option has a wire form here. */\n/**\n * The DIRECT search (spec: provider-tools): one `server_tool_use` per query.\n * `web_search_20260318` is the agentic search, which the model drives through\n * `code_execution` — encrypted stdout, nested calls — another kind, its own spec.\n */\nfunction toServerTool(spec: ProviderToolSpec): Anthropic.WebSearchTool20250305 {\n const tool: Anthropic.WebSearchTool20250305 = { type: \"web_search_20250305\", name: \"web_search\" };\n if (spec.maxUses !== undefined) tool.max_uses = spec.maxUses;\n if (spec.allowedDomains !== undefined) tool.allowed_domains = [...spec.allowedDomains];\n if (spec.blockedDomains !== undefined) tool.blocked_domains = [...spec.blockedDomains];\n return tool;\n}\n\n/** `minimal` collapses to `low`: the Messages API has no lower rung. */\nfunction toAnthropicEffort(\n effort: Exclude<ReasoningEffort, \"none\">,\n): NonNullable<Anthropic.OutputConfig[\"effort\"]> {\n return effort === \"minimal\" ? \"low\" : effort;\n}\n\n/**\n * Cache continuation — spec 005: a second breakpoint on the conversation\n * tail lets multi-turn sessions pay incremental tokens instead of re-reading\n * the whole history every turn. (Budget: 2 of the 4 allowed breakpoints —\n * one at the stable-system boundary, one here.)\n */\nfunction markConversationTail(messages: Anthropic.MessageParam[]): void {\n const last = messages.at(-1);\n if (!last || typeof last.content === \"string\") return;\n const block = last.content.at(-1);\n if (block) (block as { cache_control?: Anthropic.CacheControlEphemeral }).cache_control = { type: \"ephemeral\" };\n}\n\n/**\n * The §6.9 stable/volatile boundary becomes the cache boundary: one\n * `cache_control` breakpoint on the LAST stable block, volatile blocks after\n * it. Order is preserved — callers must already emit stable-first.\n */\nfunction toSystem(blocks: SystemBlock[]): Anthropic.TextBlockParam[] {\n const lastStable = blocks.reduce(\n (last, b, i) => (b.volatility === \"stable\" ? i : last),\n -1,\n );\n return blocks.map((b, i) => {\n const param: Anthropic.TextBlockParam = { type: \"text\", text: b.text };\n if (i === lastStable) param.cache_control = { type: \"ephemeral\" };\n return param;\n });\n}\n\nfunction toMessageParams(msg: Msg, replayReasoning: boolean): Anthropic.MessageParam[] {\n switch (msg.role) {\n case \"user\":\n return [{ role: \"user\", content: msg.blocks.map(toUserBlock) }];\n case \"assistant\": {\n const content = msg.blocks.flatMap((b) => toAssistantBlocks(b, replayReasoning));\n // An assistant turn that held only another provider's reasoning has\n // nothing this wire can carry; an empty content array is a 400.\n return content.length === 0 ? [] : [{ role: \"assistant\", content }];\n }\n case \"tool\":\n // Anthropic's wire shape: tool results travel in a user-role message.\n return [{ role: \"user\", content: msg.blocks.map(toToolResultBlock) }];\n }\n}\n\nfunction toUserBlock(block: Block): Anthropic.ContentBlockParam {\n switch (block.type) {\n case \"text\":\n return { type: \"text\", text: block.text };\n case \"media\":\n switch (block.kind) {\n case \"image\":\n return { type: \"image\", source: { type: \"url\", url: block.ref.uri } };\n case \"document\":\n return { type: \"document\", source: { type: \"url\", url: block.ref.uri } };\n case \"audio\":\n // The Messages API takes no raw audio; products transcribe upstream.\n throw new AnthropicTranslationError(\"audio media is not supported by the Anthropic adapter\");\n }\n default:\n throw new AnthropicTranslationError(`block type ${JSON.stringify(block.type)} is not valid in a user message`);\n }\n}\n\nfunction toAssistantBlocks(block: Block, replayReasoning: boolean): Anthropic.ContentBlockParam[] {\n switch (block.type) {\n case \"text\":\n return [{ type: \"text\", text: block.text }];\n case \"tool_call\":\n return [{ type: \"tool_use\", id: block.id, name: block.name, input: block.input }];\n case \"reasoning\":\n // Own blocks go back UNMODIFIED and in order when thinking is on for\n // this request; another provider's, or any block when thinking is off,\n // are skipped without error (spec: reasoning-blocks).\n return block.provider === \"anthropic\" && replayReasoning ? [toThinkingParam(block)] : [];\n case \"provider_tool_call\":\n // Own server-tool blocks go back as they came (spec: provider-tools); another provider's are skipped.\n return block.provider === \"anthropic\" ? [{ type: \"server_tool_use\", id: block.id, name: block.name, input: block.input }] : [];\n case \"provider_tool_result\":\n return block.provider === \"anthropic\" ? [toWebSearchResultParam(block)] : [];\n default:\n throw new AnthropicTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in an assistant message`,\n );\n }\n}\n\n/** The opaque half this adapter wrote: the encrypted results, whole, or the error. */\nfunction toWebSearchResultParam(block: ProviderToolResultBlock): Anthropic.WebSearchToolResultBlockParam {\n const opaque = block.opaque as { content?: unknown } | undefined;\n if (opaque?.content === undefined) {\n throw new AnthropicTranslationError(\"an Anthropic web search result carries no replayable content\");\n }\n return { type: \"web_search_tool_result\", tool_use_id: block.callId, content: opaque.content as Anthropic.WebSearchToolResultBlockParamContent };\n}\n\n/** The opaque half this adapter wrote: a signature, or a redacted block's data. */\nfunction toThinkingParam(block: ReasoningBlock): Anthropic.ContentBlockParam {\n const opaque = block.opaque as { signature?: unknown; redacted?: unknown } | undefined;\n if (typeof opaque?.redacted === \"string\") {\n return { type: \"redacted_thinking\", data: opaque.redacted };\n }\n if (typeof opaque?.signature === \"string\") {\n return { type: \"thinking\", thinking: block.text ?? \"\", signature: opaque.signature };\n }\n // Refused here rather than as a 400 from the provider: a block this\n // adapter did not write in this shape cannot be verified there either.\n throw new AnthropicTranslationError(\"an Anthropic reasoning block carries neither a signature nor redacted data\");\n}\n\nfunction toToolResultBlock(block: Block): Anthropic.ToolResultBlockParam {\n if (block.type !== \"tool_result\") {\n throw new AnthropicTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a tool message`,\n );\n }\n const param: Anthropic.ToolResultBlockParam = {\n type: \"tool_result\",\n tool_use_id: block.callId,\n // JSON.stringify yields undefined (not a string) for undefined/functions;\n // \"null\" keeps the content explicit for void tool outputs.\n content:\n typeof block.output === \"string\" ? block.output : (JSON.stringify(block.output) ?? \"null\"),\n };\n if (block.isError) param.is_error = true;\n return param;\n}\n\nfunction toTool(spec: ToolSpec): Anthropic.Tool {\n return {\n name: spec.name,\n description: spec.description,\n // The registry already derived a JSON Schema object (§6.4).\n input_schema: spec.inputSchema as Anthropic.Tool.InputSchema,\n };\n}\n\n/**\n * Raw SDK stream → neutral `ModelEvent`s. Tool inputs arrive as\n * `input_json_delta` fragments; they are accumulated per content block and\n * emitted as ONE `tool_call` with parsed input at `content_block_stop`.\n * Usage is aggregated (input + cache fields from `message_start`, output from\n * `message_delta`) and emitted once before the final `stop`.\n */\nexport async function* translateStream(\n events: AsyncIterable<Anthropic.RawMessageStreamEvent>,\n): AsyncGenerator<ModelEvent> {\n const usage: Usage = { inputTokens: 0, outputTokens: 0 };\n let stopReason: Anthropic.Message[\"stop_reason\"] = null;\n const pendingTools = new Map<number, { id: string; name: string; json: string }>();\n // Thinking arrives as text deltas plus one signature delta, redacted\n // thinking as opaque data on the block start; both are emitted as ONE\n // complete reasoning block at content_block_stop (spec: reasoning-blocks).\n const pendingThinking = new Map<number, { text: string; signature: string }>();\n const pendingRedacted = new Map<number, string>();\n // A server tool's call arrives like a tool use — input as json deltas — and its\n // result arrives whole on the block start (spec: provider-tools).\n const pendingServer = new Map<number, { id: string; name: ProviderToolCallBlock[\"name\"]; json: string }>();\n\n for await (const event of events) {\n switch (event.type) {\n case \"message_start\": {\n const u = event.message.usage;\n usage.inputTokens = u.input_tokens;\n if (u.cache_read_input_tokens != null) usage.cacheReadInputTokens = u.cache_read_input_tokens;\n if (u.cache_creation_input_tokens != null) {\n usage.cacheWriteInputTokens = u.cache_creation_input_tokens;\n }\n if (u.server_tool_use?.web_search_requests) usage.webSearchRequests = u.server_tool_use.web_search_requests;\n // The tier that SERVED, off the wire (spec: pricing-tiers) — priced\n // over the tier asked, since \"auto\" may land on standard.\n if (u.service_tier === \"standard\" || u.service_tier === \"priority\" || u.service_tier === \"batch\") {\n usage.serviceTier = u.service_tier;\n }\n break;\n }\n case \"content_block_start\":\n if (event.content_block.type === \"tool_use\") {\n pendingTools.set(event.index, {\n id: event.content_block.id,\n name: event.content_block.name,\n json: \"\",\n });\n } else if (event.content_block.type === \"thinking\") {\n pendingThinking.set(event.index, { text: event.content_block.thinking, signature: event.content_block.signature });\n } else if (event.content_block.type === \"redacted_thinking\") {\n pendingRedacted.set(event.index, event.content_block.data);\n } else if (event.content_block.type === \"server_tool_use\") {\n if (event.content_block.name !== \"web_search\") {\n throw new AnthropicTranslationError(`Unmapped Anthropic server tool ${JSON.stringify(event.content_block.name)} — provider drift?`, \"provider_drift\");\n }\n pendingServer.set(event.index, { id: event.content_block.id, name: \"web_search\", json: \"\" });\n } else if (event.content_block.type === \"web_search_tool_result\") {\n yield { type: \"provider_tool_result\", block: toProviderToolResult(event.content_block) };\n }\n break;\n case \"content_block_delta\":\n if (event.delta.type === \"text_delta\") {\n yield { type: \"text_delta\", text: event.delta.text };\n } else if (event.delta.type === \"input_json_delta\") {\n const pending = pendingTools.get(event.index) ?? pendingServer.get(event.index);\n if (pending) pending.json += event.delta.partial_json;\n } else if (event.delta.type === \"thinking_delta\") {\n const pending = pendingThinking.get(event.index);\n if (pending) pending.text += event.delta.thinking;\n } else if (event.delta.type === \"signature_delta\") {\n const pending = pendingThinking.get(event.index);\n if (pending) pending.signature = event.delta.signature;\n }\n break;\n case \"content_block_stop\": {\n const pending = pendingTools.get(event.index);\n if (pending) {\n pendingTools.delete(event.index);\n // Arguments that do not parse are the loop's to answer, and the\n // stop reason that follows says whether the wire cut them.\n yield { type: \"tool_call\", id: pending.id, name: pending.name, ...parseToolArguments(pending.json) };\n }\n const thinking = pendingThinking.get(event.index);\n if (thinking) {\n pendingThinking.delete(event.index);\n yield {\n type: \"reasoning\",\n block: { type: \"reasoning\", provider: \"anthropic\", text: thinking.text, opaque: { signature: thinking.signature } },\n };\n }\n const redacted = pendingRedacted.get(event.index);\n if (redacted !== undefined) {\n pendingRedacted.delete(event.index);\n yield { type: \"reasoning\", block: { type: \"reasoning\", provider: \"anthropic\", opaque: { redacted } } };\n }\n const server = pendingServer.get(event.index);\n if (server) {\n pendingServer.delete(event.index);\n yield {\n type: \"provider_tool_call\",\n block: { type: \"provider_tool_call\", id: server.id, name: server.name, provider: \"anthropic\", input: server.json === \"\" ? {} : (JSON.parse(server.json) as unknown) },\n };\n }\n break;\n }\n case \"message_delta\":\n if (event.delta.stop_reason != null) stopReason = event.delta.stop_reason;\n usage.outputTokens = event.usage.output_tokens;\n if (event.usage.server_tool_use?.web_search_requests) usage.webSearchRequests = event.usage.server_tool_use.web_search_requests;\n break;\n case \"message_stop\":\n yield { type: \"usage\", usage: { ...usage } };\n yield { type: \"stop\", reason: mapStopReason(stopReason) };\n break;\n }\n }\n}\n\n/** A web search result block → the neutral result: the citations, the error, and the content whole for replay. */\nfunction toProviderToolResult(block: Anthropic.WebSearchToolResultBlock): ProviderToolResultBlock {\n const result: ProviderToolResultBlock = {\n type: \"provider_tool_result\",\n callId: block.tool_use_id,\n name: \"web_search\",\n provider: \"anthropic\",\n results: [],\n opaque: { content: block.content },\n };\n if (Array.isArray(block.content)) {\n result.results = block.content.map((r) => ({\n url: r.url,\n ...(r.title !== undefined ? { title: r.title } : {}),\n ...(r.page_age != null ? { pageAge: r.page_age } : {}),\n }));\n } else {\n result.error = block.content.error_code;\n }\n return result;\n}\n\nexport function mapStopReason(reason: Anthropic.Message[\"stop_reason\"]): StopReason {\n switch (reason) {\n case \"end_turn\":\n case \"stop_sequence\":\n return \"end_turn\";\n case \"tool_use\":\n return \"tool_use\";\n case \"max_tokens\":\n return \"max_tokens\";\n case \"refusal\":\n return \"refusal\";\n case \"model_context_window_exceeded\":\n return \"context_window_exceeded\";\n case \"pause_turn\":\n // A long-running server tool paused; the loop re-sends to continue (spec: provider-tools).\n return \"pause\";\n default:\n // A reason newer than this adapter: refuse loudly rather than silently truncate.\n throw new AnthropicTranslationError(\n `Unmapped Anthropic stop_reason ${JSON.stringify(reason)} — provider drift?`,\n \"provider_drift\",\n );\n }\n}\n","/**\n * Tool arguments off the wire — spec: what-the-wire-cuts. Every adapter\n * accumulates them as text and parsed them with a bare `JSON.parse`, so a\n * response cut by `max_tokens` mid-arguments threw from inside the stream\n * and the loop never saw the stop reason that explained it. A call whose\n * arguments do not parse is still a call: `input: {}` and the raw text as\n * `malformed`, for the loop to answer.\n */\nexport function parseToolArguments(json: string): { input: unknown; malformed?: string } {\n if (json === \"\") return { input: {} };\n try {\n return { input: JSON.parse(json) as unknown };\n } catch {\n return { input: {}, malformed: json };\n }\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Block, JobHandle, JobItem, JobOutput, JobProgress, JobResult, ModelJobClient, Usage } from \"@alma-harness/core\";\n\nimport { AnthropicTranslationError, mapStopReason, toAnthropicParams } from \"./translate\";\n\n/**\n * `ModelJobClient` over the Anthropic Message Batches API — spec: model-jobs.\n * Each item's request is translated with the same function the stream uses,\n * minus `stream`; a succeeded result is a complete `Message`, translated\n * here into the neutral output the runner prices and returns.\n */\n\n/** A complete message → neutral blocks, usage and stop — the non-streaming half of spec 002. */\nexport function translateMessage(message: Anthropic.Message): JobOutput {\n const blocks: Block[] = [];\n for (const block of message.content) {\n switch (block.type) {\n case \"text\":\n blocks.push({ type: \"text\", text: block.text });\n break;\n case \"tool_use\":\n blocks.push({ type: \"tool_call\", id: block.id, name: block.name, input: block.input });\n break;\n case \"thinking\":\n blocks.push({ type: \"reasoning\", provider: \"anthropic\", text: block.thinking, opaque: { signature: block.signature } });\n break;\n case \"redacted_thinking\":\n blocks.push({ type: \"reasoning\", provider: \"anthropic\", opaque: { redacted: block.data } });\n break;\n default:\n // A job declares no tools, provider-executed ones included (spec: model-jobs).\n throw new AnthropicTranslationError(`Unmapped Anthropic content block ${JSON.stringify(block.type)} — provider drift?`, \"provider_drift\");\n }\n }\n const u = message.usage;\n const usage: Usage = { inputTokens: u.input_tokens, outputTokens: u.output_tokens };\n if (u.cache_read_input_tokens != null) usage.cacheReadInputTokens = u.cache_read_input_tokens;\n if (u.cache_creation_input_tokens != null) usage.cacheWriteInputTokens = u.cache_creation_input_tokens;\n if (u.service_tier === \"standard\" || u.service_tier === \"priority\" || u.service_tier === \"batch\") {\n usage.serviceTier = u.service_tier;\n }\n return { blocks, usage, stop: mapStopReason(message.stop_reason) };\n}\n\nexport interface AnthropicJobClientOptions {\n apiKey?: string;\n baseURL?: string;\n}\n\nexport class AnthropicJobClient implements ModelJobClient {\n readonly #client: Anthropic;\n\n constructor(opts: AnthropicJobClientOptions = {}) {\n const init: ConstructorParameters<typeof Anthropic>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new Anthropic(init);\n }\n\n async submit(items: readonly JobItem[]): Promise<JobHandle> {\n const first = items[0];\n if (!first) throw new AnthropicTranslationError(\"a batch needs at least one item\");\n const requests = items.map((item) => {\n // The item's tier is what makes it a job; the batch API has no tier\n // parameter, so the streaming translation runs with it cleared.\n const { serviceTier: _tier, ...req } = item.request;\n void _tier;\n const { stream: _stream, ...params } = toAnthropicParams(req);\n void _stream;\n return { custom_id: item.id, params: params as Anthropic.MessageCreateParamsNonStreaming };\n });\n const batch = await this.#client.messages.batches.create({ requests });\n return { provider: \"anthropic\", id: batch.id, model: first.request.model };\n }\n\n async status(handle: JobHandle): Promise<JobProgress> {\n const batch = await this.#client.messages.batches.retrieve(handle.id);\n const c = batch.request_counts;\n const total = c.processing + c.succeeded + c.errored + c.canceled + c.expired;\n const status =\n batch.processing_status === \"canceling\"\n ? \"cancelled\"\n : batch.processing_status === \"in_progress\"\n ? \"running\"\n : c.canceled === total && total > 0\n ? \"cancelled\"\n : c.expired === total && total > 0\n ? \"expired\"\n : \"done\";\n return { status, counts: { total, done: c.succeeded, failed: c.errored + c.canceled + c.expired } };\n }\n\n async *results(handle: JobHandle): AsyncIterable<JobResult> {\n const decoder = await this.#client.messages.batches.results(handle.id);\n for await (const entry of decoder) {\n const { custom_id: id, result } = entry;\n switch (result.type) {\n case \"succeeded\":\n yield { id, outcome: \"succeeded\", output: translateMessage(result.message) };\n break;\n case \"errored\": {\n const error = (result.error as { error?: { message?: string } } | undefined)?.error?.message;\n yield { id, outcome: \"errored\", ...(error !== undefined ? { error } : {}) };\n break;\n }\n case \"canceled\":\n yield { id, outcome: \"cancelled\" };\n break;\n case \"expired\":\n yield { id, outcome: \"expired\" };\n break;\n }\n }\n }\n\n async cancel(handle: JobHandle): Promise<void> {\n await this.#client.messages.batches.cancel(handle.id);\n }\n}\n","import OpenAI from \"openai\";\nimport type { ModelClient, ModelEvent, ModelRequest } from \"@alma-harness/core\";\n\nimport { toProviderError } from \"../errors\";\nimport { toOpenAIParams, translateOpenAIStream } from \"./translate\";\n\nexport interface OpenAIModelClientOptions {\n /** Omit to use the SDK's environment resolution (OPENAI_API_KEY). */\n apiKey?: string;\n baseURL?: string;\n}\n\n/**\n * `ModelClient` adapter for the OpenAI Responses API — §6.2, spec 003.\n * A thin shell: request/stream translation lives in ./translate (pure);\n * this class only owns the SDK client. What the SDK throws leaves as a\n * `ProviderError` (spec: error-taxonomy); a user abort passes through.\n */\nexport class OpenAIModelClient implements ModelClient {\n readonly #client: OpenAI;\n\n constructor(opts: OpenAIModelClientOptions = {}) {\n const init: ConstructorParameters<typeof OpenAI>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new OpenAI(init);\n }\n\n stream(req: ModelRequest, opts?: { signal?: AbortSignal }): AsyncIterable<ModelEvent> {\n // Translation throws synchronously (wrong provider, unsupported blocks)\n // before any network activity — spec 003 criterion 6.\n const params = toOpenAIParams(req);\n return translateOpenAIStream(this.#rawEvents(params, opts?.signal));\n }\n\n async *#rawEvents(\n params: OpenAI.Responses.ResponseCreateParamsStreaming,\n signal?: AbortSignal,\n ): AsyncGenerator<OpenAI.Responses.ResponseStreamEvent> {\n try {\n const stream = await this.#client.responses.create(\n params,\n signal !== undefined ? { signal } : undefined,\n );\n for await (const event of stream) yield event;\n } catch (err) {\n throw toProviderError(\"openai\", err);\n }\n }\n}\n","import type OpenAI from \"openai\";\n\nimport {\n ProviderError,\n type Block,\n type ModelEvent,\n type ModelRequest,\n type Msg,\n type ProviderFailureKind,\n type ProviderToolResultBlock,\n type ProviderToolSpec,\n type ReasoningBlock,\n type StopReason,\n type SystemBlock,\n type ToolSpec,\n type Usage,\n} from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\nimport { classifyFailure } from \"../errors\";\n\n/**\n * Pure translation between Alma's neutral vocabulary (§6.2) and the OpenAI\n * Responses API — spec 003. Side-effect-free; testable without a network.\n */\n\n/** A `ProviderError` since spec: error-taxonomy — see `AnthropicTranslationError`. */\nexport class OpenAITranslationError extends ProviderError {\n constructor(message: string, kind: ProviderFailureKind = \"rejected\") {\n super(\"openai\", kind, message);\n this.name = \"OpenAITranslationError\";\n }\n}\n\nexport function toOpenAIParams(\n req: ModelRequest,\n): OpenAI.Responses.ResponseCreateParamsStreaming {\n if (req.model.provider !== \"openai\") {\n throw new OpenAITranslationError(\n `OpenAIModelClient received a request for provider ${JSON.stringify(req.model.provider)}`,\n );\n }\n // Reasoning — spec: reasoning-blocks. Absent sends nothing and drops the\n // provider's default reasoning items as before; \"none\" disables; anything\n // else asks for the effort AND for the encrypted content, which is the only\n // form that survives `store: false` and can be carried to the next step.\n const reasoning = req.reasoning;\n const replay = reasoning !== undefined && reasoning.effort !== \"none\";\n const params: OpenAI.Responses.ResponseCreateParamsStreaming = {\n model: req.model.id,\n max_output_tokens: req.maxTokens,\n stream: true,\n // Privacy-first (§3, §10): the Responses API stores responses server-side\n // by default; the harness never leaves conversation state at the provider.\n store: false,\n input: req.messages.flatMap((m) => toInputItems(m, replay)),\n };\n if (reasoning !== undefined) {\n params.reasoning = replay ? { effort: reasoning.effort, summary: \"auto\" } : { effort: \"none\" };\n if (replay) params.include = [\"reasoning.encrypted_content\"];\n }\n // Service tier — spec: pricing-tiers. Flex and priority are request\n // parameters here; batch is a job, not a stream, and is refused.\n switch (req.serviceTier) {\n case undefined:\n case \"standard\":\n break;\n case \"flex\":\n case \"priority\":\n params.service_tier = req.serviceTier;\n break;\n default:\n throw new OpenAITranslationError(\n `the OpenAI Responses API cannot serve the ${req.serviceTier} tier on a streaming request`,\n );\n }\n const instructions = toInstructions(req.system);\n if (instructions !== \"\") params.instructions = instructions;\n // Provider-executed tools ride in the same list (spec: provider-tools); the\n // sources are asked for, so the result can cite what it read.\n const tools: OpenAI.Responses.Tool[] = [...req.tools.map(toTool), ...(req.providerTools ?? []).map(toWebSearchTool)];\n if (tools.length > 0) params.tools = tools;\n if ((req.providerTools ?? []).length > 0) params.include = [...(params.include ?? []), \"web_search_call.action.sources\"];\n return params;\n}\n\n/**\n * The neutral web search → the Responses tool. An option this wire has no\n * form for is REFUSED, never dropped; `maxUses` is not one — the loop\n * enforces it across the turn's steps (spec: what-the-wire-cuts).\n */\nfunction toWebSearchTool(spec: ProviderToolSpec): OpenAI.Responses.WebSearchTool {\n if (spec.blockedDomains !== undefined) {\n throw new OpenAITranslationError(\"the OpenAI web search has no blocked-domains form — refusing rather than searching them\");\n }\n const tool: OpenAI.Responses.WebSearchTool = { type: \"web_search\" };\n if (spec.allowedDomains !== undefined) tool.filters = { allowed_domains: [...spec.allowedDomains] };\n return tool;\n}\n\n/**\n * OpenAI has no explicit cache breakpoint (prefix caching is automatic), so\n * the §6.9 stable/volatile discipline is preserved simply by keeping the\n * blocks' order — stable first — in one instructions string.\n */\nfunction toInstructions(blocks: SystemBlock[]): string {\n return blocks.map((b) => b.text).join(\"\\n\\n\");\n}\n\nfunction toInputItems(msg: Msg, replayReasoning: boolean): OpenAI.Responses.ResponseInputItem[] {\n switch (msg.role) {\n case \"user\":\n return [{ role: \"user\", content: msg.blocks.map(toUserContentPart) }];\n case \"assistant\":\n // Text stays a message item; each tool_call becomes its own top-level\n // function_call item, preserving block order — and a reasoning item\n // goes back ahead of them, as it came (spec: reasoning-blocks).\n return msg.blocks.flatMap((b) => toAssistantItems(b, replayReasoning));\n case \"tool\":\n return msg.blocks.map(toFunctionCallOutput);\n }\n}\n\nfunction toUserContentPart(\n block: Block,\n): OpenAI.Responses.ResponseInputText | OpenAI.Responses.ResponseInputImage {\n switch (block.type) {\n case \"text\":\n return { type: \"input_text\", text: block.text };\n case \"media\":\n if (block.kind === \"image\") {\n return { type: \"input_image\", detail: \"auto\", image_url: block.ref.uri };\n }\n // Documents/audio on the Responses input surface are file-id-centric,\n // which breaks media-by-reference (§6.1) — fail loudly (spec 003):\n // the routing policy is the layer that picks capable providers.\n throw new OpenAITranslationError(\n `${block.kind} media is not supported by the OpenAI adapter`,\n );\n default:\n throw new OpenAITranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a user message`,\n );\n }\n}\n\nfunction toAssistantItems(block: Block, replayReasoning: boolean): OpenAI.Responses.ResponseInputItem[] {\n switch (block.type) {\n case \"text\":\n return [{ role: \"assistant\", content: block.text }];\n case \"tool_call\":\n return [\n {\n type: \"function_call\",\n call_id: block.id,\n name: block.name,\n arguments: JSON.stringify(block.input),\n },\n ];\n case \"reasoning\":\n // Own items go back with their encrypted content when reasoning is on\n // for this request; another provider's, or any when it is off, are\n // skipped without error (spec: reasoning-blocks).\n return block.provider === \"openai\" && replayReasoning ? [toReasoningItem(block)] : [];\n case \"provider_tool_call\":\n // The item carries both halves; the result block replays it (spec: provider-tools).\n return [];\n case \"provider_tool_result\":\n return block.provider === \"openai\" ? [toWebSearchItem(block)] : [];\n default:\n throw new OpenAITranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in an assistant message`,\n );\n }\n}\n\n/** The opaque half this adapter wrote: the whole `web_search_call` item. */\nfunction toWebSearchItem(block: ProviderToolResultBlock): OpenAI.Responses.ResponseFunctionWebSearch {\n const opaque = block.opaque as { type?: unknown } | undefined;\n if (opaque?.type !== \"web_search_call\") {\n throw new OpenAITranslationError(\"an OpenAI web search result carries no replayable item\");\n }\n return opaque as OpenAI.Responses.ResponseFunctionWebSearch;\n}\n\n/** The opaque half this adapter wrote: the item's id, summary and encrypted content. */\nfunction toReasoningItem(block: ReasoningBlock): OpenAI.Responses.ResponseReasoningItem {\n const opaque = block.opaque as\n | { id?: unknown; summary?: unknown; encrypted_content?: unknown }\n | undefined;\n if (typeof opaque?.id !== \"string\" || typeof opaque.encrypted_content !== \"string\") {\n throw new OpenAITranslationError(\"an OpenAI reasoning block carries no id or encrypted content\");\n }\n return {\n type: \"reasoning\",\n id: opaque.id,\n summary: Array.isArray(opaque.summary) ? (opaque.summary as OpenAI.Responses.ResponseReasoningItem[\"summary\"]) : [],\n encrypted_content: opaque.encrypted_content,\n };\n}\n\nfunction toFunctionCallOutput(block: Block): OpenAI.Responses.ResponseInputItem {\n if (block.type !== \"tool_result\") {\n throw new OpenAITranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a tool message`,\n );\n }\n const serialized =\n typeof block.output === \"string\" ? block.output : (JSON.stringify(block.output) ?? \"null\");\n return {\n type: \"function_call_output\",\n call_id: block.callId,\n // The Responses API has no is_error flag on function outputs — an\n // explicit prefix keeps failures visible to the model (spec 003).\n output: block.isError ? `ERROR: ${serialized}` : serialized,\n };\n}\n\nfunction toTool(spec: ToolSpec): OpenAI.Responses.FunctionTool {\n return {\n type: \"function\",\n name: spec.name,\n description: spec.description,\n parameters: spec.inputSchema,\n // Registry-derived schemas are not guaranteed strict-compatible; the\n // structured-output surface is a follow-up slice (spec 003 decision).\n strict: false,\n };\n}\n\n/**\n * Semantic stream events → neutral `ModelEvent`s. Function-call arguments\n * arrive complete on `response.output_item.done`, so each call is emitted as\n * ONE parsed `tool_call`. The terminal `response.completed` / `.incomplete`\n * event carries usage; `tool_use` is inferred from having emitted tool calls\n * (the Responses API has no finish_reason).\n */\nexport async function* translateOpenAIStream(\n events: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>,\n): AsyncGenerator<ModelEvent> {\n let sawToolCall = false;\n let sawRefusal = false;\n // The wire's usage does not count searches; the completed items do (spec: provider-tools).\n let searches = 0;\n const withSearches = (usage: Usage): Usage => (searches > 0 ? { ...usage, webSearchRequests: searches } : usage);\n\n for await (const event of events) {\n switch (event.type) {\n case \"response.output_text.delta\":\n yield { type: \"text_delta\", text: event.delta };\n break;\n case \"response.refusal.delta\":\n // Refusal content is user-visible text; the stop reason records the\n // refusal itself (spec 003).\n sawRefusal = true;\n yield { type: \"text_delta\", text: event.delta };\n break;\n case \"response.output_item.done\":\n if (event.item.type === \"function_call\") {\n sawToolCall = true;\n yield { type: \"tool_call\", id: event.item.call_id, name: event.item.name, ...parseToolArguments(event.item.arguments) };\n } else if (event.item.type === \"web_search_call\") {\n // One item, two neutral halves: the call (its action) and the result\n // (the sources, the item whole for replay) — spec: provider-tools.\n searches += 1;\n const item = event.item;\n const action = item.action;\n const sources = action.type === \"search\" ? (action.sources ?? []).map((s) => ({ url: s.url })) : [];\n yield { type: \"provider_tool_call\", block: { type: \"provider_tool_call\", id: item.id, name: \"web_search\", provider: \"openai\", input: action } };\n yield {\n type: \"provider_tool_result\",\n block: {\n type: \"provider_tool_result\",\n callId: item.id,\n name: \"web_search\",\n provider: \"openai\",\n results: sources,\n ...(item.status === \"failed\" ? { error: \"failed\" } : {}),\n opaque: item,\n },\n };\n } else if (event.item.type === \"reasoning\" && typeof event.item.encrypted_content === \"string\") {\n // The COMPLETED item, from `done`: `added` may carry partial\n // encrypted content. Without encrypted content the item cannot be\n // replayed under `store: false`, so it is dropped as before the\n // spec — that is the case where nobody asked for it.\n const item = event.item;\n const text = [\n ...(item.content ?? []).map((c) => c.text),\n ...item.summary.map((s) => s.text),\n ]\n .filter((t) => t !== \"\")\n .join(\"\\n\");\n yield {\n type: \"reasoning\",\n block: {\n type: \"reasoning\",\n provider: \"openai\",\n ...(text !== \"\" ? { text } : {}),\n opaque: { id: item.id, summary: item.summary, encrypted_content: item.encrypted_content },\n },\n };\n }\n break;\n case \"response.completed\":\n yield { type: \"usage\", usage: withSearches(usageOf(event.response)) };\n yield { type: \"stop\", reason: sawRefusal ? \"refusal\" : sawToolCall ? \"tool_use\" : \"end_turn\" };\n break;\n case \"response.incomplete\": {\n yield { type: \"usage\", usage: withSearches(usageOf(event.response)) };\n yield { type: \"stop\", reason: mapIncompleteReason(event.response) };\n break;\n }\n case \"response.failed\": {\n // A failure the wire REPORTED rather than threw: classified by its\n // code through the same table the SDK's thrown errors go through.\n const err = event.response.error;\n const message = `OpenAI response failed: ${err ? `${err.code}: ${err.message}` : \"unknown error\"}`;\n throw new OpenAITranslationError(message, classifyFailure(\"ResponseFailed\", undefined, message.toLowerCase()));\n }\n case \"error\": {\n const message = `OpenAI stream error: ${event.message}`;\n throw new OpenAITranslationError(message, classifyFailure(\"StreamError\", undefined, `${event.code ?? \"\"} ${message}`.toLowerCase()));\n }\n default:\n // created / in_progress / content parts / argument deltas etc. carry\n // no neutral information beyond the events handled above.\n break;\n }\n }\n}\n\n/** The neutral usage of a complete response — shared by the stream and the batch path. */\nexport function usageOf(response: OpenAI.Responses.Response): Usage {\n const u = response.usage;\n const cachedRead = u?.input_tokens_details?.cached_tokens ?? 0;\n const usage: Usage = {\n // Neutral semantics (spec 005): inputTokens EXCLUDES cache reads.\n // OpenAI's input_tokens includes cached_tokens; subtract to normalize —\n // otherwise the BudgetGuard would price the same conversation\n // differently per provider.\n inputTokens: Math.max(0, (u?.input_tokens ?? 0) - cachedRead),\n outputTokens: u?.output_tokens ?? 0,\n };\n if (cachedRead > 0) usage.cacheReadInputTokens = cachedRead;\n const cacheWrite = u?.input_tokens_details?.cache_write_tokens;\n if (cacheWrite !== undefined && cacheWrite > 0) usage.cacheWriteInputTokens = cacheWrite;\n // Telemetry only: reasoning tokens are already inside output_tokens.\n const reasoningTokens = u?.output_tokens_details?.reasoning_tokens;\n if (reasoningTokens !== undefined && reasoningTokens > 0) usage.reasoningTokens = reasoningTokens;\n // The tier that SERVED, off the wire (spec: pricing-tiers). `default` is\n // standard; `auto`, `scale`, `fast` and `ultrafast` are outside the\n // neutral union and leave the field absent, so pricing falls back to the\n // tier asked.\n const served = response.service_tier;\n if (served === \"default\") usage.serviceTier = \"standard\";\n else if (served === \"flex\" || served === \"priority\") usage.serviceTier = served;\n return usage;\n}\n\nexport function mapIncompleteReason(response: OpenAI.Responses.Response): StopReason {\n const reason = response.incomplete_details?.reason;\n switch (reason) {\n case \"max_output_tokens\":\n // NOTE (spec 003 decision): OpenAI's wire conflates context-window\n // exhaustion into this reason (or a 400 before the stream), so the\n // neutral \"context_window_exceeded\" is unreachable on this adapter —\n // the long-context policy must watch provider errors instead.\n return \"max_tokens\";\n case \"content_filter\":\n return \"refusal\";\n default:\n throw new OpenAITranslationError(\n `Unmapped OpenAI incomplete reason ${JSON.stringify(reason)} — provider drift?`,\n \"provider_drift\",\n );\n }\n}\n","import OpenAI, { toFile } from \"openai\";\nimport type { Block, JobHandle, JobItem, JobOutput, JobProgress, JobResult, ModelJobClient } from \"@alma-harness/core\";\n\nimport { mapIncompleteReason, OpenAITranslationError, toOpenAIParams, usageOf } from \"./translate\";\n\n/**\n * `ModelJobClient` over the OpenAI Batch API — spec: model-jobs. Items become\n * one JSONL file of `/v1/responses` requests, uploaded with purpose `batch`;\n * results come back as a JSONL file of complete `Response` objects, each\n * translated here into the neutral output the runner prices and returns.\n */\n\n/** One line of the batch input file. */\nexport interface BatchLine {\n custom_id: string;\n method: \"POST\";\n url: \"/v1/responses\";\n body: OpenAI.Responses.ResponseCreateParamsNonStreaming;\n}\n\nexport function toBatchLines(items: readonly JobItem[]): BatchLine[] {\n return items.map((item) => {\n const { serviceTier: _tier, ...req } = item.request;\n void _tier;\n const { stream: _stream, ...body } = toOpenAIParams(req);\n void _stream;\n return { custom_id: item.id, method: \"POST\", url: \"/v1/responses\", body: body as OpenAI.Responses.ResponseCreateParamsNonStreaming };\n });\n}\n\n/** A complete response → neutral blocks, usage and stop — the non-streaming half of spec 003. */\nexport function translateResponse(response: OpenAI.Responses.Response): JobOutput {\n const blocks: Block[] = [];\n let sawToolCall = false;\n let sawRefusal = false;\n for (const item of response.output) {\n switch (item.type) {\n case \"message\":\n for (const part of item.content) {\n if (part.type === \"output_text\") blocks.push({ type: \"text\", text: part.text });\n else if (part.type === \"refusal\") {\n sawRefusal = true;\n blocks.push({ type: \"text\", text: part.refusal });\n }\n }\n break;\n case \"function_call\":\n sawToolCall = true;\n blocks.push({\n type: \"tool_call\",\n id: item.call_id,\n name: item.name,\n input: item.arguments === \"\" ? {} : (JSON.parse(item.arguments) as unknown),\n });\n break;\n case \"reasoning\": {\n if (typeof item.encrypted_content !== \"string\") break;\n const text = [...(item.content ?? []).map((c) => c.text), ...item.summary.map((s) => s.text)].filter((t) => t !== \"\").join(\"\\n\");\n blocks.push({\n type: \"reasoning\",\n provider: \"openai\",\n ...(text !== \"\" ? { text } : {}),\n opaque: { id: item.id, summary: item.summary, encrypted_content: item.encrypted_content },\n });\n break;\n }\n default:\n throw new OpenAITranslationError(`Unmapped OpenAI output item ${JSON.stringify(item.type)} — provider drift?`, \"provider_drift\");\n }\n }\n const stop =\n response.incomplete_details?.reason !== undefined && response.incomplete_details?.reason !== null\n ? mapIncompleteReason(response)\n : sawRefusal\n ? \"refusal\"\n : sawToolCall\n ? \"tool_use\"\n : \"end_turn\";\n return { blocks, usage: usageOf(response), stop };\n}\n\nexport interface OpenAIJobClientOptions {\n apiKey?: string;\n baseURL?: string;\n}\n\ninterface OutputLine {\n custom_id: string;\n response?: { status_code: number; body: OpenAI.Responses.Response | { error?: { message?: string } } } | null;\n error?: { message?: string } | null;\n}\n\nexport class OpenAIJobClient implements ModelJobClient {\n readonly #client: OpenAI;\n\n constructor(opts: OpenAIJobClientOptions = {}) {\n const init: ConstructorParameters<typeof OpenAI>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new OpenAI(init);\n }\n\n async submit(items: readonly JobItem[]): Promise<JobHandle> {\n const first = items[0];\n if (!first) throw new OpenAITranslationError(\"a batch needs at least one item\");\n const jsonl = toBatchLines(items).map((line) => JSON.stringify(line)).join(\"\\n\") + \"\\n\";\n const file = await this.#client.files.create({\n file: await toFile(Buffer.from(jsonl, \"utf8\"), \"alma-batch.jsonl\", { type: \"application/jsonl\" }),\n purpose: \"batch\",\n });\n const batch = await this.#client.batches.create({\n input_file_id: file.id,\n endpoint: \"/v1/responses\",\n completion_window: \"24h\",\n });\n return { provider: \"openai\", id: batch.id, model: first.request.model };\n }\n\n async status(handle: JobHandle): Promise<JobProgress> {\n const batch = await this.#client.batches.retrieve(handle.id);\n const status =\n batch.status === \"validating\"\n ? \"queued\"\n : batch.status === \"in_progress\" || batch.status === \"finalizing\"\n ? \"running\"\n : batch.status === \"completed\"\n ? \"done\"\n : batch.status === \"failed\"\n ? \"failed\"\n : batch.status === \"expired\"\n ? \"expired\"\n : \"cancelled\";\n const c = batch.request_counts;\n return {\n status,\n ...(c ? { counts: { total: c.total, done: c.completed, failed: c.failed } } : {}),\n };\n }\n\n async *results(handle: JobHandle): AsyncIterable<JobResult> {\n const batch = await this.#client.batches.retrieve(handle.id);\n for (const fileId of [batch.output_file_id, batch.error_file_id]) {\n if (!fileId) continue;\n const text = await (await this.#client.files.content(fileId)).text();\n for (const raw of text.split(\"\\n\")) {\n if (raw.trim() === \"\") continue;\n const line = JSON.parse(raw) as OutputLine;\n const body = line.response?.body;\n if (line.response && line.response.status_code >= 200 && line.response.status_code < 300 && body && \"output\" in body) {\n yield { id: line.custom_id, outcome: \"succeeded\", output: translateResponse(body) };\n } else {\n const error =\n line.error?.message ?? (body && \"error\" in body ? body.error?.message : undefined) ?? `status ${line.response?.status_code ?? \"unknown\"}`;\n yield { id: line.custom_id, outcome: \"errored\", error };\n }\n }\n }\n }\n\n async cancel(handle: JobHandle): Promise<void> {\n await this.#client.batches.cancel(handle.id);\n }\n}\n","import OpenAI from \"openai\";\nimport type { ModelClient, ModelEvent, ModelRequest } from \"@alma-harness/core\";\n\nimport { toProviderError } from \"../errors\";\nimport {\n toOpenRouterParams,\n translateOpenRouterStream,\n type OpenRouterParams,\n type OpenRouterRouting,\n} from \"./translate\";\n\nexport interface OpenRouterModelClientOptions extends OpenRouterRouting {\n /** Omit to read OPENROUTER_API_KEY (the OpenAI SDK's own env resolution\n * would read OPENAI_API_KEY — the wrong credential for this gateway). */\n apiKey?: string;\n /** Defaults to OpenRouter's public endpoint. */\n baseURL?: string;\n}\n\nconst OPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\";\n\n/**\n * `ModelClient` adapter for OpenRouter — §6.2, spec 014 (the gateway bridge).\n * Same thin-shell shape as the first-party adapters: translation lives in\n * ./translate (pure); this class owns the SDK client and the DECLARED\n * upstream policy. A gateway erases \"who processed this data?\" unless the\n * chain is declared beforehand, so construction REFUSES to proceed without a\n * non-empty upstream allowlist — deciding after the fact enforces nothing.\n */\nexport class OpenRouterModelClient implements ModelClient {\n readonly #client: OpenAI;\n readonly #routing: OpenRouterRouting;\n\n constructor(opts: OpenRouterModelClientOptions) {\n if (!Array.isArray(opts.upstreams) || opts.upstreams.length === 0) {\n throw new Error(\n \"OpenRouterModelClient requires a non-empty `upstreams` allowlist — \" +\n \"for a gateway, the data-processing chain must be a declared fact (spec 014)\",\n );\n }\n const routing: OpenRouterRouting = { upstreams: [...opts.upstreams] };\n if (opts.allowFallbacks !== undefined) routing.allowFallbacks = opts.allowFallbacks;\n if (opts.dataCollection !== undefined) routing.dataCollection = opts.dataCollection;\n if (opts.zeroDataRetention !== undefined) routing.zeroDataRetention = opts.zeroDataRetention;\n this.#routing = routing;\n\n const apiKey = opts.apiKey ?? process.env[\"OPENROUTER_API_KEY\"];\n if (apiKey === undefined || apiKey === \"\") {\n throw new Error(\n \"OpenRouterModelClient needs an API key: pass `apiKey` or set OPENROUTER_API_KEY\",\n );\n }\n this.#client = new OpenAI({ apiKey, baseURL: opts.baseURL ?? OPENROUTER_BASE_URL });\n }\n\n stream(req: ModelRequest, opts?: { signal?: AbortSignal }): AsyncIterable<ModelEvent> {\n // Translation throws synchronously (wrong provider, unsupported blocks)\n // before any network activity, like every adapter in this package.\n const params = toOpenRouterParams(req, this.#routing);\n return translateOpenRouterStream(this.#rawChunks(params, opts?.signal));\n }\n\n async *#rawChunks(\n params: OpenRouterParams,\n signal?: AbortSignal,\n ): AsyncGenerator<OpenAI.Chat.Completions.ChatCompletionChunk> {\n try {\n const stream = await this.#client.chat.completions.create(\n params,\n signal !== undefined ? { signal } : undefined,\n );\n for await (const chunk of stream) yield chunk;\n } catch (err) {\n // What the SDK throws leaves as a `ProviderError` naming the GATEWAY\n // (spec: error-taxonomy): the upstream that actually failed is inside\n // the body, and the routing policy already knows the declared chain.\n throw toProviderError(\"openrouter\", err);\n }\n }\n}\n","import type OpenAI from \"openai\";\n\nimport {\n ProviderError,\n type Block,\n type ModelEvent,\n type ModelRequest,\n type Msg,\n type ProviderFailureKind,\n type StopReason,\n type SystemBlock,\n type ToolSpec,\n type Usage,\n} from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\n\n/**\n * Pure translation between Alma's neutral vocabulary (§6.2) and OpenRouter's\n * chat-completions wire — spec 014. A SECOND translator over the neutral\n * format, not a `baseURL` override: the first-party OpenAI adapter speaks the\n * Responses API, and nothing in its translation is reusable here.\n * Side-effect-free; testable without a network.\n */\n\n/** A `ProviderError` since spec: error-taxonomy — see `AnthropicTranslationError`. */\nexport class OpenRouterTranslationError extends ProviderError {\n constructor(message: string, kind: ProviderFailureKind = \"rejected\") {\n super(\"openrouter\", kind, message);\n this.name = \"OpenRouterTranslationError\";\n }\n}\n\n/**\n * The upstream routing policy — spec 014's core constraint: for a gateway,\n * \"who processed this data?\" must be a DECLARED fact, not an observed one.\n */\nexport interface OpenRouterRouting {\n /**\n * OpenRouter provider slugs allowed to serve requests (→ `provider.only`).\n * REQUIRED and non-empty: without it the data-processing chain has no\n * answer, so the client refuses to construct.\n */\n upstreams: readonly string[];\n /**\n * Default false: a fallback is a silent change of data processor. Enabling\n * it is a stated product choice, never a default.\n */\n allowFallbacks?: boolean;\n /**\n * Default \"deny\" — OpenRouter defaults to \"allow\", and a harness whose\n * README says \"operating on sensitive data\" must not inherit the permissive\n * default (spec 014 DECISION).\n */\n dataCollection?: \"allow\" | \"deny\";\n /** Opt-in pass-through to OpenRouter's zero-data-retention guarantee. */\n zeroDataRetention?: boolean;\n}\n\n/** The `provider` routing block OpenRouter accepts on every request. */\ninterface OpenRouterProviderBlock {\n only: string[];\n allow_fallbacks: boolean;\n data_collection: \"allow\" | \"deny\";\n zdr?: boolean;\n require_parameters?: boolean;\n}\n\n/** OpenRouter's reasoning block — a gateway extension the OpenAI SDK does not type. */\ninterface OpenRouterReasoningBlock {\n effort?: \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n enabled?: boolean;\n}\n\nexport type OpenRouterParams = OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming & {\n provider: OpenRouterProviderBlock;\n reasoning?: OpenRouterReasoningBlock;\n};\n\nexport function toOpenRouterParams(req: ModelRequest, routing: OpenRouterRouting): OpenRouterParams {\n if (req.model.provider !== \"openrouter\") {\n throw new OpenRouterTranslationError(\n `OpenRouterModelClient received a request for provider ${JSON.stringify(req.model.provider)}`,\n );\n }\n const provider: OpenRouterProviderBlock = {\n only: [...routing.upstreams],\n allow_fallbacks: routing.allowFallbacks ?? false,\n data_collection: routing.dataCollection ?? \"deny\",\n };\n if (routing.zeroDataRetention === true) provider.zdr = true;\n if (req.tools.length > 0) {\n // Not cosmetic (spec 014): tool support varies by upstream, and without\n // this a model can receive the request WITHOUT the tools it was supposed\n // to have — a silent capability failure for a loop whose control flow IS\n // tool calls.\n provider.require_parameters = true;\n }\n\n // Reasoning — spec: reasoning-blocks. The gateway normalizes effort for its\n // upstreams; `max` collapses to `xhigh`, its top rung. Own blocks are\n // replayed as `reasoning_details` only when reasoning is on for THIS request.\n const reasoning = req.reasoning;\n const replay = reasoning !== undefined && reasoning.effort !== \"none\";\n const params: OpenRouterParams = {\n model: req.model.id,\n // The SDK deprecates this in favor of the OpenAI-specific\n // max_completion_tokens; for a gateway fronting heterogeneous upstreams,\n // max_tokens is the common denominator OpenRouter documents.\n max_tokens: req.maxTokens,\n stream: true,\n // Review amendment (spec 014): streamed chat completions only carry usage\n // when asked — without this the BudgetGuard never sees a usage event.\n stream_options: { include_usage: true },\n messages: [...systemMessages(req.system), ...req.messages.flatMap((m) => toWireMessages(m, replay))],\n provider,\n };\n if (reasoning !== undefined) {\n params.reasoning = replay\n ? { effort: reasoning.effort === \"max\" ? \"xhigh\" : reasoning.effort }\n : { enabled: false };\n }\n // Service tier — spec: pricing-tiers. The gateway prices by upstream, not\n // by tier; anything but standard is refused before the network.\n if (req.serviceTier !== undefined && req.serviceTier !== \"standard\") {\n throw new OpenRouterTranslationError(\n `the OpenRouter adapter cannot serve the ${req.serviceTier} tier — the gateway prices by upstream`,\n );\n }\n // Provider-executed tools have no neutral form on the gateway (spec: provider-tools).\n if ((req.providerTools ?? []).length > 0) {\n throw new OpenRouterTranslationError(\"the OpenRouter adapter cannot declare provider-executed tools — the gateway has no neutral web search\");\n }\n if (req.tools.length > 0) params.tools = req.tools.map(toTool);\n return params;\n}\n\n/**\n * Chat completions has no instructions field; system blocks become one system\n * message. The §6.9 stable/volatile discipline is preserved by keeping the\n * blocks' order — stable first — exactly as the OpenAI adapter does; whether\n * an upstream caches the prefix varies (spec 014's cache-hygiene note).\n */\nfunction systemMessages(\n blocks: SystemBlock[],\n): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {\n if (blocks.length === 0) return [];\n return [{ role: \"system\", content: blocks.map((b) => b.text).join(\"\\n\\n\") }];\n}\n\nfunction toWireMessages(msg: Msg, replayReasoning: boolean): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {\n switch (msg.role) {\n case \"user\":\n return [{ role: \"user\", content: msg.blocks.map(toUserContentPart) }];\n case \"assistant\":\n return [toAssistantMessage(msg.blocks, replayReasoning)];\n case \"tool\":\n // Chat completions wants one tool-role message PER result, keyed by\n // tool_call_id — unlike Anthropic's single user message of results.\n return msg.blocks.map(toToolMessage);\n }\n}\n\nfunction toUserContentPart(block: Block): OpenAI.Chat.Completions.ChatCompletionContentPart {\n switch (block.type) {\n case \"text\":\n return { type: \"text\", text: block.text };\n case \"media\":\n if (block.kind === \"image\") {\n return { type: \"image_url\", image_url: { url: block.ref.uri } };\n }\n // Document/audio support varies wildly by upstream and the wire shapes\n // are provider-specific — fail loudly (the routing policy is the layer\n // that picks capable providers, spec 003's precedent).\n throw new OpenRouterTranslationError(\n `${block.kind} media is not supported by the OpenRouter adapter`,\n );\n default:\n throw new OpenRouterTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a user message`,\n );\n }\n}\n\nfunction toAssistantMessage(\n blocks: Block[],\n replayReasoning: boolean,\n): OpenAI.Chat.Completions.ChatCompletionMessageParam {\n let text = \"\";\n const toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] = [];\n const details: unknown[] = [];\n for (const block of blocks) {\n switch (block.type) {\n case \"text\":\n text += block.text;\n break;\n case \"tool_call\":\n toolCalls.push({\n id: block.id,\n type: \"function\",\n function: { name: block.name, arguments: JSON.stringify(block.input) },\n });\n break;\n case \"reasoning\": {\n // Own `reasoning_details` go back when reasoning is on for this\n // request; another provider's, or any when it is off, are skipped\n // (spec: reasoning-blocks). A block with text but no details cannot\n // be replayed and is skipped too.\n const opaque = block.opaque as { reasoning_details?: unknown } | undefined;\n if (block.provider === \"openrouter\" && replayReasoning && Array.isArray(opaque?.reasoning_details)) {\n details.push(...opaque.reasoning_details);\n }\n break;\n }\n case \"provider_tool_call\":\n case \"provider_tool_result\":\n // Another provider's search (spec: provider-tools): nothing this wire can carry; skipped.\n break;\n default:\n throw new OpenRouterTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in an assistant message`,\n );\n }\n }\n const message: OpenAI.Chat.Completions.ChatCompletionAssistantMessageParam & {\n reasoning_details?: unknown[];\n } = {\n role: \"assistant\",\n content: text === \"\" ? null : text,\n };\n if (toolCalls.length > 0) message.tool_calls = toolCalls;\n if (details.length > 0) message.reasoning_details = details;\n return message;\n}\n\nfunction toToolMessage(block: Block): OpenAI.Chat.Completions.ChatCompletionMessageParam {\n if (block.type !== \"tool_result\") {\n throw new OpenRouterTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a tool message`,\n );\n }\n const serialized =\n typeof block.output === \"string\" ? block.output : (JSON.stringify(block.output) ?? \"null\");\n return {\n role: \"tool\",\n tool_call_id: block.callId,\n // No is_error flag on this wire either — the explicit prefix keeps\n // failures visible to the model (spec 003's precedent).\n content: block.isError ? `ERROR: ${serialized}` : serialized,\n };\n}\n\nfunction toTool(spec: ToolSpec): OpenAI.Chat.Completions.ChatCompletionTool {\n return {\n type: \"function\",\n function: {\n name: spec.name,\n description: spec.description,\n parameters: spec.inputSchema,\n },\n };\n}\n\n/**\n * Chat-completion chunks → neutral `ModelEvent`s. Tool-call arguments arrive\n * as indexed fragments (id and name on the first fragment, argument pieces on\n * the rest); they are accumulated per index and emitted as ONE parsed\n * `tool_call` each when the choice finishes. The usage chunk arrives AFTER\n * `finish_reason` (with `stream_options.include_usage`), so the terminal\n * usage + stop pair is emitted at stream end — same order every adapter emits.\n */\nexport async function* translateOpenRouterStream(\n chunks: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncGenerator<ModelEvent> {\n const pendingTools = new Map<number, { id: string; name: string; json: string }>();\n let finish: string | null = null;\n let usage: Usage | null = null;\n let toolsEmitted = false;\n // Reasoning is a gateway extension on the delta (`reasoning` text and\n // `reasoning_details`), untyped by the SDK; accumulated and emitted as ONE\n // block when the choice finishes, ahead of the tool calls (spec:\n // reasoning-blocks).\n let reasoningText = \"\";\n const reasoningDetails: unknown[] = [];\n let reasoningEmitted = false;\n\n for await (const chunk of chunks) {\n const choice = chunk.choices[0];\n if (choice) {\n const extra = choice.delta as { reasoning?: string | null; reasoning_details?: unknown[] | null };\n if (typeof extra.reasoning === \"string\") reasoningText += extra.reasoning;\n if (Array.isArray(extra.reasoning_details)) reasoningDetails.push(...extra.reasoning_details);\n if (choice.delta.content != null && choice.delta.content !== \"\") {\n yield { type: \"text_delta\", text: choice.delta.content };\n }\n for (const fragment of choice.delta.tool_calls ?? []) {\n const pending = pendingTools.get(fragment.index) ?? { id: \"\", name: \"\", json: \"\" };\n if (fragment.id != null) pending.id = fragment.id;\n if (fragment.function?.name != null) pending.name = fragment.function.name;\n if (fragment.function?.arguments != null) pending.json += fragment.function.arguments;\n pendingTools.set(fragment.index, pending);\n }\n if (choice.finish_reason != null) {\n finish = choice.finish_reason;\n if (!reasoningEmitted && (reasoningText !== \"\" || reasoningDetails.length > 0)) {\n reasoningEmitted = true;\n yield {\n type: \"reasoning\",\n block: {\n type: \"reasoning\",\n provider: \"openrouter\",\n ...(reasoningText !== \"\" ? { text: reasoningText } : {}),\n ...(reasoningDetails.length > 0 ? { opaque: { reasoning_details: reasoningDetails } } : {}),\n },\n };\n }\n if (!toolsEmitted) {\n toolsEmitted = true;\n for (const [, call] of [...pendingTools.entries()].sort(([a], [b]) => a - b)) {\n yield { type: \"tool_call\", id: call.id, name: call.name, ...parseToolArguments(call.json) };\n }\n pendingTools.clear();\n }\n }\n }\n if (chunk.usage != null) {\n const cachedRead = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;\n usage = {\n // Neutral semantics (spec 005): inputTokens EXCLUDES cache reads —\n // the wire's prompt_tokens includes them.\n inputTokens: Math.max(0, chunk.usage.prompt_tokens - cachedRead),\n outputTokens: chunk.usage.completion_tokens,\n };\n if (cachedRead > 0) usage.cacheReadInputTokens = cachedRead;\n // No cache-write signal exists on this wire; the field stays absent.\n const reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens;\n if (reasoningTokens !== undefined && reasoningTokens > 0) usage.reasoningTokens = reasoningTokens;\n }\n }\n\n if (finish === null) {\n throw new OpenRouterTranslationError(\n \"OpenRouter stream ended without a finish_reason — provider drift?\",\n \"provider_drift\",\n );\n }\n if (usage !== null) yield { type: \"usage\", usage };\n yield { type: \"stop\", reason: mapFinishReason(finish) };\n}\n\nfunction mapFinishReason(reason: string): StopReason {\n switch (reason) {\n case \"stop\":\n return \"end_turn\";\n case \"tool_calls\":\n return \"tool_use\";\n case \"length\":\n return \"max_tokens\";\n case \"content_filter\":\n return \"refusal\";\n default:\n // \"error\", or a reason newer than this adapter: refuse loudly rather\n // than silently truncate. NOTE (spec 014 review amendment): context-\n // window overflow arrives as a 400 error before any stream, so the\n // neutral \"context_window_exceeded\" is unreachable on this wire — the\n // long-context policy must watch provider errors, as with the OpenAI\n // adapter.\n throw new OpenRouterTranslationError(\n `Unmapped OpenRouter finish_reason ${JSON.stringify(reason)} — provider drift?`,\n \"provider_drift\",\n );\n }\n}\n","import type { ProviderId } from \"@alma-harness/core\";\n\n/**\n * @alma-harness/providers — `ModelClient` adapters (§6.2).\n * Bi-provider from birth — `AnthropicModelClient` (spec 002) and\n * `OpenAIModelClient` (spec 003) — plus the OpenRouter gateway bridge\n * (spec 014), all over the same neutral surface.\n */\n\nexport { AnthropicModelClient } from \"./anthropic/client\";\nexport type { AnthropicModelClientOptions } from \"./anthropic/client\";\nexport {\n AnthropicTranslationError,\n toAnthropicParams,\n translateStream,\n} from \"./anthropic/translate\";\n\nexport { AnthropicJobClient, translateMessage } from \"./anthropic/jobs\";\nexport type { AnthropicJobClientOptions } from \"./anthropic/jobs\";\n\nexport { OpenAIModelClient } from \"./openai/client\";\nexport type { OpenAIModelClientOptions } from \"./openai/client\";\nexport {\n OpenAITranslationError,\n toOpenAIParams,\n translateOpenAIStream,\n} from \"./openai/translate\";\n\nexport { OpenAIJobClient, toBatchLines, translateResponse } from \"./openai/jobs\";\nexport type { BatchLine, OpenAIJobClientOptions } from \"./openai/jobs\";\n\nexport { OpenRouterModelClient } from \"./openrouter/client\";\nexport type { OpenRouterModelClientOptions } from \"./openrouter/client\";\nexport {\n OpenRouterTranslationError,\n toOpenRouterParams,\n translateOpenRouterStream,\n} from \"./openrouter/translate\";\nexport type { OpenRouterParams, OpenRouterRouting } from \"./openrouter/translate\";\n\n// What the SDKs throw → the neutral `ProviderError` — spec: error-taxonomy\nexport { classifyFailure, toProviderError } from \"./errors\";\n\nexport type { ModelClient, ModelEvent, ModelJobClient, ModelRef, ModelRequest, ProviderId } from \"@alma-harness/core\";\n\n/**\n * First-party from birth (§3 principle 4), plus the gateway bridge —\n * spec 014: `openrouter` names the EXIT, not the processor; its adapter\n * requires a declared upstream allowlist for exactly that reason.\n */\nexport const SUPPORTED_PROVIDERS = [\"anthropic\", \"openai\", \"openrouter\"] as const satisfies readonly ProviderId[];\n"],"mappings":"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","names":["ProviderError","ProviderError","Anthropic","Anthropic","ProviderError","ProviderError","toTool","OpenAI","OpenAI","OpenAI","ProviderError","ProviderError","toTool","toUserContentPart","OpenAI"]}
1
+ {"version":3,"sources":["../src/anthropic/client.ts","../src/errors.ts","../src/anthropic/translate.ts","../src/arguments.ts","../src/anthropic/jobs.ts","../src/openai/client.ts","../src/openai/translate.ts","../src/openai/jobs.ts","../src/openrouter/client.ts","../src/openrouter/translate.ts","../src/index.ts"],"sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelClient, ModelEvent, ModelRequest } from \"@alma-harness/core\";\n\nimport { toProviderError } from \"../errors\";\nimport { toAnthropicParams, translateStream } from \"./translate\";\n\nexport interface AnthropicModelClientOptions {\n /** Omit to use the SDK's environment resolution (ANTHROPIC_API_KEY, …). */\n apiKey?: string;\n baseURL?: string;\n}\n\n/**\n * `ModelClient` adapter for the Anthropic Messages API — §6.2, spec 002.\n * A thin shell: request/stream translation lives in ./translate (pure);\n * this class only owns the SDK client. What the SDK throws leaves as a\n * `ProviderError` (spec: error-taxonomy); a user abort passes through.\n */\nexport class AnthropicModelClient implements ModelClient {\n readonly #client: Anthropic;\n\n constructor(opts: AnthropicModelClientOptions = {}) {\n const init: ConstructorParameters<typeof Anthropic>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new Anthropic(init);\n }\n\n stream(req: ModelRequest, opts?: { signal?: AbortSignal }): AsyncIterable<ModelEvent> {\n // Translation throws synchronously (wrong provider, unsupported blocks)\n // before any network activity — spec 002 criterion 5.\n const params = toAnthropicParams(req);\n return translateStream(this.#rawEvents(params, opts?.signal));\n }\n\n async *#rawEvents(\n params: Anthropic.MessageCreateParamsStreaming,\n signal?: AbortSignal,\n ): AsyncGenerator<Anthropic.RawMessageStreamEvent> {\n try {\n const stream = await this.#client.messages.create(\n params,\n signal !== undefined ? { signal } : undefined,\n );\n for await (const event of stream) yield event;\n } catch (err) {\n throw toProviderError(\"anthropic\", err);\n }\n }\n}\n","import { ProviderError, type ProviderFailureKind, type ProviderId } from \"@alma-harness/core\";\n\n/**\n * What the SDKs throw → one neutral class — spec: error-taxonomy. DUCK-TYPED\n * rather than `instanceof` over two SDKs' error trees: both expose `status`,\n * `message` and the response body on `error`, the OpenAI one `code` and the\n * Anthropic one `type`; and the gateway speaks through the OpenAI SDK to\n * upstreams that shape their bodies differently. One table over the fields\n * they share is what stays true across all three.\n */\n\ninterface SdkErrorShape {\n name?: unknown;\n status?: unknown;\n code?: unknown;\n type?: unknown;\n message?: unknown;\n error?: unknown;\n}\n\nconst CONTEXT_WINDOW = /context_length_exceeded|prompt is too long|context window|maximum context length|too many tokens|exceeds the context|input length/i;\nconst OVERLOADED = /overloaded|too many requests|capacity|server_busy/i;\n\n/** Every string the body or the error carries as a code or a type, lowercased and joined. */\nfunction hintsOf(e: SdkErrorShape): string {\n const nested = (typeof e.error === \"object\" && e.error !== null ? e.error : {}) as SdkErrorShape;\n const inner = (typeof nested.error === \"object\" && nested.error !== null ? nested.error : {}) as SdkErrorShape;\n return [e.code, e.type, nested.code, nested.type, inner.code, inner.type, inner.message]\n .filter((h): h is string => typeof h === \"string\")\n .join(\" \")\n .toLowerCase();\n}\n\n/**\n * Wraps anything but a user abort. The abort passes through untouched: the\n * loop already reads its own signal, and a wrapped abort would classify a\n * cancellation as a provider failure.\n */\nexport function toProviderError(provider: ProviderId, err: unknown): unknown {\n if (err instanceof ProviderError) return err;\n const e = (typeof err === \"object\" && err !== null ? err : {}) as SdkErrorShape;\n // The SDKs do not always set `name`; the constructor's is the fallback.\n const own = typeof e.name === \"string\" && e.name !== \"Error\" ? e.name : \"\";\n const name = own !== \"\" ? own : ((err as { constructor?: { name?: unknown } } | null)?.constructor?.name as string | undefined) ?? \"\";\n if (name === \"APIUserAbortError\" || name === \"AbortError\") return err;\n const message = err instanceof Error ? err.message : String(err);\n const status = typeof e.status === \"number\" ? e.status : undefined;\n const kind = classifyFailure(name, status, `${hintsOf(e)} ${message.toLowerCase()}`);\n return new ProviderError(provider, kind, message, { ...(status !== undefined ? { status } : {}), cause: err });\n}\n\n/** The table. Exported for the one caller that has a code and no thrown error: a failure the wire REPORTED. */\nexport function classifyFailure(name: string, status: number | undefined, hints: string): ProviderFailureKind {\n if (status === 429 || /rate_limit/.test(hints)) return hints.includes(\"insufficient_quota\") ? \"rejected\" : \"rate_limited\";\n if (status === 529 || status === 503 || ((status === undefined || status >= 500) && OVERLOADED.test(hints))) return \"overloaded\";\n if (status !== undefined && status >= 500) return \"unavailable\";\n if ((status === undefined || status === 400) && CONTEXT_WINDOW.test(hints)) return \"context_window\";\n if (status !== undefined && status >= 400) return \"rejected\";\n // No status: the SDK could not reach the wire, the wire reported a server\n // failure, or the wire sent something the translation could not read.\n if (/connection|timeout|timed out|fetch|network|socket|econn|enotfound|server_error|internal|unavailable/i.test(`${name} ${hints}`)) {\n return \"unavailable\";\n }\n return \"provider_drift\";\n}\n","import type Anthropic from \"@anthropic-ai/sdk\";\n\nimport {\n ProviderError,\n type Block,\n type ModelEvent,\n type ModelRequest,\n type Msg,\n type ProviderFailureKind,\n type ProviderToolCallBlock,\n type ProviderToolKind,\n type ProviderToolResultBlock,\n type ProviderToolSpec,\n type ReasoningBlock,\n type ReasoningEffort,\n type StopReason,\n type SystemBlock,\n type ToolSpec,\n type Usage,\n} from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\n\n/**\n * Pure translation between Alma's neutral vocabulary (§6.2) and the Anthropic\n * Messages API — spec 002. Everything here is side-effect-free so the wire\n * mapping is testable without a network.\n */\n\n/**\n * Raised when a request or stream cannot be represented faithfully. A\n * `ProviderError` since spec: error-taxonomy — `rejected` before the network\n * (the request as built cannot be sent), `provider_drift` mid-stream (the\n * wire sent something this adapter does not know).\n */\nexport class AnthropicTranslationError extends ProviderError {\n constructor(message: string, kind: ProviderFailureKind = \"rejected\") {\n super(\"anthropic\", kind, message);\n this.name = \"AnthropicTranslationError\";\n }\n}\n\nexport function toAnthropicParams(req: ModelRequest): Anthropic.MessageCreateParamsStreaming {\n if (req.model.provider !== \"anthropic\") {\n throw new AnthropicTranslationError(\n `AnthropicModelClient received a request for provider ${JSON.stringify(req.model.provider)}`,\n );\n }\n // Reasoning — spec: reasoning-blocks. Absent sends nothing (the provider's\n // default, whose thinking is opt-in); \"none\" disables explicitly; anything\n // else asks for adaptive thinking at the mapped effort. Its own blocks are\n // replayed only when thinking is on for THIS request.\n const reasoning = req.reasoning;\n const replay = reasoning !== undefined && reasoning.effort !== \"none\";\n const params: Anthropic.MessageCreateParamsStreaming = {\n model: req.model.id,\n max_tokens: req.maxTokens,\n stream: true,\n system: toSystem(req.system),\n messages: req.messages.flatMap((m) => toMessageParams(m, replay, declaredKinds(req))),\n };\n if (reasoning !== undefined) {\n if (reasoning.effort === \"none\") {\n params.thinking = { type: \"disabled\" };\n } else {\n params.thinking = { type: \"adaptive\" };\n params.output_config = { effort: toAnthropicEffort(reasoning.effort as Exclude<ReasoningEffort, \"none\">) };\n }\n }\n // Service tier — spec: pricing-tiers. `priority` asks for priority capacity\n // when the account has it (\"auto\": standard otherwise, and the usage says\n // which served). Flex does not exist on this wire and batch is a job, not\n // a stream: both refused before the network, never downgraded in silence.\n switch (req.serviceTier) {\n case undefined:\n case \"standard\":\n break;\n case \"priority\":\n params.service_tier = \"auto\";\n break;\n default:\n throw new AnthropicTranslationError(\n `the Anthropic Messages API cannot serve the ${req.serviceTier} tier on a streaming request`,\n );\n }\n // Provider-executed tools ride in the same list as the registered ones (spec: provider-tools).\n const tools: Anthropic.ToolUnion[] = [...req.tools.map(toTool), ...(req.providerTools ?? []).map(toServerTool)];\n if (tools.length > 0) params.tools = tools;\n markConversationTail(params.messages);\n return params;\n}\n\n/** The neutral web search → the dated server tool; every option has a wire form here. */\n/**\n * The DIRECT search (spec: provider-tools): one `server_tool_use` per query.\n * `web_search_20260318` is the agentic search, which the model drives through\n * `code_execution` — encrypted stdout, nested calls — another kind, its own spec.\n */\nfunction toServerTool(spec: ProviderToolSpec): Anthropic.WebSearchTool20250305 {\n const tool: Anthropic.WebSearchTool20250305 = { type: \"web_search_20250305\", name: \"web_search\" };\n if (spec.maxUses !== undefined) tool.max_uses = spec.maxUses;\n if (spec.allowedDomains !== undefined) tool.allowed_domains = [...spec.allowedDomains];\n if (spec.blockedDomains !== undefined) tool.blocked_domains = [...spec.blockedDomains];\n return tool;\n}\n\n/** `minimal` collapses to `low`: the Messages API has no lower rung. */\nfunction toAnthropicEffort(\n effort: Exclude<ReasoningEffort, \"none\">,\n): NonNullable<Anthropic.OutputConfig[\"effort\"]> {\n return effort === \"minimal\" ? \"low\" : effort;\n}\n\n/**\n * Cache continuation — spec 005: a second breakpoint on the conversation\n * tail lets multi-turn sessions pay incremental tokens instead of re-reading\n * the whole history every turn. (Budget: 2 of the 4 allowed breakpoints —\n * one at the stable-system boundary, one here.)\n */\nfunction markConversationTail(messages: Anthropic.MessageParam[]): void {\n const last = messages.at(-1);\n if (!last || typeof last.content === \"string\") return;\n const block = last.content.at(-1);\n if (block) (block as { cache_control?: Anthropic.CacheControlEphemeral }).cache_control = { type: \"ephemeral\" };\n}\n\n/**\n * The §6.9 stable/volatile boundary becomes the cache boundary: one\n * `cache_control` breakpoint on the LAST stable block, volatile blocks after\n * it. Order is preserved — callers must already emit stable-first.\n */\nfunction toSystem(blocks: SystemBlock[]): Anthropic.TextBlockParam[] {\n const lastStable = blocks.reduce(\n (last, b, i) => (b.volatility === \"stable\" ? i : last),\n -1,\n );\n return blocks.map((b, i) => {\n const param: Anthropic.TextBlockParam = { type: \"text\", text: b.text };\n if (i === lastStable) param.cache_control = { type: \"ephemeral\" };\n return param;\n });\n}\n\n/** The provider-executed kinds THIS request declares: only their blocks are replayed (spec: close-060-064-findings). */\nfunction declaredKinds(req: ModelRequest): Set<ProviderToolKind> {\n return new Set((req.providerTools ?? []).map((t) => t.kind));\n}\n\nfunction toMessageParams(msg: Msg, replayReasoning: boolean, declared: Set<ProviderToolKind>): Anthropic.MessageParam[] {\n switch (msg.role) {\n case \"user\":\n return [{ role: \"user\", content: msg.blocks.map(toUserBlock) }];\n case \"assistant\": {\n const content = msg.blocks.flatMap((b) => toAssistantBlocks(b, replayReasoning, declared));\n // An assistant turn that held only another provider's reasoning has\n // nothing this wire can carry; an empty content array is a 400.\n return content.length === 0 ? [] : [{ role: \"assistant\", content }];\n }\n case \"tool\":\n // Anthropic's wire shape: tool results travel in a user-role message.\n return [{ role: \"user\", content: msg.blocks.map(toToolResultBlock) }];\n }\n}\n\nfunction toUserBlock(block: Block): Anthropic.ContentBlockParam {\n switch (block.type) {\n case \"text\":\n return { type: \"text\", text: block.text };\n case \"media\":\n switch (block.kind) {\n case \"image\":\n return { type: \"image\", source: { type: \"url\", url: block.ref.uri } };\n case \"document\":\n return { type: \"document\", source: { type: \"url\", url: block.ref.uri } };\n case \"audio\":\n // The Messages API takes no raw audio; products transcribe upstream.\n throw new AnthropicTranslationError(\"audio media is not supported by the Anthropic adapter\");\n }\n default:\n throw new AnthropicTranslationError(`block type ${JSON.stringify(block.type)} is not valid in a user message`);\n }\n}\n\nfunction toAssistantBlocks(block: Block, replayReasoning: boolean, declared: Set<ProviderToolKind>): Anthropic.ContentBlockParam[] {\n switch (block.type) {\n case \"text\":\n return [{ type: \"text\", text: block.text }];\n case \"tool_call\":\n return [{ type: \"tool_use\", id: block.id, name: block.name, input: block.input }];\n case \"reasoning\":\n // Own blocks go back UNMODIFIED and in order when thinking is on for\n // this request; another provider's, or any block when thinking is off,\n // are skipped without error (spec: reasoning-blocks).\n return block.provider === \"anthropic\" && replayReasoning ? [toThinkingParam(block)] : [];\n case \"provider_tool_call\":\n // Own server-tool blocks go back as they came (spec: provider-tools) — and only while the\n // request declares the tool: the API refuses a server_tool_use for a tool it was not given,\n // which is what a capped step looks like (spec: close-060-064-findings).\n return block.provider === \"anthropic\" && declared.has(block.name) ? [{ type: \"server_tool_use\", id: block.id, name: block.name, input: block.input }] : [];\n case \"provider_tool_result\":\n return block.provider === \"anthropic\" && declared.has(block.name) ? [toWebSearchResultParam(block)] : [];\n default:\n throw new AnthropicTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in an assistant message`,\n );\n }\n}\n\n/** The opaque half this adapter wrote: the encrypted results, whole, or the error. */\nfunction toWebSearchResultParam(block: ProviderToolResultBlock): Anthropic.WebSearchToolResultBlockParam {\n const opaque = block.opaque as { content?: unknown } | undefined;\n if (opaque?.content === undefined) {\n throw new AnthropicTranslationError(\"an Anthropic web search result carries no replayable content\");\n }\n return { type: \"web_search_tool_result\", tool_use_id: block.callId, content: opaque.content as Anthropic.WebSearchToolResultBlockParamContent };\n}\n\n/** The opaque half this adapter wrote: a signature, or a redacted block's data. */\nfunction toThinkingParam(block: ReasoningBlock): Anthropic.ContentBlockParam {\n const opaque = block.opaque as { signature?: unknown; redacted?: unknown } | undefined;\n if (typeof opaque?.redacted === \"string\") {\n return { type: \"redacted_thinking\", data: opaque.redacted };\n }\n if (typeof opaque?.signature === \"string\") {\n return { type: \"thinking\", thinking: block.text ?? \"\", signature: opaque.signature };\n }\n // Refused here rather than as a 400 from the provider: a block this\n // adapter did not write in this shape cannot be verified there either.\n throw new AnthropicTranslationError(\"an Anthropic reasoning block carries neither a signature nor redacted data\");\n}\n\nfunction toToolResultBlock(block: Block): Anthropic.ToolResultBlockParam {\n if (block.type !== \"tool_result\") {\n throw new AnthropicTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a tool message`,\n );\n }\n const param: Anthropic.ToolResultBlockParam = {\n type: \"tool_result\",\n tool_use_id: block.callId,\n // JSON.stringify yields undefined (not a string) for undefined/functions;\n // \"null\" keeps the content explicit for void tool outputs.\n content:\n typeof block.output === \"string\" ? block.output : (JSON.stringify(block.output) ?? \"null\"),\n };\n if (block.isError) param.is_error = true;\n return param;\n}\n\nfunction toTool(spec: ToolSpec): Anthropic.Tool {\n return {\n name: spec.name,\n description: spec.description,\n // The registry already derived a JSON Schema object (§6.4).\n input_schema: spec.inputSchema as Anthropic.Tool.InputSchema,\n };\n}\n\n/**\n * Raw SDK stream → neutral `ModelEvent`s. Tool inputs arrive as\n * `input_json_delta` fragments; they are accumulated per content block and\n * emitted as ONE `tool_call` with parsed input at `content_block_stop`.\n * Usage is aggregated (input + cache fields from `message_start`, output from\n * `message_delta`) and emitted once before the final `stop`.\n */\nexport async function* translateStream(\n events: AsyncIterable<Anthropic.RawMessageStreamEvent>,\n): AsyncGenerator<ModelEvent> {\n const usage: Usage = { inputTokens: 0, outputTokens: 0 };\n let stopReason: Anthropic.Message[\"stop_reason\"] = null;\n const pendingTools = new Map<number, { id: string; name: string; json: string }>();\n // Thinking arrives as text deltas plus one signature delta, redacted\n // thinking as opaque data on the block start; both are emitted as ONE\n // complete reasoning block at content_block_stop (spec: reasoning-blocks).\n const pendingThinking = new Map<number, { text: string; signature: string }>();\n const pendingRedacted = new Map<number, string>();\n // A server tool's call arrives like a tool use — input as json deltas — and its\n // result arrives whole on the block start (spec: provider-tools).\n const pendingServer = new Map<number, { id: string; name: ProviderToolCallBlock[\"name\"]; json: string }>();\n\n for await (const event of events) {\n switch (event.type) {\n case \"message_start\": {\n const u = event.message.usage;\n usage.inputTokens = u.input_tokens;\n if (u.cache_read_input_tokens != null) usage.cacheReadInputTokens = u.cache_read_input_tokens;\n if (u.cache_creation_input_tokens != null) {\n usage.cacheWriteInputTokens = u.cache_creation_input_tokens;\n }\n if (u.server_tool_use?.web_search_requests) usage.webSearchRequests = u.server_tool_use.web_search_requests;\n // The tier that SERVED, off the wire (spec: pricing-tiers) — priced\n // over the tier asked, since \"auto\" may land on standard.\n if (u.service_tier === \"standard\" || u.service_tier === \"priority\" || u.service_tier === \"batch\") {\n usage.serviceTier = u.service_tier;\n }\n break;\n }\n case \"content_block_start\":\n if (event.content_block.type === \"tool_use\") {\n pendingTools.set(event.index, {\n id: event.content_block.id,\n name: event.content_block.name,\n json: \"\",\n });\n } else if (event.content_block.type === \"thinking\") {\n pendingThinking.set(event.index, { text: event.content_block.thinking, signature: event.content_block.signature });\n } else if (event.content_block.type === \"redacted_thinking\") {\n pendingRedacted.set(event.index, event.content_block.data);\n } else if (event.content_block.type === \"server_tool_use\") {\n if (event.content_block.name !== \"web_search\") {\n throw new AnthropicTranslationError(`Unmapped Anthropic server tool ${JSON.stringify(event.content_block.name)} — provider drift?`, \"provider_drift\");\n }\n pendingServer.set(event.index, { id: event.content_block.id, name: \"web_search\", json: \"\" });\n } else if (event.content_block.type === \"web_search_tool_result\") {\n yield { type: \"provider_tool_result\", block: toProviderToolResult(event.content_block) };\n }\n break;\n case \"content_block_delta\":\n if (event.delta.type === \"text_delta\") {\n yield { type: \"text_delta\", text: event.delta.text };\n } else if (event.delta.type === \"input_json_delta\") {\n const pending = pendingTools.get(event.index) ?? pendingServer.get(event.index);\n if (pending) pending.json += event.delta.partial_json;\n } else if (event.delta.type === \"thinking_delta\") {\n const pending = pendingThinking.get(event.index);\n if (pending) pending.text += event.delta.thinking;\n } else if (event.delta.type === \"signature_delta\") {\n const pending = pendingThinking.get(event.index);\n if (pending) pending.signature = event.delta.signature;\n }\n break;\n case \"content_block_stop\": {\n const pending = pendingTools.get(event.index);\n if (pending) {\n pendingTools.delete(event.index);\n // Arguments that do not parse are the loop's to answer, and the\n // stop reason that follows says whether the wire cut them.\n yield { type: \"tool_call\", id: pending.id, name: pending.name, ...parseToolArguments(pending.json) };\n }\n const thinking = pendingThinking.get(event.index);\n if (thinking) {\n pendingThinking.delete(event.index);\n yield {\n type: \"reasoning\",\n block: { type: \"reasoning\", provider: \"anthropic\", text: thinking.text, opaque: { signature: thinking.signature } },\n };\n }\n const redacted = pendingRedacted.get(event.index);\n if (redacted !== undefined) {\n pendingRedacted.delete(event.index);\n yield { type: \"reasoning\", block: { type: \"reasoning\", provider: \"anthropic\", opaque: { redacted } } };\n }\n const server = pendingServer.get(event.index);\n if (server) {\n pendingServer.delete(event.index);\n yield {\n type: \"provider_tool_call\",\n block: { type: \"provider_tool_call\", id: server.id, name: server.name, provider: \"anthropic\", input: parseToolArguments(server.json).input },\n };\n }\n break;\n }\n case \"message_delta\":\n if (event.delta.stop_reason != null) stopReason = event.delta.stop_reason;\n usage.outputTokens = event.usage.output_tokens;\n if (event.usage.server_tool_use?.web_search_requests) usage.webSearchRequests = event.usage.server_tool_use.web_search_requests;\n break;\n case \"message_stop\":\n yield { type: \"usage\", usage: { ...usage } };\n yield { type: \"stop\", reason: mapStopReason(stopReason) };\n break;\n }\n }\n}\n\n/** A web search result block → the neutral result: the citations, the error, and the content whole for replay. */\nfunction toProviderToolResult(block: Anthropic.WebSearchToolResultBlock): ProviderToolResultBlock {\n const result: ProviderToolResultBlock = {\n type: \"provider_tool_result\",\n callId: block.tool_use_id,\n name: \"web_search\",\n provider: \"anthropic\",\n results: [],\n opaque: { content: block.content },\n };\n if (Array.isArray(block.content)) {\n result.results = block.content.map((r) => ({\n url: r.url,\n ...(r.title !== undefined ? { title: r.title } : {}),\n ...(r.page_age != null ? { pageAge: r.page_age } : {}),\n }));\n } else {\n result.error = block.content.error_code;\n }\n return result;\n}\n\nexport function mapStopReason(reason: Anthropic.Message[\"stop_reason\"]): StopReason {\n switch (reason) {\n case \"end_turn\":\n case \"stop_sequence\":\n return \"end_turn\";\n case \"tool_use\":\n return \"tool_use\";\n case \"max_tokens\":\n return \"max_tokens\";\n case \"refusal\":\n return \"refusal\";\n case \"model_context_window_exceeded\":\n return \"context_window_exceeded\";\n case \"pause_turn\":\n // A long-running server tool paused; the loop re-sends to continue (spec: provider-tools).\n return \"pause\";\n default:\n // A reason newer than this adapter: refuse loudly rather than silently truncate.\n throw new AnthropicTranslationError(\n `Unmapped Anthropic stop_reason ${JSON.stringify(reason)} — provider drift?`,\n \"provider_drift\",\n );\n }\n}\n","/**\n * Tool arguments off the wire — spec: what-the-wire-cuts. Every adapter\n * accumulates them as text and parsed them with a bare `JSON.parse`, so a\n * response cut by `max_tokens` mid-arguments threw from inside the stream\n * and the loop never saw the stop reason that explained it. A call whose\n * arguments do not parse is still a call: `input: {}` and the raw text as\n * `malformed`, for the loop to answer.\n */\nexport function parseToolArguments(json: string): { input: unknown; malformed?: string } {\n if (json === \"\") return { input: {} };\n try {\n return { input: JSON.parse(json) as unknown };\n } catch {\n return { input: {}, malformed: json };\n }\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Block, JobHandle, JobItem, JobOutput, JobProgress, JobResult, ModelJobClient, Usage } from \"@alma-harness/core\";\n\nimport { AnthropicTranslationError, mapStopReason, toAnthropicParams } from \"./translate\";\n\n/**\n * `ModelJobClient` over the Anthropic Message Batches API — spec: model-jobs.\n * Each item's request is translated with the same function the stream uses,\n * minus `stream`; a succeeded result is a complete `Message`, translated\n * here into the neutral output the runner prices and returns.\n */\n\n/** A complete message → neutral blocks, usage and stop — the non-streaming half of spec 002. */\nexport function translateMessage(message: Anthropic.Message): JobOutput {\n const blocks: Block[] = [];\n for (const block of message.content) {\n switch (block.type) {\n case \"text\":\n blocks.push({ type: \"text\", text: block.text });\n break;\n case \"tool_use\":\n blocks.push({ type: \"tool_call\", id: block.id, name: block.name, input: block.input });\n break;\n case \"thinking\":\n blocks.push({ type: \"reasoning\", provider: \"anthropic\", text: block.thinking, opaque: { signature: block.signature } });\n break;\n case \"redacted_thinking\":\n blocks.push({ type: \"reasoning\", provider: \"anthropic\", opaque: { redacted: block.data } });\n break;\n default:\n // A job declares no tools, provider-executed ones included (spec: model-jobs).\n throw new AnthropicTranslationError(`Unmapped Anthropic content block ${JSON.stringify(block.type)} — provider drift?`, \"provider_drift\");\n }\n }\n const u = message.usage;\n const usage: Usage = { inputTokens: u.input_tokens, outputTokens: u.output_tokens };\n if (u.cache_read_input_tokens != null) usage.cacheReadInputTokens = u.cache_read_input_tokens;\n if (u.cache_creation_input_tokens != null) usage.cacheWriteInputTokens = u.cache_creation_input_tokens;\n if (u.service_tier === \"standard\" || u.service_tier === \"priority\" || u.service_tier === \"batch\") {\n usage.serviceTier = u.service_tier;\n }\n return { blocks, usage, stop: mapStopReason(message.stop_reason) };\n}\n\nexport interface AnthropicJobClientOptions {\n apiKey?: string;\n baseURL?: string;\n}\n\nexport class AnthropicJobClient implements ModelJobClient {\n readonly #client: Anthropic;\n\n constructor(opts: AnthropicJobClientOptions = {}) {\n const init: ConstructorParameters<typeof Anthropic>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new Anthropic(init);\n }\n\n async submit(items: readonly JobItem[]): Promise<JobHandle> {\n const first = items[0];\n if (!first) throw new AnthropicTranslationError(\"a batch needs at least one item\");\n const requests = items.map((item) => {\n // The item's tier is what makes it a job; the batch API has no tier\n // parameter, so the streaming translation runs with it cleared.\n const { serviceTier: _tier, ...req } = item.request;\n void _tier;\n const { stream: _stream, ...params } = toAnthropicParams(req);\n void _stream;\n return { custom_id: item.id, params: params as Anthropic.MessageCreateParamsNonStreaming };\n });\n const batch = await this.#client.messages.batches.create({ requests });\n return { provider: \"anthropic\", id: batch.id, model: first.request.model };\n }\n\n async status(handle: JobHandle): Promise<JobProgress> {\n const batch = await this.#client.messages.batches.retrieve(handle.id);\n const c = batch.request_counts;\n const total = c.processing + c.succeeded + c.errored + c.canceled + c.expired;\n const status =\n batch.processing_status === \"canceling\"\n ? \"cancelled\"\n : batch.processing_status === \"in_progress\"\n ? \"running\"\n : c.canceled === total && total > 0\n ? \"cancelled\"\n : c.expired === total && total > 0\n ? \"expired\"\n : \"done\";\n return { status, counts: { total, done: c.succeeded, failed: c.errored + c.canceled + c.expired } };\n }\n\n async *results(handle: JobHandle): AsyncIterable<JobResult> {\n const decoder = await this.#client.messages.batches.results(handle.id);\n for await (const entry of decoder) {\n const { custom_id: id, result } = entry;\n switch (result.type) {\n case \"succeeded\":\n yield { id, outcome: \"succeeded\", output: translateMessage(result.message) };\n break;\n case \"errored\": {\n const error = (result.error as { error?: { message?: string } } | undefined)?.error?.message;\n yield { id, outcome: \"errored\", ...(error !== undefined ? { error } : {}) };\n break;\n }\n case \"canceled\":\n yield { id, outcome: \"cancelled\" };\n break;\n case \"expired\":\n yield { id, outcome: \"expired\" };\n break;\n }\n }\n }\n\n async cancel(handle: JobHandle): Promise<void> {\n await this.#client.messages.batches.cancel(handle.id);\n }\n}\n","import OpenAI from \"openai\";\nimport type { ModelClient, ModelEvent, ModelRequest } from \"@alma-harness/core\";\n\nimport { toProviderError } from \"../errors\";\nimport { toOpenAIParams, translateOpenAIStream } from \"./translate\";\n\nexport interface OpenAIModelClientOptions {\n /** Omit to use the SDK's environment resolution (OPENAI_API_KEY). */\n apiKey?: string;\n baseURL?: string;\n}\n\n/**\n * `ModelClient` adapter for the OpenAI Responses API — §6.2, spec 003.\n * A thin shell: request/stream translation lives in ./translate (pure);\n * this class only owns the SDK client. What the SDK throws leaves as a\n * `ProviderError` (spec: error-taxonomy); a user abort passes through.\n */\nexport class OpenAIModelClient implements ModelClient {\n readonly #client: OpenAI;\n\n constructor(opts: OpenAIModelClientOptions = {}) {\n const init: ConstructorParameters<typeof OpenAI>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new OpenAI(init);\n }\n\n stream(req: ModelRequest, opts?: { signal?: AbortSignal }): AsyncIterable<ModelEvent> {\n // Translation throws synchronously (wrong provider, unsupported blocks)\n // before any network activity — spec 003 criterion 6.\n const params = toOpenAIParams(req);\n return translateOpenAIStream(this.#rawEvents(params, opts?.signal));\n }\n\n async *#rawEvents(\n params: OpenAI.Responses.ResponseCreateParamsStreaming,\n signal?: AbortSignal,\n ): AsyncGenerator<OpenAI.Responses.ResponseStreamEvent> {\n try {\n const stream = await this.#client.responses.create(\n params,\n signal !== undefined ? { signal } : undefined,\n );\n for await (const event of stream) yield event;\n } catch (err) {\n throw toProviderError(\"openai\", err);\n }\n }\n}\n","import type OpenAI from \"openai\";\n\nimport {\n ProviderError,\n type Block,\n type ModelEvent,\n type ModelRequest,\n type Msg,\n type ProviderFailureKind,\n type ProviderToolKind,\n type ProviderToolResultBlock,\n type ProviderToolSpec,\n type ReasoningBlock,\n type StopReason,\n type SystemBlock,\n type ToolSpec,\n type Usage,\n} from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\nimport { classifyFailure } from \"../errors\";\n\n/**\n * Pure translation between Alma's neutral vocabulary (§6.2) and the OpenAI\n * Responses API — spec 003. Side-effect-free; testable without a network.\n */\n\n/** A `ProviderError` since spec: error-taxonomy — see `AnthropicTranslationError`. */\nexport class OpenAITranslationError extends ProviderError {\n constructor(message: string, kind: ProviderFailureKind = \"rejected\") {\n super(\"openai\", kind, message);\n this.name = \"OpenAITranslationError\";\n }\n}\n\nexport function toOpenAIParams(\n req: ModelRequest,\n): OpenAI.Responses.ResponseCreateParamsStreaming {\n if (req.model.provider !== \"openai\") {\n throw new OpenAITranslationError(\n `OpenAIModelClient received a request for provider ${JSON.stringify(req.model.provider)}`,\n );\n }\n // Reasoning — spec: reasoning-blocks. Absent sends nothing and drops the\n // provider's default reasoning items as before; \"none\" disables; anything\n // else asks for the effort AND for the encrypted content, which is the only\n // form that survives `store: false` and can be carried to the next step.\n const reasoning = req.reasoning;\n const replay = reasoning !== undefined && reasoning.effort !== \"none\";\n const params: OpenAI.Responses.ResponseCreateParamsStreaming = {\n model: req.model.id,\n max_output_tokens: req.maxTokens,\n stream: true,\n // Privacy-first (§3, §10): the Responses API stores responses server-side\n // by default; the harness never leaves conversation state at the provider.\n store: false,\n input: req.messages.flatMap((m) => toInputItems(m, replay, new Set((req.providerTools ?? []).map((t) => t.kind)))),\n };\n if (reasoning !== undefined) {\n params.reasoning = replay ? { effort: reasoning.effort, summary: \"auto\" } : { effort: \"none\" };\n if (replay) params.include = [\"reasoning.encrypted_content\"];\n }\n // Service tier — spec: pricing-tiers. Flex and priority are request\n // parameters here; batch is a job, not a stream, and is refused.\n switch (req.serviceTier) {\n case undefined:\n case \"standard\":\n break;\n case \"flex\":\n case \"priority\":\n params.service_tier = req.serviceTier;\n break;\n default:\n throw new OpenAITranslationError(\n `the OpenAI Responses API cannot serve the ${req.serviceTier} tier on a streaming request`,\n );\n }\n const instructions = toInstructions(req.system);\n if (instructions !== \"\") params.instructions = instructions;\n // Provider-executed tools ride in the same list (spec: provider-tools); the\n // sources are asked for, so the result can cite what it read.\n const tools: OpenAI.Responses.Tool[] = [...req.tools.map(toTool), ...(req.providerTools ?? []).map(toWebSearchTool)];\n if (tools.length > 0) params.tools = tools;\n if ((req.providerTools ?? []).length > 0) params.include = [...(params.include ?? []), \"web_search_call.action.sources\"];\n return params;\n}\n\n/**\n * The neutral web search → the Responses tool. An option this wire has no\n * form for is REFUSED, never dropped; `maxUses` is not one — the loop\n * enforces it across the turn's steps (spec: what-the-wire-cuts).\n */\nfunction toWebSearchTool(spec: ProviderToolSpec): OpenAI.Responses.WebSearchTool {\n if (spec.blockedDomains !== undefined) {\n throw new OpenAITranslationError(\"the OpenAI web search has no blocked-domains form — refusing rather than searching them\");\n }\n const tool: OpenAI.Responses.WebSearchTool = { type: \"web_search\" };\n if (spec.allowedDomains !== undefined) tool.filters = { allowed_domains: [...spec.allowedDomains] };\n return tool;\n}\n\n/**\n * OpenAI has no explicit cache breakpoint (prefix caching is automatic), so\n * the §6.9 stable/volatile discipline is preserved simply by keeping the\n * blocks' order — stable first — in one instructions string.\n */\nfunction toInstructions(blocks: SystemBlock[]): string {\n return blocks.map((b) => b.text).join(\"\\n\\n\");\n}\n\nfunction toInputItems(msg: Msg, replayReasoning: boolean, declared: Set<ProviderToolKind>): OpenAI.Responses.ResponseInputItem[] {\n switch (msg.role) {\n case \"user\":\n return [{ role: \"user\", content: msg.blocks.map(toUserContentPart) }];\n case \"assistant\":\n // Text stays a message item; each tool_call becomes its own top-level\n // function_call item, preserving block order — and a reasoning item\n // goes back ahead of them, as it came (spec: reasoning-blocks).\n return msg.blocks.flatMap((b) => toAssistantItems(b, replayReasoning, declared));\n case \"tool\":\n return msg.blocks.map(toFunctionCallOutput);\n }\n}\n\nfunction toUserContentPart(\n block: Block,\n): OpenAI.Responses.ResponseInputText | OpenAI.Responses.ResponseInputImage {\n switch (block.type) {\n case \"text\":\n return { type: \"input_text\", text: block.text };\n case \"media\":\n if (block.kind === \"image\") {\n return { type: \"input_image\", detail: \"auto\", image_url: block.ref.uri };\n }\n // Documents/audio on the Responses input surface are file-id-centric,\n // which breaks media-by-reference (§6.1) — fail loudly (spec 003):\n // the routing policy is the layer that picks capable providers.\n throw new OpenAITranslationError(\n `${block.kind} media is not supported by the OpenAI adapter`,\n );\n default:\n throw new OpenAITranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a user message`,\n );\n }\n}\n\nfunction toAssistantItems(block: Block, replayReasoning: boolean, declared: Set<ProviderToolKind>): OpenAI.Responses.ResponseInputItem[] {\n switch (block.type) {\n case \"text\":\n return [{ role: \"assistant\", content: block.text }];\n case \"tool_call\":\n return [\n {\n type: \"function_call\",\n call_id: block.id,\n name: block.name,\n arguments: JSON.stringify(block.input),\n },\n ];\n case \"reasoning\":\n // Own items go back with their encrypted content when reasoning is on\n // for this request; another provider's, or any when it is off, are\n // skipped without error (spec: reasoning-blocks).\n return block.provider === \"openai\" && replayReasoning ? [toReasoningItem(block)] : [];\n case \"provider_tool_call\":\n // The item carries both halves; the result block replays it (spec: provider-tools).\n return [];\n case \"provider_tool_result\":\n // Replayed only while the request declares the kind (spec: close-060-064-findings).\n return block.provider === \"openai\" && declared.has(block.name) ? [toWebSearchItem(block)] : [];\n default:\n throw new OpenAITranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in an assistant message`,\n );\n }\n}\n\n/** The opaque half this adapter wrote: the whole `web_search_call` item. */\nfunction toWebSearchItem(block: ProviderToolResultBlock): OpenAI.Responses.ResponseFunctionWebSearch {\n const opaque = block.opaque as { type?: unknown } | undefined;\n if (opaque?.type !== \"web_search_call\") {\n throw new OpenAITranslationError(\"an OpenAI web search result carries no replayable item\");\n }\n return opaque as OpenAI.Responses.ResponseFunctionWebSearch;\n}\n\n/** The opaque half this adapter wrote: the item's id, summary and encrypted content. */\nfunction toReasoningItem(block: ReasoningBlock): OpenAI.Responses.ResponseReasoningItem {\n const opaque = block.opaque as\n | { id?: unknown; summary?: unknown; encrypted_content?: unknown }\n | undefined;\n if (typeof opaque?.id !== \"string\" || typeof opaque.encrypted_content !== \"string\") {\n throw new OpenAITranslationError(\"an OpenAI reasoning block carries no id or encrypted content\");\n }\n return {\n type: \"reasoning\",\n id: opaque.id,\n summary: Array.isArray(opaque.summary) ? (opaque.summary as OpenAI.Responses.ResponseReasoningItem[\"summary\"]) : [],\n encrypted_content: opaque.encrypted_content,\n };\n}\n\nfunction toFunctionCallOutput(block: Block): OpenAI.Responses.ResponseInputItem {\n if (block.type !== \"tool_result\") {\n throw new OpenAITranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a tool message`,\n );\n }\n const serialized =\n typeof block.output === \"string\" ? block.output : (JSON.stringify(block.output) ?? \"null\");\n return {\n type: \"function_call_output\",\n call_id: block.callId,\n // The Responses API has no is_error flag on function outputs — an\n // explicit prefix keeps failures visible to the model (spec 003).\n output: block.isError ? `ERROR: ${serialized}` : serialized,\n };\n}\n\nfunction toTool(spec: ToolSpec): OpenAI.Responses.FunctionTool {\n return {\n type: \"function\",\n name: spec.name,\n description: spec.description,\n parameters: spec.inputSchema,\n // Registry-derived schemas are not guaranteed strict-compatible; the\n // structured-output surface is a follow-up slice (spec 003 decision).\n strict: false,\n };\n}\n\n/**\n * Semantic stream events → neutral `ModelEvent`s. Function-call arguments\n * arrive complete on `response.output_item.done`, so each call is emitted as\n * ONE parsed `tool_call`. The terminal `response.completed` / `.incomplete`\n * event carries usage; `tool_use` is inferred from having emitted tool calls\n * (the Responses API has no finish_reason).\n */\nexport async function* translateOpenAIStream(\n events: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>,\n): AsyncGenerator<ModelEvent> {\n let sawToolCall = false;\n let sawRefusal = false;\n // The wire's usage does not count searches; the completed items do (spec: provider-tools).\n let searches = 0;\n const withSearches = (usage: Usage): Usage => (searches > 0 ? { ...usage, webSearchRequests: searches } : usage);\n\n for await (const event of events) {\n switch (event.type) {\n case \"response.output_text.delta\":\n yield { type: \"text_delta\", text: event.delta };\n break;\n case \"response.refusal.delta\":\n // Refusal content is user-visible text; the stop reason records the\n // refusal itself (spec 003).\n sawRefusal = true;\n yield { type: \"text_delta\", text: event.delta };\n break;\n case \"response.output_item.done\":\n if (event.item.type === \"function_call\") {\n sawToolCall = true;\n yield { type: \"tool_call\", id: event.item.call_id, name: event.item.name, ...parseToolArguments(event.item.arguments) };\n } else if (event.item.type === \"web_search_call\") {\n // One item, two neutral halves: the call (its action) and the result\n // (the sources, the item whole for replay) — spec: provider-tools.\n searches += 1;\n const item = event.item;\n const action = item.action;\n const sources = action.type === \"search\" ? (action.sources ?? []).map((s) => ({ url: s.url })) : [];\n yield { type: \"provider_tool_call\", block: { type: \"provider_tool_call\", id: item.id, name: \"web_search\", provider: \"openai\", input: action } };\n yield {\n type: \"provider_tool_result\",\n block: {\n type: \"provider_tool_result\",\n callId: item.id,\n name: \"web_search\",\n provider: \"openai\",\n results: sources,\n ...(item.status === \"failed\" ? { error: \"failed\" } : {}),\n opaque: item,\n },\n };\n } else if (event.item.type === \"reasoning\" && typeof event.item.encrypted_content === \"string\") {\n // The COMPLETED item, from `done`: `added` may carry partial\n // encrypted content. Without encrypted content the item cannot be\n // replayed under `store: false`, so it is dropped as before the\n // spec — that is the case where nobody asked for it.\n const item = event.item;\n const text = [\n ...(item.content ?? []).map((c) => c.text),\n ...item.summary.map((s) => s.text),\n ]\n .filter((t) => t !== \"\")\n .join(\"\\n\");\n yield {\n type: \"reasoning\",\n block: {\n type: \"reasoning\",\n provider: \"openai\",\n ...(text !== \"\" ? { text } : {}),\n opaque: { id: item.id, summary: item.summary, encrypted_content: item.encrypted_content },\n },\n };\n }\n break;\n case \"response.completed\":\n yield { type: \"usage\", usage: withSearches(usageOf(event.response)) };\n yield { type: \"stop\", reason: sawRefusal ? \"refusal\" : sawToolCall ? \"tool_use\" : \"end_turn\" };\n break;\n case \"response.incomplete\": {\n yield { type: \"usage\", usage: withSearches(usageOf(event.response)) };\n yield { type: \"stop\", reason: mapIncompleteReason(event.response) };\n break;\n }\n case \"response.failed\": {\n // A failure the wire REPORTED rather than threw: classified by its\n // code through the same table the SDK's thrown errors go through.\n const err = event.response.error;\n const message = `OpenAI response failed: ${err ? `${err.code}: ${err.message}` : \"unknown error\"}`;\n throw new OpenAITranslationError(message, classifyFailure(\"ResponseFailed\", undefined, message.toLowerCase()));\n }\n case \"error\": {\n const message = `OpenAI stream error: ${event.message}`;\n throw new OpenAITranslationError(message, classifyFailure(\"StreamError\", undefined, `${event.code ?? \"\"} ${message}`.toLowerCase()));\n }\n default:\n // created / in_progress / content parts / argument deltas etc. carry\n // no neutral information beyond the events handled above.\n break;\n }\n }\n}\n\n/** The neutral usage of a complete response — shared by the stream and the batch path. */\nexport function usageOf(response: OpenAI.Responses.Response): Usage {\n const u = response.usage;\n const cachedRead = u?.input_tokens_details?.cached_tokens ?? 0;\n const usage: Usage = {\n // Neutral semantics (spec 005): inputTokens EXCLUDES cache reads.\n // OpenAI's input_tokens includes cached_tokens; subtract to normalize —\n // otherwise the BudgetGuard would price the same conversation\n // differently per provider.\n inputTokens: Math.max(0, (u?.input_tokens ?? 0) - cachedRead),\n outputTokens: u?.output_tokens ?? 0,\n };\n if (cachedRead > 0) usage.cacheReadInputTokens = cachedRead;\n const cacheWrite = u?.input_tokens_details?.cache_write_tokens;\n if (cacheWrite !== undefined && cacheWrite > 0) usage.cacheWriteInputTokens = cacheWrite;\n // Telemetry only: reasoning tokens are already inside output_tokens.\n const reasoningTokens = u?.output_tokens_details?.reasoning_tokens;\n if (reasoningTokens !== undefined && reasoningTokens > 0) usage.reasoningTokens = reasoningTokens;\n // The tier that SERVED, off the wire (spec: pricing-tiers). `default` is\n // standard; `auto`, `scale`, `fast` and `ultrafast` are outside the\n // neutral union and leave the field absent, so pricing falls back to the\n // tier asked.\n const served = response.service_tier;\n if (served === \"default\") usage.serviceTier = \"standard\";\n else if (served === \"flex\" || served === \"priority\") usage.serviceTier = served;\n return usage;\n}\n\nexport function mapIncompleteReason(response: OpenAI.Responses.Response): StopReason {\n const reason = response.incomplete_details?.reason;\n switch (reason) {\n case \"max_output_tokens\":\n // NOTE (spec 003 decision): OpenAI's wire conflates context-window\n // exhaustion into this reason (or a 400 before the stream), so the\n // neutral \"context_window_exceeded\" is unreachable on this adapter —\n // the long-context policy must watch provider errors instead.\n return \"max_tokens\";\n case \"content_filter\":\n return \"refusal\";\n default:\n throw new OpenAITranslationError(\n `Unmapped OpenAI incomplete reason ${JSON.stringify(reason)} — provider drift?`,\n \"provider_drift\",\n );\n }\n}\n","import OpenAI, { toFile } from \"openai\";\nimport type { Block, JobHandle, JobItem, JobOutput, JobProgress, JobResult, ModelJobClient } from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\nimport { mapIncompleteReason, OpenAITranslationError, toOpenAIParams, usageOf } from \"./translate\";\n\n/**\n * `ModelJobClient` over the OpenAI Batch API — spec: model-jobs. Items become\n * one JSONL file of `/v1/responses` requests, uploaded with purpose `batch`;\n * results come back as a JSONL file of complete `Response` objects, each\n * translated here into the neutral output the runner prices and returns.\n */\n\n/** One line of the batch input file. */\nexport interface BatchLine {\n custom_id: string;\n method: \"POST\";\n url: \"/v1/responses\";\n body: OpenAI.Responses.ResponseCreateParamsNonStreaming;\n}\n\nexport function toBatchLines(items: readonly JobItem[]): BatchLine[] {\n return items.map((item) => {\n const { serviceTier: _tier, ...req } = item.request;\n void _tier;\n const { stream: _stream, ...body } = toOpenAIParams(req);\n void _stream;\n return { custom_id: item.id, method: \"POST\", url: \"/v1/responses\", body: body as OpenAI.Responses.ResponseCreateParamsNonStreaming };\n });\n}\n\n/** A complete response → neutral blocks, usage and stop — the non-streaming half of spec 003. */\nexport function translateResponse(response: OpenAI.Responses.Response): JobOutput {\n const blocks: Block[] = [];\n let sawToolCall = false;\n let sawRefusal = false;\n for (const item of response.output) {\n switch (item.type) {\n case \"message\":\n for (const part of item.content) {\n if (part.type === \"output_text\") blocks.push({ type: \"text\", text: part.text });\n else if (part.type === \"refusal\") {\n sawRefusal = true;\n blocks.push({ type: \"text\", text: part.refusal });\n }\n }\n break;\n case \"function_call\":\n sawToolCall = true;\n // A cut or broken argument string is data here too, never a throw that fails the whole collect.\n blocks.push({ type: \"tool_call\", id: item.call_id, name: item.name, input: parseToolArguments(item.arguments).input });\n break;\n case \"reasoning\": {\n if (typeof item.encrypted_content !== \"string\") break;\n const text = [...(item.content ?? []).map((c) => c.text), ...item.summary.map((s) => s.text)].filter((t) => t !== \"\").join(\"\\n\");\n blocks.push({\n type: \"reasoning\",\n provider: \"openai\",\n ...(text !== \"\" ? { text } : {}),\n opaque: { id: item.id, summary: item.summary, encrypted_content: item.encrypted_content },\n });\n break;\n }\n default:\n throw new OpenAITranslationError(`Unmapped OpenAI output item ${JSON.stringify(item.type)} — provider drift?`, \"provider_drift\");\n }\n }\n const stop =\n response.incomplete_details?.reason !== undefined && response.incomplete_details?.reason !== null\n ? mapIncompleteReason(response)\n : sawRefusal\n ? \"refusal\"\n : sawToolCall\n ? \"tool_use\"\n : \"end_turn\";\n return { blocks, usage: usageOf(response), stop };\n}\n\nexport interface OpenAIJobClientOptions {\n apiKey?: string;\n baseURL?: string;\n}\n\ninterface OutputLine {\n custom_id: string;\n response?: { status_code: number; body: OpenAI.Responses.Response | { error?: { message?: string } } } | null;\n error?: { message?: string } | null;\n}\n\nexport class OpenAIJobClient implements ModelJobClient {\n readonly #client: OpenAI;\n\n constructor(opts: OpenAIJobClientOptions = {}) {\n const init: ConstructorParameters<typeof OpenAI>[0] = {};\n if (opts.apiKey !== undefined) init.apiKey = opts.apiKey;\n if (opts.baseURL !== undefined) init.baseURL = opts.baseURL;\n this.#client = new OpenAI(init);\n }\n\n async submit(items: readonly JobItem[]): Promise<JobHandle> {\n const first = items[0];\n if (!first) throw new OpenAITranslationError(\"a batch needs at least one item\");\n const jsonl = toBatchLines(items).map((line) => JSON.stringify(line)).join(\"\\n\") + \"\\n\";\n const file = await this.#client.files.create({\n file: await toFile(Buffer.from(jsonl, \"utf8\"), \"alma-batch.jsonl\", { type: \"application/jsonl\" }),\n purpose: \"batch\",\n });\n const batch = await this.#client.batches.create({\n input_file_id: file.id,\n endpoint: \"/v1/responses\",\n completion_window: \"24h\",\n });\n return { provider: \"openai\", id: batch.id, model: first.request.model };\n }\n\n async status(handle: JobHandle): Promise<JobProgress> {\n const batch = await this.#client.batches.retrieve(handle.id);\n const status =\n batch.status === \"validating\"\n ? \"queued\"\n : batch.status === \"in_progress\" || batch.status === \"finalizing\"\n ? \"running\"\n : batch.status === \"completed\"\n ? \"done\"\n : batch.status === \"failed\"\n ? \"failed\"\n : batch.status === \"expired\"\n ? \"expired\"\n : \"cancelled\";\n const c = batch.request_counts;\n return {\n status,\n ...(c ? { counts: { total: c.total, done: c.completed, failed: c.failed } } : {}),\n };\n }\n\n async *results(handle: JobHandle): AsyncIterable<JobResult> {\n const batch = await this.#client.batches.retrieve(handle.id);\n for (const fileId of [batch.output_file_id, batch.error_file_id]) {\n if (!fileId) continue;\n const text = await (await this.#client.files.content(fileId)).text();\n for (const raw of text.split(\"\\n\")) {\n if (raw.trim() === \"\") continue;\n const line = JSON.parse(raw) as OutputLine;\n const body = line.response?.body;\n if (line.response && line.response.status_code >= 200 && line.response.status_code < 300 && body && \"output\" in body) {\n yield { id: line.custom_id, outcome: \"succeeded\", output: translateResponse(body) };\n } else {\n const error =\n line.error?.message ?? (body && \"error\" in body ? body.error?.message : undefined) ?? `status ${line.response?.status_code ?? \"unknown\"}`;\n yield { id: line.custom_id, outcome: \"errored\", error };\n }\n }\n }\n }\n\n async cancel(handle: JobHandle): Promise<void> {\n await this.#client.batches.cancel(handle.id);\n }\n}\n","import OpenAI from \"openai\";\nimport type { ModelClient, ModelEvent, ModelRequest } from \"@alma-harness/core\";\n\nimport { toProviderError } from \"../errors\";\nimport {\n toOpenRouterParams,\n translateOpenRouterStream,\n type OpenRouterParams,\n type OpenRouterRouting,\n} from \"./translate\";\n\nexport interface OpenRouterModelClientOptions extends OpenRouterRouting {\n /** Omit to read OPENROUTER_API_KEY (the OpenAI SDK's own env resolution\n * would read OPENAI_API_KEY — the wrong credential for this gateway). */\n apiKey?: string;\n /** Defaults to OpenRouter's public endpoint. */\n baseURL?: string;\n}\n\nconst OPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\";\n\n/**\n * `ModelClient` adapter for OpenRouter — §6.2, spec 014 (the gateway bridge).\n * Same thin-shell shape as the first-party adapters: translation lives in\n * ./translate (pure); this class owns the SDK client and the DECLARED\n * upstream policy. A gateway erases \"who processed this data?\" unless the\n * chain is declared beforehand, so construction REFUSES to proceed without a\n * non-empty upstream allowlist — deciding after the fact enforces nothing.\n */\nexport class OpenRouterModelClient implements ModelClient {\n readonly #client: OpenAI;\n readonly #routing: OpenRouterRouting;\n\n constructor(opts: OpenRouterModelClientOptions) {\n if (!Array.isArray(opts.upstreams) || opts.upstreams.length === 0) {\n throw new Error(\n \"OpenRouterModelClient requires a non-empty `upstreams` allowlist — \" +\n \"for a gateway, the data-processing chain must be a declared fact (spec 014)\",\n );\n }\n const routing: OpenRouterRouting = { upstreams: [...opts.upstreams] };\n if (opts.allowFallbacks !== undefined) routing.allowFallbacks = opts.allowFallbacks;\n if (opts.dataCollection !== undefined) routing.dataCollection = opts.dataCollection;\n if (opts.zeroDataRetention !== undefined) routing.zeroDataRetention = opts.zeroDataRetention;\n this.#routing = routing;\n\n const apiKey = opts.apiKey ?? process.env[\"OPENROUTER_API_KEY\"];\n if (apiKey === undefined || apiKey === \"\") {\n throw new Error(\n \"OpenRouterModelClient needs an API key: pass `apiKey` or set OPENROUTER_API_KEY\",\n );\n }\n this.#client = new OpenAI({ apiKey, baseURL: opts.baseURL ?? OPENROUTER_BASE_URL });\n }\n\n stream(req: ModelRequest, opts?: { signal?: AbortSignal }): AsyncIterable<ModelEvent> {\n // Translation throws synchronously (wrong provider, unsupported blocks)\n // before any network activity, like every adapter in this package.\n const params = toOpenRouterParams(req, this.#routing);\n return translateOpenRouterStream(this.#rawChunks(params, opts?.signal));\n }\n\n async *#rawChunks(\n params: OpenRouterParams,\n signal?: AbortSignal,\n ): AsyncGenerator<OpenAI.Chat.Completions.ChatCompletionChunk> {\n try {\n const stream = await this.#client.chat.completions.create(\n params,\n signal !== undefined ? { signal } : undefined,\n );\n for await (const chunk of stream) yield chunk;\n } catch (err) {\n // What the SDK throws leaves as a `ProviderError` naming the GATEWAY\n // (spec: error-taxonomy): the upstream that actually failed is inside\n // the body, and the routing policy already knows the declared chain.\n throw toProviderError(\"openrouter\", err);\n }\n }\n}\n","import type OpenAI from \"openai\";\n\nimport {\n ProviderError,\n type Block,\n type ModelEvent,\n type ModelRequest,\n type Msg,\n type ProviderFailureKind,\n type StopReason,\n type SystemBlock,\n type ToolSpec,\n type Usage,\n} from \"@alma-harness/core\";\n\nimport { parseToolArguments } from \"../arguments\";\n\n/**\n * Pure translation between Alma's neutral vocabulary (§6.2) and OpenRouter's\n * chat-completions wire — spec 014. A SECOND translator over the neutral\n * format, not a `baseURL` override: the first-party OpenAI adapter speaks the\n * Responses API, and nothing in its translation is reusable here.\n * Side-effect-free; testable without a network.\n */\n\n/** A `ProviderError` since spec: error-taxonomy — see `AnthropicTranslationError`. */\nexport class OpenRouterTranslationError extends ProviderError {\n constructor(message: string, kind: ProviderFailureKind = \"rejected\") {\n super(\"openrouter\", kind, message);\n this.name = \"OpenRouterTranslationError\";\n }\n}\n\n/**\n * The upstream routing policy — spec 014's core constraint: for a gateway,\n * \"who processed this data?\" must be a DECLARED fact, not an observed one.\n */\nexport interface OpenRouterRouting {\n /**\n * OpenRouter provider slugs allowed to serve requests (→ `provider.only`).\n * REQUIRED and non-empty: without it the data-processing chain has no\n * answer, so the client refuses to construct.\n */\n upstreams: readonly string[];\n /**\n * Default false: a fallback is a silent change of data processor. Enabling\n * it is a stated product choice, never a default.\n */\n allowFallbacks?: boolean;\n /**\n * Default \"deny\" — OpenRouter defaults to \"allow\", and a harness whose\n * README says \"operating on sensitive data\" must not inherit the permissive\n * default (spec 014 DECISION).\n */\n dataCollection?: \"allow\" | \"deny\";\n /** Opt-in pass-through to OpenRouter's zero-data-retention guarantee. */\n zeroDataRetention?: boolean;\n}\n\n/** The `provider` routing block OpenRouter accepts on every request. */\ninterface OpenRouterProviderBlock {\n only: string[];\n allow_fallbacks: boolean;\n data_collection: \"allow\" | \"deny\";\n zdr?: boolean;\n require_parameters?: boolean;\n}\n\n/** OpenRouter's reasoning block — a gateway extension the OpenAI SDK does not type. */\ninterface OpenRouterReasoningBlock {\n effort?: \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n enabled?: boolean;\n}\n\nexport type OpenRouterParams = OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming & {\n provider: OpenRouterProviderBlock;\n reasoning?: OpenRouterReasoningBlock;\n};\n\nexport function toOpenRouterParams(req: ModelRequest, routing: OpenRouterRouting): OpenRouterParams {\n if (req.model.provider !== \"openrouter\") {\n throw new OpenRouterTranslationError(\n `OpenRouterModelClient received a request for provider ${JSON.stringify(req.model.provider)}`,\n );\n }\n const provider: OpenRouterProviderBlock = {\n only: [...routing.upstreams],\n allow_fallbacks: routing.allowFallbacks ?? false,\n data_collection: routing.dataCollection ?? \"deny\",\n };\n if (routing.zeroDataRetention === true) provider.zdr = true;\n if (req.tools.length > 0) {\n // Not cosmetic (spec 014): tool support varies by upstream, and without\n // this a model can receive the request WITHOUT the tools it was supposed\n // to have — a silent capability failure for a loop whose control flow IS\n // tool calls.\n provider.require_parameters = true;\n }\n\n // Reasoning — spec: reasoning-blocks. The gateway normalizes effort for its\n // upstreams; `max` collapses to `xhigh`, its top rung. Own blocks are\n // replayed as `reasoning_details` only when reasoning is on for THIS request.\n const reasoning = req.reasoning;\n const replay = reasoning !== undefined && reasoning.effort !== \"none\";\n const params: OpenRouterParams = {\n model: req.model.id,\n // The SDK deprecates this in favor of the OpenAI-specific\n // max_completion_tokens; for a gateway fronting heterogeneous upstreams,\n // max_tokens is the common denominator OpenRouter documents.\n max_tokens: req.maxTokens,\n stream: true,\n // Review amendment (spec 014): streamed chat completions only carry usage\n // when asked — without this the BudgetGuard never sees a usage event.\n stream_options: { include_usage: true },\n messages: [...systemMessages(req.system), ...req.messages.flatMap((m) => toWireMessages(m, replay))],\n provider,\n };\n if (reasoning !== undefined) {\n params.reasoning = replay\n ? { effort: reasoning.effort === \"max\" ? \"xhigh\" : reasoning.effort }\n : { enabled: false };\n }\n // Service tier — spec: pricing-tiers. The gateway prices by upstream, not\n // by tier; anything but standard is refused before the network.\n if (req.serviceTier !== undefined && req.serviceTier !== \"standard\") {\n throw new OpenRouterTranslationError(\n `the OpenRouter adapter cannot serve the ${req.serviceTier} tier — the gateway prices by upstream`,\n );\n }\n // Provider-executed tools have no neutral form on the gateway (spec: provider-tools).\n if ((req.providerTools ?? []).length > 0) {\n throw new OpenRouterTranslationError(\"the OpenRouter adapter cannot declare provider-executed tools — the gateway has no neutral web search\");\n }\n if (req.tools.length > 0) params.tools = req.tools.map(toTool);\n return params;\n}\n\n/**\n * Chat completions has no instructions field; system blocks become one system\n * message. The §6.9 stable/volatile discipline is preserved by keeping the\n * blocks' order — stable first — exactly as the OpenAI adapter does; whether\n * an upstream caches the prefix varies (spec 014's cache-hygiene note).\n */\nfunction systemMessages(\n blocks: SystemBlock[],\n): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {\n if (blocks.length === 0) return [];\n return [{ role: \"system\", content: blocks.map((b) => b.text).join(\"\\n\\n\") }];\n}\n\nfunction toWireMessages(msg: Msg, replayReasoning: boolean): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {\n switch (msg.role) {\n case \"user\":\n return [{ role: \"user\", content: msg.blocks.map(toUserContentPart) }];\n case \"assistant\":\n return [toAssistantMessage(msg.blocks, replayReasoning)];\n case \"tool\":\n // Chat completions wants one tool-role message PER result, keyed by\n // tool_call_id — unlike Anthropic's single user message of results.\n return msg.blocks.map(toToolMessage);\n }\n}\n\nfunction toUserContentPart(block: Block): OpenAI.Chat.Completions.ChatCompletionContentPart {\n switch (block.type) {\n case \"text\":\n return { type: \"text\", text: block.text };\n case \"media\":\n if (block.kind === \"image\") {\n return { type: \"image_url\", image_url: { url: block.ref.uri } };\n }\n // Document/audio support varies wildly by upstream and the wire shapes\n // are provider-specific — fail loudly (the routing policy is the layer\n // that picks capable providers, spec 003's precedent).\n throw new OpenRouterTranslationError(\n `${block.kind} media is not supported by the OpenRouter adapter`,\n );\n default:\n throw new OpenRouterTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a user message`,\n );\n }\n}\n\nfunction toAssistantMessage(\n blocks: Block[],\n replayReasoning: boolean,\n): OpenAI.Chat.Completions.ChatCompletionMessageParam {\n let text = \"\";\n const toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] = [];\n const details: unknown[] = [];\n for (const block of blocks) {\n switch (block.type) {\n case \"text\":\n text += block.text;\n break;\n case \"tool_call\":\n toolCalls.push({\n id: block.id,\n type: \"function\",\n function: { name: block.name, arguments: JSON.stringify(block.input) },\n });\n break;\n case \"reasoning\": {\n // Own `reasoning_details` go back when reasoning is on for this\n // request; another provider's, or any when it is off, are skipped\n // (spec: reasoning-blocks). A block with text but no details cannot\n // be replayed and is skipped too.\n const opaque = block.opaque as { reasoning_details?: unknown } | undefined;\n if (block.provider === \"openrouter\" && replayReasoning && Array.isArray(opaque?.reasoning_details)) {\n details.push(...opaque.reasoning_details);\n }\n break;\n }\n case \"provider_tool_call\":\n case \"provider_tool_result\":\n // Another provider's search (spec: provider-tools): nothing this wire can carry; skipped.\n break;\n default:\n throw new OpenRouterTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in an assistant message`,\n );\n }\n }\n const message: OpenAI.Chat.Completions.ChatCompletionAssistantMessageParam & {\n reasoning_details?: unknown[];\n } = {\n role: \"assistant\",\n content: text === \"\" ? null : text,\n };\n if (toolCalls.length > 0) message.tool_calls = toolCalls;\n if (details.length > 0) message.reasoning_details = details;\n return message;\n}\n\nfunction toToolMessage(block: Block): OpenAI.Chat.Completions.ChatCompletionMessageParam {\n if (block.type !== \"tool_result\") {\n throw new OpenRouterTranslationError(\n `block type ${JSON.stringify(block.type)} is not valid in a tool message`,\n );\n }\n const serialized =\n typeof block.output === \"string\" ? block.output : (JSON.stringify(block.output) ?? \"null\");\n return {\n role: \"tool\",\n tool_call_id: block.callId,\n // No is_error flag on this wire either — the explicit prefix keeps\n // failures visible to the model (spec 003's precedent).\n content: block.isError ? `ERROR: ${serialized}` : serialized,\n };\n}\n\nfunction toTool(spec: ToolSpec): OpenAI.Chat.Completions.ChatCompletionTool {\n return {\n type: \"function\",\n function: {\n name: spec.name,\n description: spec.description,\n parameters: spec.inputSchema,\n },\n };\n}\n\n/**\n * Chat-completion chunks → neutral `ModelEvent`s. Tool-call arguments arrive\n * as indexed fragments (id and name on the first fragment, argument pieces on\n * the rest); they are accumulated per index and emitted as ONE parsed\n * `tool_call` each when the choice finishes. The usage chunk arrives AFTER\n * `finish_reason` (with `stream_options.include_usage`), so the terminal\n * usage + stop pair is emitted at stream end — same order every adapter emits.\n */\nexport async function* translateOpenRouterStream(\n chunks: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncGenerator<ModelEvent> {\n const pendingTools = new Map<number, { id: string; name: string; json: string }>();\n let finish: string | null = null;\n let usage: Usage | null = null;\n let toolsEmitted = false;\n // Reasoning is a gateway extension on the delta (`reasoning` text and\n // `reasoning_details`), untyped by the SDK; accumulated and emitted as ONE\n // block when the choice finishes, ahead of the tool calls (spec:\n // reasoning-blocks).\n let reasoningText = \"\";\n const reasoningDetails: unknown[] = [];\n let reasoningEmitted = false;\n\n for await (const chunk of chunks) {\n const choice = chunk.choices[0];\n if (choice) {\n const extra = choice.delta as { reasoning?: string | null; reasoning_details?: unknown[] | null };\n if (typeof extra.reasoning === \"string\") reasoningText += extra.reasoning;\n if (Array.isArray(extra.reasoning_details)) reasoningDetails.push(...extra.reasoning_details);\n if (choice.delta.content != null && choice.delta.content !== \"\") {\n yield { type: \"text_delta\", text: choice.delta.content };\n }\n for (const fragment of choice.delta.tool_calls ?? []) {\n const pending = pendingTools.get(fragment.index) ?? { id: \"\", name: \"\", json: \"\" };\n if (fragment.id != null) pending.id = fragment.id;\n if (fragment.function?.name != null) pending.name = fragment.function.name;\n if (fragment.function?.arguments != null) pending.json += fragment.function.arguments;\n pendingTools.set(fragment.index, pending);\n }\n if (choice.finish_reason != null) {\n finish = choice.finish_reason;\n if (!reasoningEmitted && (reasoningText !== \"\" || reasoningDetails.length > 0)) {\n reasoningEmitted = true;\n yield {\n type: \"reasoning\",\n block: {\n type: \"reasoning\",\n provider: \"openrouter\",\n ...(reasoningText !== \"\" ? { text: reasoningText } : {}),\n ...(reasoningDetails.length > 0 ? { opaque: { reasoning_details: reasoningDetails } } : {}),\n },\n };\n }\n if (!toolsEmitted) {\n toolsEmitted = true;\n for (const [, call] of [...pendingTools.entries()].sort(([a], [b]) => a - b)) {\n yield { type: \"tool_call\", id: call.id, name: call.name, ...parseToolArguments(call.json) };\n }\n pendingTools.clear();\n }\n }\n }\n if (chunk.usage != null) {\n const cachedRead = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;\n usage = {\n // Neutral semantics (spec 005): inputTokens EXCLUDES cache reads —\n // the wire's prompt_tokens includes them.\n inputTokens: Math.max(0, chunk.usage.prompt_tokens - cachedRead),\n outputTokens: chunk.usage.completion_tokens,\n };\n if (cachedRead > 0) usage.cacheReadInputTokens = cachedRead;\n // No cache-write signal exists on this wire; the field stays absent.\n const reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens;\n if (reasoningTokens !== undefined && reasoningTokens > 0) usage.reasoningTokens = reasoningTokens;\n }\n }\n\n if (finish === null) {\n throw new OpenRouterTranslationError(\n \"OpenRouter stream ended without a finish_reason — provider drift?\",\n \"provider_drift\",\n );\n }\n if (usage !== null) yield { type: \"usage\", usage };\n yield { type: \"stop\", reason: mapFinishReason(finish) };\n}\n\nfunction mapFinishReason(reason: string): StopReason {\n switch (reason) {\n case \"stop\":\n return \"end_turn\";\n case \"tool_calls\":\n return \"tool_use\";\n case \"length\":\n return \"max_tokens\";\n case \"content_filter\":\n return \"refusal\";\n default:\n // \"error\", or a reason newer than this adapter: refuse loudly rather\n // than silently truncate. NOTE (spec 014 review amendment): context-\n // window overflow arrives as a 400 error before any stream, so the\n // neutral \"context_window_exceeded\" is unreachable on this wire — the\n // long-context policy must watch provider errors, as with the OpenAI\n // adapter.\n throw new OpenRouterTranslationError(\n `Unmapped OpenRouter finish_reason ${JSON.stringify(reason)} — provider drift?`,\n \"provider_drift\",\n );\n }\n}\n","import type { ProviderId } from \"@alma-harness/core\";\n\n/**\n * @alma-harness/providers — `ModelClient` adapters (§6.2).\n * Bi-provider from birth — `AnthropicModelClient` (spec 002) and\n * `OpenAIModelClient` (spec 003) — plus the OpenRouter gateway bridge\n * (spec 014), all over the same neutral surface.\n */\n\nexport { AnthropicModelClient } from \"./anthropic/client\";\nexport type { AnthropicModelClientOptions } from \"./anthropic/client\";\nexport {\n AnthropicTranslationError,\n toAnthropicParams,\n translateStream,\n} from \"./anthropic/translate\";\n\nexport { AnthropicJobClient, translateMessage } from \"./anthropic/jobs\";\nexport type { AnthropicJobClientOptions } from \"./anthropic/jobs\";\n\nexport { OpenAIModelClient } from \"./openai/client\";\nexport type { OpenAIModelClientOptions } from \"./openai/client\";\nexport {\n OpenAITranslationError,\n toOpenAIParams,\n translateOpenAIStream,\n} from \"./openai/translate\";\n\nexport { OpenAIJobClient, toBatchLines, translateResponse } from \"./openai/jobs\";\nexport type { BatchLine, OpenAIJobClientOptions } from \"./openai/jobs\";\n\nexport { OpenRouterModelClient } from \"./openrouter/client\";\nexport type { OpenRouterModelClientOptions } from \"./openrouter/client\";\nexport {\n OpenRouterTranslationError,\n toOpenRouterParams,\n translateOpenRouterStream,\n} from \"./openrouter/translate\";\nexport type { OpenRouterParams, OpenRouterRouting } from \"./openrouter/translate\";\n\n// What the SDKs throw → the neutral `ProviderError` — spec: error-taxonomy\nexport { classifyFailure, toProviderError } from \"./errors\";\n\nexport type { ModelClient, ModelEvent, ModelJobClient, ModelRef, ModelRequest, ProviderId } from \"@alma-harness/core\";\n\n/**\n * First-party from birth (§3 principle 4), plus the gateway bridge —\n * spec 014: `openrouter` names the EXIT, not the processor; its adapter\n * requires a declared upstream allowlist for exactly that reason.\n */\nexport const SUPPORTED_PROVIDERS = [\"anthropic\", \"openai\", \"openrouter\"] as const satisfies readonly ProviderId[];\n"],"mappings":"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","names":["ProviderError","ProviderError","Anthropic","Anthropic","ProviderError","ProviderError","toTool","OpenAI","OpenAI","OpenAI","ProviderError","ProviderError","toTool","toUserContentPart","OpenAI"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alma-harness/providers",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "ModelClient adapters for Anthropic and OpenAI over Alma's neutral message format.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "openai": "^7.5.0"
24
24
  },
25
25
  "peerDependencies": {
26
- "@alma-harness/core": "^0.2.0"
26
+ "@alma-harness/core": "^0.3.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.1.0",