@jeffusion/bungee-llms 5.19.0 → 5.21.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/dist/index.js CHANGED
@@ -8499,10 +8499,1074 @@ function createTokenAccountingSession(input, options = {}) {
8499
8499
  }
8500
8500
  };
8501
8501
  }
8502
+ // src/responses-codec/common.ts
8503
+ var DEFAULT_RESPONSES_CODEC_LIMITS = Object.freeze({
8504
+ maxRequestBytes: 8 * 1024 * 1024,
8505
+ maxOutputBytes: 8 * 1024 * 1024,
8506
+ maxArgumentBytes: 1024 * 1024,
8507
+ maxItems: 1024
8508
+ });
8509
+
8510
+ class ResponsesCodecError extends Error {
8511
+ code;
8512
+ param;
8513
+ constructor(code, message, param) {
8514
+ super(message);
8515
+ this.code = code;
8516
+ this.param = param;
8517
+ this.name = "ResponsesCodecError";
8518
+ }
8519
+ }
8520
+ function fail(code, message, param) {
8521
+ throw new ResponsesCodecError(code, message, param);
8522
+ }
8523
+ function atParam(param, run) {
8524
+ try {
8525
+ return run();
8526
+ } catch (error) {
8527
+ if (error instanceof ResponsesCodecError && error.param === undefined)
8528
+ throw new ResponsesCodecError(error.code, error.message, param);
8529
+ throw error;
8530
+ }
8531
+ }
8532
+ function record(value, where) {
8533
+ if (!value || typeof value !== "object" || Array.isArray(value))
8534
+ fail("invalid_payload", `${where} must be an object`);
8535
+ return value;
8536
+ }
8537
+ function string(value, where, nonempty = false) {
8538
+ if (typeof value !== "string" || nonempty && !value)
8539
+ fail("invalid_payload", `${where} must be a${nonempty ? " non-empty" : ""} string`);
8540
+ return value;
8541
+ }
8542
+ function list(value, where) {
8543
+ if (!Array.isArray(value))
8544
+ fail("invalid_payload", `${where} must be an array`);
8545
+ return value;
8546
+ }
8547
+ function limits(overrides) {
8548
+ const result = { ...DEFAULT_RESPONSES_CODEC_LIMITS, ...overrides };
8549
+ for (const [key, value] of Object.entries(result)) {
8550
+ if (!Number.isSafeInteger(value) || value <= 0)
8551
+ fail("invalid_limit", `${key} must be a positive integer`);
8552
+ }
8553
+ return result;
8554
+ }
8555
+ function bytes(value) {
8556
+ return new TextEncoder().encode(value).byteLength;
8557
+ }
8558
+ function bounded(value, max, where) {
8559
+ if (bytes(value) > max)
8560
+ fail("resource_limit", `${where} exceeds ${max} bytes`);
8561
+ return value;
8562
+ }
8563
+ function serialized(value, max, where) {
8564
+ let result;
8565
+ try {
8566
+ result = JSON.stringify(value);
8567
+ } catch {
8568
+ fail("invalid_payload", `${where} must be serializable JSON`);
8569
+ }
8570
+ if (result === undefined)
8571
+ fail("invalid_payload", `${where} must be serializable JSON`);
8572
+ return bounded(result, max, where);
8573
+ }
8574
+ function argumentObject(text) {
8575
+ let value;
8576
+ try {
8577
+ value = JSON.parse(text);
8578
+ } catch {
8579
+ fail("invalid_tool_arguments", "Tool arguments must be complete JSON");
8580
+ }
8581
+ return record(value, "tool arguments");
8582
+ }
8583
+ function restoreTool(name, names) {
8584
+ const result = names.get(name);
8585
+ if (result)
8586
+ return result;
8587
+ if (names.size)
8588
+ fail("unknown_tool", `Provider returned undeclared tool ${name}`);
8589
+ return { name, custom: false };
8590
+ }
8591
+ function customInput(text) {
8592
+ const value = argumentObject(text);
8593
+ if (Object.keys(value).length !== 1 || typeof value.input !== "string") {
8594
+ fail("invalid_tool_arguments", "Custom tools require exactly one string input property");
8595
+ }
8596
+ return value.input;
8597
+ }
8598
+ function customInputPrefix(text) {
8599
+ const prefix = /^\s*\{\s*"input"\s*:\s*"/.exec(text);
8600
+ if (!prefix)
8601
+ return "";
8602
+ let result = "";
8603
+ for (let i = prefix[0].length;i < text.length; ) {
8604
+ const char = text[i];
8605
+ if (char === '"')
8606
+ break;
8607
+ if (char !== "\\") {
8608
+ if (char.charCodeAt(0) < 32)
8609
+ fail("invalid_tool_arguments", "Invalid JSON string character");
8610
+ result += char;
8611
+ i++;
8612
+ continue;
8613
+ }
8614
+ const escape = text[i + 1];
8615
+ if (escape === undefined)
8616
+ break;
8617
+ const size = escape === "u" ? 6 : 2;
8618
+ if (i + size > text.length)
8619
+ break;
8620
+ try {
8621
+ result += JSON.parse(`"${text.slice(i, i + size)}"`);
8622
+ } catch {
8623
+ fail("invalid_tool_arguments", "Invalid JSON string escape");
8624
+ }
8625
+ i += size;
8626
+ }
8627
+ return result;
8628
+ }
8629
+ function terminal(reason, protocol) {
8630
+ if (typeof reason !== "string" || !reason)
8631
+ fail("missing_terminal", "Upstream has no explicit completion reason");
8632
+ const completed = protocol === "chat_completions" ? ["stop", "tool_calls", "function_call"] : ["end_turn", "tool_use", "stop_sequence"];
8633
+ if (completed.includes(reason))
8634
+ return { status: "completed", reason };
8635
+ if (reason === "length" || reason === "max_tokens" || reason === "model_context_window_exceeded")
8636
+ return { status: "incomplete", reason: "max_output_tokens" };
8637
+ if (reason === "content_filter" || reason === "refusal")
8638
+ return { status: "incomplete", reason: "content_filter" };
8639
+ if (reason === "error")
8640
+ return { status: "failed", reason: "upstream_error" };
8641
+ fail("unknown_terminal", `Unsupported upstream completion reason: ${reason}`);
8642
+ }
8643
+ function tokens(value, where) {
8644
+ if (value === undefined)
8645
+ return 0;
8646
+ if (!Number.isSafeInteger(value) || value < 0)
8647
+ fail("invalid_usage", `${where} must be a non-negative integer`);
8648
+ return value;
8649
+ }
8650
+ function normalizeUsage(raw, protocol, prior = {}) {
8651
+ if (raw === undefined || raw === null)
8652
+ return prior;
8653
+ const usage = record(raw, "usage");
8654
+ const chat = protocol === "chat_completions";
8655
+ const input = tokens(usage[chat ? "prompt_tokens" : "input_tokens"] ?? prior.input_tokens, "input_tokens");
8656
+ const output = tokens(usage[chat ? "completion_tokens" : "output_tokens"] ?? prior.output_tokens, "output_tokens");
8657
+ const details = chat ? usage.prompt_tokens_details : undefined;
8658
+ const cached = chat ? details ? record(details, "prompt_tokens_details").cached_tokens : undefined : usage.cache_read_input_tokens;
8659
+ const cacheRead = tokens(cached ?? prior.input_tokens_details?.cached_tokens, "cached_tokens");
8660
+ const cacheWrite = chat ? 0 : tokens(usage.cache_creation_input_tokens, "cache_creation_input_tokens");
8661
+ const totalInput = chat ? input : usage.input_tokens === undefined ? input : input + cacheRead + cacheWrite;
8662
+ const reasoning = chat && usage.completion_tokens_details ? record(usage.completion_tokens_details, "completion_tokens_details").reasoning_tokens : undefined;
8663
+ return {
8664
+ input_tokens: totalInput,
8665
+ output_tokens: output,
8666
+ total_tokens: totalInput + output,
8667
+ input_tokens_details: { cached_tokens: cacheRead },
8668
+ output_tokens_details: { reasoning_tokens: tokens(reasoning ?? prior.output_tokens_details?.reasoning_tokens, "reasoning_tokens") }
8669
+ };
8670
+ }
8671
+ function toolItem(id, name, args, names, status = "completed") {
8672
+ const original = restoreTool(name, names);
8673
+ const item = { id: `fc_${id}`, type: original.custom ? "custom_tool_call" : "function_call", call_id: id, name: original.name, status };
8674
+ if (original.namespace)
8675
+ item.namespace = original.namespace;
8676
+ if (original.custom)
8677
+ item.input = customInput(args);
8678
+ else {
8679
+ argumentObject(args);
8680
+ item.arguments = args;
8681
+ }
8682
+ return item;
8683
+ }
8684
+ function responsePayload(id, model, output, end, usage, error, createdAt = Math.floor(Date.now() / 1000)) {
8685
+ const result = { id, object: "response", created_at: createdAt, model, status: end.status, output, usage: Object.keys(usage).length ? usage : null };
8686
+ if (end.status === "incomplete")
8687
+ result.incomplete_details = { reason: end.reason };
8688
+ if (end.status === "failed")
8689
+ result.error = error ?? { code: end.reason, message: end.reason };
8690
+ return result;
8691
+ }
8692
+
8693
+ // src/responses-codec/request-preferences.ts
8694
+ function fields(value, allowed, path) {
8695
+ for (const key of Object.keys(value))
8696
+ if (!allowed.includes(key)) {
8697
+ fail("unsupported_request", "Unrecognized request option", `${path}.${key}`);
8698
+ }
8699
+ }
8700
+ function applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics) {
8701
+ const note = (param, action, reason) => diagnostics.push({ param, action, reason });
8702
+ if (body.stream_options !== undefined)
8703
+ atParam("stream_options", () => {
8704
+ const options = record(body.stream_options, "stream_options");
8705
+ fields(options, ["include_usage", "reasoning_summary_delivery"], "stream_options");
8706
+ if (options.include_usage !== undefined) {
8707
+ if (typeof options.include_usage !== "boolean")
8708
+ fail("invalid_payload", "Usage preference must be boolean", "stream_options.include_usage");
8709
+ note("stream_options.include_usage", protocol === "chat_completions" && body.stream === true ? "mapped" : "omitted", protocol === "chat_completions" && body.stream === true ? "stream_usage_required" : "target_usage_events");
8710
+ }
8711
+ if (options.reasoning_summary_delivery !== undefined) {
8712
+ if (options.reasoning_summary_delivery !== "sequential_cutoff")
8713
+ fail("unsupported_request", "Unsupported summary delivery preference", "stream_options.reasoning_summary_delivery");
8714
+ note("stream_options.reasoning_summary_delivery", "omitted", "source_summary_delivery_not_applicable");
8715
+ }
8716
+ });
8717
+ if (body.access_programs !== undefined)
8718
+ atParam("access_programs", () => {
8719
+ const programs = record(body.access_programs, "access_programs");
8720
+ fields(programs, ["cyber"], "access_programs");
8721
+ if (programs.cyber !== undefined && programs.cyber !== "standard") {
8722
+ if (programs.cyber === "daybreak_blue" || programs.cyber === "daybreak_red") {
8723
+ fail("unsupported_access_program", "Selected access program cannot be represented by the target protocol", "access_programs.cyber");
8724
+ }
8725
+ fail("invalid_payload", "Invalid access program", "access_programs.cyber");
8726
+ }
8727
+ note("access_programs", "omitted", "target_uses_own_access_policy");
8728
+ });
8729
+ if (body.reasoning !== undefined)
8730
+ atParam("reasoning", () => {
8731
+ const reasoning = record(body.reasoning, "reasoning");
8732
+ fields(reasoning, ["effort", "summary", "context"], "reasoning");
8733
+ if (reasoning.summary !== undefined) {
8734
+ if (typeof reasoning.summary !== "string" || !["none", "auto", "concise", "detailed"].includes(reasoning.summary))
8735
+ fail("invalid_payload", "Invalid reasoning summary preference", "reasoning.summary");
8736
+ note("reasoning.summary", "omitted", "target_summary_not_guaranteed");
8737
+ }
8738
+ if (reasoning.context !== undefined) {
8739
+ if (reasoning.context !== "all_turns")
8740
+ fail("unsupported_reasoning", "Unsupported reasoning context preference", "reasoning.context");
8741
+ note("reasoning.context", "omitted", "source_reasoning_context_not_applicable");
8742
+ }
8743
+ if (reasoning.effort !== undefined)
8744
+ atParam("reasoning.effort", () => {
8745
+ const effort = string(reasoning.effort, "reasoning effort", true);
8746
+ if (protocol === "chat_completions" && capabilities.reasoningEffort) {
8747
+ result.reasoning_effort = effort;
8748
+ note("reasoning.effort", "mapped", "target_reasoning_effort");
8749
+ } else if (protocol === "anthropic_messages" && capabilities.anthropicThinkingBudget !== undefined && effort !== "none") {
8750
+ const budget = capabilities.anthropicThinkingBudget;
8751
+ if (!Number.isSafeInteger(budget) || budget < 1024 || typeof maxTokens !== "number" || budget >= maxTokens) {
8752
+ fail("unsupported_reasoning", "Anthropic thinking requires an explicit budget below max_output_tokens");
8753
+ }
8754
+ result.thinking = { type: "enabled", budget_tokens: budget };
8755
+ note("reasoning.effort", "mapped", "explicit_target_thinking_budget");
8756
+ } else
8757
+ note("reasoning.effort", "omitted", "target_reasoning_effort_unavailable");
8758
+ });
8759
+ });
8760
+ if (body.text !== undefined)
8761
+ atParam("text", () => {
8762
+ const text = record(body.text, "text");
8763
+ fields(text, ["verbosity", "format"], "text");
8764
+ if (text.verbosity !== undefined) {
8765
+ if (typeof text.verbosity !== "string" || !["low", "medium", "high"].includes(text.verbosity))
8766
+ fail("invalid_payload", "Invalid text verbosity preference", "text.verbosity");
8767
+ note("text.verbosity", "omitted", "target_verbosity_not_available");
8768
+ }
8769
+ });
8770
+ }
8771
+
8772
+ // src/responses-codec/request.ts
8773
+ var contentNormalizer = new OpenAIProtocolConversion({ trimWhitespace: false });
8774
+ function definitionKey(value) {
8775
+ if (Array.isArray(value))
8776
+ return `[${value.map(definitionKey).join(",")}]`;
8777
+ if (value && typeof value === "object")
8778
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${definitionKey(value[key])}`).join(",")}}`;
8779
+ return JSON.stringify(value);
8780
+ }
8781
+ function decodeResponsesRequest(raw, protocol, capabilities = {}) {
8782
+ const body = record(raw, "Responses request");
8783
+ const budget = limits(capabilities.limits);
8784
+ serialized(body, budget.maxRequestBytes, "request");
8785
+ const supported = new Set([
8786
+ "model",
8787
+ "input",
8788
+ "instructions",
8789
+ "tools",
8790
+ "tool_choice",
8791
+ "reasoning",
8792
+ "text",
8793
+ "stream",
8794
+ "temperature",
8795
+ "top_p",
8796
+ "parallel_tool_calls",
8797
+ "metadata",
8798
+ "max_output_tokens",
8799
+ "previous_response_id",
8800
+ "conversation",
8801
+ "response_id",
8802
+ "background",
8803
+ "n",
8804
+ "truncation",
8805
+ "include",
8806
+ "store",
8807
+ "prompt_cache_key",
8808
+ "client_metadata",
8809
+ "stream_options",
8810
+ "access_programs"
8811
+ ]);
8812
+ for (const key of Object.keys(body))
8813
+ if (!supported.has(key))
8814
+ fail("unsupported_request", "Unrecognized Responses request field", key);
8815
+ if (body.store === true)
8816
+ fail("unsupported_request", "Stored Responses require caller-owned persistence");
8817
+ for (const key of ["previous_response_id", "conversation", "response_id"]) {
8818
+ if (body[key] != null)
8819
+ fail("unresolved_reference", `${key} must be resolved before protocol conversion`);
8820
+ }
8821
+ if (body.background === true)
8822
+ fail("unsupported_request", "Background Responses cannot be represented by this codec");
8823
+ if (body.n !== undefined && body.n !== 1)
8824
+ fail("unsupported_request", "Only one response candidate is supported");
8825
+ if (body.truncation !== undefined && body.truncation !== "disabled")
8826
+ fail("unsupported_request", "Automatic input truncation cannot be represented");
8827
+ if (body.include !== undefined && list(body.include, "include").some((field) => field !== "reasoning.encrypted_content"))
8828
+ fail("unsupported_request", "Requested extra Responses output fields cannot be represented");
8829
+ if (body.prompt_cache_key !== undefined)
8830
+ string(body.prompt_cache_key, "prompt_cache_key");
8831
+ if (body.client_metadata !== undefined)
8832
+ record(body.client_metadata, "client_metadata");
8833
+ const model = string(body.model, "model", true);
8834
+ const diagnostics = [];
8835
+ const input = atParam("input", () => typeof body.input === "string" ? [{ role: "user", content: body.input }] : list(body.input, "input"));
8836
+ if (input.length > budget.maxItems)
8837
+ fail("resource_limit", "Too many input items", "input");
8838
+ const canonicalInput = [];
8839
+ const toolNames = new Map;
8840
+ const byOriginal = new Map;
8841
+ const definitions = new Map;
8842
+ let examinedTools = 0;
8843
+ const tools = [];
8844
+ const identity = (name, namespace) => JSON.stringify([namespace ?? null, name]);
8845
+ const register = (name, namespace, custom) => {
8846
+ const key = identity(name, namespace);
8847
+ if (byOriginal.has(key))
8848
+ fail("tool_name_collision", "Duplicate tool identity");
8849
+ if (toolNames.size >= budget.maxItems)
8850
+ fail("resource_limit", "Too many tools");
8851
+ const wire = `bungee_tool_${toolNames.size}`;
8852
+ toolNames.set(wire, { name, namespace, custom });
8853
+ byOriginal.set(key, wire);
8854
+ return wire;
8855
+ };
8856
+ const expandTool = (rawTool, namespace, path = "tools") => {
8857
+ if (++examinedTools > budget.maxItems)
8858
+ fail("resource_limit", "Too many tool declarations");
8859
+ const tool = record(rawTool, "tool");
8860
+ if (namespace === undefined && capabilities.omitOptionalWebSearch && ["web_search", "web_search_preview"].includes(String(tool.type)) && (body.tool_choice === undefined || body.tool_choice === "auto" || body.tool_choice === "none"))
8861
+ return;
8862
+ const allowed = tool.type === "namespace" ? ["type", "name", "description", "tools"] : tool.type === "custom" ? ["type", "name", "description", "format"] : ["type", "name", "description", "parameters", "strict"];
8863
+ for (const key of Object.keys(tool))
8864
+ if (!allowed.includes(key))
8865
+ fail("unsupported_tool", "Unsupported tool definition field", `${path}.${key}`);
8866
+ if (tool.type === "namespace") {
8867
+ if (namespace !== undefined)
8868
+ fail("unsupported_tool", "Nested tool namespaces are not supported");
8869
+ const name = string(tool.name, "namespace name", true);
8870
+ for (const [index, child] of list(tool.tools, "namespace tools").entries()) {
8871
+ const childPath = `${path}.tools[${index}]`;
8872
+ atParam(childPath, () => expandTool(child, name, childPath));
8873
+ }
8874
+ return;
8875
+ }
8876
+ if (tool.type !== "function" && tool.type !== "custom")
8877
+ fail("unsupported_tool", "This tool type cannot be executed by Codex through the target protocol");
8878
+ serialized(tool, budget.maxArgumentBytes, "tool definition");
8879
+ const name = string(tool.name, "tool name", true);
8880
+ const custom = tool.type === "custom";
8881
+ if (tool.strict !== undefined && typeof tool.strict !== "boolean")
8882
+ fail("invalid_payload", "Tool strict must be boolean");
8883
+ if (protocol === "anthropic_messages" && tool.strict === true)
8884
+ fail("unsupported_tool", "Anthropic strict tool enforcement requires an explicit provider capability");
8885
+ if (custom && tool.format !== undefined) {
8886
+ const format = record(tool.format, "custom format");
8887
+ if (format.type !== "text" && format.type !== "grammar")
8888
+ fail("unsupported_tool", "Unknown custom tool format");
8889
+ }
8890
+ const key = identity(name, namespace);
8891
+ const signature = definitionKey(tool);
8892
+ if (definitions.has(key)) {
8893
+ if (definitions.get(key) !== signature)
8894
+ fail("tool_name_collision", "Conflicting definitions for the same tool identity");
8895
+ return;
8896
+ }
8897
+ const wire = register(name, namespace, custom);
8898
+ definitions.set(key, signature);
8899
+ const parameters = custom ? { type: "object", properties: { input: { type: "string" } }, required: ["input"], additionalProperties: false } : record(tool.parameters ?? { type: "object", properties: {} }, "tool parameters");
8900
+ const description = tool.description === undefined ? undefined : string(tool.description, "tool description");
8901
+ const customHint = custom ? `Pass the complete original custom tool input verbatim in the input string.${tool.format ? ` Original input format: ${JSON.stringify(tool.format)}` : ""}` : undefined;
8902
+ if (protocol === "chat_completions") {
8903
+ tools.push({ type: "function", function: { name: wire, parameters, ...description || customHint ? { description: [description, customHint].filter(Boolean).join(`
8904
+ `) } : {}, ...tool.strict !== undefined ? { strict: tool.strict } : {} } });
8905
+ } else {
8906
+ tools.push({ name: wire, input_schema: parameters, ...description || customHint ? { description: [description, customHint].filter(Boolean).join(`
8907
+ `) } : {} });
8908
+ }
8909
+ };
8910
+ if (body.tools !== undefined)
8911
+ atParam("tools", () => {
8912
+ for (const [index, tool] of list(body.tools, "tools").entries())
8913
+ atParam(`tools[${index}]`, () => expandTool(tool, undefined, `tools[${index}]`));
8914
+ });
8915
+ for (const [index, rawItem] of input.entries())
8916
+ atParam(`input[${index}]`, () => {
8917
+ const item = record(rawItem, "input item");
8918
+ if (item.type !== "additional_tools") {
8919
+ canonicalInput.push(item);
8920
+ return;
8921
+ }
8922
+ if (item.content !== undefined)
8923
+ fail("invalid_payload", "A tool declaration carrier cannot also contain message content", `input[${index}].content`);
8924
+ for (const [toolIndex, tool] of list(item.tools, "additional tools").entries()) {
8925
+ const toolPath = `input[${index}].tools[${toolIndex}]`;
8926
+ atParam(toolPath, () => expandTool(tool, undefined, toolPath));
8927
+ }
8928
+ });
8929
+ const resolve = (item, custom) => {
8930
+ const name = string(item.name, "call name", true);
8931
+ const namespace = item.namespace === undefined ? undefined : string(item.namespace, "call namespace", true);
8932
+ let wire = byOriginal.get(identity(name, namespace));
8933
+ if (wire === undefined)
8934
+ wire = register(name, namespace, custom);
8935
+ if (toolNames.get(wire).custom !== custom)
8936
+ fail("tool_name_collision", "Historical tool kind conflicts with its current declaration");
8937
+ return wire;
8938
+ };
8939
+ const normalizeContent = (rawContent, role) => {
8940
+ if (typeof rawContent === "string")
8941
+ return rawContent;
8942
+ const parts = list(rawContent, "message content");
8943
+ for (const rawPart of parts) {
8944
+ const part = record(rawPart, "content part");
8945
+ if (part.type === "input_text" || part.type === "output_text" || part.type === "text")
8946
+ string(part.text, "content text");
8947
+ else if (part.type === "refusal" && role === "assistant")
8948
+ string(part.refusal, "refusal");
8949
+ else if (part.type === "input_image" && role === "user") {
8950
+ if (protocol !== "chat_completions")
8951
+ fail("unsupported_content", "Anthropic image conversion requires provider-specific URL/data support");
8952
+ string(part.image_url, "image_url", true);
8953
+ if (part.detail !== undefined && !["auto", "low", "high"].includes(String(part.detail)))
8954
+ fail("unsupported_content", "Unknown image detail");
8955
+ } else
8956
+ fail("unsupported_content", "Unsupported message content type");
8957
+ if (part.annotations !== undefined && list(part.annotations, "annotations").length)
8958
+ fail("unsupported_content", "Output annotations cannot be represented in input history");
8959
+ }
8960
+ const normalized = contentNormalizer.normalizeResponsesMessageContent(parts.map((raw) => {
8961
+ const part = raw;
8962
+ return part.type === "refusal" ? { type: "output_text", text: part.refusal } : part;
8963
+ }));
8964
+ for (let i = 0;i < parts.length; i++) {
8965
+ const part = parts[i];
8966
+ if (part.type === "input_image" && part.detail !== undefined)
8967
+ normalized[i].image_url.detail = part.detail;
8968
+ }
8969
+ return normalized;
8970
+ };
8971
+ const chat = [];
8972
+ const messageParams = new WeakMap;
8973
+ let messageParam = "instructions";
8974
+ const callIds = new Set;
8975
+ const answered = new Set;
8976
+ let pendingReasoning = "";
8977
+ const addMessage = (message) => {
8978
+ if (chat.length >= budget.maxItems)
8979
+ fail("resource_limit", "Too many input messages");
8980
+ chat.push(message);
8981
+ messageParams.set(message, messageParam);
8982
+ };
8983
+ if (body.instructions !== undefined)
8984
+ addMessage({ role: "system", content: string(body.instructions, "instructions") });
8985
+ for (const [inputIndex, rawItem] of input.entries())
8986
+ atParam(`input[${inputIndex}]`, () => {
8987
+ messageParam = `input[${inputIndex}]`;
8988
+ const item = record(rawItem, "input item");
8989
+ if (item.type === "additional_tools")
8990
+ return;
8991
+ if (item.encrypted_content != null || item.type === "compaction")
8992
+ fail("unsupported_content", "Encrypted reasoning and compaction cannot be restored");
8993
+ if (item.type === "item_reference")
8994
+ fail("unresolved_reference", "Input item references must be resolved before conversion");
8995
+ if (item.type === "reasoning") {
8996
+ if (protocol !== "chat_completions" || !(capabilities.reasoningHistory || capabilities.reasoningEffort))
8997
+ fail("unsupported_reasoning", "Reasoning history requires an explicit compatible reasoning capability");
8998
+ if (item.content != null)
8999
+ fail("unsupported_reasoning", "Reasoning content requires an explicit history mapping", `${messageParam}.content`);
9000
+ const summary = list(item.summary, "reasoning summary");
9001
+ pendingReasoning += summary.map((part) => {
9002
+ const p = record(part, "reasoning summary");
9003
+ if (p.type !== "summary_text")
9004
+ fail("unsupported_reasoning", "Unknown reasoning summary");
9005
+ return string(p.text, "summary text");
9006
+ }).join("");
9007
+ return;
9008
+ }
9009
+ if (item.type === "function_call" || item.type === "custom_tool_call") {
9010
+ const custom = item.type === "custom_tool_call";
9011
+ const id = string(item.call_id, "call_id", true);
9012
+ if (callIds.has(id))
9013
+ fail("invalid_tool_history", "Duplicate tool call identifier");
9014
+ callIds.add(id);
9015
+ const args = custom ? JSON.stringify({ input: string(item.input, "custom input") }) : string(item.arguments, "arguments");
9016
+ bounded(args, budget.maxArgumentBytes, "arguments");
9017
+ argumentObject(args);
9018
+ const call = { id, type: "function", function: { name: resolve(item, custom), arguments: args } };
9019
+ const previous = chat.at(-1);
9020
+ if (previous?.role === "assistant") {
9021
+ previous.tool_calls = [...previous.tool_calls ?? [], call];
9022
+ if (pendingReasoning)
9023
+ previous.reasoning_content = pendingReasoning;
9024
+ } else
9025
+ addMessage({ role: "assistant", content: null, tool_calls: [call], ...pendingReasoning ? { reasoning_content: pendingReasoning } : {} });
9026
+ pendingReasoning = "";
9027
+ return;
9028
+ }
9029
+ if (item.type === "function_call_output" || item.type === "custom_tool_call_output") {
9030
+ const id = string(item.call_id, "call_id", true);
9031
+ if (!callIds.has(id) || answered.has(id))
9032
+ fail("invalid_tool_history", "Unmatched or duplicate tool result");
9033
+ answered.add(id);
9034
+ const output = typeof item.output === "string" ? item.output : normalizeContent(item.output, "tool");
9035
+ addMessage({ role: "tool", tool_call_id: id, content: output, ...item.is_error === true ? { is_error: true } : {} });
9036
+ return;
9037
+ }
9038
+ if (item.type !== undefined && item.type !== "message")
9039
+ fail("unsupported_content", "Unsupported input item type");
9040
+ const role = string(item.role, "message role", true);
9041
+ if (!["user", "assistant", "system", "developer"].includes(role))
9042
+ fail("unsupported_content", "Unknown message role");
9043
+ const message = { role, content: normalizeContent(item.content, role) };
9044
+ if (pendingReasoning) {
9045
+ if (role !== "assistant")
9046
+ fail("unsupported_reasoning", "Reasoning must precede an assistant item");
9047
+ message.reasoning_content = pendingReasoning;
9048
+ pendingReasoning = "";
9049
+ }
9050
+ addMessage(message);
9051
+ });
9052
+ if (pendingReasoning)
9053
+ fail("unsupported_reasoning", "Orphan reasoning history cannot be represented");
9054
+ const result = { model, messages: chat };
9055
+ for (const key of ["stream", "temperature", "top_p", "parallel_tool_calls", "metadata"])
9056
+ if (body[key] !== undefined)
9057
+ result[key] = body[key];
9058
+ if (protocol === "chat_completions" && body.stream === true)
9059
+ result.stream_options = { include_usage: true };
9060
+ const maxTokens = body.max_output_tokens ?? capabilities.maxOutputTokens;
9061
+ if (maxTokens !== undefined) {
9062
+ if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0)
9063
+ fail("invalid_payload", "max_output_tokens must be a positive integer");
9064
+ result[protocol === "chat_completions" ? "max_completion_tokens" : "max_tokens"] = maxTokens;
9065
+ }
9066
+ if (tools.length)
9067
+ result.tools = tools;
9068
+ if (body.tool_choice !== undefined) {
9069
+ const choice = body.tool_choice;
9070
+ if (typeof choice === "string") {
9071
+ if (!["auto", "none", "required"].includes(choice))
9072
+ fail("unsupported_tool_choice", `Unsupported tool choice ${choice}`);
9073
+ result.tool_choice = protocol === "chat_completions" ? choice : { type: choice === "required" ? "any" : choice };
9074
+ } else {
9075
+ const c = record(choice, "tool_choice");
9076
+ if (c.type !== "function" && c.type !== "custom")
9077
+ fail("unsupported_tool_choice", "Forced hosted tools are unsupported");
9078
+ const wire = byOriginal.get(identity(string(c.name, "tool choice name", true), c.namespace === undefined ? undefined : string(c.namespace, "tool choice namespace")));
9079
+ if (!wire || !tools.some((t) => t.name === wire || t.function?.name === wire))
9080
+ fail("unsupported_tool_choice", "Forced tool is not declared");
9081
+ result.tool_choice = protocol === "chat_completions" ? { type: "function", function: { name: wire } } : { type: "tool", name: wire };
9082
+ }
9083
+ }
9084
+ applyResponsesPreferences(body, result, protocol, capabilities, maxTokens, diagnostics);
9085
+ if (body.text !== undefined)
9086
+ atParam("text.format", () => {
9087
+ const text = record(body.text, "text");
9088
+ if (text.format !== undefined) {
9089
+ const format = record(text.format, "text format");
9090
+ const allowed = format.type === "json_schema" ? ["type", "name", "schema", "strict", "description"] : ["type"];
9091
+ for (const key of Object.keys(format))
9092
+ if (!allowed.includes(key))
9093
+ fail("unsupported_request", "Unsupported output format field", `text.format.${key}`);
9094
+ if (format.type === "json_schema") {
9095
+ atParam("text.format.name", () => string(format.name, "output schema name", true));
9096
+ atParam("text.format.schema", () => record(format.schema, "output schema"));
9097
+ if (format.strict !== undefined && typeof format.strict !== "boolean")
9098
+ fail("invalid_payload", "Output schema strict must be boolean", "text.format.strict");
9099
+ if (format.description !== undefined)
9100
+ atParam("text.format.description", () => string(format.description, "output schema description"));
9101
+ }
9102
+ if (protocol !== "chat_completions" && format.type !== "text")
9103
+ fail("unsupported_request", "Anthropic structured output requires a separate provider capability");
9104
+ if (format.type === "json_schema")
9105
+ result.response_format = { type: "json_schema", json_schema: { name: format.name, schema: format.schema, ...format.strict !== undefined ? { strict: format.strict } : {}, ...format.description !== undefined ? { description: format.description } : {} } };
9106
+ else if (format.type === "json_object" || format.type === "text") {
9107
+ if (protocol === "chat_completions")
9108
+ result.response_format = format;
9109
+ } else
9110
+ fail("unsupported_request", "Unknown text format");
9111
+ }
9112
+ });
9113
+ if (protocol === "anthropic_messages") {
9114
+ const system = [];
9115
+ const messages = [];
9116
+ const append = (role, content) => {
9117
+ const previous = messages.at(-1);
9118
+ if (previous?.role === role)
9119
+ previous.content.push(...content);
9120
+ else
9121
+ messages.push({ role, content });
9122
+ };
9123
+ for (const msg of chat) {
9124
+ if (msg.role === "system" || msg.role === "developer") {
9125
+ if (messages.length)
9126
+ fail("unsupported_content", "Mid-conversation system instructions cannot be relocated safely", `${messageParams.get(msg)}.role`);
9127
+ system.push(typeof msg.content === "string" ? msg.content : msg.content.map((p) => string(p.text, "system text")).join(""));
9128
+ continue;
9129
+ }
9130
+ if (msg.role === "tool") {
9131
+ append("user", [{ type: "tool_result", tool_use_id: msg.tool_call_id, content: msg.content, ...msg.is_error ? { is_error: true } : {} }]);
9132
+ } else {
9133
+ const content = msg.content == null ? [] : typeof msg.content === "string" ? [{ type: "text", text: msg.content }] : msg.content;
9134
+ for (const call of msg.tool_calls ?? []) {
9135
+ const fn = call.function;
9136
+ content.push({ type: "tool_use", id: call.id, name: fn.name, input: argumentObject(fn.arguments) });
9137
+ }
9138
+ append(msg.role, content);
9139
+ }
9140
+ }
9141
+ result.messages = messages;
9142
+ if (system.length)
9143
+ result.system = system.join(`
9144
+ `);
9145
+ if (maxTokens === undefined)
9146
+ fail("missing_capability", "Anthropic requires max_output_tokens or capabilities.maxOutputTokens");
9147
+ if (body.parallel_tool_calls !== undefined) {
9148
+ result.tool_choice = { ...record(result.tool_choice ?? { type: "auto" }, "tool_choice"), disable_parallel_tool_use: body.parallel_tool_calls === false };
9149
+ delete result.parallel_tool_calls;
9150
+ }
9151
+ }
9152
+ return { body: result, toolNames, canonicalInput, diagnostics };
9153
+ }
9154
+ // src/responses-codec/events.ts
9155
+ class ResponsesEventEncoder {
9156
+ protocol;
9157
+ model;
9158
+ toolNames;
9159
+ budget;
9160
+ id = `resp_${crypto.randomUUID().replaceAll("-", "")}`;
9161
+ createdAt = Math.floor(Date.now() / 1000);
9162
+ output = [];
9163
+ states = new Map;
9164
+ anthropicBlocks = new Map;
9165
+ usage = {};
9166
+ end;
9167
+ error;
9168
+ started = false;
9169
+ anthropicStarted = false;
9170
+ anthropicStopped = false;
9171
+ finalized = false;
9172
+ poisoned = false;
9173
+ sequence = 0;
9174
+ retained = 0;
9175
+ constructor(protocol, model, toolNames = new Map, configuredLimits) {
9176
+ this.protocol = protocol;
9177
+ this.model = model;
9178
+ this.toolNames = toolNames;
9179
+ this.budget = limits(configuredLimits);
9180
+ }
9181
+ event(type, fields = {}) {
9182
+ return { type, sequence_number: this.sequence++, ...fields };
9183
+ }
9184
+ start(events) {
9185
+ if (this.started)
9186
+ return;
9187
+ this.started = true;
9188
+ const payload = { id: this.id, object: "response", created_at: this.createdAt, model: this.model, status: "in_progress", output: [], usage: null };
9189
+ events.push(this.event("response.created", { response: payload }), this.event("response.in_progress", { response: { ...payload } }));
9190
+ }
9191
+ retain(text) {
9192
+ this.retained += bytes(text);
9193
+ if (this.retained > this.budget.maxOutputBytes)
9194
+ fail("resource_limit", "Accumulated response output exceeds maxOutputBytes");
9195
+ }
9196
+ state(key, kind) {
9197
+ const existing = this.states.get(key);
9198
+ if (existing) {
9199
+ if (existing.kind !== kind)
9200
+ fail("invalid_stream", "Output item changed kind");
9201
+ return existing;
9202
+ }
9203
+ if (this.output.length >= this.budget.maxItems)
9204
+ fail("resource_limit", "Too many output items");
9205
+ const state = { key, index: this.output.length, kind, id: `${this.id}_item_${this.output.length}`, text: "", name: "", callId: "", args: "", emittedInput: "", added: false, closed: false };
9206
+ this.states.set(key, state);
9207
+ this.output.push(state);
9208
+ return state;
9209
+ }
9210
+ fields(state) {
9211
+ return { response_id: this.id, item_id: state.id, output_index: state.index };
9212
+ }
9213
+ item(state, status, strict = false) {
9214
+ if (state.kind === "tool") {
9215
+ if (strict)
9216
+ return { ...toolItem(state.callId, state.name, state.args || "{}", this.toolNames, status), id: state.id };
9217
+ const original = state.original ?? restoreTool(state.name, this.toolNames);
9218
+ return {
9219
+ id: state.id,
9220
+ type: original.custom ? "custom_tool_call" : "function_call",
9221
+ call_id: state.callId,
9222
+ name: original.name,
9223
+ ...original.namespace ? { namespace: original.namespace } : {},
9224
+ status,
9225
+ ...original.custom ? { input: state.emittedInput } : { arguments: state.args }
9226
+ };
9227
+ }
9228
+ if (state.kind === "reasoning")
9229
+ return { id: state.id, type: "reasoning", summary: status === "in_progress" ? [] : [{ type: "summary_text", text: state.text }] };
9230
+ return {
9231
+ id: state.id,
9232
+ type: "message",
9233
+ role: "assistant",
9234
+ status,
9235
+ content: status === "in_progress" ? [] : [this.part(state)]
9236
+ };
9237
+ }
9238
+ part(state) {
9239
+ return state.kind === "refusal" ? { type: "refusal", refusal: state.text } : state.kind === "reasoning" ? { type: "summary_text", text: state.text } : { type: "output_text", text: state.text, annotations: [] };
9240
+ }
9241
+ add(state, events, final = false) {
9242
+ if (state.added)
9243
+ return;
9244
+ if (state.kind === "tool") {
9245
+ if (!state.callId || !state.name || this.toolNames.size && !this.toolNames.has(state.name))
9246
+ return;
9247
+ if (!final && !state.args && [...this.toolNames.keys()].some((name) => name !== state.name && name.startsWith(state.name)))
9248
+ return;
9249
+ state.original = restoreTool(state.name, this.toolNames);
9250
+ }
9251
+ state.added = true;
9252
+ const initial = this.item(state, "in_progress");
9253
+ if (state.kind === "tool") {
9254
+ if (state.original?.custom)
9255
+ initial.input = "";
9256
+ else
9257
+ initial.arguments = "";
9258
+ }
9259
+ events.push(this.event("response.output_item.added", { response_id: this.id, output_index: state.index, item: initial }));
9260
+ if (state.kind !== "tool") {
9261
+ const part = state.kind === "reasoning" ? { type: "summary_text", text: "" } : state.kind === "refusal" ? { type: "refusal", refusal: "" } : { type: "output_text", text: "", annotations: [] };
9262
+ events.push(this.event(state.kind === "reasoning" ? "response.reasoning_summary_part.added" : "response.content_part.added", { ...this.fields(state), [state.kind === "reasoning" ? "summary_index" : "content_index"]: 0, part }));
9263
+ }
9264
+ }
9265
+ appendText(state, delta, events) {
9266
+ if (state.closed)
9267
+ fail("invalid_stream", "Delta arrived after content block stop");
9268
+ this.retain(delta);
9269
+ state.text += delta;
9270
+ this.add(state, events);
9271
+ const type = state.kind === "reasoning" ? "response.reasoning_summary_text.delta" : state.kind === "refusal" ? "response.refusal.delta" : "response.output_text.delta";
9272
+ if (delta)
9273
+ events.push(this.event(type, { ...this.fields(state), [state.kind === "reasoning" ? "summary_index" : "content_index"]: 0, delta }));
9274
+ }
9275
+ emitArgs(state, events, fragment = "", first = false) {
9276
+ if (!state.added)
9277
+ return;
9278
+ if (state.original?.custom) {
9279
+ const prefix = customInputPrefix(state.args);
9280
+ if (!prefix.startsWith(state.emittedInput))
9281
+ fail("invalid_tool_arguments", "Custom input prefix changed");
9282
+ const delta = prefix.slice(state.emittedInput.length);
9283
+ state.emittedInput = prefix;
9284
+ if (delta)
9285
+ events.push(this.event("response.custom_tool_call_input.delta", { ...this.fields(state), delta }));
9286
+ } else {
9287
+ const delta = first ? state.args : fragment;
9288
+ if (delta)
9289
+ events.push(this.event("response.function_call_arguments.delta", { ...this.fields(state), delta }));
9290
+ }
9291
+ }
9292
+ toolDelta(state, raw, events) {
9293
+ if (state.closed)
9294
+ fail("invalid_stream", "Tool delta arrived after content block stop");
9295
+ if (raw.id !== undefined) {
9296
+ const id = string(raw.id, "tool call id", true);
9297
+ if (state.callId && state.callId !== id)
9298
+ fail("invalid_stream", "Tool call id changed");
9299
+ if (!state.callId)
9300
+ this.retain(id);
9301
+ state.callId = id;
9302
+ }
9303
+ const fn = raw.function === undefined ? {} : record(raw.function, "tool function");
9304
+ if (fn.name !== undefined) {
9305
+ const name = string(fn.name, "tool name");
9306
+ if (state.added && name)
9307
+ fail("invalid_stream", "Tool name changed after output_item.added");
9308
+ this.retain(name);
9309
+ state.name += name;
9310
+ }
9311
+ const args = fn.arguments === undefined ? "" : string(fn.arguments, "tool arguments delta");
9312
+ this.retain(args);
9313
+ state.args = bounded(state.args + args, this.budget.maxArgumentBytes, "tool arguments");
9314
+ const wasAdded = state.added;
9315
+ this.add(state, events);
9316
+ this.emitArgs(state, events, args, !wasAdded);
9317
+ }
9318
+ push(raw) {
9319
+ if (this.poisoned)
9320
+ fail("invalid_stream", "Encoder cannot continue after a codec error");
9321
+ if (this.finalized)
9322
+ fail("invalid_stream", "Event arrived after response terminal");
9323
+ try {
9324
+ const chunk = record(raw, "stream event");
9325
+ serialized(chunk, this.budget.maxOutputBytes, "stream event");
9326
+ const events = [];
9327
+ this.start(events);
9328
+ if (chunk.error !== undefined || chunk.type === "error") {
9329
+ this.error = record(chunk.error, "upstream error");
9330
+ this.end = { status: "failed", reason: "upstream_error" };
9331
+ this.anthropicStopped = true;
9332
+ return [...events, ...this.complete()];
9333
+ }
9334
+ if (this.protocol === "chat_completions")
9335
+ this.chat(chunk, events);
9336
+ else
9337
+ this.anthropic(chunk, events);
9338
+ return events;
9339
+ } catch (error) {
9340
+ this.poisoned = true;
9341
+ throw error;
9342
+ }
9343
+ }
9344
+ chat(chunk, events) {
9345
+ const choices = list(chunk.choices, "choices");
9346
+ if (choices.length > 1)
9347
+ fail("unsupported_response", "Multiple response candidates are not supported");
9348
+ if (choices.length) {
9349
+ const choice = record(choices[0], "choice");
9350
+ if (choice.index !== undefined && choice.index !== 0)
9351
+ fail("unsupported_response", "Only choice index 0 is supported");
9352
+ const delta = record(choice.delta ?? {}, "choice delta");
9353
+ for (const key of Object.keys(delta))
9354
+ if (!["role", "content", "refusal", "reasoning_content", "reasoning", "tool_calls", "function_call", "annotations"].includes(key))
9355
+ fail("unsupported_content", `Unsupported Chat output field ${key}`);
9356
+ if (delta.annotations !== undefined && list(delta.annotations, "annotations").length)
9357
+ fail("unsupported_content", "Chat output annotations cannot be faithfully converted");
9358
+ if (delta.role !== undefined && delta.role !== "assistant")
9359
+ fail("unsupported_response", "Response role must be assistant");
9360
+ if (this.end && Object.values(delta).some((v) => v !== undefined && v !== null && v !== "" && (!Array.isArray(v) || v.length)))
9361
+ fail("invalid_stream", "Output arrived after finish_reason");
9362
+ const reasoning = delta.reasoning_content ?? delta.reasoning;
9363
+ if (reasoning !== undefined && reasoning !== null)
9364
+ this.appendText(this.state("reasoning", "reasoning"), string(reasoning, "reasoning delta"), events);
9365
+ if (delta.content !== undefined && delta.content !== null)
9366
+ this.appendText(this.state("text", "text"), string(delta.content, "text delta"), events);
9367
+ if (delta.refusal !== undefined && delta.refusal !== null)
9368
+ this.appendText(this.state("refusal", "refusal"), string(delta.refusal, "refusal delta"), events);
9369
+ if (delta.tool_calls !== undefined) {
9370
+ for (const rawTool of list(delta.tool_calls, "tool_calls")) {
9371
+ const tool = record(rawTool, "tool call");
9372
+ if (!Number.isSafeInteger(tool.index) || tool.index < 0)
9373
+ fail("invalid_stream", "Streamed tool requires a non-negative integer index");
9374
+ if (tool.type !== undefined && tool.type !== "function")
9375
+ fail("unsupported_tool", "Unknown upstream tool call type");
9376
+ this.toolDelta(this.state(`tool:${tool.index}`, "tool"), tool, events);
9377
+ }
9378
+ }
9379
+ if (delta.function_call !== undefined)
9380
+ fail("unsupported_response", "Legacy function_call lacks a stable call_id");
9381
+ if (choice.finish_reason !== undefined && choice.finish_reason !== null) {
9382
+ const next = terminal(choice.finish_reason, this.protocol);
9383
+ if (this.end && (this.end.status !== next.status || this.end.reason !== next.reason))
9384
+ fail("invalid_stream", "Conflicting finish reasons");
9385
+ this.end = next;
9386
+ }
9387
+ }
9388
+ this.usage = normalizeUsage(chunk.usage, this.protocol, this.usage);
9389
+ }
9390
+ anthropic(chunk, events) {
9391
+ if (chunk.type === "ping")
9392
+ return;
9393
+ if (this.anthropicStopped)
9394
+ fail("invalid_stream", "Event arrived after message_stop");
9395
+ if (chunk.type === "message_start") {
9396
+ if (this.anthropicStarted)
9397
+ fail("invalid_stream", "Duplicate message_start");
9398
+ const message = record(chunk.message, "message");
9399
+ this.anthropicStarted = true;
9400
+ this.usage = normalizeUsage(message.usage, this.protocol, this.usage);
9401
+ return;
9402
+ }
9403
+ if (!this.anthropicStarted)
9404
+ fail("invalid_stream", "Anthropic event arrived before message_start");
9405
+ if (chunk.type === "message_delta") {
9406
+ const delta = record(chunk.delta, "message delta");
9407
+ if (delta.stop_reason != null) {
9408
+ const next = terminal(delta.stop_reason, this.protocol);
9409
+ if (this.end && (this.end.status !== next.status || this.end.reason !== next.reason))
9410
+ fail("invalid_stream", "Conflicting stop reasons");
9411
+ this.end = next;
9412
+ }
9413
+ this.usage = normalizeUsage(chunk.usage, this.protocol, this.usage);
9414
+ return;
9415
+ }
9416
+ if (chunk.type === "message_stop") {
9417
+ if (!this.end)
9418
+ fail("missing_terminal", "message_stop has no preceding stop_reason");
9419
+ if ([...this.anthropicBlocks.values()].some((s) => !s.closed))
9420
+ fail("invalid_stream", "message_stop arrived before content_block_stop");
9421
+ this.anthropicStopped = true;
9422
+ return;
9423
+ }
9424
+ if (!Number.isSafeInteger(chunk.index) || chunk.index < 0)
9425
+ fail("invalid_stream", "Anthropic content requires a non-negative integer index");
9426
+ const index = chunk.index;
9427
+ if (this.end)
9428
+ fail("invalid_stream", "Content arrived after stop_reason");
9429
+ if (chunk.type === "content_block_start") {
9430
+ if (this.anthropicBlocks.has(index))
9431
+ fail("invalid_stream", "Duplicate content_block_start");
9432
+ const block = record(chunk.content_block, "content block");
9433
+ const kind = block.type === "tool_use" ? "tool" : block.type === "text" ? "text" : block.type === "thinking" ? "reasoning" : undefined;
9434
+ if (!kind)
9435
+ fail("unsupported_content", `Unsupported Anthropic block ${String(block.type)}`);
9436
+ const state = this.state(`block:${index}`, kind);
9437
+ this.anthropicBlocks.set(index, state);
9438
+ if (kind === "tool") {
9439
+ const input = record(block.input ?? {}, "tool input");
9440
+ this.toolDelta(state, { id: block.id, function: { name: block.name, ...Object.keys(input).length ? { arguments: serialized(input, this.budget.maxArgumentBytes, "tool input") } : {} } }, events);
9441
+ } else
9442
+ this.appendText(state, string(block[kind === "text" ? "text" : "thinking"] ?? "", "block text"), events);
9443
+ return;
9444
+ }
9445
+ const state = this.anthropicBlocks.get(index);
9446
+ if (!state || state.closed)
9447
+ fail("invalid_stream", "Content event has no open block");
9448
+ if (chunk.type === "content_block_stop") {
9449
+ state.closed = true;
9450
+ return;
9451
+ }
9452
+ if (chunk.type !== "content_block_delta")
9453
+ fail("unsupported_response", `Unknown Anthropic event ${String(chunk.type)}`);
9454
+ const delta = record(chunk.delta, "content delta");
9455
+ if (delta.type === "input_json_delta" && state.kind === "tool")
9456
+ this.toolDelta(state, { function: { arguments: delta.partial_json } }, events);
9457
+ else if (delta.type === "text_delta" && state.kind === "text")
9458
+ this.appendText(state, string(delta.text, "text delta"), events);
9459
+ else if (delta.type === "thinking_delta" && state.kind === "reasoning")
9460
+ this.appendText(state, string(delta.thinking, "thinking delta"), events);
9461
+ else if (delta.type === "signature_delta" && state.kind === "reasoning") {
9462
+ bounded(string(delta.signature, "signature"), this.budget.maxArgumentBytes, "signature");
9463
+ } else
9464
+ fail("unsupported_content", `Unsupported content delta ${String(delta.type)}`);
9465
+ }
9466
+ complete() {
9467
+ if (!this.end)
9468
+ fail("missing_terminal", "Stream ended without an explicit upstream terminal");
9469
+ const events = [];
9470
+ const items = [];
9471
+ const callIds = new Set;
9472
+ for (const state of this.output) {
9473
+ const strict = this.end.status === "completed";
9474
+ if (state.kind === "tool") {
9475
+ if (!state.callId || !state.name)
9476
+ fail("invalid_tool_arguments", "Tool call is missing id or name");
9477
+ if (callIds.has(state.callId))
9478
+ fail("invalid_tool_arguments", "Duplicate provider call_id");
9479
+ callIds.add(state.callId);
9480
+ state.original ??= restoreTool(state.name, this.toolNames);
9481
+ this.add(state, events, true);
9482
+ if (!state.added)
9483
+ fail("unknown_tool", `Unknown tool ${state.name}`);
9484
+ if (strict) {
9485
+ argumentObject(state.args || "{}");
9486
+ if (state.original.custom) {
9487
+ const text = customInput(state.args);
9488
+ if (!text.startsWith(state.emittedInput))
9489
+ fail("invalid_tool_arguments", "Custom tool input changed");
9490
+ if (text.length > state.emittedInput.length)
9491
+ events.push(this.event("response.custom_tool_call_input.delta", { ...this.fields(state), delta: text.slice(state.emittedInput.length) }));
9492
+ state.emittedInput = text;
9493
+ }
9494
+ }
9495
+ }
9496
+ const item = this.item(state, this.end.status === "completed" ? "completed" : "incomplete", strict);
9497
+ const fields = this.fields(state);
9498
+ if (state.kind === "tool") {
9499
+ const custom = state.original.custom;
9500
+ events.push(this.event(custom ? "response.custom_tool_call_input.done" : "response.function_call_arguments.done", { ...fields, [custom ? "input" : "arguments"]: custom ? item.input : item.arguments }));
9501
+ } else {
9502
+ const reasoning = state.kind === "reasoning";
9503
+ const textField = state.kind === "refusal" ? "refusal" : "text";
9504
+ events.push(this.event(reasoning ? "response.reasoning_summary_text.done" : state.kind === "refusal" ? "response.refusal.done" : "response.output_text.done", { ...fields, [reasoning ? "summary_index" : "content_index"]: 0, [textField]: state.text }));
9505
+ events.push(this.event(reasoning ? "response.reasoning_summary_part.done" : "response.content_part.done", { ...fields, [reasoning ? "summary_index" : "content_index"]: 0, part: this.part(state) }));
9506
+ }
9507
+ events.push(this.event("response.output_item.done", { response_id: this.id, output_index: state.index, item }));
9508
+ items.push(item);
9509
+ }
9510
+ const response = responsePayload(this.id, this.model, items, this.end, this.usage, this.error, this.createdAt);
9511
+ serialized(response, this.budget.maxOutputBytes, "final response");
9512
+ events.push(this.event(`response.${this.end.status}`, { response }));
9513
+ this.finalized = true;
9514
+ return events;
9515
+ }
9516
+ finish() {
9517
+ if (this.poisoned)
9518
+ fail("invalid_stream", "Encoder cannot finish after a codec error");
9519
+ if (this.finalized)
9520
+ return [];
9521
+ try {
9522
+ if (this.protocol === "anthropic_messages" && !this.anthropicStopped)
9523
+ fail("missing_terminal", "Anthropic stream ended before message_stop");
9524
+ return this.complete();
9525
+ } catch (error) {
9526
+ this.poisoned = true;
9527
+ throw error;
9528
+ }
9529
+ }
9530
+ }
9531
+
9532
+ // src/responses-codec/result.ts
9533
+ function encodeResponsesResult(raw, protocol, model, toolNames = new Map, configuredLimits) {
9534
+ const body = record(raw, "upstream response");
9535
+ const budget = limits(configuredLimits);
9536
+ serialized(body, budget.maxOutputBytes, "upstream response");
9537
+ if (body.error !== undefined || body.type === "error")
9538
+ return responsePayload(`resp_${crypto.randomUUID().replaceAll("-", "")}`, model, [], { status: "failed", reason: "upstream_error" }, {}, record(body.error, "upstream error"));
9539
+ const encoder = new ResponsesEventEncoder(protocol, model, toolNames, configuredLimits);
9540
+ if (protocol === "chat_completions") {
9541
+ const choices = list(body.choices, "choices");
9542
+ if (choices.length !== 1)
9543
+ fail("unsupported_response", "Exactly one response choice is required");
9544
+ const choice = record(choices[0], "choice");
9545
+ const message = record(choice.message, "message");
9546
+ const delta = { ...message };
9547
+ if (message.tool_calls !== undefined)
9548
+ delta.tool_calls = list(message.tool_calls, "tool_calls").map((tool, index) => ({ ...record(tool, "tool call"), index }));
9549
+ encoder.push({ choices: [{ index: choice.index ?? 0, delta, finish_reason: choice.finish_reason }], usage: body.usage });
9550
+ } else {
9551
+ encoder.push({ type: "message_start", message: { usage: body.usage } });
9552
+ for (const [index, rawBlock] of list(body.content, "content").entries()) {
9553
+ const block = record(rawBlock, "content block");
9554
+ encoder.push({ type: "content_block_start", index, content_block: block });
9555
+ encoder.push({ type: "content_block_stop", index });
9556
+ }
9557
+ encoder.push({ type: "message_delta", delta: { stop_reason: body.stop_reason } });
9558
+ encoder.push({ type: "message_stop" });
9559
+ }
9560
+ const end = encoder.finish().at(-1);
9561
+ if (!end)
9562
+ fail("missing_terminal", "JSON response has no terminal result");
9563
+ return record(end.response, "Responses result");
9564
+ }
8502
9565
  export {
8503
9566
  AnthropicAdapter,
8504
9567
  AnthropicToGeminiConverter,
8505
9568
  AnthropicToOpenAIConverter,
9569
+ DEFAULT_RESPONSES_CODEC_LIMITS,
8506
9570
  GeminiToAnthropicConverter,
8507
9571
  GeminiToOpenAIConverter,
8508
9572
  LLMProtocolAdapterRegistry,
@@ -8517,6 +9581,8 @@ export {
8517
9581
  OpenAIToGeminiConverter,
8518
9582
  PROVIDER_TOKEN_ACCOUNTING_CAPABILITY_MATRIX,
8519
9583
  ProtocolTransformerRegistry,
9584
+ ResponsesCodecError,
9585
+ ResponsesEventEncoder,
8520
9586
  SUPPORTED_TOKEN_ACCOUNTING_PROVIDERS,
8521
9587
  TOKEN_ACCOUNTING_AUTHORITIES,
8522
9588
  TOKEN_ACCOUNTING_OUTCOMES,
@@ -8531,6 +9597,8 @@ export {
8531
9597
  countTextTokens,
8532
9598
  createPartialTokenAccountingEvent,
8533
9599
  createTokenAccountingSession,
9600
+ decodeResponsesRequest,
9601
+ encodeResponsesResult,
8534
9602
  finalizeTokenAccountingEvent,
8535
9603
  generateAnthropicMessageId,
8536
9604
  generateGeminiCandidateId,