omnius 1.0.873 → 1.0.875

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/index.js +2 -2
  2. package/dist/library.js +259 -62
  3. package/dist/providerRegistry.d.ts +6 -0
  4. package/dist/tui/native/worker.js +91 -10
  5. package/docs/DISCOVERY.json +1054 -130
  6. package/docs/DISCOVERY.md +26 -1
  7. package/docs/guides/endpoint-requirements.md +33 -0
  8. package/docs/guides/sponsored-inference-mesh.md +84 -0
  9. package/docs/work-orders/evidence/chat-session-document-revision-20261017.json +57 -0
  10. package/docs/work-orders/evidence/chat-session-legacy-document-20261017.json +60 -0
  11. package/docs/work-orders/evidence/chat-session-live-sidecar-20261017.json +158 -0
  12. package/docs/work-orders/evidence/chat-session-shared-store-20261017.json +89 -0
  13. package/docs/work-orders/evidence/endpoint-completion-verification-20261017.json +95 -0
  14. package/docs/work-orders/evidence/endpoint-source-verification-20261017.json +175 -0
  15. package/docs/work-orders/evidence/expanded-variant-repair-20261017.json +88 -0
  16. package/docs/work-orders/evidence/final-presentation-native-20261017/final-160x48.txt +48 -0
  17. package/docs/work-orders/evidence/final-presentation-native-20261017/final-60x20.txt +20 -0
  18. package/docs/work-orders/evidence/final-presentation-native-20261017/report.json +1225 -0
  19. package/docs/work-orders/evidence/final-presentation-native-20261017/rich-final-160x48.txt +48 -0
  20. package/docs/work-orders/evidence/final-presentation-native-20261017.json +116 -0
  21. package/docs/work-orders/evidence/inference-route-attempts-20261017.json +82 -0
  22. package/docs/work-orders/evidence/mesh-source-verification-20261017.json +46 -0
  23. package/docs/work-orders/evidence/mesh-transport-verification-20261017.json +156 -0
  24. package/docs/work-orders/evidence/opencode-endpoint-regression-20261017.json +984 -0
  25. package/docs/work-orders/evidence/project-session-delete-shared-state-20261017.json +54 -0
  26. package/docs/work-orders/evidence/public-session-live-20261017.json +52 -0
  27. package/docs/work-orders/evidence/session-live-sidecar-profile-20261017.json +315 -0
  28. package/docs/work-orders/evidence/session-live-write-profile-20261017.json +167 -0
  29. package/docs/work-orders/evidence/sticky-catalog-candidate-release-20261017.json +314 -0
  30. package/npm-shrinkwrap.json +1705 -202
  31. package/package.json +78 -34
package/dist/library.js CHANGED
@@ -340816,10 +340816,6 @@ var OPENAI_CAPABILITIES = [
340816
340816
  ...CHAT_CAPABILITIES,
340817
340817
  "embeddings"
340818
340818
  ];
340819
- var noAuth = (required = false) => ({
340820
- strategy: "none",
340821
- required
340822
- });
340823
340819
  var bearerAuth = (required, keyPrefix, staticHeaders) => ({
340824
340820
  strategy: "bearer",
340825
340821
  required,
@@ -340834,9 +340830,27 @@ var descriptor = (value) => Object.freeze({
340834
340830
  ...value.auth,
340835
340831
  ...value.auth.staticHeaders ? { staticHeaders: Object.freeze({ ...value.auth.staticHeaders }) } : {}
340836
340832
  }),
340837
- capabilities: Object.freeze([...value.capabilities])
340833
+ capabilities: Object.freeze([...value.capabilities]),
340834
+ ...value.availability ? { availability: Object.freeze({ ...value.availability }) } : {}
340838
340835
  });
