claudish 7.50.0 → 7.52.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
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
729
729
  });
730
730
 
731
731
  // src/version.ts
732
- var VERSION = "7.50.0";
732
+ var VERSION = "7.52.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -27509,6 +27509,9 @@ function loadConfig() {
27509
27509
  if (config2.customEndpoints !== undefined) {
27510
27510
  merged.customEndpoints = config2.customEndpoints;
27511
27511
  }
27512
+ if (config2.predefinedEndpoints !== undefined) {
27513
+ merged.predefinedEndpoints = config2.predefinedEndpoints;
27514
+ }
27512
27515
  if (config2.behavior !== undefined) {
27513
27516
  merged.behavior = config2.behavior;
27514
27517
  }
@@ -27933,24 +27936,44 @@ function getApiKeyInfo(providerName) {
27933
27936
  oauthFallback: def.oauthFallback
27934
27937
  };
27935
27938
  }
27939
+ function describeMissingCredential(providerName) {
27940
+ const info = getApiKeyInfo(providerName);
27941
+ const keyNames = info?.envVar ? [info.envVar, ...info.aliases ?? []].join(" or ") : undefined;
27942
+ const signup = info?.url ? ` Get one at ${info.url}.` : "";
27943
+ const def = getProviderByName(providerName);
27944
+ if (isLocalTransport(providerName)) {
27945
+ const where = def ? ` Claudish will use ${getEffectiveBaseUrl(def)}.` : "";
27946
+ const keyClause = keyNames ? ` (Only set ${keyNames} if your local server requires a bearer token.)` : "";
27947
+ return `Provider "${providerName}" is a LOCAL server and is not enabled. ` + "Enable it in `claudish config` (Providers tab), or add " + `"localProviders": ["${providerName}"] to ~/.claudish/config.json.${where}${keyClause}`;
27948
+ }
27949
+ if (def?.oauthFallback) {
27950
+ const keyClause = keyNames ? ` Or set ${keyNames} (env, config, or 1Password import) to use a metered API key instead.${signup}` : "";
27951
+ return `No credential for provider "${providerName}". Sign in with ` + `\`claudish login ${providerName}\` to use your existing subscription.${keyClause}`;
27952
+ }
27953
+ return keyNames ? `No API key for provider "${providerName}". Set ${keyNames} (env, config, or 1Password import).${signup}` : `No API key for provider "${providerName}".`;
27954
+ }
27936
27955
  function getDisplayName(providerName) {
27937
27956
  const def = getProviderByName(providerName);
27938
27957
  return def?.displayName || providerName.charAt(0).toUpperCase() + providerName.slice(1);
27939
27958
  }
