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/bin/claudish.cjs +80 -4
- package/dist/index.js +1161 -181
- package/package.json +5 -5
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.
|
|
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
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
|
|
27945
|
-
|
|
27946
|
-
|
|
27947
|
-
|
|
27948
|
-
|
|
27949
|
-
|
|
27950
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
30767
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34139
|
-
|
|
34140
|
-
|
|
34141
|
-
|
|
34142
|
-
|
|
34143
|
-
|
|
34144
|
-
|
|
34145
|
-
|
|
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
|
-
|
|
34149
|
-
|
|
34150
|
-
|
|
34151
|
-
|
|
34152
|
-
|
|
34153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
42683
|
-
|
|
42684
|
-
|
|
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
|
|
42700
|
-
|
|
42701
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
45027
|
+
signalProcessTree(proc, "SIGTERM");
|
|
44161
45028
|
entry.killHandle = setTimeout(() => {
|
|
44162
45029
|
try {
|
|
44163
|
-
proc
|
|
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
|
|
45088
|
+
signalProcessTree(entry.process, "SIGTERM");
|
|
44222
45089
|
entry.killHandle = setTimeout(() => {
|
|
44223
45090
|
try {
|
|
44224
|
-
entry.process
|
|
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
|
|
45122
|
+
signalProcessTree(entry.process, "SIGTERM");
|
|
44256
45123
|
promises.push(new Promise((resolve2) => {
|
|
44257
45124
|
const timeout = setTimeout(() => {
|
|
44258
45125
|
try {
|
|
44259
|
-
entry.process
|
|
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
|
|
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
|
|
49708
|
-
|
|
49709
|
-
|
|
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
|
-
|
|
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
|
|
50558
|
-
if (
|
|
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
|
-
|
|
50606
|
-
|
|
50607
|
-
|
|
50608
|
-
|
|
50609
|
-
|
|
50610
|
-
|
|
50611
|
-
|
|
50612
|
-
|
|
50613
|
-
|
|
50614
|
-
|
|
50615
|
-
|
|
50616
|
-
|
|
50617
|
-
|
|
50618
|
-
|
|
50619
|
-
|
|
50620
|
-
|
|
50621
|
-
|
|
50622
|
-
|
|
50623
|
-
|
|
50624
|
-
|
|
50625
|
-
|
|
50626
|
-
|
|
50627
|
-
|
|
50628
|
-
|
|
50629
|
-
|
|
50630
|
-
|
|
50631
|
-
|
|
50632
|
-
|
|
50633
|
-
|
|
50634
|
-
|
|
50635
|
-
|
|
50636
|
-
|
|
50637
|
-
|
|
50638
|
-
|
|
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
|
-
|
|
50642
|
-
|
|
50643
|
-
|
|
50644
|
-
|
|
50645
|
-
|
|
50646
|
-
|
|
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,
|
|
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,
|
|
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) {
|