@jeffusion/bungee-core 6.4.1 → 6.5.1

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.
@@ -5104,7 +5104,21 @@ function validateUsage(raw, protocol) {
5104
5104
  if (raw === undefined || raw === null)
5105
5105
  return;
5106
5106
  const u = record(raw, "usage");
5107
- const keys = protocol === "chat_completions" ? ["prompt_tokens", "completion_tokens", "total_tokens", "prompt_tokens_details", "completion_tokens_details"] : protocol === "anthropic_messages" ? ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"] : ["input_tokens", "output_tokens", "total_tokens", "input_tokens_details", "output_tokens_details"];
5107
+ if (protocol === "chat_completions") {
5108
+ for (const key of ["prompt_tokens", "completion_tokens", "total_tokens"])
5109
+ if (u[key] !== undefined && u[key] !== null)
5110
+ integer(u[key], `usage.${key}`);
5111
+ for (const [container, counter] of [["prompt_tokens_details", "cached_tokens"], ["completion_tokens_details", "reasoning_tokens"]]) {
5112
+ const value = u[container];
5113
+ if (value === undefined || value === null)
5114
+ continue;
5115
+ const details = record(value, `usage.${container}`);
5116
+ if (details[counter] !== undefined && details[counter] !== null)
5117
+ integer(details[counter], `usage.${container}.${counter}`);
5118
+ }
5119
+ return;
5120
+ }
5121
+ const keys = protocol === "anthropic_messages" ? ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"] : ["input_tokens", "output_tokens", "total_tokens", "input_tokens_details", "output_tokens_details"];
5108
5122
  fields2(u, keys, "usage", "unsupported_response");
5109
5123
  for (const [key, value] of Object.entries(u)) {
5110
5124
  if (key.endsWith("_details")) {
@@ -6515,8 +6529,10 @@ class AgentRelay {
6515
6529
  }
6516
6530
 
6517
6531
  // plugins/llm-protocol-adapter/server/service.ts
6518
- var REASONING_RULES_VERSION = "2026-10-12.1";
6532
+ var REASONING_RULES_VERSION = "2026-10-24.1";
6519
6533
  var concrete = new Set(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
6534
+ var effortProtocols = new Set(["responses", "chat_completions", "anthropic_messages"]);
6535
+ var anthropicEfforts = new Set(["low", "medium", "high", "xhigh", "max"]);
6520
6536
  function identityError(code, message) {
6521
6537
  throw new ResponsesCodecError(code, message, "modelIdentity");
6522
6538
  }
@@ -6535,41 +6551,29 @@ function checkedIdentity(value, model) {
6535
6551
  identityError("model_identity_mismatch", "Declared wire model differs from the actual target");
6536
6552
  return identity;
6537
6553
  }
6538
- function rule(provider, model, protocol) {
6539
- if (provider === "zai" && ["glm-5.3", "glm-5.3-flash"].includes(model) && protocol === "chat_completions")
6540
- return { values: ["low", "high", "max"], default: "max", kind: "chat" };
6541
- if (provider === "anthropic" && protocol === "anthropic_messages") {
6542
- if (["claude-opus-4-7", "claude-opus-4-8", "claude-opus-5", "claude-opus-5-5", "claude-sonnet-5", "claude-sonnet-5-5", "claude-haiku-5-5"].includes(model))
6543
- return { values: ["low", "medium", "high", "xhigh", "max"], default: ["claude-opus-5-5", "claude-haiku-5-5"].includes(model) ? "medium" : "high", kind: "anthropic" };
6544
- if (["claude-opus-4-6", "claude-sonnet-4-6"].includes(model))
6545
- return { values: ["low", "medium", "high", "max"], default: "high", kind: "anthropic" };
6546
- }
6547
- return null;
6548
- }
6549
6554
  function resolveCapabilities(catalog, context) {
6550
6555
  const info = catalog.model(context);
6551
6556
  if (!info)
6552
6557
  return null;
6553
- const mapping = rule(context.provider, context.model, context.targetProtocol), restrictions = context.restrictions;
6554
- const candidates = info.reasoningOptionsStatus === "known" ? info.reasoningOptions?.flatMap((option) => option.type === "effort" ? option.values.filter((value) => typeof value === "string" && concrete.has(value)) : []) ?? [] : [];
6555
- const selected = mapping && info.reasoning && restrictions?.reasoning !== false ? [...new Set(candidates)].filter((v) => mapping.values.includes(v)) : [];
6556
- const supportedEfforts = mapping && selected.includes(mapping.default) ? selected : [];
6557
- const defaultEffort = supportedEfforts.length ? mapping.default : null;
6558
- const wirePolicy = supportedEfforts.length ? { effortMap: Object.fromEntries(supportedEfforts.map((value) => [value, value])), ...mapping.kind === "anthropic" ? { anthropicThinkingMode: "adaptive" } : {} } : null;
6558
+ const restrictions = context.restrictions, reasoningAllowed = info.reasoning && restrictions?.reasoning !== false;
6559
+ const allowed = context.targetProtocol === "anthropic_messages" ? anthropicEfforts : concrete;
6560
+ const candidates = reasoningAllowed && effortProtocols.has(context.targetProtocol) && info.reasoningOptionsStatus === "known" ? info.reasoningOptions?.flatMap((option) => option.type === "effort" ? option.values.filter((value) => typeof value === "string" && allowed.has(value)) : []) ?? [] : [];
6561
+ const supportedEfforts = [...new Set(candidates)];
6562
+ const wirePolicy = supportedEfforts.length ? { effortMap: Object.fromEntries(supportedEfforts.map((value) => [value, value])) } : null;
6559
6563
  return {
6560
6564
  ...info,
6561
6565
  contextWindow: info.contextWindow === null ? null : Math.min(info.contextWindow, restrictions?.contextWindow ?? info.contextWindow),
6562
6566
  toolCall: info.toolCall && restrictions?.tools !== false,
6563
6567
  inputModalities: info.inputModalities.filter((v) => v !== "image" || restrictions?.images !== false),
6564
6568
  supportedEfforts,
6565
- defaultEffort,
6569
+ defaultEffort: null,
6566
6570
  rulesVersion: REASONING_RULES_VERSION,
6567
6571
  wirePolicy,
6568
6572
  codecCapabilities: {
6569
6573
  maxOutputTokens: info.outputLimit ?? undefined,
6570
- reasoningHistory: info.reasoning && restrictions?.reasoning !== false,
6574
+ reasoningHistory: reasoningAllowed,
6571
6575
  reasoningEffort: supportedEfforts.length > 0,
6572
- anthropicEffort: mapping?.kind === "anthropic" && supportedEfforts.length > 0,
6576
+ anthropicEffort: supportedEfforts.length > 0,
6573
6577
  anthropicStructuredOutput: context.provider === "anthropic" && /^claude-(?:opus-(?:4-[5678]|5(?:-5)?)|sonnet-(?:4-[56]|5(?:-5)?)|haiku-(?:4-5|5-5))$/.test(context.model),
6574
6578
  anthropicStrictTools: context.provider === "anthropic" && /^claude-(?:opus-(?:4-[5678]|5(?:-5)?)|sonnet-(?:4-[56]|5(?:-5)?)|haiku-(?:4-5|5-5))$/.test(context.model),
6575
6579
  geminiJsonSchema: false
@@ -6582,6 +6586,10 @@ function fail2(param) {
6582
6586
  function wireEffort(body, protocol) {
6583
6587
  return protocol === "responses" ? body?.reasoning?.effort : protocol === "chat_completions" ? body?.reasoning_effort : protocol === "anthropic_messages" ? body?.output_config?.effort : body?.generationConfig?.thinkingConfig?.thinkingLevel;
6584
6588
  }
6589
+ function chatMessages(body) {
6590
+ const messages = body && typeof body === "object" ? body.messages : undefined;
6591
+ return Array.isArray(messages) ? messages : [];
6592
+ }
6585
6593
  function conversionService(catalog, worker, history, runBackground) {
6586
6594
  const relay = new AgentRelay(history, Date.now, runBackground);
6587
6595
  return {
@@ -6617,7 +6625,7 @@ function conversionService(catalog, worker, history, runBackground) {
6617
6625
  identityError("model_identity_unavailable", "Declared catalog model is unavailable");
6618
6626
  const entryProfile = profile;
6619
6627
  let catalogVersion = profile?.catalogVersion ?? null;
6620
- let selected = context.selectedEffort;
6628
+ let selected = context.selectedEffort, rewrittenDeveloper = false;
6621
6629
  const passthrough = context.sourceProtocol === context.targetProtocol;
6622
6630
  if (!passthrough && selected !== undefined && (!profile || !profile.supportedEfforts.includes(selected)))
6623
6631
  fail2("reasoning.effort");
@@ -6626,6 +6634,7 @@ function conversionService(catalog, worker, history, runBackground) {
6626
6634
  capabilities: profile?.codecCapabilities ?? context.capabilities,
6627
6635
  reasoningPolicy: profile?.wirePolicy ?? context.reasoningPolicy
6628
6636
  });
6637
+ const deepseekChat = context.sourceProtocol === "responses" && context.targetProtocol === "chat_completions" && profile?.provider === "deepseek";
6629
6638
  let disposed = false;
6630
6639
  const session = {
6631
6640
  convertRequest(raw) {
@@ -6636,8 +6645,14 @@ function conversionService(catalog, worker, history, runBackground) {
6636
6645
  selected = effort;
6637
6646
  }
6638
6647
  const result = engine.convertRequest(raw);
6639
- if (!passthrough && selected !== undefined && context.targetProtocol === "chat_completions" && profile?.provider === "zai")
6640
- result.body.thinking = { type: "enabled" };
6648
+ rewrittenDeveloper = false;
6649
+ if (deepseekChat) {
6650
+ for (const message of chatMessages(result.body))
6651
+ if (message.role === "developer") {
6652
+ message.role = "system";
6653
+ rewrittenDeveloper = true;
6654
+ }
6655
+ }
6641
6656
  return result;
6642
6657
  },
6643
6658
  convertResponse: (raw) => engine.convertResponse(raw),
@@ -6678,15 +6693,15 @@ function conversionService(catalog, worker, history, runBackground) {
6678
6693
  identityError("model_identity_unavailable", "Declared catalog model is unavailable");
6679
6694
  if (actual && catalogVersion !== null && actual.catalogVersion !== catalogVersion)
6680
6695
  identityError("model_identity_catalog_changed", "Target catalog version differs from the generation snapshot");
6696
+ if (rewrittenDeveloper && actual?.provider !== "deepseek")
6697
+ throw new ResponsesCodecError("unsupported_content", "DeepSeek developer-role conversion cannot be sent to another target provider", "messages");
6698
+ if (context.sourceProtocol === "responses" && protocol === "chat_completions" && actual?.provider === "deepseek" && chatMessages(body).some((message) => message?.role === "developer"))
6699
+ throw new ResponsesCodecError("unsupported_content", "DeepSeek target requires developer messages to be converted", "messages");
6681
6700
  const sameNativeIdentity = entryProfile ? !!actual && actual.provider === entryProfile.provider && actual.model === entryProfile.model : !identity && model === context.model;
6682
6701
  if (selected !== undefined) {
6683
6702
  if (!(passthrough && sameNativeIdentity && wireEffort(body, protocol) === selected)) {
6684
6703
  if (!entryProfile || !actual || actual.rulesVersion !== entryProfile.rulesVersion || !actual.supportedEfforts.includes(selected) || wireEffort(body, protocol) !== selected)
6685
6704
  fail2("reasoning.effort");
6686
- if (protocol === "anthropic_messages" && body?.thinking?.type !== "adaptive")
6687
- fail2("thinking.type");
6688
- if (protocol === "chat_completions" && actual.provider === "zai" && body?.thinking?.type !== "enabled")
6689
- fail2("thinking.type");
6690
6705
  }
6691
6706
  } else if (!profile && actual && identity) {
6692
6707
  profile = structuredClone(actual);
@@ -6715,7 +6730,13 @@ function adapterError(error) {
6715
6730
  }
6716
6731
  function responseHandler(input) {
6717
6732
  let context, terminal, flushed = false;
6718
- const dispose = () => input.session.dispose();
6733
+ let disposed = false;
6734
+ const dispose = () => {
6735
+ if (disposed)
6736
+ return;
6737
+ disposed = true;
6738
+ input.session.dispose();
6739
+ };
6719
6740
  input.signal?.addEventListener("abort", dispose, { once: true });
6720
6741
  const release = () => {
6721
6742
  input.signal?.removeEventListener("abort", dispose);
@@ -6730,6 +6751,7 @@ function responseHandler(input) {
6730
6751
  return { bodyRequirements() {
6731
6752
  return { request: "none", response: ["json", "sse-json"] };
6732
6753
  }, register(hooks) {
6754
+ hooks.onFinally.tap("llm-adapter.dispose", release);
6733
6755
  hooks.onValidateOutbound.tapPromise("llm-adapter.target", async (outbound) => {
6734
6756
  check();
6735
6757
  try {
@@ -7147,4 +7169,4 @@ export {
7147
7169
  targetPath
7148
7170
  };
7149
7171
 
7150
- //# debugId=7C2FFBD5947C86DA64756E2164756E21
7172
+ //# debugId=0868FA94A110E88F64756E2164756E21