claudish 14.3.0 → 14.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +219 -21
- package/package.json +5 -5
- package/skills/claudish-usage/SKILL.md +6 -2
package/dist/index.js
CHANGED
|
@@ -575,7 +575,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
575
575
|
});
|
|
576
576
|
|
|
577
577
|
// src/version.ts
|
|
578
|
-
var VERSION = "14.
|
|
578
|
+
var VERSION = "14.4.1";
|
|
579
579
|
|
|
580
580
|
// src/redact.ts
|
|
581
581
|
function isPlaceholder(value) {
|
|
@@ -40453,6 +40453,20 @@ var init_claude_sse_sink = __esm(() => {
|
|
|
40453
40453
|
init_redact();
|
|
40454
40454
|
});
|
|
40455
40455
|
|
|
40456
|
+
// src/in-band-api-error.ts
|
|
40457
|
+
function formatInBandApiError(code, message) {
|
|
40458
|
+
return `
|
|
40459
|
+
|
|
40460
|
+
[API Error: ${code} ${message}]`;
|
|
40461
|
+
}
|
|
40462
|
+
function parseInBandApiError(answer, opts = {}) {
|
|
40463
|
+
const trimmed = answer.trim();
|
|
40464
|
+
const m = /^\[API Error: (\S+)([\s\S]*)\]$/.exec(trimmed) ?? (opts.trailing ? /\n\n\[API Error: (\S+)([^\n\]]*)\]$/.exec(trimmed) : null);
|
|
40465
|
+
if (!m)
|
|
40466
|
+
return null;
|
|
40467
|
+
return { code: m[1], text: `${m[1]}${m[2]}` };
|
|
40468
|
+
}
|
|
40469
|
+
|
|
40456
40470
|
// src/handlers/shared/stream-parsers/responses/parallel-wrapper.ts
|
|
40457
40471
|
function stripFunctionsPrefix(name) {
|
|
40458
40472
|
return name.startsWith("functions.") ? name.slice("functions.".length) : name;
|
|
@@ -41161,13 +41175,9 @@ function sum(values) {
|
|
|
41161
41175
|
function apiErrorText(code, message, opts) {
|
|
41162
41176
|
const isCtxOverflow = code === "context_length_exceeded" || /context (length|window)|exceeds? the context|too long|maximum context/i.test(message);
|
|
41163
41177
|
if (!isCtxOverflow)
|
|
41164
|
-
return
|
|
41165
|
-
|
|
41166
|
-
[API Error: ${code} ${message}]`;
|
|
41178
|
+
return formatInBandApiError(code, message);
|
|
41167
41179
|
const cap = opts.contextWindow && opts.contextWindow > 0 ? ` ~${Math.round(opts.contextWindow / 1000)}K tokens` : "";
|
|
41168
|
-
return `
|
|
41169
|
-
|
|
41170
|
-
[Context limit reached] This model's backend enforces a smaller context window${cap} than its API spec. ` + "Run /clear to start fresh (/compact will also fail \u2014 it re-sends the full conversation), " + `or route the model via \`oai@${opts.modelName}\` to use the full-size window.`;
|
|
41180
|
+
return formatInBandApiError(code, `Context limit reached. This model's backend enforces a smaller context window${cap} than its API spec. ` + "Run /clear to start fresh (/compact will also fail \u2014 it re-sends the full conversation), " + `or route the model via \`oai@${opts.modelName}\` to use the full-size window.`);
|
|
41171
41181
|
}
|
|
41172
41182
|
function stopReasonFor(incomplete, hasToolUse) {
|
|
41173
41183
|
if (incomplete)
|
|
@@ -57687,6 +57697,10 @@ function findCatalogEntry(entries, model) {
|
|
|
57687
57697
|
return loweredId;
|
|
57688
57698
|
return entries.find((entry) => entry.aliases.some((alias) => alias.toLowerCase() === lowered));
|
|
57689
57699
|
}
|
|
57700
|
+
function catalogModelId(model, cachePath) {
|
|
57701
|
+
const entries = cachePath ? readAllModelsCache(cachePath)?.entries : getCatalogEntries();
|
|
57702
|
+
return entries ? findCatalogEntry(entries, model)?.modelId : undefined;
|
|
57703
|
+
}
|
|
57690
57704
|
function gatherFromConnections(entry, candidates, excluded, unmappedRoutes, cachePath) {
|
|
57691
57705
|
for (const connection of entry.aggregators ?? []) {
|
|
57692
57706
|
if (connection.routeStatus !== "mapped")
|
|
@@ -86620,7 +86634,9 @@ function classifyRunOutput(input) {
|
|
|
86620
86634
|
return v;
|
|
86621
86635
|
}
|
|
86622
86636
|
function classifyTurnSignals(input) {
|
|
86623
|
-
const {
|
|
86637
|
+
const { stopReason, promptRead } = input;
|
|
86638
|
+
const inBand = input.apiError ? null : parseInBandApiError(input.answer, { trailing: promptRead?.complete === false });
|
|
86639
|
+
const apiError = input.apiError ?? (inBand ? { status: null, text: inBand.text } : null);
|
|
86624
86640
|
if (apiError) {
|
|
86625
86641
|
const status = apiError.status === null ? "" : ` (status ${apiError.status})`;
|
|
86626
86642
|
return {
|
|
@@ -89693,15 +89709,15 @@ function stripUnrequestedProvider(spec, required, isCatalogModel = isCanonicalCa
|
|
|
89693
89709
|
return spec;
|
|
89694
89710
|
return spec.slice(spec.indexOf("@") + 1);
|
|
89695
89711
|
}
|
|
89696
|
-
function userRequiredProvidersArg(value, specs) {
|
|
89712
|
+
function userRequiredProvidersArg(value, specs, name = "user_required_provider") {
|
|
89697
89713
|
if (value === undefined || value === null)
|
|
89698
89714
|
return new Set;
|
|
89699
89715
|
if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
|
|
89700
|
-
throw new Error(`Invalid '
|
|
89716
|
+
throw new Error(`Invalid '${name}': expected an array of model specs.`);
|
|
89701
89717
|
}
|
|
89702
89718
|
for (const spec of value) {
|
|
89703
89719
|
if (!specs.includes(spec)) {
|
|
89704
|
-
throw new Error(`Invalid '
|
|
89720
|
+
throw new Error(`Invalid '${name}': ${JSON.stringify(spec)} is not one of the ` + `models (${specs.join(", ")}). Entries must match a 'models' or 'judges' entry exactly.`);
|
|
89705
89721
|
}
|
|
89706
89722
|
}
|
|
89707
89723
|
return new Set(value);
|
|
@@ -89729,6 +89745,132 @@ function strippedNote(stripped) {
|
|
|
89729
89745
|
return;
|
|
89730
89746
|
return `Provider removed so claudish routing decides: ${stripped.map((x) => `${x.from} -> ${x.to}`).join(", ")}. ` + "Pass 'user_required_provider' only when the user named the provider.";
|
|
89731
89747
|
}
|
|
89748
|
+
function liveModelChoiceFacts(opts = {}) {
|
|
89749
|
+
const recommended = readLastRecommendedModels()?.models ?? null;
|
|
89750
|
+
if (!recommended || recommended.length === 0) {
|
|
89751
|
+
log("[mcp] model check skipped: no recommended models list on disk");
|
|
89752
|
+
}
|
|
89753
|
+
let rules;
|
|
89754
|
+
return {
|
|
89755
|
+
recommended: recommended && recommended.length > 0 ? recommended : null,
|
|
89756
|
+
userRule: (model) => {
|
|
89757
|
+
rules ??= loadRoutingRules();
|
|
89758
|
+
return matchRoutingRuleKey(model, rules) !== null;
|
|
89759
|
+
},
|
|
89760
|
+
inCatalog: (model) => !gatherRouteCandidates(model).catalogMiss,
|
|
89761
|
+
namespaceClaim: (model) => gatherRouteCandidates(model).candidates.length > 0,
|
|
89762
|
+
canonicalId: (model) => catalogModelId(model),
|
|
89763
|
+
catalogMissAllowed: (model) => {
|
|
89764
|
+
if (opts.projectRulesReadable === false)
|
|
89765
|
+
return true;
|
|
89766
|
+
if (!gatherRouteCandidates(model).catalogReadable)
|
|
89767
|
+
return true;
|
|
89768
|
+
const fallback = effectiveDefaultProvider();
|
|
89769
|
+
return fallback !== "" && fallback !== DEFAULT_FALLBACK_PROVIDER;
|
|
89770
|
+
}
|
|
89771
|
+
};
|
|
89772
|
+
}
|
|
89773
|
+
function familyOf(id) {
|
|
89774
|
+
return /^[a-z]+/i.exec(id)?.[0].toLowerCase() ?? id.toLowerCase();
|
|
89775
|
+
}
|
|
89776
|
+
function bareNameVerdict(spec, recommendedIds, facts) {
|
|
89777
|
+
if (parseModelChain(spec).length !== 1 || nativeRouteFor(spec) !== null)
|
|
89778
|
+
return;
|
|
89779
|
+
const parsed = parseModelSpec(spec);
|
|
89780
|
+
if (parsed.isExplicitProvider || parsed.isLegacySyntax)
|
|
89781
|
+
return;
|
|
89782
|
+
const model = parsed.model;
|
|
89783
|
+
if (recommendedIds.has(model) || facts.userRule(model))
|
|
89784
|
+
return;
|
|
89785
|
+
const canonical = facts.canonicalId?.(model);
|
|
89786
|
+
if (canonical !== undefined && recommendedIds.has(canonical))
|
|
89787
|
+
return;
|
|
89788
|
+
if (facts.inCatalog(model))
|
|
89789
|
+
return { kind: "not_recommended", model };
|
|
89790
|
+
if (facts.namespaceClaim(model) || facts.catalogMissAllowed?.(model))
|
|
89791
|
+
return;
|
|
89792
|
+
return { kind: "unknown_model", model };
|
|
89793
|
+
}
|
|
89794
|
+
function modelChoiceProblems(pairs, required, facts = liveModelChoiceFacts()) {
|
|
89795
|
+
const recommended = facts.recommended;
|
|
89796
|
+
if (!recommended)
|
|
89797
|
+
return [];
|
|
89798
|
+
const ids = new Set(recommended.map((m) => m.id));
|
|
89799
|
+
const problems = [];
|
|
89800
|
+
for (const { given, spec } of pairs) {
|
|
89801
|
+
if (required.has(given))
|
|
89802
|
+
continue;
|
|
89803
|
+
const verdict = bareNameVerdict(spec, ids, facts);
|
|
89804
|
+
if (!verdict)
|
|
89805
|
+
continue;
|
|
89806
|
+
const { kind, model } = verdict;
|
|
89807
|
+
const family = familyOf(model);
|
|
89808
|
+
const matching = [
|
|
89809
|
+
...new Set(recommended.filter((m) => familyOf(m.id) === family || m.provider.toLowerCase() === model.toLowerCase()).sort(compareByReleaseDateDesc).map((m) => m.id))
|
|
89810
|
+
];
|
|
89811
|
+
problems.push({ spec: given, kind, matching });
|
|
89812
|
+
}
|
|
89813
|
+
if (problems.length > 0) {
|
|
89814
|
+
log(`[mcp] model check: ${problems.map((p) => `${p.spec} ${p.kind}`).join(", ")}`);
|
|
89815
|
+
}
|
|
89816
|
+
return problems;
|
|
89817
|
+
}
|
|
89818
|
+
function recommendedChoices(recommended) {
|
|
89819
|
+
const byId = new Map;
|
|
89820
|
+
for (const m of recommended) {
|
|
89821
|
+
const row = byId.get(m.id);
|
|
89822
|
+
if (row) {
|
|
89823
|
+
row.tiers.push(m.recommendationTier);
|
|
89824
|
+
continue;
|
|
89825
|
+
}
|
|
89826
|
+
byId.set(m.id, {
|
|
89827
|
+
id: m.id,
|
|
89828
|
+
vendor: m.provider,
|
|
89829
|
+
tiers: [m.recommendationTier],
|
|
89830
|
+
...m.releaseDate ? { released: m.releaseDate } : {}
|
|
89831
|
+
});
|
|
89832
|
+
}
|
|
89833
|
+
return [...byId.values()];
|
|
89834
|
+
}
|
|
89835
|
+
function recommendedSearchMatches(query, recommended) {
|
|
89836
|
+
const words = query.toLowerCase().split(/[\s,/]+/).filter((w) => w.length >= 2);
|
|
89837
|
+
if (words.length === 0)
|
|
89838
|
+
return [];
|
|
89839
|
+
const hits = recommended.filter((m) => {
|
|
89840
|
+
const id = m.id.toLowerCase();
|
|
89841
|
+
return words.some((w) => id.includes(w) || familyOf(m.id) === familyOf(w) || m.provider.toLowerCase() === w);
|
|
89842
|
+
});
|
|
89843
|
+
return recommendedChoices([...hits].sort(compareByReleaseDateDesc));
|
|
89844
|
+
}
|
|
89845
|
+
function modelChoiceResult(problems, recommended, how) {
|
|
89846
|
+
const unknown = problems.filter((p) => p.kind === "unknown_model");
|
|
89847
|
+
const old = problems.filter((p) => p.kind === "not_recommended");
|
|
89848
|
+
const lines = [
|
|
89849
|
+
`${unknown.length > 0 ? "unknown_model" : "not_recommended"}: nothing was started.`
|
|
89850
|
+
];
|
|
89851
|
+
if (unknown.length > 0) {
|
|
89852
|
+
lines.push(`Not a model id: ${unknown.map((p) => JSON.stringify(p.spec)).join(", ")}. ` + "Pick the recommended model that best fits the task (for a family name, the newest " + "of the matching ids) and call again with its id.");
|
|
89853
|
+
}
|
|
89854
|
+
if (old.length > 0) {
|
|
89855
|
+
lines.push(`Not in the recommended list: ${old.map((p) => JSON.stringify(p.spec)).join(", ")}. ` + "Re-evaluate the choice against the recommended models below and call again with one " + `of them. If the user named this exact model, call again ${how}.`);
|
|
89856
|
+
}
|
|
89857
|
+
const matching = Object.fromEntries(problems.map((p) => [p.spec, p.matching]));
|
|
89858
|
+
lines.push(`Matching recommended ids: ${JSON.stringify(matching, null, 2)}`);
|
|
89859
|
+
const efforts = effortClarification([...new Set(problems.flatMap((p) => p.matching))]);
|
|
89860
|
+
if (efforts) {
|
|
89861
|
+
lines.push("Recommended effort for those ids, by task type (pass your task's level with the model " + `you pick): ${JSON.stringify(efforts, null, 2)}`);
|
|
89862
|
+
}
|
|
89863
|
+
lines.push(`Recommended models: ${JSON.stringify(recommendedChoices(recommended), null, 2)}`);
|
|
89864
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
89865
|
+
`) }], isError: true };
|
|
89866
|
+
}
|
|
89867
|
+
function modelChoiceStop(pairs, required, how, opts = {}) {
|
|
89868
|
+
const facts = liveModelChoiceFacts(opts);
|
|
89869
|
+
const problems = modelChoiceProblems(pairs, required, facts);
|
|
89870
|
+
if (problems.length === 0 || !facts.recommended)
|
|
89871
|
+
return;
|
|
89872
|
+
return modelChoiceResult(problems, facts.recommended, how);
|
|
89873
|
+
}
|
|
89732
89874
|
function buildChildClaudeFlags(agent, claudeFlags) {
|
|
89733
89875
|
const extra = typeof claudeFlags === "string" ? claudeFlags.split(/\s+/).filter(Boolean) : [];
|
|
89734
89876
|
const named = typeof agent === "string" ? agent.trim() : "";
|
|
@@ -89845,6 +89987,12 @@ function optionalString(raw, name) {
|
|
|
89845
89987
|
throw new ContractErrorException("invalid_args", `'${name}' must be a non-empty string`);
|
|
89846
89988
|
return raw;
|
|
89847
89989
|
}
|
|
89990
|
+
function requiredString(raw, name) {
|
|
89991
|
+
const value = optionalString(raw, name);
|
|
89992
|
+
if (value === undefined)
|
|
89993
|
+
throw new ContractErrorException("invalid_args", `'${name}' is required`);
|
|
89994
|
+
return value;
|
|
89995
|
+
}
|
|
89848
89996
|
function optionalBoolean(raw, name) {
|
|
89849
89997
|
if (raw === undefined || raw === null)
|
|
89850
89998
|
return false;
|
|
@@ -89953,6 +90101,7 @@ ${block.join(`
|
|
|
89953
90101
|
type: "boolean",
|
|
89954
90102
|
description: "True only when the USER named the provider ('use OpenRouter for it'): the " + "'provider@model' spec is then sent as given."
|
|
89955
90103
|
},
|
|
90104
|
+
user_required_model: { type: "boolean", description: USER_REQUIRED_MODEL_DESCRIPTION },
|
|
89956
90105
|
prompt: { type: "string", description: "The prompt to send to the model" },
|
|
89957
90106
|
system_prompt: { type: "string", description: "Optional system prompt" },
|
|
89958
90107
|
effort: {
|
|
@@ -89968,6 +90117,9 @@ ${block.join(`
|
|
|
89968
90117
|
handler: async (args, ctx) => {
|
|
89969
90118
|
try {
|
|
89970
90119
|
const { specs, stripped } = strippedProviders([args.model], new Set(args.user_required_provider === true ? [args.model] : []));
|
|
90120
|
+
const choice = modelChoiceStop([{ given: args.model, spec: specs[0] }], new Set(args.user_required_model === true ? [args.model] : []), "with user_required_model: true");
|
|
90121
|
+
if (choice)
|
|
90122
|
+
return choice;
|
|
89971
90123
|
const recommended = args.effort === undefined ? effortClarification([args.model]) : undefined;
|
|
89972
90124
|
if (recommended) {
|
|
89973
90125
|
return needsEffortResult(recommended, `with effort: "<level>" (effort: "${KEEP_EFFORT}" keeps Claude Code's own)`);
|
|
@@ -90065,6 +90217,26 @@ ${block.join(`
|
|
|
90065
90217
|
return compareByReleaseDateDesc(orderingKey(a.model), orderingKey(b.model));
|
|
90066
90218
|
}).slice(0, maxResults);
|
|
90067
90219
|
const catalogMatches = searchCatalogModels(query, Math.max(maxResults, 5));
|
|
90220
|
+
const recommendedHits = recommendedSearchMatches(query, readLastRecommendedModels()?.models ?? []);
|
|
90221
|
+
const renderRecommendedSection = () => {
|
|
90222
|
+
if (recommendedHits.length === 0)
|
|
90223
|
+
return "";
|
|
90224
|
+
let s = `## Recommended models (pass one of these)
|
|
90225
|
+
|
|
90226
|
+
`;
|
|
90227
|
+
s += `| Bare name | Vendor | Tiers | Released |
|
|
90228
|
+
`;
|
|
90229
|
+
s += `|-----------|--------|-------|----------|
|
|
90230
|
+
`;
|
|
90231
|
+
for (const m of recommendedHits) {
|
|
90232
|
+
s += `| ${m.id} | ${m.vendor} | ${m.tiers.join(", ")} | ${m.released ?? "-"} |
|
|
90233
|
+
`;
|
|
90234
|
+
}
|
|
90235
|
+
return `${s}
|
|
90236
|
+
`;
|
|
90237
|
+
};
|
|
90238
|
+
const useWith = () => recommendedHits.length > 0 ? `
|
|
90239
|
+
Use with: run_prompt(model="${recommendedHits[0].id}", prompt="your prompt")` : "\nNo recommended model matches this query: call `list_models` for the recommended set.";
|
|
90068
90240
|
const renderCatalogSection = () => {
|
|
90069
90241
|
if (catalogMatches.length === 0)
|
|
90070
90242
|
return "";
|
|
@@ -90081,12 +90253,17 @@ ${block.join(`
|
|
|
90081
90253
|
s += `| ${m.modelId} | ${m.matchedAlias ?? "-"} | ${plans} |
|
|
90082
90254
|
`;
|
|
90083
90255
|
}
|
|
90084
|
-
s += `
|
|
90085
|
-
Pass the **bare name**. Routing puts a subscription ahead of the metered API and ` + "rewrites the model to that plan's wire id for you. An aggregator-qualified id " + "(`moonshotai/...`, `accounts/fireworks/...`) pins that aggregator and bills per token.\n";
|
|
90256
|
+
s += "\nOnly the recommended models above start without `user_required_model`. " + "Pass the **bare name**. Routing puts a subscription ahead of the metered API and " + "rewrites the model to that plan's wire id for you. An aggregator-qualified id " + "(`moonshotai/...`, `accounts/fireworks/...`) pins that aggregator and bills per token.\n";
|
|
90086
90257
|
return s;
|
|
90087
90258
|
};
|
|
90088
90259
|
if (results.length === 0) {
|
|
90089
90260
|
const catalog = renderCatalogSection();
|
|
90261
|
+
if (recommendedHits.length > 0) {
|
|
90262
|
+
const text = `No OpenRouter listing matches "${query}".
|
|
90263
|
+
|
|
90264
|
+
${renderRecommendedSection()}${catalog}${useWith()}`;
|
|
90265
|
+
return { content: [{ type: "text", text }] };
|
|
90266
|
+
}
|
|
90090
90267
|
const text = catalog ? `No OpenRouter listing matches "${query}", but claudish's catalog knows these:
|
|
90091
90268
|
${catalog}` : `No models found matching "${query}".
|
|
90092
90269
|
|
|
@@ -90096,6 +90273,7 @@ ${catalog}` : `No models found matching "${query}".
|
|
|
90096
90273
|
let output = `# Search Results for "${query}"
|
|
90097
90274
|
|
|
90098
90275
|
`;
|
|
90276
|
+
output += renderRecommendedSection();
|
|
90099
90277
|
output += `## OpenRouter listing
|
|
90100
90278
|
|
|
90101
90279
|
`;
|
|
@@ -90114,9 +90292,7 @@ ${catalog}` : `No models found matching "${query}".
|
|
|
90114
90292
|
`;
|
|
90115
90293
|
}
|
|
90116
90294
|
output += renderCatalogSection();
|
|
90117
|
-
|
|
90118
|
-
output += `
|
|
90119
|
-
Use with: run_prompt(model="${suggested}", prompt="your prompt")`;
|
|
90295
|
+
output += useWith();
|
|
90120
90296
|
return { content: [{ type: "text", text: output }] };
|
|
90121
90297
|
}
|
|
90122
90298
|
});
|
|
@@ -90300,6 +90476,11 @@ Use with: run_prompt(model="${suggested}", prompt="your prompt")`;
|
|
|
90300
90476
|
items: { type: "string" },
|
|
90301
90477
|
description: "The 'models' or 'judges' entries whose provider the USER named ('run Kimi on " + "OpenRouter'), exactly as written there. Every other 'provider@' on a catalog " + "model is removed so claudish routing picks the provider from the user's setup; " + "pass bare names unless the user asked for a provider."
|
|
90302
90478
|
},
|
|
90479
|
+
user_required_model: {
|
|
90480
|
+
type: "array",
|
|
90481
|
+
items: { type: "string" },
|
|
90482
|
+
description: "The 'models' or 'judges' entries the USER named as exact models, exactly as " + "written there: they run even when not in the recommended list. Never list a " + "model you chose yourself."
|
|
90483
|
+
},
|
|
90303
90484
|
input_file: {
|
|
90304
90485
|
type: "string",
|
|
90305
90486
|
description: "PREFERRED. Path to a file holding the task prompt, relative to the working " + "directory. Use this rather than `input` for anything longer than a sentence: " + "a prompt passed inline is echoed verbatim in the caller's terminal, where a " + "200-line review brief buries every other argument and makes the call " + "unreadable. Write the brief to the session directory first (input.md is the " + "conventional name) and point here."
|
|
@@ -90384,7 +90565,15 @@ Use with: run_prompt(model="${suggested}", prompt="your prompt")`;
|
|
|
90384
90565
|
const existing = join56(resolved, "input.md");
|
|
90385
90566
|
return existsSync37(existing) ? readFileSync37(existing, "utf-8") : undefined;
|
|
90386
90567
|
};
|
|
90387
|
-
|
|
90568
|
+
const runsModels = mode === "run" || mode === "run-and-judge";
|
|
90569
|
+
const runsJudges = mode === "judge" || mode === "run-and-judge";
|
|
90570
|
+
const choice = modelChoiceStop([
|
|
90571
|
+
...runsModels && givenModels && models ? givenModels.map((given, i) => ({ given, spec: models[i] })) : [],
|
|
90572
|
+
...runsJudges && givenJudges && judges ? givenJudges.map((given, i) => ({ given, spec: judges[i] })) : []
|
|
90573
|
+
], userRequiredProvidersArg(args.user_required_model, [...givenModels ?? [], ...givenJudges ?? []], "user_required_model"), 'with user_required_model: ["<that entry>"]');
|
|
90574
|
+
if (choice)
|
|
90575
|
+
return choice;
|
|
90576
|
+
if (runsModels && givenModels && args.efforts === undefined) {
|
|
90388
90577
|
const recommended = effortClarification(givenModels);
|
|
90389
90578
|
if (recommended) {
|
|
90390
90579
|
return needsEffortResult(recommended, `with efforts: {"<model>": "<level>"} (efforts: {} keeps Claude Code's own for every model)`);
|
|
@@ -90673,6 +90862,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
90673
90862
|
type: "boolean",
|
|
90674
90863
|
description: "True only when the USER named the provider: the 'provider@model' spec is then " + "used as given."
|
|
90675
90864
|
},
|
|
90865
|
+
user_required_model: { type: "boolean", description: USER_REQUIRED_MODEL_DESCRIPTION },
|
|
90676
90866
|
prompt: {
|
|
90677
90867
|
type: "string",
|
|
90678
90868
|
description: "Initial prompt to send. If omitted, send later via send_input."
|
|
@@ -90720,6 +90910,11 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
90720
90910
|
await assertMagmuxAvailable();
|
|
90721
90911
|
const { specs, stripped } = strippedProviders([args.model], new Set(args.user_required_provider === true ? [args.model] : []));
|
|
90722
90912
|
const requestedModel = specs[0];
|
|
90913
|
+
const choice = modelChoiceStop([{ given: args.model, spec: requestedModel }], new Set(args.user_required_model === true ? [args.model] : []), "with user_required_model: true", {
|
|
90914
|
+
projectRulesReadable: typeof args.work_dir !== "string" || resolve8(args.work_dir) === process.cwd()
|
|
90915
|
+
});
|
|
90916
|
+
if (choice)
|
|
90917
|
+
return choice;
|
|
90723
90918
|
const recommended = args.effort === undefined ? effortClarification([args.model]) : undefined;
|
|
90724
90919
|
if (recommended) {
|
|
90725
90920
|
return needsEffortResult(recommended, `with effort: "<level>" (effort: "${KEEP_EFFORT}" keeps Claude Code's own)`);
|
|
@@ -90787,7 +90982,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
90787
90982
|
},
|
|
90788
90983
|
group: "session",
|
|
90789
90984
|
handler: async (args) => {
|
|
90790
|
-
const result = sessionManager.sendInput(args.session_id, args.text);
|
|
90985
|
+
const result = sessionManager.sendInput(requiredString(args.session_id, "session_id"), requiredString(args.text, "text"));
|
|
90791
90986
|
return {
|
|
90792
90987
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
90793
90988
|
};
|
|
@@ -90810,7 +91005,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
90810
91005
|
group: "session",
|
|
90811
91006
|
handler: async (args) => {
|
|
90812
91007
|
try {
|
|
90813
|
-
const output = sessionManager.getOutput(args.session_id, args.tail_lines);
|
|
91008
|
+
const output = sessionManager.getOutput(requiredString(args.session_id, "session_id"), args.tail_lines);
|
|
90814
91009
|
return {
|
|
90815
91010
|
content: [{ type: "text", text: JSON.stringify(output) }]
|
|
90816
91011
|
};
|
|
@@ -90891,7 +91086,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
90891
91086
|
group: "session",
|
|
90892
91087
|
handler: async (args) => {
|
|
90893
91088
|
try {
|
|
90894
|
-
const diagnostics = sessionManager.getDiagnostics(args.session_id, args.event_limit);
|
|
91089
|
+
const diagnostics = sessionManager.getDiagnostics(requiredString(args.session_id, "session_id"), args.event_limit);
|
|
90895
91090
|
return {
|
|
90896
91091
|
content: [{ type: "text", text: JSON.stringify(diagnostics) }]
|
|
90897
91092
|
};
|
|
@@ -91114,7 +91309,7 @@ Each session is an interactive Claude Code running the model in its own headless
|
|
|
91114
91309
|
5. FAILED or TIMEOUT \u2014 or COMPLETED with an empty or surprising answer \u2014 call get_diagnostics. It returns the child's final screen, the upstream error bodies, the recent state records, the resolved model chain and the paths to the full records. It needs no re-run and no debug flag.
|
|
91115
91310
|
6. capture_session reads a session's current screen; resize_session sets its terminal size (any time, mid-turn included); cancel_session stops it. Nothing stops a session for being idle: a child inside a long build is silent and working.
|
|
91116
91311
|
|
|
91117
|
-
The session_id from create_session is the key for every session tool.`, proxyInstance = null, proxyStarting = null, SCORE_TIE_EPSILON = 0.000001, NEXT_STEP, KEEP_EFFORT = "default", sanitize2, TOOL_MODES;
|
|
91312
|
+
The session_id from create_session is the key for every session tool.`, proxyInstance = null, proxyStarting = null, SCORE_TIE_EPSILON = 0.000001, NEXT_STEP, KEEP_EFFORT = "default", USER_REQUIRED_MODEL_DESCRIPTION, sanitize2, TOOL_MODES;
|
|
91118
91313
|
var init_mcp_server = __esm(() => {
|
|
91119
91314
|
init_server2();
|
|
91120
91315
|
init_stdio2();
|
|
@@ -91146,6 +91341,8 @@ var init_mcp_server = __esm(() => {
|
|
|
91146
91341
|
init_preflight_chain();
|
|
91147
91342
|
init_probe_live();
|
|
91148
91343
|
init_provider_definitions();
|
|
91344
|
+
init_route_candidates();
|
|
91345
|
+
init_routing_rules();
|
|
91149
91346
|
init_proxy_server();
|
|
91150
91347
|
init_recommended_listing();
|
|
91151
91348
|
init_redact();
|
|
@@ -91172,6 +91369,7 @@ var init_mcp_server = __esm(() => {
|
|
|
91172
91369
|
empty_output: "retry once; if it repeats, drop the model",
|
|
91173
91370
|
shape_mismatch: "the answer (assistant text after the task was read) did not match. Every assistant " + "message of the turn was read from the transcript, so nothing was lost in transit \u2014 " + "the model did not produce it. Re-prompt with the required format restated; do NOT " + "count this slot as a vote"
|
|
91174
91371
|
};
|
|
91372
|
+
USER_REQUIRED_MODEL_DESCRIPTION = "True only when the USER named this exact model: it then runs even when it is not in the " + "recommended list. Never set it for a model you chose yourself.";
|
|
91175
91373
|
sanitize2 = sanitizeForReport;
|
|
91176
91374
|
TOOL_MODES = ["all", "low-level", "agentic", "session", "debug"];
|
|
91177
91375
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.4.1",
|
|
4
4
|
"description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"ai"
|
|
72
72
|
],
|
|
73
73
|
"optionalDependencies": {
|
|
74
|
-
"@claudish/magmux-darwin-arm64": "14.
|
|
75
|
-
"@claudish/magmux-darwin-x64": "14.
|
|
76
|
-
"@claudish/magmux-linux-arm64": "14.
|
|
77
|
-
"@claudish/magmux-linux-x64": "14.
|
|
74
|
+
"@claudish/magmux-darwin-arm64": "14.4.1",
|
|
75
|
+
"@claudish/magmux-darwin-x64": "14.4.1",
|
|
76
|
+
"@claudish/magmux-linux-arm64": "14.4.1",
|
|
77
|
+
"@claudish/magmux-linux-x64": "14.4.1"
|
|
78
78
|
},
|
|
79
79
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
80
80
|
"license": "MIT",
|
|
@@ -58,9 +58,13 @@ subscriptions first, their preferences and manual overrides. Do not choose it yo
|
|
|
58
58
|
`create_session` / `run_prompt`: `true`).
|
|
59
59
|
3. **User named exact models:** pass them as given. No `preflight` or `search_models`
|
|
60
60
|
call is needed to check them: claudish routes and checks them. You still read
|
|
61
|
-
`list_models` once for their efforts (next section).
|
|
61
|
+
`list_models` once for their efforts (next section). A named model outside the
|
|
62
|
+
recommended list answers `not_recommended`; list it in `user_required_model` (`team`:
|
|
63
|
+
the entries; `create_session` / `run_prompt`: `true`) and call again.
|
|
62
64
|
4. **User named only a family or vendor** ("a GPT model", "minimax", "latest Grok"): call
|
|
63
|
-
`list_models` and take the newest model of that family from it, as a bare name.
|
|
65
|
+
`list_models` and take the newest model of that family from it, as a bare name. A
|
|
66
|
+
family name (`gpt`) or a model you recall (`gpt-5`) answers `unknown_model` /
|
|
67
|
+
`not_recommended` with the recommended ids: pick from them and call again.
|
|
64
68
|
|
|
65
69
|
### 🎚️ Reasoning Effort per Model
|
|
66
70
|
|