340839
340836
  var registrations = [
340837
+ {
340838
+ descriptor: descriptor({
340839
+ id: "mesh",
340840
+ aliases: [],
340841
+ label: "Sponsored inference mesh",
340842
+ protocol: "openai-chat",
340843
+ local: false,
340844
+ apiPrefix: "/v1",
340845
+ paths: { chat: "/v1/chat/completions", models: "/v1/models" },
340846
+ auth: bearerAuth(true),
340847
+ capabilities: CHAT_CAPABILITIES,
340848
+ defaultUrl: "http://127.0.0.1:11436/v1",
340849
+ description: "Opt-in remote sponsor inference through your private authenticated mesh gateway."
340850
+ }),
340851
+ // A loopback port is not provider identity. Select this transport explicitly.
340852
+ match: () => false
340853
+ },
340840
340854
  {
340841
340855
  descriptor: descriptor({
340842
340856
  id: "builtin-free",
@@ -340844,14 +340858,14 @@ var registrations = [
340844
340858
  label: "Built-in free",
340845
340859
  protocol: "openai-chat",
340846
340860
  local: false,
340847
- apiPrefix: "/inference/v1",
340848
- paths: { chat: "/inference/v1/chat/completions", models: "/inference/v1/models" },
340861
+ apiPrefix: "/inference/openai/v1",
340862
+ paths: { chat: "/inference/openai/v1/chat/completions", models: "/inference/v1/models" },
340849
340863
  auth: { strategy: "bearer", required: false, staticHeaders: { Authorization: "Bearer public" } },
340850
340864
  capabilities: CHAT_CAPABILITIES,
340851
- defaultUrl: "https://opencode.ai/inference/v1",
340865
+ defaultUrl: "https://opencode.ai/inference/openai/v1",
340852
340866
  description: "Built-in models with public access where permitted and optional provider-bound API credentials."
340853
340867
  }),
340854
- match: (url) => url.protocol === "https:" && url.hostname.toLowerCase() === "opencode.ai" && !url.port && /^\/inference\/v1(?:\/(?:models|chat\/completions|responses))?\/?$/.test(url.pathname)
340868
+ match: (url) => url.protocol === "https:" && url.hostname.toLowerCase() === "opencode.ai" && !url.port && /^\/inference\/(?:openai\/)?v1(?:\/(?:models|chat\/completions|responses))?\/?$/.test(url.pathname)
340855
340869
  },
340856
340870
  {
340857
340871
  descriptor: descriptor({
@@ -341028,12 +341042,12 @@ var registrations = [
341028
341042
  },
341029
341043
  auth: bearerAuth(true),
341030
341044
  capabilities: OPENAI_CAPABILITIES,
341031
- defaultUrl: "https://api.together.xyz/v1",
341045
+ defaultUrl: "https://api.together.ai/v1",
341032
341046
  apiKeyUrl: "https://api.together.xyz/settings/api-keys",
341033
341047
  docsUrl: "https://docs.together.ai",
341034
341048
  description: "Together OpenAI-compatible inference."
341035
341049
  }),
341036
- match: (url) => url.hostname.toLowerCase() === "api.together.xyz"
341050
+ match: (url) => ["api.together.ai", "api.together.xyz"].includes(url.hostname.toLowerCase())
341037
341051
  },
341038
341052
  {
341039
341053
  descriptor: descriptor({
@@ -341163,7 +341177,8 @@ var registrations = [
341163
341177
  defaultUrl: "https://api.hyperbolic.xyz/v1",
341164
341178
  apiKeyUrl: "https://app.hyperbolic.xyz/settings",
341165
341179
  docsUrl: "https://docs.hyperbolic.xyz",
341166
- description: "Hyperbolic OpenAI-compatible inference."
341180
+ description: "Retired Hyperbolic serverless API. Explicit custom deployments require their own URL.",
341181
+ availability: { state: "retired", reason: "Hyperbolic's serverless inference API has been retired; old serverless models no longer accept requests, including with valid keys.", docsUrl: "https://www.hyperbolic.ai/docs/faq/inference-models" }
341167
341182
  }),
341168
341183
  match: (url) => url.hostname.toLowerCase() === "api.hyperbolic.xyz"
341169
341184
  },
@@ -341182,7 +341197,7 @@ var registrations = [
341182
341197
  embeddings: "/api/embed",
341183
341198
  health: "/api/tags"
341184
341199
  },
341185
- auth: noAuth(),
341200
+ auth: bearerAuth(false),
341186
341201
  capabilities: OPENAI_CAPABILITIES,
341187
341202
  defaultUrl: "http://127.0.0.1:11434",
341188
341203
  docsUrl: "https://docs.ollama.com/api",
@@ -341336,7 +341351,7 @@ function customDescriptor(protocol2, rawUrl) {
341336
341351
  strategy: "anthropic-api-key",
341337
341352
  required: false,
341338
341353
  staticHeaders: { "anthropic-version": "2023-06-01" }
341339
- } : protocol2 === "ollama" ? noAuth() : bearerAuth(false),
341354
+ } : bearerAuth(false),
341340
341355
  capabilities: protocol2 === "anthropic-messages" ? CHAT_CAPABILITIES : OPENAI_CAPABILITIES,
341341
341356
  defaultUrl: `${url.origin}${endpointPath}`,
341342
341357
  description: `Custom endpoint with explicit ${protocol2} protocol.`
@@ -341384,7 +341399,8 @@ function listProviderDescriptors() {
341384
341399
  ...entry.auth,
341385
341400
  ...entry.auth.staticHeaders ? { staticHeaders: { ...entry.auth.staticHeaders } } : {}
341386
341401
  },
341387
- capabilities: [...entry.capabilities]
341402
+ capabilities: [...entry.capabilities],
341403
+ ...entry.availability ? { availability: { ...entry.availability } } : {}
341388
341404
  }));
341389
341405
  }