27940
- function getEffectiveBaseUrl(def) {
27941
- if (def.baseUrlEnvVars) {
27942
- for (const envVar of def.baseUrlEnvVars) {
27943
- const fromConfig = getEndpoint(envVar);
27944
- if (fromConfig)
27945
- return fromConfig;
27946
- }
27947
- for (const envVar of def.baseUrlEnvVars) {
27948
- const value = process.env[envVar];
27949
- if (value)
27950
- return value;
27951
- }
27959
+ function baseUrlOverrideCandidates(baseUrlEnvVars) {
27960
+ const found = [];
27961
+ if (!baseUrlEnvVars || baseUrlEnvVars.length === 0)
27962
+ return found;
27963
+ for (const envVar of baseUrlEnvVars) {
27964
+ const fromConfig = getEndpoint(envVar);
27965
+ if (fromConfig)
27966
+ found.push({ envVar, value: fromConfig, source: "config" });
27967
+ }
27968
+ for (const envVar of baseUrlEnvVars) {
27969
+ const value = process.env[envVar];
27970
+ if (value)
27971
+ found.push({ envVar, value, source: "env" });
27952
27972
  }
27953
- return def.baseUrl;
27973
+ return found;
27974
+ }
27975
+ function getEffectiveBaseUrl(def) {
27976
+ return baseUrlOverrideCandidates(def.baseUrlEnvVars)[0]?.value ?? def.baseUrl;
27954
27977
  }
27955
27978
  function isLocalTransport(providerName) {
27956
27979
  if (!_localProvidersCache) {
@@ -30753,6 +30776,7 @@ class AntigravityProviderTransport {
30753
30776
  if (!data?.buckets?.length)
30754
30777
  return;
30755
30778
  const lines = [];
30779
+ let sawThrottledModel = false;
30756
30780
  for (const bucket of data.buckets) {
30757
30781
  if (!bucket.modelId)
30758
30782
  continue;
@@ -30761,15 +30785,24 @@ class AntigravityProviderTransport {
30761
30785
  hour: "2-digit",
30762
30786
  minute: "2-digit"
30763
30787
  }) : "?";
30764
- lines.push(` ${bucket.modelId}: ${pct} remaining (resets ${reset})`);
30788
+ const mine = this.isThrottledModel(bucket.modelId);
30789
+ if (mine)
30790
+ sawThrottledModel = true;
30791
+ lines.push(` ${mine ? "> " : " "}${bucket.modelId}: ${pct} remaining (resets ${reset})`);
30765
30792
  }
30766
- if (lines.length > 0) {
30767
- logStderr(`[Antigravity] Quota status:
30793
+ if (lines.length === 0)
30794
+ return;
30795
+ const requested = this.servedModelName && this.servedModelName !== this.modelName ? `${this.modelName} (served as ${this.servedModelName})` : this.modelName;
30796
+ const header = sawThrottledModel ? `[Antigravity] Quota status (> = ${requested}):` : `[Antigravity] Quota status \u2014 NOTE: ${requested} has no quota bucket below, ` + "so these figures do not explain its rate limit:";
30797
+ logStderr(`${header}
30768
30798
  ${lines.join(`
30769
30799
  `)}`);
30770
- }
30771
30800
  } catch {}
30772
30801
  }
30802
+ isThrottledModel(bucketModelId) {
30803
+ const id = bucketModelId.toLowerCase();
30804
+ return id === this.modelName.toLowerCase() || id === this.servedModelName.toLowerCase();
30805
+ }
30773
30806
  async getQuotaRemaining(modelName) {
30774
30807
  if (!this.accessToken || !this.projectId)
30775
30808
  return;
@@ -30850,6 +30883,22 @@ var init_antigravity_credential = __esm(() => {
30850
30883
  init_antigravity_user();
30851
30884
  });
30852
30885
 
30886
+ // src/auth/credentials/local-api-key.ts
30887
+ function resolveLocalApiKey(q) {
30888
+ return realValue(process.env[q.envVar]) || (q.aliases ?? []).map((a) => realValue(process.env[a])).find((v) => !!v) || realValue(getApiKey(q.envVar));
30889
+ }
30890
+ function hasLocalApiKey(q) {
30891
+ try {
30892
+ return !!resolveLocalApiKey(q);
30893
+ } catch {
30894
+ return false;
30895
+ }
30896
+ }
30897
+ var init_local_api_key = __esm(() => {
30898
+ init_env_placeholder();
30899
+ init_profile_config();
30900
+ });
30901
+
30853
30902
  // src/auth/credentials/api-key-credential.ts
30854
30903
  import { existsSync as existsSync8 } from "fs";
30855
30904
  import { homedir as homedir10 } from "os";
@@ -30877,7 +30926,7 @@ class ApiKeyCredentialProvider {
30877
30926
  this.declaredKey = descriptor.declaredKey;
30878
30927
  }
30879
30928
  resolveFromEnvConfig() {
30880
- return realValue(process.env[this.envVar]) || this.aliases.map((a) => realValue(process.env[a])).find((v) => !!v) || realValue(getApiKey(this.envVar)) || realValue(this.resolveDeclared());
30929
+ return resolveLocalApiKey({ envVar: this.envVar, aliases: this.aliases }) || realValue(this.resolveDeclared());
30881
30930
  }
30882
30931
  resolveDeclared() {
30883
30932
  try {
@@ -30958,7 +31007,7 @@ class ApiKeyCredentialProvider {
30958
31007
  }
30959
31008
  var init_api_key_credential = __esm(() => {
30960
31009
  init_env_placeholder();
30961
- init_profile_config();
31010
+ init_local_api_key();
30962
31011
  init_op_source();
30963
31012
  });
30964
31013
 
@@ -32359,7 +32408,7 @@ var init_authority = __esm(() => {
32359
32408
  });
32360
32409
 
32361
32410
  // src/config-schema.ts
32362
- var BuiltinDefaultProviderSchema, CustomEndpointSimpleSchema, CustomEndpointComplexSchema, CustomEndpointSchema, DefaultProviderSchema;
32411
+ var BuiltinDefaultProviderSchema, CustomEndpointSimpleSchema, CustomEndpointComplexSchema, CustomEndpointSchema, PredefinedEndpointsConfigSchema, DefaultProviderSchema;
32363
32412
  var init_config_schema = __esm(() => {
32364
32413
  init_zod();
32365
32414
  BuiltinDefaultProviderSchema = exports_external.enum([
@@ -32394,6 +32443,11 @@ var init_config_schema = __esm(() => {
32394
32443
  CustomEndpointSimpleSchema,
32395
32444
  CustomEndpointComplexSchema
32396
32445
  ]);
32446
+ PredefinedEndpointsConfigSchema = exports_external.object({
32447
+ enabled: exports_external.boolean().optional(),
32448
+ disable: exports_external.array(exports_external.string()).optional(),
32449
+ enable: exports_external.array(exports_external.string()).optional()
32450
+ });
32397
32451
  DefaultProviderSchema = exports_external.union([BuiltinDefaultProviderSchema, exports_external.string().min(1)]);
32398
32452
  });
32399
32453
 
@@ -32631,12 +32685,14 @@ var init_deepseek_model_dialect = __esm(() => {
32631
32685
  }
32632
32686
  applyNativeReasoning(request, originalRequest) {
32633
32687
  const effort = this.resolveEffortLevel(originalRequest);
32634
- if (effort && this.isV4Model()) {
32688
+ if (effort && this.acceptsReasoningControls()) {
32635
32689
  if (effort === "none" || effort === "minimal") {
32636
32690
  request.thinking = { type: "disabled" };
32637
32691
  log(`[DeepSeekModelDialect] effort ${effort} -> thinking.type: disabled for ${this.modelId}`);
32638
32692
  } else {
32639
- const value = effort === "xhigh" || effort === "max" ? "max" : "high";
32693
+ const reasoning = this.lookupReasoningCapability();
32694
+ const clamped = reasoning?.control === "effort" && reasoning.efforts?.length ? this.clampToAdvertisedEffort(effort, reasoning) : undefined;
32695
+ const value = clamped ?? (effort === "xhigh" || effort === "max" ? "max" : "high");
32640
32696
  request.reasoning_effort = value;
32641
32697
  log(`[DeepSeekModelDialect] effort ${effort} -> reasoning_effort: ${value} for ${this.modelId}`);
32642
32698
  }
@@ -32648,9 +32704,15 @@ var init_deepseek_model_dialect = __esm(() => {
32648
32704
  }
32649
32705
  return request;
32650
32706
  }
32651
- isV4Model() {
32707
+ acceptsReasoningControls() {
32708
+ const reasoning = this.lookupReasoningCapability();
32709
+ if (reasoning)
32710
+ return reasoning.supported === true && reasoning.control === "effort";
32652
32711
  const model = this.modelId.toLowerCase();
32653
- return model.includes("v4") || model.includes("deepseek-chat") || model.includes("deepseek-reasoner");
32712
+ if (model.includes("deepseek-chat") || model.includes("deepseek-reasoner"))
32713
+ return true;
32714
+ const version2 = /(?:^|[^a-z0-9])v(\d+)/.exec(model);
32715
+ return version2 ? Number(version2[1]) >= 4 : false;
32654
32716
  }
32655
32717
  shouldHandle(modelId) {
32656
32718
  return matchesModelFamily(modelId, "deepseek");
@@ -34061,11 +34123,87 @@ var init_glm_model_dialect = __esm(() => {
34061
34123
  };
34062
34124
  });
34063
34125
 
34126
+ // src/adapters/grok-effort-support.ts
34127
+ function acceptsReasoningEffort(modelId) {
34128
+ const id = norm(modelId);
34129
+ if (!id)
34130
+ return false;
34131
+ if (learnedRejects.has(id))
34132
+ return false;
34133
+ const reasoning = lookupModelReasoning(id) ?? lookupModelReasoning(modelId);
34134
+ if (reasoning)
34135
+ return reasoning.supported === true && reasoning.control === "effort";
34136
+ return !SEED_REJECTS.some((re) => re.test(id));
34137
+ }
34138
+ function catalogReasoningFor(modelId) {
34139
+ return lookupModelReasoning(norm(modelId)) ?? lookupModelReasoning(modelId);
34140
+ }
34141
+ function rememberReasoningEffortRejected(modelId) {
34142
+ const id = norm(modelId);
34143
+ if (!id || learnedRejects.has(id))
34144
+ return;
34145
+ learnedRejects.add(id);
34146
+ log(`[GrokEffortSupport] ${id} rejected reasoning_effort \u2014 not sending it again this session.`);
34147
+ }
34148
+ function isReasoningEffortRejection(errorText) {
34149
+ if (!errorText)
34150
+ return false;
34151
+ return /does not support parameter\s+`?reasoning[_]?effort`?/i.test(errorText);
34152
+ }
34153
+ function acceptsReasoningEffortValue(modelId, value) {
34154
+ return !learnedValueRejects.has(pairKey(modelId, value));
34155
+ }
34156
+ function rememberReasoningEffortValueRejected(modelId, value) {
34157
+ const key = pairKey(modelId, value);
34158
+ if (learnedValueRejects.has(key))
34159
+ return;
34160
+ learnedValueRejects.add(key);
34161
+ log(`[GrokEffortSupport] ${norm(modelId)} rejected reasoning_effort value "${value}" \u2014 ` + "falling back and not sending it again this session.");
34162
+ }
34163
+ function rejectedReasoningEffortValue(errorText) {
34164
+ if (!errorText)
34165
+ return null;
34166
+ const m = /does not support\s+`?reasoning[_]?effort`?\s+value\s+`?([a-z]+)`?/i.exec(errorText);
34167
+ return m ? m[1].toLowerCase() : null;
34168
+ }
34169
+ function fallbackReasoningEffortValue(value) {
34170
+ switch (value.toLowerCase()) {
34171
+ case "none":
34172
+ return "low";
34173
+ case "minimal":
34174
+ return "low";
34175
+ case "low":
34176
+ return null;
34177
+ case "medium":
34178
+ return "low";
34179
+ case "high":
34180
+ return "medium";
34181
+ default:
34182
+ return null;
34183
+ }
34184
+ }
34185
+ var SEED_REJECTS, learnedRejects, norm = (modelId) => modelId.trim().toLowerCase().replace(/^x-ai\//, ""), learnedValueRejects, pairKey = (modelId, value) => `${norm(modelId)}\x00${value.toLowerCase()}`;
34186
+ var init_grok_effort_support = __esm(() => {
34187
+ init_logger();
34188
+ init_model_catalog();
34189
+ SEED_REJECTS = [
34190
+ /non-reasoning/i,
34191
+ /^grok-2/i,
34192
+ /^grok-4(?![.\d])(?!.*fast-reasoning)/i,
34193
+ /^grok-build-/i,
34194
+ /^grok-code-/i,
34195
+ /^grok-4\.20/i
34196
+ ];
34197
+ learnedRejects = new Set;
34198
+ learnedValueRejects = new Set;
34199
+ });
34200
+
34064
34201
  // src/adapters/grok-model-dialect.ts
34065
34202
  var GrokModelDialect;
34066
34203
  var init_grok_model_dialect = __esm(() => {
34067
34204
  init_logger();
34068
34205
  init_base_api_format();
34206
+ init_grok_effort_support();
34069
34207
  init_model_catalog();
34070
34208
  GrokModelDialect = class GrokModelDialect extends BaseAPIFormat {
34071
34209
  xmlBuffer = "";
@@ -34128,34 +34266,78 @@ var init_grok_model_dialect = __esm(() => {
34128
34266
  return request;
34129
34267
  }
34130
34268
  effortToReasoningEffort(effort) {
34131
- const model = this.modelId.toLowerCase();
34132
- const isMini = model.includes("mini");
34133
- const isGrok43 = /grok-4\.3(\b|[-.]|$)/.test(model);
34134
- const isFastReasoning = model.includes("fast-reasoning");
34135
- if (!(isMini || isGrok43 || isFastReasoning)) {
34269
+ if (!acceptsReasoningEffort(this.modelId)) {
34136
34270
  return;
34137
34271
  }
34138
- if (isMini) {
34139
- switch (effort) {
34140
- case "high":
34141
- case "xhigh":
34142
- case "max":
34143
- return "high";
34144
- default:
34145
- return "low";
34272
+ let value;
34273
+ const reasoning = catalogReasoningFor(this.modelId);
34274
+ const clamped = reasoning ? this.clampToAdvertisedEffort(effort, reasoning) : undefined;
34275
+ if (clamped) {
34276
+ value = clamped;
34277
+ } else {
34278
+ const model = this.modelId.toLowerCase();
34279
+ const isMini = model.includes("mini");
34280
+ if (isMini) {
34281
+ switch (effort) {
34282
+ case "high":
34283
+ case "xhigh":
34284
+ case "max":
34285
+ value = "high";
34286
+ break;
34287
+ default:
34288
+ value = "low";
34289
+ }
34290
+ } else {
34291
+ switch (effort) {
34292
+ case "none":
34293
+ value = "none";
34294
+ break;
34295
+ case "minimal":
34296
+ case "low":
34297
+ value = "low";
34298
+ break;
34299
+ case "medium":
34300
+ value = "medium";
34301
+ break;
34302
+ default:
34303
+ value = "high";
34304
+ }
34146
34305
  }
34147
34306
  }
34148
- switch (effort) {
34149
- case "none":
34150
- return "none";
34151
- case "minimal":
34152
- case "low":
34153
- return "low";
34154
- case "medium":
34155
- return "medium";
34156
- default:
34157
- return "high";
34307
+ let guard = 0;
34308
+ while (!acceptsReasoningEffortValue(this.modelId, value) && guard++ < 8) {
34309
+ const next = fallbackReasoningEffortValue(value);
34310
+ if (!next)
34311
+ return;
34312
+ value = next;
34158
34313
  }
34314
+ return value;
34315
+ }
34316
+ recoverFromRejection(payload, errorText) {
34317
+ if (!payload || payload.reasoning_effort === undefined)
34318
+ return null;
34319
+ if (isReasoningEffortRejection(errorText)) {
34320
+ rememberReasoningEffortRejected(this.modelId);
34321
+ const next = { ...payload };
34322
+ delete next.reasoning_effort;
34323
+ return { payload: next, note: `dropped reasoning_effort for ${this.modelId}` };
34324
+ }
34325
+ const rejected = rejectedReasoningEffortValue(errorText);
34326
+ if (rejected) {
34327
+ rememberReasoningEffortValueRejected(this.modelId, rejected);
34328
+ const fallback = fallbackReasoningEffortValue(rejected);
34329
+ const next = { ...payload };
34330
+ if (fallback) {
34331
+ next.reasoning_effort = fallback;
34332
+ return {
34333
+ payload: next,
34334
+ note: `reasoning_effort "${rejected}" -> "${fallback}" for ${this.modelId}`
34335
+ };
34336
+ }
34337
+ delete next.reasoning_effort;
34338
+ return { payload: next, note: `dropped unsupported reasoning_effort for ${this.modelId}` };
34339
+ }
34340
+ return null;
34159
34341
  }
34160
34342
  parseXmlParameters(xmlContent) {
34161
34343
  const params = {};
@@ -36035,6 +36217,45 @@ Do not invent a different filename, and do not derive one from the task. Claude
36035
36217
  PLAN_MODE_RULES = [planFilePathRule];
36036
36218
  });
36037
36219
 
36220
+ // src/behavior/rules/session-context.ts
36221
+ function hasInjectedSessionContext(systemText2, messages) {
36222
+ const haystacks = [systemText2 ?? ""];
36223
+ const first = Array.isArray(messages) ? messages[0] : undefined;
36224
+ if (first && typeof first === "object") {
36225
+ const content = first.content;
36226
+ if (typeof content === "string") {
36227
+ haystacks.push(content);
36228
+ } else if (Array.isArray(content)) {
36229
+ for (const block of content) {
36230
+ const text = block?.text;
36231
+ if (typeof text === "string")
36232
+ haystacks.push(text);
36233
+ }
36234
+ }
36235
+ }
36236
+ return haystacks.some((h) => {
36237
+ const lower = h.toLowerCase();
36238
+ return SESSION_CONTEXT_MARKERS.some((m) => lower.includes(m));
36239
+ });
36240
+ }
36241
+ var SESSION_CONTEXT_MARKERS, NOTE, noHarnessEchoRule, SESSION_CONTEXT_RULES;
36242
+ var init_session_context = __esm(() => {
36243
+ SESSION_CONTEXT_MARKERS = ["sessionstart hook additional context", "hook additional context"];
36244
+ NOTE = "Operational context note. This session may include text injected by the harness " + "itself \u2014 SessionStart hook output, coaching or insight blocks, tooling banners, " + "status lines. That material is addressed to you, not to the user, and it is not " + "part of the task. Never reproduce, summarise, or quote it in your answer, and never " + "let it open your response. Begin your answer with the requested deliverable and " + "nothing before it.";
36245
+ noHarnessEchoRule = {
36246
+ id: "session-context/no-harness-echo",
36247
+ description: "Stop foreign models opening their answer with harness-injected session context " + "(SessionStart hook output, coaching/insight blocks).",
36248
+ defaultSeverity: "fix",
36249
+ appliesTo: ({ isNativeAnthropic }) => !isNativeAnthropic,
36250
+ onRequest(ctx) {
36251
+ if (!hasInjectedSessionContext(ctx.systemText, ctx.messages))
36252
+ return [];
36253
+ return [{ type: "injectSystemNote", text: NOTE }];
36254
+ }
36255
+ };
36256
+ SESSION_CONTEXT_RULES = [noHarnessEchoRule];
36257
+ });
36258
+
36038
36259
  // src/behavior/hooks.ts
36039
36260
  import { isAbsolute, resolve } from "path";
36040
36261
  function isBehaviorRule(value) {
@@ -36244,6 +36465,7 @@ var init_behavior = __esm(() => {
36244
36465
  init_config();
36245
36466
  init_engine();
36246
36467
  init_plan_mode();
36468
+ init_session_context();
36247
36469
  init_engine();
36248
36470
  init_config();
36249
36471
  init_harness();
@@ -36253,7 +36475,7 @@ var init_behavior = __esm(() => {
36253
36475
  init_corpus();
36254
36476
  init_aggregate();
36255
36477
  init_upload();
36256
- BUILTIN_RULES = [...PLAN_MODE_RULES];
36478
+ BUILTIN_RULES = [...PLAN_MODE_RULES, ...SESSION_CONTEXT_RULES];
36257
36479
  hookRules = [];
36258
36480
  });
36259
36481
 
@@ -36581,7 +36803,14 @@ class OpenAIProviderTransport {
36581
36803
  async getHeaders() {
36582
36804
  const headers = {};
36583
36805
  if (this.apiKey) {
36584
- headers.Authorization = `Bearer ${this.apiKey}`;
36806
+ if (this.provider.authScheme === "x-api-key") {
36807
+ headers["x-api-key"] = this.apiKey;
36808
+ } else {
36809
+ headers.Authorization = `Bearer ${this.apiKey}`;
36810
+ }
36811
+ }
36812
+ if (this.provider.headers) {
36813
+ Object.assign(headers, this.provider.headers);
36585
36814
  }
36586
36815
  return headers;
36587
36816
  }
@@ -40789,6 +41018,30 @@ class ComposedHandler {
40789
41018
  }
40790
41019
  log(`[${this.provider.displayName}] Response status: ${response.status}`);
40791
41020
  this.capturePlanUsage(response);
41021
+ if (!response.ok) {
41022
+ if (response.status >= 400 && response.status < 500 && this.modelAdapter?.recoverFromRejection) {
41023
+ const errorText = await response.clone().text();
41024
+ const recovery = this.modelAdapter.recoverFromRejection(requestPayload, errorText);
41025
+ if (recovery) {
41026
+ log(`[${this.provider.displayName}] Parameter rejected \u2014 retrying: ${recovery.note}`);
41027
+ requestPayload = recovery.payload;
41028
+ const retrySerialized = this.provider.serializeBody?.(requestPayload);
41029
+ const retryHeaders = await this.provider.getHeaders();
41030
+ retryHeaders["Content-Type"] = retrySerialized?.contentType ?? "application/json";
41031
+ const retryResp = await fetch(endpoint, {
41032
+ method: "POST",
41033
+ headers: retryHeaders,
41034
+ body: retrySerialized?.body ?? JSON.stringify(requestPayload),
41035
+ ...this.provider.getRequestInit?.() || {}
41036
+ });
41037
+ if (retryResp.ok) {
41038
+ response = retryResp;
41039
+ } else {
41040
+ log(`[${this.provider.displayName}] Retry after ${recovery.note} still failed ` + `(HTTP ${retryResp.status})`);
41041
+ }
41042
+ }
41043
+ }
41044
+ }
40792
41045
  if (!response.ok) {
40793
41046
  if (response.status === 401 && this.provider.forceRefreshAuth) {
40794
41047
  log(`[${this.provider.displayName}] Got 401, forcing auth refresh and retrying`);
@@ -41383,6 +41636,21 @@ var init_composed_handler = __esm(() => {
41383
41636
  STREAM_RETRY_DELAYS_MS = [3000, 15000, 30000];
41384
41637
  });
41385
41638
 
41639
+ // src/providers/endpoint-diagnostics.ts
41640
+ function recordEndpointUnavailable(name, reason) {
41641
+ reasons.set(name, reason);
41642
+ }
41643
+ function clearEndpointUnavailable(name) {
41644
+ reasons.delete(name);
41645
+ }
41646
+ function getEndpointUnavailableReason(name) {
41647
+ return reasons.get(name);
41648
+ }
41649
+ var reasons;
41650
+ var init_endpoint_diagnostics = __esm(() => {
41651
+ reasons = new Map;
41652
+ });
41653
+
41386
41654
  // src/providers/devin/devin-request.ts
41387
41655
  import { randomUUID as randomUUID3 } from "crypto";
41388
41656
  function encodeChatMetadata(meta3) {
@@ -42645,6 +42913,21 @@ var init_litellm = __esm(() => {
42645
42913
  });
42646
42914
 
42647
42915
  // src/providers/custom-endpoints-loader.ts
42916
+ function registerEndpoint(name, entry, ovr) {
42917
+ const validated = CustomEndpointSchema.parse(entry);
42918
+ const def = buildProviderDefinition(name, validated, ovr);
42919
+ const profile = buildProviderProfile(validated, ovr);
42920
+ registerRuntimeProvider(def);
42921
+ registerRuntimeProfile(name, profile);
42922
+ credentials.registerApiKeyProvider({
42923
+ name: def.name,
42924
+ envVar: def.apiKeyEnvVar,
42925
+ aliases: def.apiKeyAliases,
42926
+ authScheme: def.authScheme === "x-api-key" ? "x-api-key" : "bearer",
42927
+ declaredKey: () => resolveDeclaredEndpointKey(validated)
42928
+ });
42929
+ clearEndpointUnavailable(name);
42930
+ }
42648
42931
  function loadCustomEndpoints(config2) {
42649
42932
  const result = { registered: 0, errors: [] };
42650
42933
  const raw = config2.customEndpoints;
@@ -42652,17 +42935,7 @@ function loadCustomEndpoints(config2) {
42652
42935
  return result;
42653
42936
  for (const [name, entry] of Object.entries(raw)) {
42654
42937
  try {
42655
- const validated = CustomEndpointSchema.parse(entry);
42656
- const def = buildProviderDefinition(name, validated);
42657
- const profile = buildProviderProfile(validated);
42658
- registerRuntimeProvider(def);
42659
- registerRuntimeProfile(name, profile);
42660
- credentials.registerApiKeyProvider({
42661
- name: def.name,
42662
- envVar: def.apiKeyEnvVar,
42663
- authScheme: def.authScheme === "x-api-key" ? "x-api-key" : "bearer",
42664
- declaredKey: () => resolveDeclaredEndpointKey(validated)
42665
- });
42938
+ registerEndpoint(name, entry);
42666
42939
  result.registered++;
42667
42940
  } catch (err) {
42668
42941
  const message = err instanceof exports_external.ZodError ? err.issues.map((i) => i.message).join(", ") : err instanceof Error ? err.message : String(err);
@@ -42671,22 +42944,28 @@ function loadCustomEndpoints(config2) {
42671
42944
  }
42672
42945
  return result;
42673
42946
  }
42674
- function buildProviderDefinition(name, ep) {
42947
+ function buildProviderDefinition(name, ep, ovr) {
42948
+ const apiKeyEnvVar = ovr?.apiKeyEnvVar ?? customEndpointKeyEnvVar(name);
42949
+ const apiKeyAliases = ovr?.apiKeyAliases;
42950
+ const baseUrlEnvVars = ovr?.baseUrlEnvVars;
42951
+ const apiKeyUrl = ovr?.apiKeyUrl ?? "";
42675
42952
  if (ep.kind === "simple") {
42676
42953
  return {
42677
42954
  name,
42678
42955
  displayName: name,
42679
42956
  transport: ep.format,
42680
42957
  baseUrl: stripTrailingSlash(ep.url),
42958
+ baseUrlEnvVars,
42681
42959
  apiPath: "/chat/completions",
42682
- apiKeyEnvVar: `CUSTOM_${sanitizeEnvName(name)}_KEY`,
42683
- apiKeyDescription: `${name} (custom endpoint)`,
42684
- apiKeyUrl: "",
42960
+ apiKeyEnvVar,
42961
+ apiKeyAliases,
42962
+ apiKeyDescription: ovr?.apiKeyDescription ?? `${name} (custom endpoint)`,
42963
+ apiKeyUrl,
42685
42964
  shortcuts: [name],
42686
42965
  legacyPrefixes: [],
42687
42966
  isDirectApi: true,
42688
42967
  shortestPrefix: name,
42689
- description: `Custom endpoint: ${name}`,
42968
+ description: ovr?.description ?? `Custom endpoint: ${name}`,
42690
42969
  authScheme: "bearer"
42691
42970
  };
42692
42971
  }
@@ -42695,33 +42974,68 @@ function buildProviderDefinition(name, ep) {
42695
42974
  displayName: ep.displayName,
42696
42975
  transport: ep.transport,
42697
42976
  baseUrl: stripTrailingSlash(ep.baseUrl),
42977
+ baseUrlEnvVars,
42698
42978
  apiPath: ep.apiPath ?? "/v1/chat/completions",
42699
- apiKeyEnvVar: `CUSTOM_${sanitizeEnvName(name)}_KEY`,
42700
- apiKeyDescription: `${ep.displayName} (custom endpoint)`,
42701
- apiKeyUrl: "",
42979
+ apiKeyEnvVar,
42980
+ apiKeyAliases,
42981
+ apiKeyDescription: ovr?.apiKeyDescription ?? `${ep.displayName} (custom endpoint)`,
42982
+ apiKeyUrl,
42702
42983
  shortcuts: [name],
42703
42984
  legacyPrefixes: [],
42704
42985
  isDirectApi: true,
42705
42986
  shortestPrefix: name,
42706
- description: `Custom endpoint: ${ep.displayName}`,
42987
+ description: ovr?.description ?? `Custom endpoint: ${ep.displayName}`,
42707
42988
  headers: ep.headers,
42708
42989
  authScheme: ep.authScheme ?? "bearer"
42709
42990
  };
42710
42991
  }
42711
- function buildProviderProfile(ep) {
42992
+ function buildProviderProfile(ep, ovr) {
42712
42993
  return {
42713
42994
  createHandler(ctx) {
42714
42995
  const apiKey = ctx.apiKey || resolveCustomEndpointApiKey(ep);
42996
+ const declaredBaseUrl = ep.kind === "simple" ? ep.url : ep.baseUrl;
42997
+ const resolved = classifyEndpointBaseUrl(declaredBaseUrl, ovr?.baseUrlEnvVars);
42998
+ if (!resolved.ok) {
42999
+ const reason = describeBadBaseUrlOverride(resolved, declaredBaseUrl);
43000
+ console.error(`[claudish] ${reason}`);
43001
+ recordEndpointUnavailable(ctx.provider.name, `its ${reason}`);
43002
+ return null;
43003
+ }
43004
+ const baseUrl = resolved.url;
42715
43005
  if (ep.kind === "simple") {
42716
- return buildSimpleHandler(ep, ctx, apiKey);
43006
+ return buildSimpleHandler(ep, ctx, apiKey, baseUrl);
42717
43007
  }
42718
- return buildComplexHandler(ep, ctx, apiKey);
43008
+ return buildComplexHandler(ep, ctx, apiKey, baseUrl);
42719
43009
  }
42720
43010
  };
42721
43011
  }
42722
- function buildSimpleHandler(ep, ctx, apiKey) {
43012
+ function classifyEndpointBaseUrl(declared, baseUrlEnvVars) {
43013
+ for (const candidate of baseUrlOverrideCandidates(baseUrlEnvVars)) {
43014
+ const value = realValue(candidate.value)?.trim();
43015
+ if (!value)
43016
+ continue;
43017
+ if (!isHttpUrl(value)) {
43018
+ return { ok: false, envVar: candidate.envVar, value, source: candidate.source };
43019
+ }
43020
+ return { ok: true, url: stripTrailingSlash(value) };
43021
+ }
43022
+ return { ok: true, url: stripTrailingSlash(declared) };
43023
+ }
43024
+ function describeBadBaseUrlOverride(bad, declared) {
43025
+ const where = bad.source === "config" ? `config.endpoints["${bad.envVar}"] is set to '${bad.value}'` : `${bad.envVar} is set to '${bad.value}'`;
43026
+ const remedy = bad.source === "config" ? "Fix or remove it (claudish config -> Providers)." : "Fix or unset it.";
43027
+ return `${where}, which is not a valid http(s) URL. ` + `${remedy} (Not falling back to ${declared} \u2014 the override was set on purpose.)`;
43028
+ }
43029
+ function isHttpUrl(value) {
43030
+ try {
43031
+ const parsed = new URL(value);
43032
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
43033
+ } catch {
43034
+ return false;
43035
+ }
43036
+ }
43037
+ function buildSimpleHandler(ep, ctx, apiKey, baseUrl) {
42723
43038
  const finalModel = ep.modelPrefix ? `${ep.modelPrefix}${ctx.modelName}` : ctx.modelName;
42724
- const baseUrl = stripTrailingSlash(ep.url);
42725
43039
  if (ep.format === "openai") {
42726
43040
  const remoteProvider2 = {
42727
43041
  name: ctx.provider.name,
@@ -42756,9 +43070,8 @@ function buildSimpleHandler(ep, ctx, apiKey) {
42756
43070
  ...ctx.sharedOpts
42757
43071
  });
42758
43072
  }
42759
- function buildComplexHandler(ep, ctx, apiKey) {
43073
+ function buildComplexHandler(ep, ctx, apiKey, baseUrl) {
42760
43074
  const finalModel = ep.modelPrefix ? `${ep.modelPrefix}${ctx.modelName}` : ctx.modelName;
42761
- const baseUrl = stripTrailingSlash(ep.baseUrl);
42762
43075
  const apiPath = ep.apiPath ?? "/v1/chat/completions";
42763
43076
  switch (ep.transport) {
42764
43077
  case "litellm": {
@@ -42830,6 +43143,9 @@ function resolveDeclaredEndpointKey(ep) {
42830
43143
  function stripTrailingSlash(url2) {
42831
43144
  return url2.replace(/\/+$/, "");
42832
43145
  }
43146
+ function customEndpointKeyEnvVar(name) {
43147
+ return `CUSTOM_${sanitizeEnvName(name)}_KEY`;
43148
+ }
42833
43149
  function sanitizeEnvName(name) {
42834
43150
  return name.toUpperCase().replace(/[^A-Z0-9]/g, "_");
42835
43151
  }
@@ -42840,13 +43156,518 @@ var init_custom_endpoints_loader = __esm(() => {
42840
43156
  init_openai_api_format();
42841
43157
  init_authority();
42842
43158
  init_config_schema();
43159
+ init_env_placeholder();
42843
43160
  init_composed_handler();
43161
+ init_endpoint_diagnostics();
43162
+ init_provider_definitions();
42844
43163
  init_runtime_providers();
42845
43164
  init_anthropic_compat();
42846
43165
  init_litellm();
42847
43166
  init_openai();
42848
43167
  });
42849
43168
 
43169
+ // src/providers/picker-alias-extra.ts
43170
+ var PROVIDER_FILTER_ALIAS_EXTRA;
43171
+ var init_picker_alias_extra = __esm(() => {
43172
+ PROVIDER_FILTER_ALIAS_EXTRA = {
43173
+ gem: "google",
43174
+ zen: "opencode-zen"
43175
+ };
43176
+ });
43177
+
43178
+ // src/providers/predefined-catalog.ts
43179
+ var PREDEFINED_ENDPOINTS;
43180
+ var init_predefined_catalog = __esm(() => {
43181
+ PREDEFINED_ENDPOINTS = [
43182
+ {
43183
+ name: "groq",
43184
+ displayName: "Groq",
43185
+ baseUrl: "https://api.groq.com/openai",
43186
+ apiPath: "/v1/chat/completions",
43187
+ format: "openai",
43188
+ apiKeyEnvVar: "GROQ_API_KEY",
43189
+ apiKeyUrl: "https://console.groq.com/keys",
43190
+ description: "Groq LPU inference (groq@)",
43191
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43192
+ },
43193
+ {
43194
+ name: "cerebras",
43195
+ displayName: "Cerebras",
43196
+ baseUrl: "https://api.cerebras.ai",
43197
+ apiPath: "/v1/chat/completions",
43198
+ format: "openai",
43199
+ apiKeyEnvVar: "CEREBRAS_API_KEY",
43200
+ apiKeyUrl: "https://cloud.cerebras.ai",
43201
+ description: "Cerebras Inference wafer-scale hosting (cerebras@)",
43202
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43203
+ },
43204
+ {
43205
+ name: "together",
43206
+ displayName: "Together AI",
43207
+ baseUrl: "https://api.together.xyz",
43208
+ apiPath: "/v1/chat/completions",
43209
+ format: "openai",
43210
+ apiKeyEnvVar: "TOGETHER_API_KEY",
43211
+ apiKeyUrl: "https://api.together.xyz/settings/api-keys",
43212
+ description: "Together AI direct inference API (together@) \u2014 distinct from the 'together-ai' vendor id models-index uses as an aggregator label",
43213
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43214
+ },
43215
+ {
43216
+ name: "fireworks",
43217
+ displayName: "Fireworks AI",
43218
+ baseUrl: "https://api.fireworks.ai/inference",
43219
+ apiPath: "/v1/chat/completions",
43220
+ format: "openai",
43221
+ apiKeyEnvVar: "FIREWORKS_API_KEY",
43222
+ apiKeyUrl: "https://fireworks.ai/account/api-keys",
43223
+ description: "Fireworks AI serverless inference (fireworks@)",
43224
+ evidence: {
43225
+ tier: "probe",
43226
+ verdict: "model-gate",
43227
+ status: 404,
43228
+ measuredAt: "2026-08-14",
43229
+ note: "404 rejecting the fake model ('Model not found, inaccessible, and/or not deployed'); the bogus sibling path answered 'Path not found', so the route resolved"
43230
+ }
43231
+ },
43232
+ {
43233
+ name: "deepinfra",
43234
+ displayName: "DeepInfra",
43235
+ baseUrl: "https://api.deepinfra.com/v1/openai",
43236
+ apiPath: "/chat/completions",
43237
+ format: "openai",
43238
+ apiKeyEnvVar: "DEEPINFRA_API_KEY",
43239
+ apiKeyUrl: "https://deepinfra.com/dash/api_keys",
43240
+ description: "DeepInfra hosted open models (deepinfra@)",
43241
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43242
+ },
43243
+ {
43244
+ name: "nebius",
43245
+ displayName: "Nebius AI Studio",
43246
+ baseUrl: "https://api.studio.nebius.com",
43247
+ apiPath: "/v1/chat/completions",
43248
+ format: "openai",
43249
+ apiKeyEnvVar: "NEBIUS_API_KEY",
43250
+ apiKeyUrl: "https://studio.nebius.com",
43251
+ description: "Nebius AI Studio hosted open models (nebius@)",
43252
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43253
+ },
43254
+ {
43255
+ name: "hyperbolic",
43256
+ displayName: "Hyperbolic",
43257
+ baseUrl: "https://api.hyperbolic.xyz",
43258
+ apiPath: "/v1/chat/completions",
43259
+ format: "openai",
43260
+ apiKeyEnvVar: "HYPERBOLIC_API_KEY",
43261
+ apiKeyUrl: "https://app.hyperbolic.xyz",
43262
+ description: "Hyperbolic decentralized GPU inference (hyperbolic@)",
43263
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43264
+ },
43265
+ {
43266
+ name: "sambanova",
43267
+ displayName: "SambaNova Cloud",
43268
+ baseUrl: "https://api.sambanova.ai",
43269
+ apiPath: "/v1/chat/completions",
43270
+ format: "openai",
43271
+ apiKeyEnvVar: "SAMBANOVA_API_KEY",
43272
+ apiKeyUrl: "https://cloud.sambanova.ai",
43273
+ description: "SambaNova Cloud RDU inference (sambanova@)",
43274
+ evidence: {
43275
+ tier: "probe",
43276
+ verdict: "model-gate",
43277
+ status: 404,
43278
+ measuredAt: "2026-08-14",
43279
+ note: "404 naming our fake model ('The model `probe-nonexistent-model-zzz` does not exist'), i.e. the route resolved and the model was rejected before auth"
43280
+ }
43281
+ },
43282
+ {
43283
+ name: "novita",
43284
+ displayName: "Novita AI",
43285
+ baseUrl: "https://api.novita.ai/v3/openai",
43286
+ apiPath: "/chat/completions",
43287
+ format: "openai",
43288
+ apiKeyEnvVar: "NOVITA_API_KEY",
43289
+ apiKeyUrl: "https://novita.ai/settings",
43290
+ description: "Novita AI hosted open models (novita@)",
43291
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43292
+ },
43293
+ {
43294
+ name: "baseten",
43295
+ displayName: "Baseten",
43296
+ baseUrl: "https://inference.baseten.co",
43297
+ apiPath: "/v1/chat/completions",
43298
+ format: "openai",
43299
+ apiKeyEnvVar: "BASETEN_API_KEY",
43300
+ apiKeyUrl: "https://app.baseten.co",
43301
+ description: "Baseten model APIs (baseten@)",
43302
+ evidence: { tier: "probe", verdict: "auth-realm", status: 403, measuredAt: "2026-08-14" }
43303
+ },
43304
+ {
43305
+ name: "perplexity",
43306
+ displayName: "Perplexity",
43307
+ baseUrl: "https://api.perplexity.ai",
43308
+ apiPath: "/chat/completions",
43309
+ format: "openai",
43310
+ apiKeyEnvVar: "PERPLEXITY_API_KEY",
43311
+ apiKeyUrl: "https://www.perplexity.ai/settings/api",
43312
+ description: "Perplexity Sonar search-grounded models (perplexity@)",
43313
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43314
+ },
43315
+ {
43316
+ name: "venice",
43317
+ displayName: "Venice AI",
43318
+ baseUrl: "https://api.venice.ai/api",
43319
+ apiPath: "/v1/chat/completions",
43320
+ format: "openai",
43321
+ apiKeyEnvVar: "VENICE_API_KEY",
43322
+ apiKeyUrl: "https://venice.ai/settings/api",
43323
+ description: "Venice AI privacy-focused open models (venice@)",
43324
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43325
+ },
43326
+ {
43327
+ name: "chutes",
43328
+ displayName: "Chutes",
43329
+ baseUrl: "https://llm.chutes.ai",
43330
+ apiPath: "/v1/chat/completions",
43331
+ format: "openai",
43332
+ apiKeyEnvVar: "CHUTES_API_KEY",
43333
+ description: "Chutes decentralized inference on Bittensor (chutes@)",
43334
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43335
+ },
43336
+ {
43337
+ name: "featherless",
43338
+ displayName: "Featherless AI",
43339
+ baseUrl: "https://api.featherless.ai",
43340
+ apiPath: "/v1/chat/completions",
43341
+ format: "openai",
43342
+ apiKeyEnvVar: "FEATHERLESS_API_KEY",
43343
+ description: "Featherless AI serverless HuggingFace model hosting (featherless@)",
43344
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43345
+ },
43346
+ {
43347
+ name: "parasail",
43348
+ displayName: "Parasail",
43349
+ baseUrl: "https://api.parasail.io",
43350
+ apiPath: "/v1/chat/completions",
43351
+ format: "openai",
43352
+ apiKeyEnvVar: "PARASAIL_API_KEY",
43353
+ description: "Parasail on-demand GPU inference (parasail@)",
43354
+ evidence: {
43355
+ tier: "probe",
43356
+ verdict: "auth-realm",
43357
+ status: 401,
43358
+ measuredAt: "2026-08-14",
43359
+ note: "non-OpenAI error shape: plain text 'Unauthorized. Invalid token.' under content-type application/json;charset=ISO-8859-1. Classified terminal on the 401 status, so no retry is burned; see validation/error-shape-probe.md"
43360
+ }
43361
+ },
43362
+ {
43363
+ name: "inference-net",
43364
+ displayName: "Inference.net",
43365
+ baseUrl: "https://api.inference.net",
43366
+ apiPath: "/v1/chat/completions",
43367
+ format: "openai",
43368
+ apiKeyEnvVar: "INFERENCE_NET_API_KEY",
43369
+ description: "Inference.net distributed open-model inference (inference-net@)",
43370
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43371
+ },
43372
+ {
43373
+ name: "aimlapi",
43374
+ displayName: "AI/ML API",
43375
+ baseUrl: "https://api.aimlapi.com",
43376
+ apiPath: "/v1/chat/completions",
43377
+ format: "openai",
43378
+ apiKeyEnvVar: "AIMLAPI_API_KEY",
43379
+ description: "AI/ML API multi-vendor aggregator (aimlapi@)",
43380
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43381
+ },
43382
+ {
43383
+ name: "requesty",
43384
+ displayName: "Requesty",
43385
+ baseUrl: "https://router.requesty.ai",
43386
+ apiPath: "/v1/chat/completions",
43387
+ format: "openai",
43388
+ apiKeyEnvVar: "REQUESTY_API_KEY",
43389
+ description: "Requesty LLM router (requesty@)",
43390
+ evidence: { tier: "probe", verdict: "auth-realm", status: 403, measuredAt: "2026-08-14" }
43391
+ },
43392
+ {
43393
+ name: "nanogpt",
43394
+ displayName: "NanoGPT",
43395
+ baseUrl: "https://nano-gpt.com/api",
43396
+ apiPath: "/v1/chat/completions",
43397
+ format: "openai",
43398
+ apiKeyEnvVar: "NANOGPT_API_KEY",
43399
+ description: "NanoGPT pay-per-prompt multi-vendor aggregator (nanogpt@)",
43400
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43401
+ },
43402
+ {
43403
+ name: "cohere",
43404
+ displayName: "Cohere",
43405
+ baseUrl: "https://api.cohere.ai/compatibility",
43406
+ apiPath: "/v1/chat/completions",
43407
+ format: "openai",
43408
+ apiKeyEnvVar: "COHERE_API_KEY",
43409
+ apiKeyUrl: "https://dashboard.cohere.com/api-keys",
43410
+ description: "Cohere Command models via their OpenAI-compatibility layer (cohere@)",
43411
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43412
+ },
43413
+ {
43414
+ name: "scaleway",
43415
+ displayName: "Scaleway Generative APIs",
43416
+ baseUrl: "https://api.scaleway.ai",
43417
+ apiPath: "/v1/chat/completions",
43418
+ format: "openai",
43419
+ apiKeyEnvVar: "SCALEWAY_API_KEY",
43420
+ apiKeyUrl: "https://console.scaleway.com",
43421
+ description: "Scaleway Generative APIs, EU-hosted open models (scaleway@)",
43422
+ evidence: { tier: "probe", verdict: "auth-realm", status: 403, measuredAt: "2026-08-14" }
43423
+ },
43424
+ {
43425
+ name: "upstage",
43426
+ displayName: "Upstage",
43427
+ baseUrl: "https://api.upstage.ai",
43428
+ apiPath: "/v1/chat/completions",
43429
+ format: "openai",
43430
+ apiKeyEnvVar: "UPSTAGE_API_KEY",
43431
+ apiKeyUrl: "https://console.upstage.ai",
43432
+ description: "Upstage Solar models (upstage@)",
43433
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43434
+ },
43435
+ {
43436
+ name: "writer",
43437
+ displayName: "Writer",
43438
+ baseUrl: "https://api.writer.com",
43439
+ apiPath: "/v1/chat/completions",
43440
+ format: "openai",
43441
+ apiKeyEnvVar: "WRITER_API_KEY",
43442
+ description: "Writer Palmyra models (writer@)",
43443
+ evidence: {
43444
+ tier: "probe",
43445
+ verdict: "auth-realm",
43446
+ status: 401,
43447
+ measuredAt: "2026-08-14",
43448
+ note: 'non-OpenAI error shape: {"tpe":"fail.auth","errors":[{"description":\u2026}]} with no top-level `error` object. Classified terminal on the 401 status, so no retry is burned; see validation/error-shape-probe.md'
43449
+ }
43450
+ },
43451
+ {
43452
+ name: "moonshot-cn",
43453
+ displayName: "Moonshot AI (China)",
43454
+ baseUrl: "https://api.moonshot.cn",
43455
+ apiPath: "/v1/chat/completions",
43456
+ format: "openai",
43457
+ apiKeyEnvVar: "MOONSHOT_CN_API_KEY",
43458
+ apiKeyUrl: "https://platform.moonshot.cn",
43459
+ description: "Moonshot AI China-region endpoint (moonshot-cn@) \u2014 a separate product from the builtin Kimi provider reached by moonshot@/kimi@",
43460
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43461
+ },
43462
+ {
43463
+ name: "tuningengines",
43464
+ displayName: "Tuning Engines",
43465
+ baseUrl: "https://api.tuningengines.com",
43466
+ apiPath: "/v1/chat/completions",
43467
+ format: "openai",
43468
+ apiKeyEnvVar: "TUNING_ENGINES_API_KEY",
43469
+ baseUrlEnvVars: ["TUNING_ENGINES_BASE_URL"],
43470
+ description: "Tuning Engines enterprise LLM gateway (tuningengines@); self-hosted instances point TUNING_ENGINES_BASE_URL at their own host",
43471
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43472
+ }
43473
+ ];
43474
+ });
43475
+
43476
+ // src/providers/predefined-endpoints.ts
43477
+ function warnOnce2(message) {
43478
+ if (warnedMessages2.has(message))
43479
+ return;
43480
+ warnedMessages2.add(message);
43481
+ console.error(message);
43482
+ }
43483
+ function activeCatalog() {
43484
+ return catalogOverride ?? PREDEFINED_ENDPOINTS;
43485
+ }
43486
+ function reservedNamespace() {
43487
+ const reserved = new Map;
43488
+ for (const def of BUILTIN_PROVIDERS) {
43489
+ reserved.set(def.name.toLowerCase(), def.name);
43490
+ for (const shortcut of def.shortcuts) {
43491
+ reserved.set(shortcut.toLowerCase(), def.name);
43492
+ }
43493
+ for (const legacy of def.legacyPrefixes) {
43494
+ reserved.set(legacy.prefix.replace(/[/:]+$/, "").toLowerCase(), def.name);
43495
+ }
43496
+ }
43497
+ for (const [alias, owner] of Object.entries(PROVIDER_FILTER_ALIAS_EXTRA)) {
43498
+ reserved.set(alias.toLowerCase(), owner);
43499
+ }
43500
+ return reserved;
43501
+ }
43502
+ function readOptOut(config2) {
43503
+ const off = { disabled: true, disable: new Set, enable: new Set };
43504
+ if (process.env[KILL_SWITCH_ENV] === "1")
43505
+ return off;
43506
+ const raw = config2?.predefinedEndpoints;
43507
+ let parsed = {};
43508
+ if (raw !== undefined) {
43509
+ const result = PredefinedEndpointsConfigSchema.safeParse(raw);
43510
+ if (result.success) {
43511
+ parsed = result.data;
43512
+ } else {
43513
+ warnOnce2("[claudish] config 'predefinedEndpoints' is not valid and was ignored: " + result.error.issues.map((i) => `${i.path.join(".") || "(root)"} ${i.message}`).join(", "));
43514
+ }
43515
+ }
43516
+ return {
43517
+ disabled: parsed.enabled === false,
43518
+ disable: new Set((parsed.disable ?? []).map((n) => n.trim().toLowerCase())),
43519
+ enable: new Set((parsed.enable ?? []).map((n) => n.trim().toLowerCase()))
43520
+ };
43521
+ }
43522
+ function compileToCustomEndpoint(entry) {
43523
+ return {
43524
+ kind: "complex",
43525
+ displayName: entry.displayName,
43526
+ transport: entry.format,
43527
+ baseUrl: entry.baseUrl,
43528
+ apiPath: entry.apiPath,
43529
+ apiKey: `\${${entry.apiKeyEnvVar}}`,
43530
+ authScheme: entry.authScheme,
43531
+ headers: entry.headers,
43532
+ modelPrefix: entry.modelPrefix
43533
+ };
43534
+ }
43535
+ function credentialEnvVars(entry) {
43536
+ return {
43537
+ envVar: entry.apiKeyEnvVar,
43538
+ aliases: [...entry.apiKeyAliases ?? [], customEndpointKeyEnvVar(entry.name)]
43539
+ };
43540
+ }
43541
+ function overridesFor(entry) {
43542
+ const { envVar, aliases } = credentialEnvVars(entry);
43543
+ return {
43544
+ apiKeyEnvVar: envVar,
43545
+ apiKeyAliases: aliases,
43546
+ baseUrlEnvVars: entry.baseUrlEnvVars,
43547
+ apiKeyUrl: entry.apiKeyUrl ?? "",
43548
+ apiKeyDescription: `${entry.displayName} (${envVar})`,
43549
+ description: entry.description ?? `${entry.displayName} (bundled endpoint)`
43550
+ };
43551
+ }
43552
+ function loadPredefinedEndpoints(config2, opts = {}) {
43553
+ const result = { registered: [], skipped: [] };
43554
+ const optOut = readOptOut(config2);
43555
+ const catalog = opts.catalog ?? activeCatalog();
43556
+ const runtime = getRuntimeProviders();
43557
+ const noteStale = (entry, name, reason) => {
43558
+ if (!ownRegistrations.has(name) || !runtime.has(entry.name))
43559
+ return;
43560
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' is no longer eligible (${reason}) but stays ` + "registered for the rest of this process \u2014 runtime provider registration cannot be " + "undone. Restart claudish to apply the change.");
43561
+ };
43562
+ if (optOut.disabled) {
43563
+ for (const entry of catalog) {
43564
+ result.skipped.push({ name: entry.name, reason: "catalog off" });
43565
+ noteStale(entry, entry.name.toLowerCase(), "catalog off");
43566
+ }
43567
+ return result;
43568
+ }
43569
+ const reserved = reservedNamespace();
43570
+ const userEndpoints = new Set(Object.keys(config2?.customEndpoints ?? {}).map((n) => n.toLowerCase()));
43571
+ const seen = new Set;
43572
+ for (const entry of catalog) {
43573
+ const name = entry.name.toLowerCase();
43574
+ const skip = (reason) => result.skipped.push({ name: entry.name, reason });
43575
+ const skipStale = (reason) => {
43576
+ skip(reason);
43577
+ noteStale(entry, name, reason);
43578
+ };
43579
+ if (seen.has(name)) {
43580
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' appears more than once in the bundled ` + "catalog. The first row wins; the later one is ignored.");
43581
+ skip("duplicate row");
43582
+ continue;
43583
+ }
43584
+ seen.add(name);
43585
+ const owner = reserved.get(name);
43586
+ if (owner) {
43587
+ const reason = `'${entry.name}' is already claimed by builtin provider '${owner}' ` + "(as its name, a shortcut, or a legacy prefix)";
43588
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: ${reason}. ` + "The builtin wins; the bundled entry is inactive.");
43589
+ recordEndpointUnavailable(entry.name, `bundled endpoint was skipped because ${reason}`);
43590
+ skip("collides with builtin");
43591
+ continue;
43592
+ }
43593
+ if (runtime.has(entry.name) && !ownRegistrations.has(name)) {
43594
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: a provider named ` + `'${entry.name}' is already registered for this process.`);
43595
+ skip("already registered");
43596
+ continue;
43597
+ }
43598
+ if (userEndpoints.has(name)) {
43599
+ if (hasLocalApiKey({ envVar: entry.apiKeyEnvVar })) {
43600
+ warnOnce2(`[claudish] customEndpoints['${entry.name}'] replaces the bundled entry entirely; ` + `${entry.apiKeyEnvVar} no longer applies to it. Add ` + `"apiKey": "\${${entry.apiKeyEnvVar}}" to that entry to keep using it.`);
43601
+ }
43602
+ skipStale("replaced by customEndpoints");
43603
+ continue;
43604
+ }
43605
+ if (optOut.disable.has(name)) {
43606
+ skipStale("disabled in config");
43607
+ continue;
43608
+ }
43609
+ const { envVar, aliases } = credentialEnvVars(entry);
43610
+ const permitted = optOut.enable.has(name) || hasLocalApiKey({ envVar, aliases });
43611
+ if (!permitted) {
43612
+ skipStale("no local credential");
43613
+ continue;
43614
+ }
43615
+ const resolvedUrl = classifyEndpointBaseUrl(entry.baseUrl, entry.baseUrlEnvVars);
43616
+ if (!resolvedUrl.ok) {
43617
+ const detail = describeBadBaseUrlOverride(resolvedUrl, entry.baseUrl);
43618
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: ${detail}`);
43619
+ recordEndpointUnavailable(entry.name, detail);
43620
+ skipStale("invalid base URL override");
43621
+ continue;
43622
+ }
43623
+ try {
43624
+ registerEndpoint(entry.name, compileToCustomEndpoint(entry), overridesFor(entry));
43625
+ ownRegistrations.add(name);
43626
+ result.registered.push(entry.name);
43627
+ } catch (err) {
43628
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: ` + `${err instanceof Error ? err.message : String(err)}`);
43629
+ skip("invalid catalog row");
43630
+ }
43631
+ }
43632
+ return result;
43633
+ }
43634
+ var KILL_SWITCH_ENV = "CLAUDISH_NO_PREDEFINED_ENDPOINTS", warnedMessages2, ownRegistrations, catalogOverride = null;
43635
+ var init_predefined_endpoints = __esm(() => {
43636
+ init_local_api_key();
43637
+ init_config_schema();
43638
+ init_custom_endpoints_loader();
43639
+ init_endpoint_diagnostics();
43640
+ init_picker_alias_extra();
43641
+ init_predefined_catalog();
43642
+ init_provider_definitions();
43643
+ init_runtime_providers();
43644
+ warnedMessages2 = new Set;
43645
+ ownRegistrations = new Set;
43646
+ });
43647
+
43648
+ // src/providers/endpoint-registration.ts
43649
+ var exports_endpoint_registration = {};
43650
+ __export(exports_endpoint_registration, {
43651
+ invalidateEndpointRegistration: () => invalidateEndpointRegistration,
43652
+ ensureEndpointsRegistered: () => ensureEndpointsRegistered
43653
+ });
43654
+ function ensureEndpointsRegistered(opts = {}) {
43655
+ if (registered && !opts.force)
43656
+ return;
43657
+ registered = true;
43658
+ try {
43659
+ loadPredefinedEndpoints(opts.config ?? loadConfig());
43660
+ } catch {}
43661
+ }
43662
+ function invalidateEndpointRegistration() {
43663
+ registered = false;
43664
+ }
43665
+ var registered = false;
43666
+ var init_endpoint_registration = __esm(() => {
43667
+ init_profile_config();
43668
+ init_predefined_endpoints();
43669
+ });
43670
+
42850
43671
  // src/providers/provider-registry.ts
42851
43672
  function resolveBaseUrl2(envVar, fallbackEnvVars, staticDefault) {
42852
43673
  for (const v of [envVar, ...fallbackEnvVars]) {
@@ -43715,7 +44536,9 @@ async function prepareParentRoutingContext() {
43715
44536
  if (!parentRoutingContextReady) {
43716
44537
  parentRoutingContextReady = true;
43717
44538
  try {
43718
- loadCustomEndpoints(loadConfig());
44539
+ const config2 = loadConfig();
44540
+ ensureEndpointsRegistered({ config: config2 });
44541
+ loadCustomEndpoints(config2);
43719
44542
  } catch {}
43720
44543
  }
43721
44544
  try {
@@ -43765,6 +44588,7 @@ var init_prehydrate = __esm(() => {
43765
44588
  init_auto_route();
43766
44589
  init_catalog_client();
43767
44590
  init_custom_endpoints_loader();
44591
+ init_endpoint_registration();
43768
44592
  init_model_parser();
43769
44593
  init_onepassword();
43770
44594
  init_provider_resolver();
@@ -44021,6 +44845,48 @@ var init_signal_watcher = __esm(() => {
44021
44845
  QUESTION_PATTERNS = [/\?\s*$/m, /\bchoose\b.*:/im, /\bselect\b.*:/im, /\benter\b.*:/im];
44022
44846
  });
44023
44847
 
44848
+ // src/process-tree.ts
44849
+ function signalProcessTree(proc, signal) {
44850
+ if (KILL_PROCESS_GROUP && proc.pid) {
44851
+ try {
44852
+ process.kill(-proc.pid, signal);
44853
+ } catch {}
44854
+ }
44855
+ try {
44856
+ if (!proc.killed)
44857
+ proc.kill(signal);
44858
+ } catch {}
44859
+ }
44860
+ function waitForExit(proc, ms) {
44861
+ if (proc.exitCode !== null || proc.signalCode !== null)
44862
+ return Promise.resolve(true);
44863
+ return new Promise((resolve2) => {
44864
+ let settled = false;
44865
+ const done = (value) => {
44866
+ if (settled)
44867
+ return;
44868
+ settled = true;
44869
+ proc.off("exit", onExit);
44870
+ resolve2(value);
44871
+ };
44872
+ const onExit = () => done(true);
44873
+ proc.once("exit", onExit);
44874
+ const t = setTimeout(() => done(false), ms);
44875
+ t.unref?.();
44876
+ });
44877
+ }
44878
+ async function terminateChildTree(proc, graceMs = TERMINATE_GRACE_MS) {
44879
+ signalProcessTree(proc, "SIGTERM");
44880
+ if (await waitForExit(proc, graceMs))
44881
+ return true;
44882
+ signalProcessTree(proc, "SIGKILL");
44883
+ return waitForExit(proc, graceMs);
44884
+ }
44885
+ var KILL_PROCESS_GROUP, TERMINATE_GRACE_MS = 5000;
44886
+ var init_process_tree = __esm(() => {
44887
+ KILL_PROCESS_GROUP = process.platform !== "win32";
44888
+ });
44889
+
44024
44890
  // src/spawn-claudish.ts
44025
44891
  function resolveClaudishSpawn(env = process.env) {
44026
44892
  const bin = env[CLAUDISH_BIN_ENV]?.trim();
@@ -44077,7 +44943,8 @@ class SessionManager {
44077
44943
  const proc = spawn(spawnTarget.command, [...spawnTarget.prefixArgs, ...args], {
44078
44944
  cwd: opts.cwd ?? process.cwd(),
44079
44945
  stdio: ["pipe", "pipe", "pipe"],
44080
- shell: false
44946
+ shell: false,
44947
+ detached: KILL_PROCESS_GROUP
44081
44948
  });
44082
44949
  const scrollback = new ScrollbackBuffer(this.scrollbackCapacity);
44083
44950
  const watcher = new SignalWatcher(sessionId2, (sid, data) => {
@@ -44157,10 +45024,10 @@ class SessionManager {
44157
45024
  });
44158
45025
  entry.timeoutHandle = setTimeout(() => {
44159
45026
  if (!proc.killed) {
44160
- proc.kill("SIGTERM");
45027
+ signalProcessTree(proc, "SIGTERM");
44161
45028
  entry.killHandle = setTimeout(() => {
44162
45029
  try {
44163
- proc.kill("SIGKILL");
45030
+ signalProcessTree(proc, "SIGKILL");
44164
45031
  } catch {}
44165
45032
  }, KILL_GRACE_MS);
44166
45033
  entry.info.status = "timeout";
@@ -44218,10 +45085,10 @@ class SessionManager {
44218
45085
  entry.info.completedAt = new Date().toISOString();
44219
45086
  entry.watcher.forceState("cancelled", "Session cancelled");
44220
45087
  if (!entry.process.killed) {
44221
- entry.process.kill("SIGTERM");
45088
+ signalProcessTree(entry.process, "SIGTERM");
44222
45089
  entry.killHandle = setTimeout(() => {
44223
45090
  try {
44224
- entry.process.kill("SIGKILL");
45091
+ signalProcessTree(entry.process, "SIGKILL");
44225
45092
  } catch {}
44226
45093
  }, KILL_GRACE_MS);
44227
45094
  }
@@ -44252,11 +45119,11 @@ class SessionManager {
44252
45119
  const promises = [];
44253
45120
  for (const [, entry] of this.sessions) {
44254
45121
  if (!entry.process.killed) {
44255
- entry.process.kill("SIGTERM");
45122
+ signalProcessTree(entry.process, "SIGTERM");
44256
45123
  promises.push(new Promise((resolve2) => {
44257
45124
  const timeout = setTimeout(() => {
44258
45125
  try {
44259
- entry.process.kill("SIGKILL");
45126
+ signalProcessTree(entry.process, "SIGKILL");
44260
45127
  } catch {}
44261
45128
  resolve2();
44262
45129
  }, KILL_GRACE_MS);
@@ -44302,6 +45169,7 @@ class SessionManager {
44302
45169
  }
44303
45170
  var DEFAULT_MAX_SESSIONS = 20, DEFAULT_SCROLLBACK = 2000, DEFAULT_TIMEOUT = 600, MAX_TIMEOUT = 3600, KILL_GRACE_MS = 5000;
44304
45171
  var init_session_manager = __esm(() => {
45172
+ init_process_tree();
44305
45173
  init_scrollback_buffer();
44306
45174
  init_signal_watcher();
44307
45175
  });
@@ -47534,38 +48402,6 @@ var init_native_handler = __esm(() => {
47534
48402
  init_anthropic_error();
47535
48403
  });
47536
48404
 
47537
- // src/providers/api-key-map.ts
47538
- var API_KEY_MAP;
47539
- var init_api_key_map = __esm(() => {
47540
- API_KEY_MAP = {
47541
- litellm: { envVar: "LITELLM_API_KEY" },
47542
- openrouter: { envVar: "OPENROUTER_API_KEY" },
47543
- google: { envVar: "GEMINI_API_KEY" },
47544
- openai: { envVar: "OPENAI_API_KEY" },
47545
- minimax: { envVar: "MINIMAX_API_KEY" },
47546
- "minimax-coding": { envVar: "MINIMAX_CODING_API_KEY" },
47547
- kimi: { envVar: "MOONSHOT_API_KEY", aliases: ["KIMI_API_KEY"] },
47548
- "kimi-coding": { envVar: "KIMI_CODING_API_KEY" },
47549
- glm: { envVar: "ZHIPU_API_KEY", aliases: ["GLM_API_KEY"] },
47550
- "glm-coding": { envVar: "GLM_CODING_API_KEY", aliases: ["ZAI_CODING_API_KEY"] },
47551
- "z-ai": { envVar: "ZAI_API_KEY" },
47552
- deepseek: { envVar: "DEEPSEEK_API_KEY" },
47553
- mistralai: { envVar: "MISTRAL_API_KEY" },
47554
- sakana: { envVar: "SAKANA_API_KEY" },
47555
- "sakana-subscription": {
47556
- envVar: "SAKANA_SUBSCRIPTION_API_KEY",
47557
- aliases: ["SAKANA_CODING_API_KEY"]
47558
- },
47559
- "qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
47560
- "qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
47561
- ollamacloud: { envVar: "OLLAMA_API_KEY" },
47562
- "opencode-zen": { envVar: "OPENCODE_API_KEY" },
47563
- "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
47564
- vertex: { envVar: "VERTEX_API_KEY", aliases: ["VERTEX_PROJECT"] },
47565
- poe: { envVar: "POE_API_KEY" }
47566
- };
47567
- });
47568
-
47569
48405
  // src/providers/devin/tool-descriptions.ts
47570
48406
  function currentMonthAndYear(now = new Date) {
47571
48407
  return `${MONTHS[now.getMonth()]} ${now.getFullYear()}`;
@@ -49411,7 +50247,9 @@ __export(exports_proxy_server, {
49411
50247
  });
49412
50248
  async function createProxyServer(port, _openrouterApiKey, model, monitorMode = false, anthropicApiKey, modelMap, options = {}) {
49413
50249
  try {
49414
- const customEpResult = loadCustomEndpoints(loadConfig());
50250
+ const config2 = loadConfig();
50251
+ ensureEndpointsRegistered({ config: config2 });
50252
+ const customEpResult = loadCustomEndpoints(config2);
49415
50253
  if (customEpResult.registered > 0) {
49416
50254
  log(`[Proxy] Registered ${customEpResult.registered} custom endpoint(s) from config`);
49417
50255
  }
@@ -49704,9 +50542,11 @@ ${plan.hint}` : `[Route] ${plan.reason}`;
49704
50542
  if (hasExplicitProvider) {
49705
50543
  const parsedExplicit = parseModelSpec(target);
49706
50544
  if (parsedExplicit.provider !== "openrouter") {
49707
- const keyInfo = API_KEY_MAP[parsedExplicit.provider];
49708
- const keyNames = keyInfo ? [keyInfo.envVar, ...keyInfo.aliases ?? []].join(" or ") : undefined;
49709
- const hint = keyNames ? `No API key for provider "${parsedExplicit.provider}". Set ${keyNames} (env, config, or 1Password import).` : `No API key for provider "${parsedExplicit.provider}".`;
50545
+ const recorded = getEndpointUnavailableReason(parsedExplicit.provider);
50546
+ if (recorded) {
50547
+ throw new RoutingError(`Explicit model "${target}" could not be routed \u2014 ${recorded}`);
50548
+ }
50549
+ const hint = describeMissingCredential(parsedExplicit.provider);
49710
50550
  throw new RoutingError(`Explicit model "${target}" could not be routed \u2014 its provider has no credential. ${hint}`);
49711
50551
  }
49712
50552
  }
@@ -49858,11 +50698,13 @@ var init_proxy_server = __esm(() => {
49858
50698
  init_logger();
49859
50699
  init_model_loader();
49860
50700
  init_profile_config();
49861
- init_api_key_map();
49862
50701
  init_auto_route();
49863
50702
  init_catalog_client();
49864
50703
  init_custom_endpoints_loader();
50704
+ init_endpoint_diagnostics();
50705
+ init_endpoint_registration();
49865
50706
  init_model_parser();
50707
+ init_provider_definitions();
49866
50708
  init_provider_profiles();
49867
50709
  init_provider_registry();
49868
50710
  init_provider_resolver();
@@ -50214,6 +51056,7 @@ __export(exports_team_orchestrator, {
50214
51056
  runModels: () => runModels,
50215
51057
  resolveCaptureMode: () => resolveCaptureMode,
50216
51058
  parseJudgeVotes: () => parseJudgeVotes,
51059
+ meaningfulStderr: () => meaningfulStderr,
50217
51060
  judgeResponses: () => judgeResponses,
50218
51061
  getStatus: () => getStatus,
50219
51062
  fisherYatesShuffle: () => fisherYatesShuffle,
@@ -50222,6 +51065,9 @@ __export(exports_team_orchestrator, {
50222
51065
  aggregateVerdict: () => aggregateVerdict,
50223
51066
  TEAM_CAPTURE_ENV_VAR: () => TEAM_CAPTURE_ENV_VAR,
50224
51067
  STDOUT_TAIL_LIMIT: () => STDOUT_TAIL_LIMIT,
51068
+ GRACE_INTERVAL_MS: () => GRACE_INTERVAL_MS,
51069
+ DRAIN_TIMEOUT_MS: () => DRAIN_TIMEOUT_MS,
51070
+ DEFAULT_STALL_SECONDS: () => DEFAULT_STALL_SECONDS,
50225
51071
  DEFAULT_MIN_OUTPUT_BYTES: () => DEFAULT_MIN_OUTPUT_BYTES
50226
51072
  });
50227
51073
  import { spawn as spawn2 } from "child_process";
@@ -50294,6 +51140,13 @@ function classifyRunOutput(opts) {
50294
51140
  }
50295
51141
  return null;
50296
51142
  }
51143
+ function meaningfulStderr(stderr) {
51144
+ if (!stderr)
51145
+ return "";
51146
+ return stderr.split(`
51147
+ `).filter((line) => line.trim().length > 0).filter((line) => !BENIGN_STDERR_PATTERNS.some((re) => re.test(line))).join(`
51148
+ `).trim();
51149
+ }
50297
51150
  function persistErrorLog(errorLogPath, header, stderr, stdoutTail) {
50298
51151
  const parts = [`=== ${redactSecrets(header)} ===`, ""];
50299
51152
  parts.push("--- stderr ---", stderr.trim() ? redactSecrets(stderr) : "(empty)", "");
@@ -50405,13 +51258,40 @@ async function runModels(sessionPath, opts = {}) {
50405
51258
  const minOutputBytes = opts.minOutputBytes ?? DEFAULT_MIN_OUTPUT_BYTES;
50406
51259
  const requirePattern = opts.requirePattern;
50407
51260
  const captureMode = resolveCaptureMode(opts.captureMode);
51261
+ function reconcileTimedOutOutput(id, finalBytes, stdoutTail, stderr) {
51262
+ const current = statusCache.models[id];
51263
+ if (!current || current.state !== "TIMEOUT")
51264
+ return;
51265
+ if (finalBytes <= current.outputSize)
51266
+ return;
51267
+ const degraded = classifyRunOutput({
51268
+ outputSize: finalBytes,
51269
+ stdoutTail,
51270
+ stderr,
51271
+ minOutputBytes
51272
+ });
51273
+ if (degraded) {
51274
+ updateModelStatus(id, { outputSize: finalBytes });
51275
+ return;
51276
+ }
51277
+ updateModelStatus(id, {
51278
+ state: "COMPLETED",
51279
+ outputSize: finalBytes,
51280
+ completedAt: new Date().toISOString(),
51281
+ error: undefined
51282
+ });
51283
+ const note = `Recovered after the deadline: the child flushed ${finalBytes} B while shutting down, ` + "so its answer is complete and is being counted. The run still exceeded its timeout.";
51284
+ const rt = runtimes.get(id);
51285
+ if (rt)
51286
+ persistErrorLog(rt.errorLogPath, `RECOVERED: ${note}`, stderr, stdoutTail);
51287
+ opts.onStatusChange?.(id, statusCache.models[id]);
51288
+ }
50408
51289
  mkdirSync12(statsDir(sessionPath), { recursive: true });
50409
51290
  const processes = new Map;
50410
51291
  const runtimes = new Map;
50411
51292
  const sigintHandler = () => {
50412
51293
  for (const [, proc] of processes) {
50413
- if (!proc.killed)
50414
- proc.kill("SIGTERM");
51294
+ signalProcessTree(proc, "SIGTERM");
50415
51295
  }
50416
51296
  process.exit(1);
50417
51297
  };
@@ -50437,6 +51317,7 @@ async function runModels(sessionPath, opts = {}) {
50437
51317
  const proc = spawn2(teamSpawnTarget.command, [...teamSpawnTarget.prefixArgs, ...args], {
50438
51318
  stdio: ["pipe", "pipe", "pipe"],
50439
51319
  shell: false,
51320
+ detached: KILL_PROCESS_GROUP,
50440
51321
  env: {
50441
51322
  ...process.env,
50442
51323
  CLAUDISH_TOKEN_FILE: tokenFileFor(sessionPath, anonId)
@@ -50497,6 +51378,7 @@ async function runModels(sessionPath, opts = {}) {
50497
51378
  return;
50498
51379
  if (statusCache.models[anonId].state === "TIMEOUT") {
50499
51380
  resolved = true;
51381
+ reconcileTimedOutOutput(anonId, byteCount, stdoutTail, stderr);
50500
51382
  resolve4();
50501
51383
  return;
50502
51384
  }
@@ -50554,13 +51436,8 @@ async function runModels(sessionPath, opts = {}) {
50554
51436
  };
50555
51437
  outputStream.on("close", finish);
50556
51438
  proc.on("exit", (code) => {
50557
- const current = statusCache.models[anonId];
50558
- if (current?.state === "TIMEOUT") {
50559
- resolved = true;
50560
- resolve4();
50561
- return;
50562
- }
50563
- if (stderr) {
51439
+ const timedOut = statusCache.models[anonId]?.state === "TIMEOUT";
51440
+ if (!timedOut && meaningfulStderr(stderr)) {
50564
51441
  writeFileSync12(errorLogPath, redactSecrets(stderr), "utf-8");
50565
51442
  }
50566
51443
  exitCode = code;
@@ -50602,48 +51479,98 @@ async function runModels(sessionPath, opts = {}) {
50602
51479
  emitProgress();
50603
51480
  const progressHandle = setInterval(() => emitProgress("running"), POLL_MS);
50604
51481
  progressHandle.unref?.();
50605
- let timeoutHandle = null;
50606
- await Promise.race([
50607
- Promise.all(completionPromises),
50608
- new Promise((resolve4) => {
50609
- timeoutHandle = setTimeout(() => {
50610
- for (const [id, proc] of processes) {
50611
- const current = statusCache.models[id];
50612
- if (current.state === "RUNNING") {
50613
- if (!proc.killed)
50614
- proc.kill("SIGTERM");
50615
- const rt = runtimes.get(id);
50616
- rt?.flushPartial();
50617
- const stderr = rt?.getStderr() ?? "";
50618
- const stdoutTail = rt?.getStdoutTail() ?? "";
50619
- const bytes2 = rt?.getByteCount() ?? 0;
50620
- const detail = `Killed by the orchestrator after ${timeoutMs / 1000}s with ${bytes2} B of stdout. ` + "That figure counts the ANSWER, not the wire format, so 0 B means the child had " + `not produced an assistant message yet \u2014 "did not finish", not "produced nothing".`;
50621
- if (rt)
50622
- persistErrorLog(rt.errorLogPath, `TIMEOUT: ${detail}`, stderr, stdoutTail);
50623
- updateModelStatus(id, {
50624
- state: "TIMEOUT",
50625
- completedAt: new Date().toISOString(),
50626
- outputSize: bytes2,
50627
- error: rt ? {
50628
- model: id,
50629
- command: rt.command,
50630
- reason: "timeout",
50631
- detail,
50632
- stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
50633
- stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail).slice(-2000) : undefined,
50634
- errorLogPath: rt.errorLogPath,
50635
- workDir: sessionPath
50636
- } : undefined
50637
- });
50638
- opts.onStatusChange?.(id, statusCache.models[id]);
50639
- }
51482
+ const graceEnabled = opts.graceExtension ?? true;
51483
+ const maxGraceMs = Math.max(0, (opts.maxGraceSeconds ?? timeoutMs / 1000) * 1000);
51484
+ const stallMs = Math.max(0, (opts.stallSeconds ?? DEFAULT_STALL_SECONDS) * 1000);
51485
+ const idleMsFor = (id) => {
51486
+ const s = readTokenStats(sessionPath, id);
51487
+ if (!s || typeof s.updated_at !== "number" || s.updated_at <= 0)
51488
+ return null;
51489
+ return Math.max(0, Date.now() - s.updated_at);
51490
+ };
51491
+ const graceStartedAt = new Map;
51492
+ const graceUsedMs = (id, now) => {
51493
+ const start = graceStartedAt.get(id);
51494
+ return start === undefined ? 0 : Math.max(0, now - start);
51495
+ };
51496
+ const runningIds = () => [...processes.keys()].filter((id) => statusCache.models[id]?.state === "RUNNING");
51497
+ const timeoutModel = async (id, why) => {
51498
+ const proc = processes.get(id);
51499
+ if (!proc || statusCache.models[id]?.state !== "RUNNING")
51500
+ return;
51501
+ const rt = runtimes.get(id);
51502
+ rt?.flushPartial();
51503
+ const stderr = rt?.getStderr() ?? "";
51504
+ const stdoutTail = rt?.getStdoutTail() ?? "";
51505
+ const bytes2 = rt?.getByteCount() ?? 0;
51506
+ const grace = graceUsedMs(id, Date.now());
51507
+ const detail = `Killed by the orchestrator after ${(timeoutMs + grace) / 1000}s ` + `(deadline ${timeoutMs / 1000}s${grace ? ` + ${grace / 1000}s grace` : ""}) ` + `with ${bytes2} B of stdout \u2014 ${why}. ` + "That figure counts the ANSWER, not the wire format, so 0 B means the child had " + `not produced an assistant message yet \u2014 "did not finish", not "produced nothing".`;
51508
+ if (rt)
51509
+ persistErrorLog(rt.errorLogPath, `TIMEOUT: ${detail}`, stderr, stdoutTail);
51510
+ updateModelStatus(id, {
51511
+ state: "TIMEOUT",
51512
+ completedAt: new Date().toISOString(),
51513
+ outputSize: bytes2,
51514
+ error: rt ? {
51515
+ model: id,
51516
+ command: rt.command,
51517
+ reason: "timeout",
51518
+ detail,
51519
+ stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
51520
+ stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail).slice(-2000) : undefined,
51521
+ errorLogPath: rt.errorLogPath,
51522
+ workDir: sessionPath
51523
+ } : undefined
51524
+ });
51525
+ opts.onStatusChange?.(id, statusCache.models[id]);
51526
+ const stopped = await terminateChildTree(proc);
51527
+ if (!stopped) {
51528
+ persistErrorLog(rt?.errorLogPath ?? join28(sessionPath, "errors", `${id}.log`), "TIMEOUT: child survived SIGKILL \u2014 it may still be running and billing", stderr, stdoutTail);
51529
+ }
51530
+ };
51531
+ const allDone = Promise.all(completionPromises);
51532
+ let settled = false;
51533
+ allDone.then(() => {
51534
+ settled = true;
51535
+ }, () => {
51536
+ settled = true;
51537
+ });
51538
+ const deadlineWatcher = (async () => {
51539
+ await delay(timeoutMs);
51540
+ for (;; ) {
51541
+ if (settled)
51542
+ return;
51543
+ const running = runningIds();
51544
+ if (running.length === 0)
51545
+ return;
51546
+ const extended = [];
51547
+ const now = Date.now();
51548
+ for (const id of running) {
51549
+ const idleMs = idleMsFor(id);
51550
+ const usedGrace = graceUsedMs(id, now);
51551
+ if (!graceEnabled) {
51552
+ await timeoutModel(id, "deadline reached (grace extension disabled)");
51553
+ } else if (usedGrace >= maxGraceMs) {
51554
+ await timeoutModel(id, `grace exhausted after ${Math.round(usedGrace / 1000)}s of extra time`);
51555
+ } else if (idleMs === null) {
51556
+ await timeoutModel(id, "deadline reached with no measurable progress to extend for");
51557
+ } else if (idleMs >= stallMs) {
51558
+ await timeoutModel(id, `no measurable progress for ${Math.round(idleMs / 1000)}s`);
51559
+ } else {
51560
+ if (!graceStartedAt.has(id))
51561
+ graceStartedAt.set(id, now);
51562
+ extended.push(id);
50640
51563
  }
50641
- resolve4();
50642
- }, timeoutMs);
50643
- })
50644
- ]);
50645
- if (timeoutHandle !== null)
50646
- clearTimeout(timeoutHandle);
51564
+ }
51565
+ if (extended.length === 0)
51566
+ return;
51567
+ emitProgress("running");
51568
+ await delay(Math.min(GRACE_INTERVAL_MS, Math.max(1000, stallMs)));
51569
+ }
51570
+ })().catch(() => {});
51571
+ await Promise.race([allDone, deadlineWatcher]);
51572
+ if (!settled)
51573
+ await Promise.race([allDone, delay(DRAIN_TIMEOUT_MS)]);
50647
51574
  clearInterval(progressHandle);
50648
51575
  emitProgress("settled");
50649
51576
  process.off("SIGINT", sigintHandler);
@@ -50829,14 +51756,19 @@ function formatVerdict(verdict, sessionPath) {
50829
51756
  }
50830
51757
  return output;
50831
51758
  }
50832
- var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, SENTINEL_MODELS;
51759
+ var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, DRAIN_TIMEOUT_MS = 1e4, GRACE_INTERVAL_MS = 60000, DEFAULT_STALL_SECONDS = 90, delay = (ms) => new Promise((resolve4) => {
51760
+ const t = setTimeout(resolve4, ms);
51761
+ t.unref?.();
51762
+ }), BENIGN_STDERR_PATTERNS, SENTINEL_MODELS;
50833
51763
  var init_team_orchestrator = __esm(() => {
50834
51764
  init_prehydrate();
51765
+ init_process_tree();
50835
51766
  init_redact();
50836
51767
  init_team_stats();
50837
51768
  init_team_stream_capture();
50838
51769
  API_ERROR_RE = /\[API Error:\s*([^\]]{0,300})\]/i;
50839
51770
  BG_CEILING_RE = /Background tasks still running after (\d+)s; terminating/i;
51771
+ BENIGN_STDERR_PATTERNS = [/^\s*\[claude-code:unrecognized_model\]/];
50840
51772
  SENTINEL_MODELS = new Set([
50841
51773
  "internal",
50842
51774
  "default",
@@ -52034,6 +52966,7 @@ async function serveCommand(args) {
52034
52966
  console.error("[claudish serve] --models <path> is required");
52035
52967
  process.exit(1);
52036
52968
  }
52969
+ ensureEndpointsRegistered();
52037
52970
  let slotMap;
52038
52971
  let slotIds;
52039
52972
  try {
@@ -52054,6 +52987,7 @@ async function serveCommand(args) {
52054
52987
  await new Promise(() => {});
52055
52988
  }
52056
52989
  var init_serve_command = __esm(() => {
52990
+ init_endpoint_registration();
52057
52991
  init_proxy_server();
52058
52992
  });
52059
52993
 
@@ -52378,6 +53312,11 @@ function findMagmuxBinary() {
52378
53312
  throw new Error(`magmux not found. Install it:
52379
53313
  brew install MadAppGang/tap/magmux`);
52380
53314
  }
53315
+ function withoutControlPanes(evt) {
53316
+ if (!Array.isArray(evt.panes))
53317
+ return evt;
53318
+ return { ...evt, panes: evt.panes.filter((p) => p?.control !== true) };
53319
+ }
52381
53320
  async function subscribeToMagmux(sockPath, onEvent) {
52382
53321
  let client = null;
52383
53322
  for (let attempt = 0;attempt < 40; attempt++) {
@@ -52414,7 +53353,7 @@ async function subscribeToMagmux(sockPath, onEvent) {
52414
53353
  const evt = JSON.parse(line);
52415
53354
  onEvent?.(evt);
52416
53355
  if (evt.type === "results") {
52417
- finalResults = evt;
53356
+ finalResults = withoutControlPanes(evt);
52418
53357
  }
52419
53358
  } catch {}
52420
53359
  }
@@ -65350,7 +66289,8 @@ var init_antigravity_oauth = __esm(() => {
65350
66289
  var exports_auth_commands = {};
65351
66290
  __export(exports_auth_commands, {
65352
66291
  logoutCommand: () => logoutCommand,
65353
- loginCommand: () => loginCommand
66292
+ loginCommand: () => loginCommand,
66293
+ findProvider: () => findProvider
65354
66294
  });
65355
66295
  function getAuthStatus(provider) {
65356
66296
  const hasCredentials = provider.registryKeys.some((k) => hasOAuthCredentials(k)) || provider.name === "antigravity" && hasSharedAntigravityToken();
@@ -65865,7 +66805,7 @@ __export(exports_model_selector, {
65865
66805
  buildDiscoveredModelRows: () => buildDiscoveredModelRows
65866
66806
  });
65867
66807
  function isUserDeployedProvider(value) {
65868
- return LOCAL_OR_USER_DEPLOYED.has(value);
66808
+ return LOCAL_OR_USER_DEPLOYED.has(value) || getRuntimeProviders().has(value);
65869
66809
  }
65870
66810
  function firebaseSlugToProviderName(slug) {
65871
66811
  const lower = slug.toLowerCase();
@@ -66625,7 +67565,7 @@ async function selectProfile(profiles) {
66625
67565
  async function confirmAction(message) {
66626
67566
  return dist_default4({ message, default: false });
66627
67567
  }
66628
- var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIAS_EXTRA, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
67568
+ var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
66629
67569
  var init_model_selector = __esm(() => {
66630
67570
  init_dist16();
66631
67571
  init_model_catalog();
@@ -66635,7 +67575,9 @@ var init_model_selector = __esm(() => {
66635
67575
  init_model_catalog2();
66636
67576
  init_model_discovery();
66637
67577
  init_registry2();
67578
+ init_picker_alias_extra();
66638
67579
  init_provider_definitions();
67580
+ init_runtime_providers();
66639
67581
  init_probe_discovery();
66640
67582
  pickerProviderToFirebaseSlug = {
66641
67583
  openrouter: "openrouter",
@@ -66663,10 +67605,6 @@ var init_model_selector = __esm(() => {
66663
67605
  output: "SUB",
66664
67606
  average: "SUB"
66665
67607
  };
66666
- PROVIDER_FILTER_ALIAS_EXTRA = {
66667
- gem: "google",
66668
- zen: "opencode-zen"
66669
- };
66670
67608
  PICKER_COPY = {
66671
67609
  openrouter: { description: "580+ models via unified API" },
66672
67610
  "opencode-zen": { name: "OpenCode Zen", description: "Free models, no API key needed" },
@@ -69369,6 +70307,38 @@ var init_probe_tui_runtime = __esm(() => {
69369
70307
  init_probe_tui_app();
69370
70308
  });
69371
70309
 
70310
+ // src/providers/api-key-map.ts
70311
+ var API_KEY_MAP;
70312
+ var init_api_key_map = __esm(() => {
70313
+ API_KEY_MAP = {
70314
+ litellm: { envVar: "LITELLM_API_KEY" },
70315
+ openrouter: { envVar: "OPENROUTER_API_KEY" },
70316
+ google: { envVar: "GEMINI_API_KEY" },
70317
+ openai: { envVar: "OPENAI_API_KEY" },
70318
+ minimax: { envVar: "MINIMAX_API_KEY" },
70319
+ "minimax-coding": { envVar: "MINIMAX_CODING_API_KEY" },
70320
+ kimi: { envVar: "MOONSHOT_API_KEY", aliases: ["KIMI_API_KEY"] },
70321
+ "kimi-coding": { envVar: "KIMI_CODING_API_KEY" },
70322
+ glm: { envVar: "ZHIPU_API_KEY", aliases: ["GLM_API_KEY"] },
70323
+ "glm-coding": { envVar: "GLM_CODING_API_KEY", aliases: ["ZAI_CODING_API_KEY"] },
70324
+ "z-ai": { envVar: "ZAI_API_KEY" },
70325
+ deepseek: { envVar: "DEEPSEEK_API_KEY" },
70326
+ mistralai: { envVar: "MISTRAL_API_KEY" },
70327
+ sakana: { envVar: "SAKANA_API_KEY" },
70328
+ "sakana-subscription": {
70329
+ envVar: "SAKANA_SUBSCRIPTION_API_KEY",
70330
+ aliases: ["SAKANA_CODING_API_KEY"]
70331
+ },
70332
+ "qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
70333
+ "qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
70334
+ ollamacloud: { envVar: "OLLAMA_API_KEY" },
70335
+ "opencode-zen": { envVar: "OPENCODE_API_KEY" },
70336
+ "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
70337
+ vertex: { envVar: "VERTEX_API_KEY", aliases: ["VERTEX_PROJECT"] },
70338
+ poe: { envVar: "POE_API_KEY" }
70339
+ };
70340
+ });
70341
+
69372
70342
  // src/providers/probe-runner.ts
69373
70343
  function pinProbeModelSpec(link) {
69374
70344
  if (link.provider === "native-anthropic")
@@ -69688,6 +70658,7 @@ async function parseArgs(args) {
69688
70658
  probeTimeoutMs = parsed * 1000;
69689
70659
  }
69690
70660
  }
70661
+ ensureEndpointsRegistered();
69691
70662
  await probeModelRouting(expandedModels, hasJsonFlag, {
69692
70663
  live: !noProbeFlag,
69693
70664
  timeoutMs: probeTimeoutMs
@@ -71029,6 +72000,7 @@ var init_cli = __esm(() => {
71029
72000
  init_profile_config();
71030
72001
  init_api_key_map();
71031
72002
  init_api_key_provenance();
72003
+ init_endpoint_registration();
71032
72004
  init_model_parser();
71033
72005
  init_probe_live();
71034
72006
  init_probe_runner();
@@ -76734,6 +77706,7 @@ function App({ requestLogin } = {}) {
76734
77706
  const selectedProviderIsLocal = !!(selectedProvider.isLocal || selectedProviderDef?.isLocal);
76735
77707
  const selectedLocalEnabled = selectedProviderIsLocal && isLocalProviderEnabled(selectedProvider.catalogName, config3);
76736
77708
  const refreshConfig = useCallback3(() => {
77709
+ ensureEndpointsRegistered({ force: true });
76737
77710
  setConfig(loadConfig());
76738
77711
  setBufStats(getBufferStats());
76739
77712
  setOpTick((t) => t + 1);
@@ -78287,6 +79260,7 @@ var init_App = __esm(() => {
78287
79260
  init_op_source();
78288
79261
  init_profile_config();
78289
79262
  init_default_routing_rules();
79263
+ init_endpoint_registration();
78290
79264
  init_local_liveness();
78291
79265
  init_onepassword_config();
78292
79266
  init_onepassword();
@@ -78326,6 +79300,7 @@ import { createCliRenderer as createCliRenderer2 } from "@opentui/core";
78326
79300
  import { createRoot as createRoot2 } from "@opentui/react";
78327
79301
  import { jsxDEV as jsxDEV17 } from "@opentui/react/jsx-dev-runtime";
78328
79302
  async function startConfigTui() {
79303
+ ensureEndpointsRegistered();
78329
79304
  setStderrQuiet(true);
78330
79305
  const loginRequest = {
78331
79306
  slug: null
@@ -78385,6 +79360,7 @@ var init_tui = __esm(() => {
78385
79360
  init_codex_oauth();
78386
79361
  init_kimi_oauth();
78387
79362
  init_logger();
79363
+ init_endpoint_registration();
78388
79364
  init_App();
78389
79365
  if (isDirectRun) {
78390
79366
  startConfigTui().catch((err) => {
@@ -82515,6 +83491,10 @@ async function runCli() {
82515
83491
  }
82516
83492
  try {
82517
83493
  const cliConfig = await traceSpan("startup:parse-args", () => parseArgs2(process.argv.slice(2)));
83494
+ await traceSpan("startup:endpoint-registration", async () => {
83495
+ const { ensureEndpointsRegistered: ensureEndpointsRegistered2 } = await Promise.resolve().then(() => (init_endpoint_registration(), exports_endpoint_registration));
83496
+ ensureEndpointsRegistered2();
83497
+ });
82518
83498
  if (cliConfig.team && cliConfig.team.length > 0) {
82519
83499
  let prompt = cliConfig.claudeArgs.join(" ");
82520
83500
  if (cliConfig.inputFile) {