341390
341406
  function detectProviderDescriptor(rawUrl) {
@@ -341436,7 +341452,7 @@ function resolveProviderTransport(options) {
341436
341452
  }
341437
341453
  if (provider.id === "builtin-free") {
341438
341454
  const url = parseProviderUrl(rawUrl);
341439
- if (url.protocol !== "https:" || url.hostname.toLowerCase() !== "opencode.ai" || url.port || url.username || url.password || url.search || url.hash || !/^\/inference\/v1(?:\/(?:models|chat\/completions|responses))?\/?$/.test(url.pathname)) {
341455
+ if (url.protocol !== "https:" || url.hostname.toLowerCase() !== "opencode.ai" || url.port || url.username || url.password || url.search || url.hash || !/^\/inference\/(?:openai\/)?v1(?:\/(?:models|chat\/completions|responses))?\/?$/.test(url.pathname)) {
341440
341456
  throw new UnknownProviderProtocolError("Built-in inference requires its exact registered HTTPS endpoint.");
341441
341457
  }
341442
341458
  }
@@ -341619,7 +341635,7 @@ function toProviderInfo(provider) {
341619
341635
  };
341620
341636
  }
341621
341637
  var PROVIDER_PRESETS = [
341622
- ...listProviderDescriptors().map(toProviderInfo),
341638
+ ...listProviderDescriptors().filter((provider) => provider.availability?.state !== "retired").map(toProviderInfo),
341623
341639
  {
341624
341640
  id: "custom",
341625
341641
  label: "Custom endpoint",
@@ -341631,7 +341647,7 @@ var PROVIDER_PRESETS = [
341631
341647
  ];
341632
341648
 
341633
341649
  // packages/backend-vllm/dist/builtin-free-credentials.js
341634
- var BUILTIN_INFERENCE_ENDPOINT = "https://opencode.ai/inference/v1";
341650
+ var BUILTIN_INFERENCE_ENDPOINTS = /* @__PURE__ */ new Set(["https://opencode.ai/inference/v1", "https://opencode.ai/inference/openai/v1"]);
341635
341651
  function hostEnvironment() {
341636
341652
  return globalThis.process?.env ?? {};
341637
341653
  }
@@ -341649,7 +341665,7 @@ function resolveBuiltInFreeApiKey(baseUrl, env = hostEnvironment()) {
341649
341665
  } catch {
341650
341666
  return void 0;
341651
341667
  }
341652
- if (url.username || url.password || url.search || url.hash || url.href.replace(/\/+$/, "") !== BUILTIN_INFERENCE_ENDPOINT)
341668
+ if (url.username || url.password || url.search || url.hash || !BUILTIN_INFERENCE_ENDPOINTS.has(url.href.replace(/\/+$/, "")))
341653
341669
  return void 0;
341654
341670
  const key = env["OPENCODE_API_KEY"];
341655
341671
  if (key === void 0 || key === "" || key === "public")
@@ -343363,8 +343379,12 @@ init_dist5();
343363
343379
  // packages/orchestrator/dist/opencode-free-models.js
343364
343380
  import { createHash as createHash44, randomBytes as randomBytes6 } from "node:crypto";
343365
343381
  var OPENCODE_MODEL_CATALOG_URL = "https://models.opencode.ai/api.json";
343366
- var OPENCODE_INFERENCE_BASE_URL = "https://opencode.ai/inference/v1";
343367
- var OPENCODE_AVAILABLE_MODELS_URL = `${OPENCODE_INFERENCE_BASE_URL}/models`;
343382
+ var OPENCODE_LEGACY_INFERENCE_BASE_URL = "https://opencode.ai/inference/v1";
343383
+ var OPENCODE_INFERENCE_BASE_URL = "https://opencode.ai/inference/openai/v1";
343384
+ function isOpenCodeInferenceBaseUrl(value) {
343385
+ return value === OPENCODE_INFERENCE_BASE_URL || value === OPENCODE_LEGACY_INFERENCE_BASE_URL;
343386
+ }
343387
+ var OPENCODE_AVAILABLE_MODELS_URL = `${OPENCODE_LEGACY_INFERENCE_BASE_URL}/models`;
343368
343388
  var OPENCODE_COMPATIBILITY_CLIENT_VERSION = "2.0.27";
343369
343389
  var observedModelCompatibility = Object.freeze({
343370
343390
  "openai-responses": Object.freeze({
@@ -343382,6 +343402,10 @@ var observedModelCompatibility = Object.freeze({
343382
343402
  // Production chat + review accepted the unchanged host JSON contract only
343383
343403
  // when native response_format was omitted, observed 2026-10-13.
343384
343404
  "big-pickle": Object.freeze({ nativeStructuredOutput: false }),
343405
+ // Canonical chat rejects the real child completion schema unless every
343406
+ // object forbids additional properties (2026-10-17). Preserve its original
343407
+ // host contract through prompt-only framing instead of tightening it.
343408
+ "beam-free": Object.freeze({ nativeStructuredOutput: false }),
343385
343409
  // Step accepts simple native JSON but rejects complex Telegram control
343386
343410
  // schemas with HTTP400. The identical generation + review contract passed
343387
343411
  // when only native framing was omitted (2026-10-13); prefer prompt-only
@@ -343500,7 +343524,7 @@ function tryParseOpenCodeFreeModelCatalog(json5) {
343500
343524
  if (!object2(catalog) || !object2(catalog.opencode))
343501
343525
  return void 0;
343502
343526
  const provider = catalog.opencode;
343503
- if (provider.id !== "opencode" || provider.api !== OPENCODE_INFERENCE_BASE_URL || !["@ai-sdk/openai-compatible", "@ai-sdk/openai"].includes(String(provider.npm)) || !object2(provider.models))
343527
+ if (provider.id !== "opencode" || !isOpenCodeInferenceBaseUrl(provider.api) || !["@ai-sdk/openai-compatible", "@ai-sdk/openai"].includes(String(provider.npm)) || !object2(provider.models))
343504
343528
  return void 0;
343505
343529
  const entries = Object.entries(provider.models);
343506
343530
  if (entries.length > MAX_OPENCODE_CATALOG_MODELS)
@@ -343511,7 +343535,7 @@ function tryParseOpenCodeFreeModelCatalog(json5) {
343511
343535
  continue;
343512
343536
  if (model.limit.input !== void 0 && (!positiveInteger2(model.limit.input) || model.limit.input > model.limit.context))
343513
343537
  continue;
343514
- if (model.provider !== void 0 && (!object2(model.provider) || model.provider.api !== void 0 && model.provider.api !== OPENCODE_INFERENCE_BASE_URL))
343538
+ if (model.provider !== void 0 && (!object2(model.provider) || model.provider.api !== void 0 && !isOpenCodeInferenceBaseUrl(model.provider.api)))
343515
343539
  continue;
343516
343540
  const transport = object2(model.provider) ? model.provider : {};
343517
343541
  const npm = transport.npm ?? provider.npm;
@@ -343590,7 +343614,7 @@ var FREE_INFERENCE_SEEDS = Object.freeze([
343590
343614
  name: "Built-in free inference",
343591
343615
  chatUrl: `${OPENCODE_INFERENCE_BASE_URL}/chat/completions`,
343592
343616
  modelsUrl: OPENCODE_MODEL_CATALOG_URL,
343593
- availabilityModelsUrl: `${OPENCODE_INFERENCE_BASE_URL}/models`,
343617
+ availabilityModelsUrl: OPENCODE_AVAILABLE_MODELS_URL,
343594
343618
  model: "nemotron-3.5-lightning-free",
343595
343619
  nativeToolsHint: true,
343596
343620
  catalogKind: "opencode",
@@ -343752,14 +343776,15 @@ function parseKeylessInferenceRegistry(html) {
343752
343776
 
343753
343777
  // packages/orchestrator/dist/builtin-free-snapshot.js
343754
343778
  var rows = [
343755
- ["muse-spark-1.3-contributor-free", "Muse Spark 1.3 Free", 1048576, 131072, void 0, true, true],
343756
- ["step-5-preview-free", "Step 5 Preview Free", 1e6, 65536, 1e6, true],
343757
- ["nemotron-3-ultra-free", "Nemotron 3 Ultra Free", 1e6, 128e3, void 0, void 0],
343758
- ["mimo-v2.6-flash-free", "MiMo-V2.6-Flash Free", 2e5, 32e3, void 0, void 0],
343759
- ["nemotron-3.5-lightning-free", "Nemotron 3.5 Lightning Free", 262144, 262144, void 0, true],
343779
+ ["beam-free", "Beam Free", 1048576, 131072, void 0, true],
343760
343780
  ["big-pickle", "Big Pickle", 2e5, 32e3, 16e4, true],
343761
- ["cat-jam-free", "Cat Jam Free", 262144, 131072, void 0, true],
343781
+ ["deepseek-v4.1-flash-free", "DeepSeek V4.1 Flash Free", 2e5, 32e3, void 0, true],
343782
+ ["glm-5.3-flash-free", "GLM-5.3-Flash Free", 2e5, 32e3, void 0, true],
343762
343783
  ["longcat-2.5-preview-free", "LongCat 2.5 Preview Free", 1e6, 131072, void 0, void 0],
343784
+ ["mimo-v2.6-flash-free", "MiMo-V2.6-Flash Free", 2e5, 32e3, void 0, void 0],
343785
+ ["nemotron-3-ultra-free", "Nemotron 3 Ultra Free", 1e6, 128e3, void 0, void 0],
343786
+ ["nemotron-3.5-lightning-free", "Nemotron 3.5 Lightning Free", 262144, 262144, void 0, true],
343787
+ ["nemotron-3.5-super-vl-free", "Nemotron 3.5 Super VL Free", 1048576, 131072, 1048576, void 0],
343763
343788
  ["space-bunny-free", "Space Bunny Free", 1048576, 524288, 524288, void 0]
343764
343789
  ];
343765
343790
  var metadata = Object.fromEntries(rows.map(([id5, name, context, output, input, structured_output, responses]) => [id5, {
@@ -343779,6 +343804,15 @@ var BUILTIN_FREE_MODEL_SNAPSHOT = Object.freeze(parseOpenCodeFreeModelCatalog(JS
343779
343804
  models: metadata
343780
343805
  } })).map((model) => Object.freeze(model)));
343781
343806
  var BUILTIN_FREE_MODEL_IDS = new Set(BUILTIN_FREE_MODEL_SNAPSHOT.map((model) => model.model));
343807
+ var BUILTIN_FREE_LEGACY_MODEL_IDS = /* @__PURE__ */ new Set([
343808
+ "cat-jam-free",
343809
+ "muse-spark-1.3-contributor-free",
343810
+ "step-5-preview-free"
343811
+ ]);
343812
+ var BUILTIN_FREE_RECOGNIZED_MODEL_IDS = /* @__PURE__ */ new Set([
343813
+ ...BUILTIN_FREE_MODEL_IDS,
343814
+ ...BUILTIN_FREE_LEGACY_MODEL_IDS
343815
+ ]);
343782
343816
 
343783
343817
  // packages/orchestrator/dist/inference-provider-error.js
343784
343818
  import { createHash as createHash45 } from "node:crypto";
@@ -343988,13 +344022,13 @@ function packet(value, completeResponse = false) {
343988
344022
  if (delta[field] !== void 0 && delta[field] !== null && typeof delta[field] !== "string")
343989
344023
  throw new OpenAIChatResponseError();
343990
344024
  }
343991
- if (delta.tool_calls !== void 0 && !Array.isArray(delta.tool_calls))
344025
+ if (delta.tool_calls !== void 0 && delta.tool_calls !== null && !Array.isArray(delta.tool_calls))
343992
344026
  throw new OpenAIChatResponseError();
343993
344027
  const calls = Array.isArray(delta.tool_calls) ? delta.tool_calls.map((call2, index) => {
343994
- if (!object5(call2) || call2.function !== void 0 && !object5(call2.function) || call2.id !== void 0 && typeof call2.id !== "string" || call2.index !== void 0 && (!Number.isSafeInteger(call2.index) || Number(call2.index) < 0))
344028
+ if (!object5(call2) || call2.function !== void 0 && !object5(call2.function) || call2.id !== void 0 && !(call2.id === null && !complete) && typeof call2.id !== "string" || call2.index !== void 0 && (!Number.isSafeInteger(call2.index) || Number(call2.index) < 0))
343995
344029
  throw new OpenAIChatResponseError();
343996
344030
  const fn = object5(call2.function) ? call2.function : {};
343997
- if (fn.name !== void 0 && typeof fn.name !== "string")
344031
+ if (fn.name !== void 0 && !(fn.name === null && !complete) && typeof fn.name !== "string")
343998
344032
  throw new OpenAIChatResponseError();
343999
344033
  const args = fn.arguments;
344000
344034
  if (args !== void 0 && typeof args !== "string" && !object5(args))
@@ -344009,11 +344043,13 @@ function packet(value, completeResponse = false) {
344009
344043
  if (typeof call2.id !== "string" || !call2.id.trim() || typeof fn.name !== "string" || !fn.name.trim() || !object5(parsed))
344010
344044
  throw new OpenAIChatResponseError();
344011
344045
  }
344012
- return {
344013
- ...call2,
344014
- index: call2.index ?? index,
344015
- function: { ...fn, ...object5(args) ? { arguments: JSON.stringify(args) } : {} }
344016
- };
344046
+ const normalizedCall = { ...call2 };
344047
+ const normalizedFunction = { ...fn, ...object5(args) ? { arguments: JSON.stringify(args) } : {} };
344048
+ if (!complete && normalizedCall.id === null)
344049
+ delete normalizedCall.id;
344050
+ if (!complete && normalizedFunction.name === null)
344051
+ delete normalizedFunction.name;
344052
+ return { ...normalizedCall, index: call2.index ?? index, function: normalizedFunction };
344017
344053
  }) : void 0;
344018
344054
  return {
344019
344055
  ...choice,
@@ -345833,9 +345869,24 @@ var InvalidInferenceResponse = class extends Error {
345833
345869
  };
345834
345870
  var InvalidDiscoveryMetadata = class extends Error {
345835
345871
  };
345872
+ var CatalogModelUnavailable = class extends Error {
345873
+ };
345836
345874
  var OutputReservationUnavailable = class extends Error {
345837
345875
  };
345876
+ var InferenceRoutePolicyError = class extends Error {
345877
+ failure;
345878
+ code = "inference_route_failed";
345879
+ fatal = true;
345880
+ retryable = false;
345881
+ constructor(failure) {
345882
+ super(`Inference request failed (${failure.category}${failure.status === void 0 ? "" : `; HTTP ${failure.status}`}${failure.accessReason ? `; ${failure.accessReason}` : failure.validationReason ? `; ${failure.validationReason}` : failure.reason && failure.reason !== "terminal" ? `; ${failure.reason}` : ""}).`);
345883
+ this.failure = failure;
345884
+ this.name = "InferenceRoutePolicyError";
345885
+ }
345886
+ };
345838
345887
  var cooldowns = /* @__PURE__ */ new Map();
345888
+ var successfulRoutes = /* @__PURE__ */ new WeakMap();
345889
+ var SAME_MODEL_ATTEMPTS = 3;
345839
345890
  var requestTransports = /* @__PURE__ */ new WeakMap();
345840
345891
  var responseValidators = /* @__PURE__ */ new WeakMap();
345841
345892
  var nextResponseValidator = 0;
@@ -345908,12 +345959,25 @@ function errorChain(error) {
345908
345959
  chain.push(current2);
345909
345960
  return chain;
345910
345961
  }
345962
+ function explicitModelUnavailable(error) {
345963
+ return errorChain(error).some((value) => {
345964
+ if (value instanceof InferenceProviderError && value.status === 429 && value.reason === "upstream")
345965
+ return true;
345966
+ if (!object12(value) || value.name !== "InferenceHttpError" || ![400, 404, 422, 503].includes(Number(value.status)) || !object12(value.responseJson) || !object12(value.responseJson.error))
345967
+ return false;
345968
+ return ["model_not_found", "model_not_available", "model_unavailable", "unsupported_model"].includes(String(value.responseJson.error.code));
345969
+ });
345970
+ }
345971
+ function sameModelTransient(error, request) {
345972
+ const failure = safeFailure(error, request);
345973
+ return !errorChain(error).some((value) => object12(value) && (value.fatal === true || value.retryable === false) || value instanceof AttemptTimeout && (value.deadlineKind === "remote_aggregate" || value.deadlineKind === "caller_request") || value instanceof SyntaxError || value instanceof InvalidInferenceResponse || value instanceof ExpectedInferenceResponseError || object12(value) && value.name === "OpenAIChatResponseError") && (failure.category === "rate_limit" || failure.category === "timeout" || failure.category === "upstream_unavailable" || failure.category === "empty_response" || failure.category === "transport" && transportFailure(error));
345974
+ }
345911
345975
  function safeFailure(error, request, route, streamFrames) {
345912
345976
  const status2 = errorStatus(error);
345913
345977
  const chain = errorChain(error);
345914
345978
  const upstream429 = status2 === 429 && chain.some((value) => value instanceof InferenceProviderError && value.reason === "upstream");
345915
345979
  const access = chain.find((value) => value instanceof InferenceProviderAccessError);
345916
- const category = access ? "access_denied" : upstream429 ? "upstream_unavailable" : status2 === 429 ? "rate_limit" : status2 === 401 || status2 === 403 ? "authentication" : status2 !== void 0 && status2 >= 400 && status2 < 500 ? "request_rejected" : status2 !== void 0 && status2 >= 500 ? "upstream_unavailable" : chain.some((value) => value instanceof AttemptTimeout || object12(value) && (value.name === "TimeoutError" || value.name === "AbortError" || value.code === "ETIMEDOUT") || value instanceof Error && /^stream timeout: no response or chunk within [0-9]+s$/.test(value.message)) ? "timeout" : chain.some((value) => value instanceof EmptyInferenceResponse) ? "empty_response" : chain.some((value) => value instanceof SyntaxError || value instanceof InvalidInferenceResponse || value instanceof ExpectedInferenceResponseError || object12(value) && value.name === "OpenAIChatResponseError") ? "invalid_response" : "transport";
345980
+ const category = chain.some((value) => value instanceof CatalogModelUnavailable) ? "model_unavailable" : access ? "access_denied" : upstream429 ? "upstream_unavailable" : status2 === 429 ? "rate_limit" : status2 === 401 || status2 === 403 ? "authentication" : status2 !== void 0 && status2 >= 400 && status2 < 500 ? "request_rejected" : status2 !== void 0 && status2 >= 500 ? "upstream_unavailable" : chain.some((value) => value instanceof AttemptTimeout || object12(value) && (value.name === "TimeoutError" || value.name === "AbortError" || value.code === "ETIMEDOUT") || value instanceof Error && /^stream timeout: no response or chunk within [0-9]+s$/.test(value.message)) ? "timeout" : chain.some((value) => value instanceof EmptyInferenceResponse) ? "empty_response" : chain.some((value) => value instanceof SyntaxError || value instanceof InvalidInferenceResponse || value instanceof ExpectedInferenceResponseError || object12(value) && value.name === "OpenAIChatResponseError") ? "invalid_response" : "transport";
345917
345981
  const validation = chain.find((value) => value instanceof ExpectedInferenceResponseError);
345918
345982
  const providerSchemaError = chain.find((value) => value instanceof InferenceProviderError && value.reason === "unsupported_schema");
345919
345983
  const malformedEnvelope = chain.some((value) => object12(value) && value.name === "OpenAIChatResponseError");
@@ -345923,6 +345987,7 @@ function safeFailure(error, request, route, streamFrames) {
345923
345987
  return {
345924
345988
  category,
345925
345989
  ...status2 === void 0 ? {} : { status: status2 },
345990
+ ...chain.some((value) => value instanceof CatalogModelUnavailable) ? { catalogReason: "not_in_active_free_catalog" } : {},
345926
345991
  ...access instanceof InferenceProviderAccessError ? { accessReason: access.reason, responseBodySha256: access.responseBodySha256 } : {},
345927
345992
  ...deadline instanceof AttemptTimeout ? { deadlineKind: deadline.deadlineKind } : providerIdle ? { deadlineKind: "provider_idle" } : {},
345928
345993
  ...streamFrames ? {
@@ -346006,6 +346071,7 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346006
346071
  policy;
346007
346072
  builtInPrimaryRequest = false;
346008
346073
  builtInAlternativesApproved = false;
346074
+ stickyRouting = false;
346009
346075
  now;
346010
346076
  env;
346011
346077
  lastRoute;
@@ -346038,6 +346104,55 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346038
346104
  capacityPolicyScope() {
346039
346105
  return createHash47("sha256").update(canonicalContract(this.policy)).digest("hex");
346040
346106
  }
346107
+ stickyKey() {
346108
+ return createHash47("sha256").update(canonicalContract({
346109
+ primary: this.capacityPrimaryScope(),
346110
+ policy: this.capacityPolicyScope(),
346111
+ credential: resolveBuiltInFreeApiKey(OPENCODE_INFERENCE_BASE_URL, this.env) ?? "public"
346112
+ })).digest("hex");
346113
+ }
346114
+ rememberRoute(route) {
346115
+ if (!this.stickyRouting && !this.builtInPrimaryRequest)
346116
+ return;
346117
+ const fetcher = this.options.fetch ?? fetch;
346118
+ let entries = successfulRoutes.get(fetcher);
346119
+ if (!entries)
346120
+ successfulRoutes.set(fetcher, entries = /* @__PURE__ */ new Map());
346121
+ if (entries.size >= 64)
346122
+ entries.delete(entries.keys().next().value);
346123
+ const { policyAttempt: _attempt, transientFailures: _failures, retryPendingMs: _retry, schemaRecoveryPending: _schema, modelAccessRestricted: _access, lastAttemptRateLimited: _rate, ...retained } = route;
346124
+ entries.set(this.stickyKey(), retained);
346125
+ }
346126
+ /** One bounded decision for both unary and streaming attempts. No transcript writes. */
346127
+ handleStickyFailure(request, candidate, deadline, error, frames) {
346128
+ let disposition = "surface_error";
346129
+ const category = safeFailure(error, request, candidate, frames).category;
346130
+ let reason = category === "authentication" ? "authentication" : category === "access_denied" ? "access_restricted" : category === "request_rejected" ? "request_rejected" : category === "invalid_response" ? "response_contract" : category === "transport" ? "adapter_or_configuration" : "terminal";
346131
+ let retryDelayMs;
346132
+ if (this.learnNativeSchemaRestriction(request, candidate, error)) {
346133
+ disposition = "repair_same_model";
346134
+ reason = "unsupported_capability";
346135
+ } else if (explicitModelUnavailable(error) || errorChain(error).some((value) => value instanceof InferenceProviderError && value.reason === "unsupported_schema")) {
346136
+ disposition = "rotate_model";
346137
+ reason = explicitModelUnavailable(error) ? "model_unavailable" : "unsupported_capability";
346138
+ const entries = successfulRoutes.get(this.options.fetch ?? fetch), key = this.stickyKey();
346139
+ if (entries?.get(key) && routeKey(entries.get(key)) === routeKey(candidate))
346140
+ entries.delete(key);
346141
+ } else if (sameModelTransient(error, request)) {
346142
+ reason = "transient";
346143
+ candidate.transientFailures = (candidate.transientFailures ?? 0) + 1;
346144
+ const hint = errorChain(error).find((value) => object12(value) && typeof value.retryAfterMs === "number" && Number.isFinite(value.retryAfterMs));
346145
+ retryDelayMs = Math.max(1e3 * 2 ** (candidate.transientFailures - 1), object12(hint) && typeof hint.retryAfterMs === "number" ? Math.max(0, hint.retryAfterMs) : 0);
346146
+ if (candidate.transientFailures < SAME_MODEL_ATTEMPTS && retryDelayMs < deadline - this.now()) {
346147
+ disposition = "retry_same_model";
346148
+ candidate.retryPendingMs = retryDelayMs;
346149
+ }
346150
+ }
346151
+ const detail = { disposition, reason, attempt: candidate.policyAttempt ?? 1, ...retryDelayMs === void 0 ? {} : { retryDelayMs } };
346152
+ this.lifecycle(request, "failed", candidate, error, frames, void 0, detail);
346153
+ if (disposition === "surface_error")
346154
+ throw new InferenceRoutePolicyError({ ...safeFailure(error, request, candidate, frames), ...detail });
346155
+ }
346041
346156
  /** Read-only route discovery; never loads or generates with any local/remote model. */
346042
346157
  async prepareContextCapacity(request, options = {}) {
346043
346158
  if (request.configuredEndpointOnly === true || request.maxTokens <= 0 || !Number.isFinite(request.maxTokens))
@@ -346160,7 +346275,7 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346160
346275
  return localInferenceBrokerReason(reason);
346161
346276
  }
346162
346277
  builtInScope() {
346163
- if (this.primary.providerId !== "builtin-free" || this.primary.baseUrl !== OPENCODE_INFERENCE_BASE_URL)
346278
+ if (this.primary.providerId !== "builtin-free" || !isOpenCodeInferenceBaseUrl(this.primary.baseUrl))
346164
346279
  throw new Error("Built-in inference requires its exact registered public endpoint.");
346165
346280
  this.reloadPolicy();
346166
346281
  const scope = new _RemoteInferenceFallback(this.primary, this.makeBackend, { ...this.options, policy: this.policy });
@@ -346256,7 +346371,7 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346256
346371
  request = { ...request };
346257
346372
  requestTransports.set(request, "unary");
346258
346373
  const completedBinding = completedCapacityBinding(request);
346259
- const deadline = Math.min(this.now() + this.policy.totalTimeoutMs, this.preparedCapacity(request)?.deadline ?? Infinity);
346374
+ const deadline = Math.min(this.now() + this.policy.totalTimeoutMs, this.preparedCapacity(request)?.deadline ?? Infinity, request.timeoutMs > 0 ? this.now() + request.timeoutMs : Infinity);
346260
346375
  for await (const candidate of this.retryableCandidates(request, deadline)) {
346261
346376
  if (this.now() >= deadline)
346262
346377
  break;
@@ -346276,16 +346391,18 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346276
346391
  this.fenceRemoteDeadline(bounded3, deadline);
346277
346392
  return remote.chatCompletion(remoteRequest);
346278
346393
  }, remaining <= this.policy.requestTimeoutMs ? "remote_aggregate" : "remote_attempt");
346394
+ this.fenceRemoteDeadline(request, deadline);
346395
+ if ((request.responseFormat ?? request.response_format) && result?.outputTruncated)
346396
+ throw new ExpectedInferenceResponseError(InputValidationFailure.Truncated, expectedInferenceResponseKind(request));
346279
346397
  if (!usableResult(result))
346280
346398
  throw new EmptyInferenceResponse();
346281
346399
  const validation = validateRemoteMessage(result.choices[0]?.message, request);
346282
346400
  if (validation.normalizedContent !== void 0 && result.choices[0]?.message)
346283
346401
  result.choices[0].message.content = validation.normalizedContent;
346284
- if ((request.responseFormat ?? request.response_format) && result.outputTruncated)
346285
- throw new ExpectedInferenceResponseError(InputValidationFailure.Truncated, expectedInferenceResponseKind(request));
346286
346402
  cooldowns.delete(routeKey(candidate));
346287
346403
  cooldowns.delete(rateLimitKey(candidate));
346288
346404
  cooldowns.delete(contractCooldownKey(candidate, request));
346405
+ this.rememberRoute(candidate);
346289
346406
  this.lifecycle(request, "succeeded", candidate, void 0, void 0, completedBinding);
346290
346407
  return this.scrubber(candidate)(result);
346291
346408
  } catch (error) {
@@ -346294,6 +346411,10 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346294
346411
  this.discovery(request, candidate, { stage: "eligibility", outcome: "no_eligible_models", modelCount: 0 });
346295
346412
  continue;
346296
346413
  }
346414
+ if (this.stickyRouting || this.builtInPrimaryRequest) {
346415
+ this.handleStickyFailure(request, candidate, deadline, error);
346416
+ continue;
346417
+ }
346297
346418
  this.lifecycle(request, "failed", candidate, error);
346298
346419
  if (this.restrictedModel(candidate, error))
346299
346420
  continue;
@@ -346373,7 +346494,7 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346373
346494
  requestTransports.set(request, "stream");
346374
346495
  const completedBinding = completedCapacityBinding(request);
346375
346496
  let emitted = false;
346376
- const deadline = Math.min(this.now() + this.policy.totalTimeoutMs, this.preparedCapacity(request)?.deadline ?? Infinity);
346497
+ const deadline = Math.min(this.now() + this.policy.totalTimeoutMs, this.preparedCapacity(request)?.deadline ?? Infinity, request.timeoutMs > 0 ? this.now() + request.timeoutMs : Infinity);
346377
346498
  for await (const candidate of this.retryableCandidates(request, deadline)) {
346378
346499
  if (this.now() >= deadline)
346379
346500
  break;
@@ -346411,9 +346532,11 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346411
346532
  for await (const chunk of this.scrubStream(track(), candidate)) {
346412
346533
  yield chunk;
346413
346534
  }
346535
+ this.fenceRemoteDeadline(request, deadline);
346414
346536
  cooldowns.delete(routeKey(candidate));
346415
346537
  cooldowns.delete(rateLimitKey(candidate));
346416
346538
  cooldowns.delete(contractCooldownKey(candidate, request));
346539
+ this.rememberRoute(candidate);
346417
346540
  this.lifecycle(request, "succeeded", candidate, void 0, void 0, completedBinding);
346418
346541
  return;
346419
346542
  } catch (error) {
@@ -346422,9 +346545,15 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346422
346545
  this.discovery(request, candidate, { stage: "eligibility", outcome: "no_eligible_models", modelCount: 0 });
346423
346546
  continue;
346424
346547
  }
346425
- this.lifecycle(request, "failed", candidate, error, frames);
346426
- if (emitted)
346548
+ if (emitted) {
346549
+ this.lifecycle(request, "failed", candidate, error, frames);
346427
346550
  throw new StreamInterruptedError(safeFailure(error, request, candidate, frames));
346551
+ }
346552
+ if (this.stickyRouting || this.builtInPrimaryRequest) {
346553
+ this.handleStickyFailure(request, candidate, deadline, error, frames);
346554
+ continue;
346555
+ }
346556
+ this.lifecycle(request, "failed", candidate, error, frames);
346428
346557
  if (this.restrictedModel(candidate, error))
346429
346558
  continue;
346430
346559
  if (errorChain(error).some((value) => value instanceof AttemptTimeout && value.deadlineKind === "remote_aggregate"))
@@ -346687,7 +346816,7 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346687
346816
  }
346688
346817
  this.lifecycle(request, "selected", route);
346689
346818
  }
346690
- lifecycle(request, phase, route, error, frames, completedBinding) {
346819
+ lifecycle(request, phase, route, error, frames, completedBinding, decision2) {
346691
346820
  this.fence(request);
346692
346821
  if (!request.onInferenceRouteLifecycle)
346693
346822
  return;
@@ -346707,6 +346836,7 @@ var RemoteInferenceFallback = class _RemoteInferenceFallback {
346707
346836
  } } : {},
346708
346837
  ...phase === "failed" ? { failure: {
346709
346838
  ...safeFailure(error, request, route, frames),
346839
+ ...decision2,
346710
346840
  ...!route && this.localBrokerReason(error) ? { brokerReason: this.localBrokerReason(error) } : {}
346711
346841
  } } : {}
346712
346842
  };
@@ -347074,6 +347204,14 @@ ${line}`).join("");
347074
347204
  };
347075
347205
  for await (const candidate of candidates()) {
347076
347206
  const key = routeKey(candidate);
347207
+ if (candidate.baseUrl === OPENCODE_INFERENCE_BASE_URL && candidate.keyless?.clientIdentity === "opencode")
347208
+ this.stickyRouting = true;
347209
+ if (this.stickyRouting || this.builtInPrimaryRequest) {
347210
+ if (this.now() >= deadline)
347211
+ return;
347212
+ yield* this.stickyAttempts(request, candidate, deadline);
347213
+ continue;
347214
+ }
347077
347215
  if (!priority.has(key))
347078
347216
  priority.set(key, priority.size);
347079
347217
  if ((routeCooldown(candidate, request)?.until ?? 0) > this.now()) {
@@ -347137,7 +347275,63 @@ ${line}`).join("");
347137
347275
  defer(candidate);
347138
347276
  }
347139
347277
  }
347278
+ async *stickyAttempts(request, candidate, deadline) {
347279
+ let schemaRetries = 0;
347280
+ while (this.now() < deadline) {
347281
+ this.fence(request);
347282
+ candidate.policyAttempt = (candidate.policyAttempt ?? 0) + 1;
347283
+ yield candidate;
347284
+ if (candidate.schemaRecoveryPending && schemaRetries < SUPPORTED_PROVIDER_SCHEMA_ADAPTATIONS.length) {
347285
+ candidate.schemaRecoveryPending = false;
347286
+ schemaRetries++;
347287
+ continue;
347288
+ }
347289
+ if (candidate.retryPendingMs === void 0)
347290
+ return;
347291
+ const delay2 = candidate.retryPendingMs;
347292
+ candidate.retryPendingMs = void 0;
347293
+ await this.waitForRetry(request, delay2);
347294
+ this.fence(request);
347295
+ if (this.now() >= deadline)
347296
+ throw new InferenceRoutePolicyError({ category: "timeout", deadlineKind: "remote_aggregate", disposition: "surface_error", reason: "transient", attempt: candidate.policyAttempt });
347297
+ }
347298
+ }
347299
+ async waitForRetry(request, delay2) {
347300
+ let timer;
347301
+ try {
347302
+ const pause = new Promise((resolve40) => {
347303
+ timer = setTimeout(resolve40, delay2);
347304
+ });
347305
+ await (request.signal ? this.abortable(pause, request.signal) : pause);
347306
+ } finally {
347307
+ clearTimeout(timer);
347308
+ }
347309
+ }
347140
347310
  async *candidates(request, deadline, includeCooling = false) {
347311
+ const sticky = successfulRoutes.get(this.options.fetch ?? fetch)?.get(this.stickyKey());
347312
+ const eligible = sticky && this.compatible(request, sticky, true) ? sticky : void 0;
347313
+ const pending = [];
347314
+ let matched = !eligible;
347315
+ for await (const candidate of this.catalogCandidates(request, deadline, includeCooling)) {
347316
+ const preferredCatalogComplete = eligible?.baseUrl === OPENCODE_INFERENCE_BASE_URL && candidate.baseUrl === eligible.baseUrl && eligible.keyless?.catalogKind === "opencode" && candidate.keyless?.catalogKind === "opencode";
347317
+ if (!matched && routeKey(candidate) !== routeKey(eligible) && !preferredCatalogComplete) {
347318
+ pending.push(candidate);
347319
+ continue;
347320
+ }
347321
+ if (!matched) {
347322
+ this.stickyRouting = true;
347323
+ matched = true;
347324
+ yield candidate;
347325
+ for (const prior of pending)
347326
+ yield prior;
347327
+ pending.length = 0;
347328
+ } else
347329
+ yield candidate;
347330
+ }
347331
+ for (const candidate of pending)
347332
+ yield candidate;
347333
+ }
347334
+ async *catalogCandidates(request, deadline, includeCooling = false) {
347141
347335
  const seen = /* @__PURE__ */ new Set();
347142
347336
  const configured = [];
347143
347337
  for (const profile3 of this.policy.providers.slice(0, MAX_CONFIGURED_PROVIDERS)) {
@@ -347312,9 +347506,23 @@ ${line}`).join("");
347312
347506
  const zeroPrice = (value) => (typeof value === "number" || typeof value === "string" && value.trim().length > 0) && Number.isFinite(Number(value)) && Number(value) === 0;
347313
347507
  const models = raw.data.filter((model) => chatCatalogModel(model, anonymousTier) && (!kiloCatalog || object12(model.pricing) && zeroPrice(model.pricing.prompt) && zeroPrice(model.pricing.completion) && Object.values(model.pricing).every(zeroPrice)));
347314
347508
  const unique4 = [...new Map(models.map((model) => [String(model.id), model])).values()];
347315
- const preferredModel = this.builtInPrimaryRequest && openCodeCatalog ? this.primary.model : route.model;
347509
+ const remembered = successfulRoutes.get(fetcher)?.get(this.stickyKey());
347510
+ const rememberedHere = remembered && remembered.baseUrl === route.baseUrl && this.compatible(request, remembered, true);
347511
+ const preferredModel = rememberedHere ? remembered.model : this.builtInPrimaryRequest && openCodeCatalog ? this.primary.model : route.model;
347316
347512
  const preferred = unique4.find((model) => model.id === preferredModel);
347317
- const ordered = unique4.sort((a, b) => this.builtInPrimaryRequest && openCodeCatalog && a.id === preferredModel !== (b.id === preferredModel) ? a.id === preferredModel ? -1 : 1 : compareFallbackCatalogModels(a, b, route.model));
347513
+ if (openCodeCatalog && !preferred && (rememberedHere || this.builtInPrimaryRequest) && cache3.get(modelsUrl)?.source !== "bundled") {
347514
+ const removed = { ...route, model: preferredModel };
347515
+ this.discovery(request, removed, {
347516
+ stage: "eligibility",
347517
+ outcome: "unavailable",
347518
+ modelCount: 0,
347519
+ reason: "selected_model_not_in_active_free_catalog"
347520
+ });
347521
+ this.lifecycle(request, "failed", removed, new CatalogModelUnavailable(), void 0, void 0, { disposition: "rotate_model", reason: "model_unavailable", attempt: 0 });
347522
+ if (rememberedHere)
347523
+ successfulRoutes.get(fetcher)?.delete(this.stickyKey());
347524
+ }
347525
+ const ordered = unique4.sort((a, b) => (rememberedHere || this.builtInPrimaryRequest) && openCodeCatalog && a.id === preferredModel !== (b.id === preferredModel) ? a.id === preferredModel ? -1 : 1 : compareFallbackCatalogModels(a, b, route.model));
347318
347526
  const compatible = [];
347319
347527
  const modelLimit = openCodeCatalog ? MAX_OPENCODE_CATALOG_MODELS : MAX_MODELS_PER_PROVIDER;
347320
347528
  for (const selected of ordered) {
@@ -352995,11 +353203,6 @@ var StructuredMemoryExtractionQueue = class {
352995
353203
  }
352996
353204
  };
352997
353205
 
352998
- // packages/orchestrator/dist/inference-provider-presentation.js
352999
- function inferenceProviderDisplayName(providerId) {
353000
- return providerId === "opencode" || providerId === "builtin-free" ? "built-in free" : providerId ?? "remote";
353001
- }
353002
-
353003
353206
  // packages/orchestrator/dist/native-ollama-tool-calls.js
353004
353207
  function object13(value) {
353005
353208
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -393265,12 +393468,6 @@ ${read.content}`;
393265
393468
  onInferenceRoute: (route) => {
393266
393469
  assertCurrent();
393267
393470
  outbound.onInferenceRoute?.(route);
393268
- this.emit({
393269
- type: "status",
393270
- content: `Local inference unavailable; using ${inferenceProviderDisplayName(route.providerId)} (${route.model}).`,
393271
- turn: operation.turn,
393272
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
393273
- });
393274
393471
  },
393275
393472
  onInferenceRouteLifecycle: (receipt) => {
393276
393473
  assertCurrent();
@@ -427998,7 +428195,7 @@ var OllamaAgenticBackend = class _OllamaAgenticBackend {
427998
428195
  this._inferenceFallbackApprovalHandler = handler;
427999
428196
  }
428000
428197
  isBuiltInPrimary() {
428001
- return this._provider.providerId === "builtin-free" && this._provider.urls.chat === `${OPENCODE_INFERENCE_BASE_URL}/chat/completions`;
428198
+ return this._provider.providerId === "builtin-free" && isOpenCodeInferenceBaseUrl(this._provider.urls.chat?.replace(/\/chat\/completions$/, ""));
428002
428199
  }
428003
428200
  remoteFallback() {
428004
428201
  return this._remoteFallback ??= new RemoteInferenceFallback({
@@ -428378,7 +428575,7 @@ var OllamaAgenticBackend = class _OllamaAgenticBackend {
428378
428575
  };
428379
428576
  }
428380
428577
  usesOpenCodeCompatibility(request) {
428381
- return request.remoteClientIdentity === "opencode" && request.configuredEndpointOnly === true && this._provider.protocol === "openai-chat" && this._provider.urls.chat === `${OPENCODE_INFERENCE_BASE_URL}/chat/completions`;
428578
+ return request.remoteClientIdentity === "opencode" && request.configuredEndpointOnly === true && this._provider.protocol === "openai-chat" && isOpenCodeInferenceBaseUrl(this._provider.urls.chat?.replace(/\/chat\/completions$/, ""));
428382
428579
  }
428383
428580
  usesOpenCodeResponses(request) {
428384
428581
  return this.usesOpenCodeCompatibility(request) && request.remoteOpenCodeApiShape === "responses";