claudish 7.24.0 → 7.25.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.
Files changed (2) hide show
  1. package/dist/index.js +697 -143
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -651,7 +651,7 @@ var init_onepassword_config = __esm(() => {
651
651
  });
652
652
 
653
653
  // src/version.ts
654
- var VERSION = "7.24.0";
654
+ var VERSION = "7.25.0";
655
655
 
656
656
  // src/logger.ts
657
657
  var exports_logger = {};
@@ -27006,6 +27006,9 @@ function loadConfig() {
27006
27006
  if (config2.customEndpoints !== undefined) {
27007
27007
  merged.customEndpoints = config2.customEndpoints;
27008
27008
  }
27009
+ if (config2.behavior !== undefined) {
27010
+ merged.behavior = config2.behavior;
27011
+ }
27009
27012
  return merged;
27010
27013
  } catch (error46) {
27011
27014
  console.error(`Warning: Failed to load config, using defaults: ${error46}`);
@@ -36790,6 +36793,555 @@ ${text}`;
36790
36793
  };
36791
36794
  });
36792
36795
 
36796
+ // ../../node_modules/.bun/zod@4.1.13/node_modules/zod/index.js
36797
+ var init_zod = __esm(() => {
36798
+ init_external2();
36799
+ init_external2();
36800
+ });
36801
+
36802
+ // src/behavior/config.ts
36803
+ function parseBehaviorConfig(raw2) {
36804
+ if (raw2 === undefined || raw2 === null)
36805
+ return {};
36806
+ const result = BehaviorConfigSchema.safeParse(raw2);
36807
+ if (!result.success) {
36808
+ logStderr(`[behavior] Ignoring invalid "behavior" config: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
36809
+ return {};
36810
+ }
36811
+ return result.data;
36812
+ }
36813
+ function resolveSeverity(ruleId, defaultSeverity, config2) {
36814
+ const rules = config2.rules;
36815
+ if (!rules)
36816
+ return defaultSeverity;
36817
+ const exact = rules[ruleId];
36818
+ if (exact)
36819
+ return exact;
36820
+ let best = null;
36821
+ for (const [pattern, severity] of Object.entries(rules)) {
36822
+ if (!pattern.includes("*"))
36823
+ continue;
36824
+ if (!globMatches(pattern, ruleId))
36825
+ continue;
36826
+ const len = pattern.replace(/\*/g, "").length;
36827
+ if (!best || len > best.len)
36828
+ best = { len, severity };
36829
+ }
36830
+ return best ? best.severity : defaultSeverity;
36831
+ }
36832
+ function globMatches(pattern, value) {
36833
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
36834
+ return new RegExp(`^${escaped}$`).test(value);
36835
+ }
36836
+ var SeveritySchema, BehaviorConfigSchema;
36837
+ var init_config = __esm(() => {
36838
+ init_zod();
36839
+ init_logger();
36840
+ SeveritySchema = exports_external.enum(["off", "warn", "fix"]);
36841
+ BehaviorConfigSchema = exports_external.object({
36842
+ preset: exports_external.string().optional(),
36843
+ rules: exports_external.record(exports_external.string(), SeveritySchema).optional(),
36844
+ hooks: exports_external.array(exports_external.string()).optional(),
36845
+ observer: exports_external.object({
36846
+ enabled: exports_external.boolean().optional(),
36847
+ mode: exports_external.enum(["off", "suggest", "enforce"]).optional(),
36848
+ model: exports_external.string().optional(),
36849
+ timeoutMs: exports_external.number().int().positive().optional()
36850
+ }).optional()
36851
+ });
36852
+ });
36853
+
36854
+ // src/behavior/harness.ts
36855
+ function textOf(value) {
36856
+ if (!value)
36857
+ return "";
36858
+ if (typeof value === "string")
36859
+ return value;
36860
+ if (Array.isArray(value)) {
36861
+ let out = "";
36862
+ for (const part of value) {
36863
+ if (typeof part === "string")
36864
+ out += part;
36865
+ else if (typeof part?.text === "string")
36866
+ out += part.text;
36867
+ else if (typeof part?.content === "string")
36868
+ out += part.content;
36869
+ }
36870
+ return out;
36871
+ }
36872
+ if (typeof value?.text === "string")
36873
+ return value.text;
36874
+ return "";
36875
+ }
36876
+ function matchPlanPath(text) {
36877
+ if (!PLAN_MODE_HINT.test(text))
36878
+ return;
36879
+ for (const re of PLAN_PATH_PATTERNS) {
36880
+ const m = re.exec(text);
36881
+ if (m?.[1])
36882
+ return m[1];
36883
+ }
36884
+ return;
36885
+ }
36886
+ function detectHarnessFacts(claudeRequest) {
36887
+ const facts = { planModeActive: false };
36888
+ let planPath = matchPlanPath(textOf(claudeRequest?.system));
36889
+ if (!planPath && Array.isArray(claudeRequest?.messages)) {
36890
+ const messages = claudeRequest.messages;
36891
+ for (let i = messages.length - 1;i >= 0; i--) {
36892
+ planPath = matchPlanPath(textOf(messages[i]?.content));
36893
+ if (planPath)
36894
+ break;
36895
+ }
36896
+ }
36897
+ if (planPath) {
36898
+ facts.planModeActive = true;
36899
+ facts.planFilePath = planPath;
36900
+ const slash = planPath.lastIndexOf("/");
36901
+ if (slash > 0)
36902
+ facts.planDir = planPath.slice(0, slash);
36903
+ }
36904
+ return facts;
36905
+ }
36906
+ var PLAN_PATH_PATTERNS, PLAN_MODE_HINT;
36907
+ var init_harness = __esm(() => {
36908
+ PLAN_PATH_PATTERNS = [
36909
+ /You should create your plan at\s+(\S+?\.md)/,
36910
+ /A plan file already exists at\s+(\S+?\.md)/,
36911
+ /Read-only except plan file\s*\(([^)]+\.md)\)/
36912
+ ];
36913
+ PLAN_MODE_HINT = /plan file|create your plan at|Plan mode is active|Plan mode still active/i;
36914
+ });
36915
+
36916
+ // src/behavior/engine.ts
36917
+ class BehaviorSession {
36918
+ active;
36919
+ modelId;
36920
+ providerName;
36921
+ facts = { planModeActive: false };
36922
+ bufferedTools = new Set;
36923
+ constructor(active, modelId, providerName) {
36924
+ this.active = active;
36925
+ this.modelId = modelId;
36926
+ this.providerName = providerName;
36927
+ }
36928
+ armBuffering() {
36929
+ const armed = new Set;
36930
+ for (const { rule, severity } of this.active) {
36931
+ if (severity !== "fix")
36932
+ continue;
36933
+ if (rule.armed && !rule.armed(this.facts))
36934
+ continue;
36935
+ for (const t of rule.interceptsTools ?? [])
36936
+ armed.add(t);
36937
+ }
36938
+ this.bufferedTools = armed;
36939
+ }
36940
+ get harness() {
36941
+ return this.facts;
36942
+ }
36943
+ get isNoop() {
36944
+ return this.active.length === 0;
36945
+ }
36946
+ applyRequest(claudeRequest, claudeTools, tools, messages) {
36947
+ if (this.active.length === 0)
36948
+ return;
36949
+ this.facts = detectHarnessFacts(claudeRequest);
36950
+ this.armBuffering();
36951
+ const ctx = {
36952
+ modelId: this.modelId,
36953
+ providerName: this.providerName,
36954
+ isNativeAnthropic: false,
36955
+ claudeRequest,
36956
+ claudeTools,
36957
+ tools,
36958
+ messages,
36959
+ harness: this.facts
36960
+ };
36961
+ for (const { rule, severity } of this.active) {
36962
+ if (!rule.onRequest)
36963
+ continue;
36964
+ let actions = [];
36965
+ try {
36966
+ actions = rule.onRequest(ctx) ?? [];
36967
+ } catch (err) {
36968
+ log(`[behavior] rule ${rule.id} onRequest threw: ${err}`);
36969
+ continue;
36970
+ }
36971
+ for (const action of actions)
36972
+ this.applyAction(rule.id, severity, action, ctx);
36973
+ }
36974
+ }
36975
+ interceptsTool(toolName) {
36976
+ return this.bufferedTools.has(toolName);
36977
+ }
36978
+ repairToolCall(toolName, rawArgs) {
36979
+ if (!this.bufferedTools.has(toolName))
36980
+ return null;
36981
+ let args = {};
36982
+ try {
36983
+ const parsed = JSON.parse(rawArgs || "{}");
36984
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
36985
+ args = parsed;
36986
+ } catch {
36987
+ return null;
36988
+ }
36989
+ let changed = false;
36990
+ for (const { rule, severity } of this.active) {
36991
+ if (!rule.onToolCall)
36992
+ continue;
36993
+ if (!(rule.interceptsTools ?? []).includes(toolName))
36994
+ continue;
36995
+ let actions = [];
36996
+ try {
36997
+ actions = rule.onToolCall({
36998
+ modelId: this.modelId,
36999
+ toolName,
37000
+ args,
37001
+ rawArgs,
37002
+ harness: this.facts
37003
+ }) ?? [];
37004
+ } catch (err) {
37005
+ log(`[behavior] rule ${rule.id} onToolCall threw: ${err}`);
37006
+ continue;
37007
+ }
37008
+ for (const action of actions) {
37009
+ if (action.type === "warn") {
37010
+ log(`[behavior] ${rule.id} (warn): ${action.message}`);
37011
+ continue;
37012
+ }
37013
+ if (action.type !== "repairToolArgs")
37014
+ continue;
37015
+ if (severity !== "fix") {
37016
+ log(`[behavior] ${rule.id} (warn-only, not applied): ${action.reason}`);
37017
+ continue;
37018
+ }
37019
+ args = action.args;
37020
+ changed = true;
37021
+ log(`[behavior] ${rule.id} repaired ${toolName}: ${action.reason}`);
37022
+ }
37023
+ }
37024
+ return changed ? JSON.stringify(args) : null;
37025
+ }
37026
+ applyAction(ruleId, severity, action, ctx) {
37027
+ if (action.type === "warn") {
37028
+ log(`[behavior] ${ruleId} (warn): ${action.message}`);
37029
+ return;
37030
+ }
37031
+ if (severity !== "fix") {
37032
+ log(`[behavior] ${ruleId} (warn-only, not applied): ${action.type}`);
37033
+ return;
37034
+ }
37035
+ switch (action.type) {
37036
+ case "injectSystemNote": {
37037
+ const req = ctx.claudeRequest;
37038
+ if (typeof req.system === "string") {
37039
+ req.system = `${req.system}
37040
+
37041
+ ${action.text}`;
37042
+ } else if (Array.isArray(req.system)) {
37043
+ req.system.push({ type: "text", text: action.text });
37044
+ } else {
37045
+ req.system = action.text;
37046
+ }
37047
+ log(`[behavior] ${ruleId} injected system note (${action.text.length} chars)`);
37048
+ break;
37049
+ }
37050
+ case "rewriteToolDescription": {
37051
+ let hits = 0;
37052
+ for (const t of ctx.claudeTools) {
37053
+ if (t?.name !== action.tool)
37054
+ continue;
37055
+ t.description = `${t.description ?? ""}${action.append}`;
37056
+ hits++;
37057
+ }
37058
+ for (const t of ctx.tools) {
37059
+ const fn = t?.function ?? t;
37060
+ if (fn?.name !== action.tool)
37061
+ continue;
37062
+ fn.description = `${fn.description ?? ""}${action.append}`;
37063
+ hits++;
37064
+ }
37065
+ log(`[behavior] ${ruleId} rewrote description of ${action.tool} (${hits} site(s))`);
37066
+ break;
37067
+ }
37068
+ case "repairToolArgs":
37069
+ log(`[behavior] ${ruleId} returned repairToolArgs from onRequest \u2014 ignored`);
37070
+ break;
37071
+ }
37072
+ }
37073
+ }
37074
+
37075
+ class BehaviorEngine {
37076
+ config;
37077
+ rules;
37078
+ constructor(config2, rules) {
37079
+ this.config = config2;
37080
+ this.rules = rules;
37081
+ }
37082
+ startSession(params) {
37083
+ const active = [];
37084
+ if (!params.isNativeAnthropic) {
37085
+ for (const rule of this.rules) {
37086
+ const severity = resolveSeverity(rule.id, rule.defaultSeverity, this.config);
37087
+ if (severity === "off")
37088
+ continue;
37089
+ let applies = false;
37090
+ try {
37091
+ applies = rule.appliesTo(params);
37092
+ } catch (err) {
37093
+ log(`[behavior] rule ${rule.id} appliesTo threw: ${err}`);
37094
+ continue;
37095
+ }
37096
+ if (applies)
37097
+ active.push({ rule, severity });
37098
+ }
37099
+ }
37100
+ if (active.length > 0) {
37101
+ log(`[behavior] ${active.length} rule(s) active for ${params.modelId}: ` + active.map((a) => `${a.rule.id}=${a.severity}`).join(", "));
37102
+ }
37103
+ return new BehaviorSession(active, params.modelId, params.providerName);
37104
+ }
37105
+ }
37106
+ var init_engine = __esm(() => {
37107
+ init_logger();
37108
+ init_config();
37109
+ init_harness();
37110
+ });
37111
+
37112
+ // src/behavior/rules/plan-mode.ts
37113
+ function directoryOf(filePath) {
37114
+ const slash = filePath.lastIndexOf("/");
37115
+ return slash > 0 ? filePath.slice(0, slash) : undefined;
37116
+ }
37117
+ var WRITE_TOOLS, planFilePathRule, PLAN_MODE_RULES;
37118
+ var init_plan_mode = __esm(() => {
37119
+ WRITE_TOOLS = ["Write", "Edit", "NotebookEdit"];
37120
+ planFilePathRule = {
37121
+ id: "plan-mode/plan-file-path",
37122
+ description: "Keep plan-mode writes on the plan file Claude Code assigned, and name that " + "path in the ExitPlanMode description.",
37123
+ defaultSeverity: "fix",
37124
+ interceptsTools: WRITE_TOOLS,
37125
+ appliesTo: ({ isNativeAnthropic }) => !isNativeAnthropic,
37126
+ armed: (facts) => facts.planModeActive === true,
37127
+ onRequest(ctx) {
37128
+ const { planModeActive, planFilePath } = ctx.harness;
37129
+ if (!planModeActive || !planFilePath)
37130
+ return [];
37131
+ const hasExitPlanMode = ctx.claudeTools.some((t) => t?.name === "ExitPlanMode") || ctx.tools.some((t) => (t?.function ?? t)?.name === "ExitPlanMode");
37132
+ if (!hasExitPlanMode)
37133
+ return [];
37134
+ return [
37135
+ {
37136
+ type: "rewriteToolDescription",
37137
+ tool: "ExitPlanMode",
37138
+ append: `
37139
+
37140
+ ## Plan file for THIS session
37141
+ Your plan MUST be written to exactly this path:
37142
+ ${planFilePath}
37143
+ Do not invent a different filename, and do not derive one from the task. Claude Code reads only that exact path; a plan written anywhere else is invisible to it and the approval will show "No plan found".`
37144
+ }
37145
+ ];
37146
+ },
37147
+ onToolCall(ctx) {
37148
+ const { planFilePath, planDir } = ctx.harness;
37149
+ if (!planFilePath || !planDir)
37150
+ return [];
37151
+ const filePath = ctx.args.file_path;
37152
+ if (typeof filePath !== "string" || filePath === planFilePath)
37153
+ return [];
37154
+ if (directoryOf(filePath) !== planDir)
37155
+ return [];
37156
+ return [
37157
+ {
37158
+ type: "repairToolArgs",
37159
+ args: { ...ctx.args, file_path: planFilePath },
37160
+ reason: `redirected ${ctx.toolName} from ${filePath} to the session's assigned ` + `plan file ${planFilePath}`
37161
+ }
37162
+ ];
37163
+ }
37164
+ };
37165
+ PLAN_MODE_RULES = [planFilePathRule];
37166
+ });
37167
+
37168
+ // src/behavior/hooks.ts
37169
+ import { isAbsolute, resolve } from "path";
37170
+ function isBehaviorRule(value) {
37171
+ return !!value && typeof value === "object" && typeof value.id === "string" && value.id.length > 0 && typeof value.appliesTo === "function" && (value.onRequest === undefined || typeof value.onRequest === "function") && (value.onToolCall === undefined || typeof value.onToolCall === "function");
37172
+ }
37173
+ function collectRules(mod) {
37174
+ const found = [];
37175
+ const consider = (v) => {
37176
+ if (Array.isArray(v))
37177
+ v.forEach(consider);
37178
+ else if (isBehaviorRule(v))
37179
+ found.push(v);
37180
+ };
37181
+ consider(mod?.default);
37182
+ consider(mod?.rules);
37183
+ for (const [key, value] of Object.entries(mod ?? {})) {
37184
+ if (key === "default" || key === "rules")
37185
+ continue;
37186
+ consider(value);
37187
+ }
37188
+ return [...new Set(found)];
37189
+ }
37190
+ function shortName(path) {
37191
+ const base = path.split("/").pop() ?? path;
37192
+ return base.replace(/\.[cm]?[jt]s$/, "");
37193
+ }
37194
+ async function loadHookRules(paths, cwd = process.cwd()) {
37195
+ if (!paths?.length)
37196
+ return [];
37197
+ const loaded = [];
37198
+ const seen = new Set;
37199
+ for (const raw2 of paths) {
37200
+ const abs = isAbsolute(raw2) ? raw2 : resolve(cwd, raw2);
37201
+ const rules = await importHook(abs, raw2);
37202
+ for (const rule of rules)
37203
+ namespaceInto(rule, abs, seen, loaded);
37204
+ }
37205
+ if (loaded.length > 0) {
37206
+ logStderr(`[behavior] Loaded ${loaded.length} hook rule(s): ${loaded.map((r) => r.id).join(", ")}`);
37207
+ }
37208
+ return loaded;
37209
+ }
37210
+ async function importHook(abs, raw2) {
37211
+ let mod;
37212
+ try {
37213
+ mod = await import(abs);
37214
+ } catch (err) {
37215
+ logStderr(`[behavior] Skipping hook ${raw2}: ${err instanceof Error ? err.message : err}`);
37216
+ return [];
37217
+ }
37218
+ const rules = collectRules(mod);
37219
+ if (rules.length === 0) {
37220
+ logStderr(`[behavior] Hook ${raw2} exported no valid BehaviorRule \u2014 skipped`);
37221
+ }
37222
+ return rules;
37223
+ }
37224
+ function namespaceInto(rule, abs, seen, out) {
37225
+ const namespaced = `hook:${shortName(abs)}/${rule.id}`;
37226
+ if (seen.has(namespaced)) {
37227
+ logStderr(`[behavior] Duplicate hook rule ${namespaced} \u2014 keeping the first`);
37228
+ return;
37229
+ }
37230
+ seen.add(namespaced);
37231
+ out.push({
37232
+ ...rule,
37233
+ id: namespaced,
37234
+ defaultSeverity: rule.defaultSeverity ?? "warn"
37235
+ });
37236
+ }
37237
+ var init_hooks = __esm(() => {
37238
+ init_logger();
37239
+ });
37240
+
37241
+ // src/behavior/observer/digest.ts
37242
+ var PATH_KEYS;
37243
+ var init_digest = __esm(() => {
37244
+ PATH_KEYS = new Set(["file_path", "path", "notebook_path", "filePath"]);
37245
+ });
37246
+
37247
+ // src/providers/ollama-discovery.ts
37248
+ function ollamaBaseUrl() {
37249
+ return process.env.OLLAMA_HOST || process.env.OLLAMA_BASE_URL || "http://localhost:11434";
37250
+ }
37251
+ async function fetchOllamaModels(options = {}) {
37252
+ const { enrichCapabilities = true } = options;
37253
+ const host = ollamaBaseUrl();
37254
+ try {
37255
+ const response = await fetch(`${host}/api/tags`, {
37256
+ signal: AbortSignal.timeout(3000)
37257
+ });
37258
+ if (!response.ok)
37259
+ return [];
37260
+ const data = await response.json();
37261
+ const models = data.models || [];
37262
+ const enriched = await Promise.all(models.map(async (m) => {
37263
+ let capabilities = [];
37264
+ if (enrichCapabilities) {
37265
+ try {
37266
+ const showResponse = await fetch(`${host}/api/show`, {
37267
+ method: "POST",
37268
+ headers: { "Content-Type": "application/json" },
37269
+ body: JSON.stringify({ name: m.name }),
37270
+ signal: AbortSignal.timeout(2000)
37271
+ });
37272
+ if (showResponse.ok) {
37273
+ const showData = await showResponse.json();
37274
+ capabilities = showData.capabilities || [];
37275
+ }
37276
+ } catch {}
37277
+ }
37278
+ const nameLower = String(m.name).toLowerCase();
37279
+ const supportsTools = capabilities.includes("tools");
37280
+ const isEmbeddingModel = capabilities.includes("embedding") || nameLower.includes("embed");
37281
+ const sizeInfo = m.details?.parameter_size || "unknown size";
37282
+ const toolsIndicator = supportsTools ? "\u2713 tools" : "\u2717 no tools";
37283
+ return {
37284
+ id: `ollama/${m.name}`,
37285
+ name: m.name,
37286
+ description: `Local Ollama model (${sizeInfo}, ${toolsIndicator})`,
37287
+ provider: "ollama",
37288
+ pricing: { prompt: "0", completion: "0" },
37289
+ isLocal: true,
37290
+ supportsTools,
37291
+ isEmbeddingModel,
37292
+ capabilities,
37293
+ details: m.details,
37294
+ size: m.size
37295
+ };
37296
+ }));
37297
+ return enriched.filter((m) => !m.isEmbeddingModel);
37298
+ } catch {
37299
+ return [];
37300
+ }
37301
+ }
37302
+
37303
+ // src/behavior/observer/client.ts
37304
+ var init_client = __esm(() => {
37305
+ init_logger();
37306
+ });
37307
+
37308
+ // src/behavior/observer/corpus.ts
37309
+ var WRITE_TOOLS2;
37310
+ var init_corpus = __esm(() => {
37311
+ WRITE_TOOLS2 = new Set(["Write", "Edit", "NotebookEdit"]);
37312
+ });
37313
+
37314
+ // src/behavior/index.ts
37315
+ function createBehaviorEngine(rawConfig, extraRules = []) {
37316
+ return new BehaviorEngine(parseBehaviorConfig(rawConfig), [...BUILTIN_RULES, ...extraRules]);
37317
+ }
37318
+ function getBehaviorEngine() {
37319
+ if (!sharedEngine) {
37320
+ sharedEngine = createBehaviorEngine(loadConfig().behavior, hookRules);
37321
+ }
37322
+ return sharedEngine;
37323
+ }
37324
+ function registerHookRules(rules) {
37325
+ hookRules = [...hookRules, ...rules];
37326
+ sharedEngine = null;
37327
+ }
37328
+ var BUILTIN_RULES, sharedEngine = null, hookRules;
37329
+ var init_behavior = __esm(() => {
37330
+ init_profile_config();
37331
+ init_config();
37332
+ init_engine();
37333
+ init_plan_mode();
37334
+ init_engine();
37335
+ init_config();
37336
+ init_harness();
37337
+ init_hooks();
37338
+ init_digest();
37339
+ init_client();
37340
+ init_corpus();
37341
+ BUILTIN_RULES = [...PLAN_MODE_RULES];
37342
+ hookRules = [];
37343
+ });
37344
+
36793
37345
  // src/middleware/manager.ts
36794
37346
  class MiddlewareManager {
36795
37347
  middlewares = [];
@@ -37112,7 +37664,7 @@ class OpenAIProviderTransport {
37112
37664
  delayMs = 500 * (attempt + 1);
37113
37665
  }
37114
37666
  log(`[${this.displayName}] 429 rate limited, retry ${attempt + 1}/${maxRetries} in ${(delayMs / 1000).toFixed(1)}s`);
37115
- await new Promise((resolve) => setTimeout(resolve, delayMs));
37667
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
37116
37668
  continue;
37117
37669
  }
37118
37670
  return response;
@@ -37831,11 +38383,11 @@ async function runConsentPrompt(ctx) {
37831
38383
  Does NOT send: prompts, paths, API keys, or credentials.
37832
38384
  Disable anytime: claudish telemetry off
37833
38385
  `);
37834
- const answer = await new Promise((resolve) => {
38386
+ const answer = await new Promise((resolve2) => {
37835
38387
  const rl = createInterface({ input: process.stdin, output: process.stderr });
37836
38388
  rl.question("Send anonymous error report? [y/N] ", (ans) => {
37837
38389
  rl.close();
37838
- resolve(ans.trim().toLowerCase());
38390
+ resolve2(ans.trim().toLowerCase());
37839
38391
  });
37840
38392
  });
37841
38393
  const accepted = answer === "y" || answer === "yes";
@@ -38576,8 +39128,8 @@ async function sniffResponsesStreamHead(response, opts = {}) {
38576
39128
  return { kind: "clean", response: replayResponse() };
38577
39129
  }
38578
39130
  let timer;
38579
- const timeout = new Promise((resolve) => {
38580
- timer = setTimeout(() => resolve("timeout"), remaining);
39131
+ const timeout = new Promise((resolve2) => {
39132
+ timer = setTimeout(() => resolve2("timeout"), remaining);
38581
39133
  });
38582
39134
  let result;
38583
39135
  try {
@@ -39306,6 +39858,7 @@ function createResponsesStreamHandler(c, response, opts) {
39306
39858
  let lastActivity = Date.now();
39307
39859
  let pingInterval = null;
39308
39860
  let isClosed = false;
39861
+ const streamMetadata = new Map;
39309
39862
  const functionCalls = new Map;
39310
39863
  const openToolBlocks = new Set;
39311
39864
  const stream = new ReadableStream({
@@ -39337,6 +39890,13 @@ data: ${JSON.stringify(data)}
39337
39890
  };
39338
39891
  const closeTools = () => {
39339
39892
  for (const fnCall of openToolBlocks) {
39893
+ if (fnCall.buffered && fnCall.arguments) {
39894
+ send("content_block_delta", {
39895
+ type: "content_block_delta",
39896
+ index: fnCall.index,
39897
+ delta: { type: "input_json_delta", partial_json: fnCall.arguments }
39898
+ });
39899
+ }
39340
39900
  send("content_block_stop", { type: "content_block_stop", index: fnCall.index });
39341
39901
  }
39342
39902
  openToolBlocks.clear();
@@ -39383,6 +39943,14 @@ data: ${JSON.stringify(data)}
39383
39943
  }
39384
39944
  try {
39385
39945
  const event = JSON.parse(data);
39946
+ if (opts.middlewareManager) {
39947
+ await opts.middlewareManager.afterStreamChunk({
39948
+ modelId: opts.modelName,
39949
+ chunk: event,
39950
+ delta: event,
39951
+ metadata: streamMetadata
39952
+ });
39953
+ }
39386
39954
  if (getLogLevel() === "debug" && event.type) {
39387
39955
  log(`[ResponsesSSE] Event: ${event.type}`);
39388
39956
  }
@@ -39414,7 +39982,8 @@ data: ${JSON.stringify(data)}
39414
39982
  name: fnName,
39415
39983
  arguments: "",
39416
39984
  index: curIdx++,
39417
- claudeId: callId
39985
+ claudeId: callId,
39986
+ buffered: opts.shouldBufferTool?.(fnName) === true
39418
39987
  };
39419
39988
  functionCalls.set(openaiCallId, fnCallData);
39420
39989
  if (itemId && itemId !== openaiCallId) {
@@ -39463,11 +40032,13 @@ data: ${JSON.stringify(data)}
39463
40032
  const fnCall = functionCalls.get(callId);
39464
40033
  if (fnCall) {
39465
40034
  fnCall.arguments += event.delta || "";
39466
- send("content_block_delta", {
39467
- type: "content_block_delta",
39468
- index: fnCall.index,
39469
- delta: { type: "input_json_delta", partial_json: event.delta || "" }
39470
- });
40035
+ if (!fnCall.buffered) {
40036
+ send("content_block_delta", {
40037
+ type: "content_block_delta",
40038
+ index: fnCall.index,
40039
+ delta: { type: "input_json_delta", partial_json: event.delta || "" }
40040
+ });
40041
+ }
39471
40042
  }
39472
40043
  } else if (event.type === "response.output_item.done") {
39473
40044
  if (event.item?.type === "reasoning" && event.item.encrypted_content) {
@@ -39482,6 +40053,23 @@ data: ${JSON.stringify(data)}
39482
40053
  const callId = event.item.call_id || event.item.id;
39483
40054
  const fnCall = functionCalls.get(callId) || functionCalls.get(event.item.id);
39484
40055
  if (fnCall && openToolBlocks.has(fnCall)) {
40056
+ if (fnCall.buffered) {
40057
+ let finalArgs = fnCall.arguments;
40058
+ try {
40059
+ const repaired = opts.onToolCall?.(fnCall.name, finalArgs);
40060
+ if (typeof repaired === "string" && repaired !== finalArgs) {
40061
+ log(`[ResponsesSSE] tool call repaired: ${fnCall.name}`);
40062
+ finalArgs = repaired;
40063
+ }
40064
+ } catch (err) {
40065
+ log(`[ResponsesSSE] onToolCall threw for ${fnCall.name}: ${err}`);
40066
+ }
40067
+ send("content_block_delta", {
40068
+ type: "content_block_delta",
40069
+ index: fnCall.index,
40070
+ delta: { type: "input_json_delta", partial_json: finalArgs }
40071
+ });
40072
+ }
39485
40073
  send("content_block_stop", { type: "content_block_stop", index: fnCall.index });
39486
40074
  openToolBlocks.delete(fnCall);
39487
40075
  }
@@ -39573,6 +40161,9 @@ data: ${JSON.stringify(data)}
39573
40161
  isClosed = true;
39574
40162
  if (opts.onTokenUpdate)
39575
40163
  opts.onTokenUpdate(inputTokens, outputTokens);
40164
+ if (opts.middlewareManager) {
40165
+ await opts.middlewareManager.afterStreamComplete(opts.modelName, streamMetadata);
40166
+ }
39576
40167
  safeClose();
39577
40168
  } catch (error46) {
39578
40169
  if (pingInterval) {
@@ -39809,6 +40400,7 @@ class ComposedHandler {
39809
40400
  explicitAdapter;
39810
40401
  modelAdapter;
39811
40402
  middlewareManager;
40403
+ behaviorEngine;
39812
40404
  tokenTracker;
39813
40405
  targetModel;
39814
40406
  bareModelName;
@@ -39835,6 +40427,7 @@ class ComposedHandler {
39835
40427
  this.middlewareManager.register(new GeminiThoughtSignatureMiddleware);
39836
40428
  }
39837
40429
  this.middlewareManager.initialize().catch((err) => log(`[ComposedHandler:${this.bareModelName}] Middleware init error: ${err}`));
40430
+ this.behaviorEngine = getBehaviorEngine();
39838
40431
  this.tokenTracker = new TokenTracker(port, {
39839
40432
  contextWindow: this.getModelContextWindow(),
39840
40433
  providerName: provider.name,
@@ -39947,6 +40540,22 @@ class ComposedHandler {
39947
40540
  log(`[${this.provider.displayName}] Tools: ${toolNames}`);
39948
40541
  }
39949
40542
  }
40543
+ await this.middlewareManager.beforeRequest({
40544
+ modelId: this.bareModelName,
40545
+ messages,
40546
+ tools,
40547
+ stream: true,
40548
+ claudeRequest,
40549
+ claudeTools: claudeRequest.tools ?? []
40550
+ });
40551
+ const behaviorSession = this.behaviorEngine.startSession({
40552
+ modelId: this.bareModelName,
40553
+ providerName: this.provider.name,
40554
+ isNativeAnthropic: /^claude[-.]/i.test(this.bareModelName) || this.provider.name === "anthropic"
40555
+ });
40556
+ if (!behaviorSession.isNoop) {
40557
+ behaviorSession.applyRequest(claudeRequest, claudeRequest.tools ?? [], tools, messages);
40558
+ }
39950
40559
  let requestPayload = adapter.buildPayload(claudeRequest, messages, tools);
39951
40560
  const extraFields = this.provider.getExtraPayloadFields?.();
39952
40561
  if (extraFields) {
@@ -39993,12 +40602,6 @@ class ComposedHandler {
39993
40602
  if (this.provider.transformPayload) {
39994
40603
  requestPayload = this.provider.transformPayload(requestPayload);
39995
40604
  }
39996
- await this.middlewareManager.beforeRequest({
39997
- modelId: this.bareModelName,
39998
- messages,
39999
- tools,
40000
- stream: true
40001
- });
40002
40605
  const endpoint = this.provider.getEndpoint(this.targetModel);
40003
40606
  const headers = await this.provider.getHeaders();
40004
40607
  headers["Content-Type"] = "application/json";
@@ -40280,7 +40883,7 @@ class ComposedHandler {
40280
40883
  };
40281
40884
  return this.handleStream(c, response, adapter, claudeRequest, toolNameMap, onStreamComplete, (code, message) => {
40282
40885
  streamApiError = { code, message };
40283
- });
40886
+ }, behaviorSession);
40284
40887
  }
40285
40888
  async settleResponsesStreamHead(initial, reissue) {
40286
40889
  let response = initial;
@@ -40299,7 +40902,7 @@ class ComposedHandler {
40299
40902
  };
40300
40903
  }
40301
40904
  log(`[${this.provider.displayName}] in-stream ${verdict.code} before any output \u2014 ` + `retry ${attempt + 1}/${STREAM_RETRY_DELAYS_MS.length} in ${delayMs / 1000}s`);
40302
- await new Promise((resolve) => setTimeout(resolve, delayMs));
40905
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
40303
40906
  let next;
40304
40907
  try {
40305
40908
  next = await reissue();
@@ -40328,7 +40931,7 @@ class ComposedHandler {
40328
40931
  resolveStreamFormat() {
40329
40932
  return this.provider.overrideStreamFormat?.() ?? this.explicitAdapter?.getStreamFormat() ?? this.modelAdapter?.getStreamFormat() ?? this.getAdapter().getStreamFormat();
40330
40933
  }
40331
- handleStream(c, response, adapter, claudeRequest, toolNameMap, onComplete, onApiError) {
40934
+ handleStream(c, response, adapter, claudeRequest, toolNameMap, onComplete, onApiError, behaviorSession) {
40332
40935
  let pendingOnComplete = onComplete;
40333
40936
  const onTokenUpdate = (input, output) => {
40334
40937
  const strategy = this.options.tokenStrategy || "standard";
@@ -40365,7 +40968,10 @@ class ComposedHandler {
40365
40968
  toolNameMap: adapter.getToolNameMap(),
40366
40969
  contextWindow: lookupModelForProvider(this.bareModelName, this.provider.name),
40367
40970
  onApiError,
40368
- priorInputTokens
40971
+ priorInputTokens,
40972
+ middlewareManager: this.middlewareManager,
40973
+ shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40974
+ onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null
40369
40975
  });
40370
40976
  case "anthropic-sse":
40371
40977
  return createAnthropicPassthroughStream(c, response, {
@@ -40461,6 +41067,7 @@ var STREAM_RETRY_DELAYS_MS;
40461
41067
  var init_composed_handler = __esm(() => {
40462
41068
  init_dialect_manager();
40463
41069
  init_logger();
41070
+ init_behavior();
40464
41071
  init_middleware();
40465
41072
  init_openai();
40466
41073
  init_vision_proxy();
@@ -41258,12 +41865,6 @@ var init_api_key_map = __esm(() => {
41258
41865
  };
41259
41866
  });
41260
41867
 
41261
- // ../../node_modules/.bun/zod@4.1.13/node_modules/zod/index.js
41262
- var init_zod = __esm(() => {
41263
- init_external2();
41264
- init_external2();
41265
- });
41266
-
41267
41868
  // src/adapters/anthropic-api-format.ts
41268
41869
  var AnthropicAPIFormat;
41269
41870
  var init_anthropic_api_format = __esm(() => {
@@ -41541,7 +42142,7 @@ class AnthropicProviderTransport {
41541
42142
  delayMs = 500 * (attempt + 1);
41542
42143
  }
41543
42144
  log(`[${this.displayName}] 429 rate limited, retry ${attempt + 1}/${maxRetries} in ${(delayMs / 1000).toFixed(1)}s`);
41544
- await new Promise((resolve) => setTimeout(resolve, delayMs));
42145
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
41545
42146
  continue;
41546
42147
  }
41547
42148
  return response;
@@ -41722,14 +42323,14 @@ async function discoverViaOllama(baseUrl, cacheKey) {
41722
42323
  let connectionError;
41723
42324
  let loadedRaw = [];
41724
42325
  try {
41725
- loadedRaw = await fetchOllamaModels(`${baseUrl}/api/ps`);
42326
+ loadedRaw = await fetchOllamaModels2(`${baseUrl}/api/ps`);
41726
42327
  } catch (e) {
41727
42328
  connectionError = classifyFetchError(e, `${baseUrl}/api/ps`);
41728
42329
  }
41729
42330
  let allRaw = loadedRaw;
41730
42331
  if (allRaw.length === 0) {
41731
42332
  try {
41732
- allRaw = await fetchOllamaModels(`${baseUrl}/api/tags`);
42333
+ allRaw = await fetchOllamaModels2(`${baseUrl}/api/tags`);
41733
42334
  } catch (e) {
41734
42335
  connectionError ??= classifyFetchError(e, `${baseUrl}/api/tags`);
41735
42336
  }
@@ -41840,7 +42441,7 @@ function extractLMStudioModels(body) {
41840
42441
  }
41841
42442
  return out;
41842
42443
  }
41843
- async function fetchOllamaModels(url2) {
42444
+ async function fetchOllamaModels2(url2) {
41844
42445
  const response = await fetch(url2, {
41845
42446
  method: "GET",
41846
42447
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
@@ -42272,7 +42873,7 @@ var init_ollama_api_format = __esm(() => {
42272
42873
  // src/providers/api-key-provenance.ts
42273
42874
  import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
42274
42875
  import { homedir as homedir18 } from "os";
42275
- import { join as join18, resolve } from "path";
42876
+ import { join as join18, resolve as resolve2 } from "path";
42276
42877
  function activeConfigPath() {
42277
42878
  return activeGlobalConfigFile(join18(homedir18(), ".claudish", "config.json"));
42278
42879
  }
@@ -42292,7 +42893,7 @@ function resolveApiKeyProvenance(envVar, aliases) {
42292
42893
  const allVars = [envVar, ...aliases || []];
42293
42894
  const dotenvValue = readDotenvKey(allVars);
42294
42895
  layers.push({
42295
- source: `.env (${resolve(".env")})`,
42896
+ source: `.env (${resolve2(".env")})`,
42296
42897
  maskedValue: maskKey(dotenvValue),
42297
42898
  isActive: false
42298
42899
  });
@@ -42350,7 +42951,7 @@ function formatProvenanceLog(p) {
42350
42951
  }
42351
42952
  function readDotenvKey(envVars) {
42352
42953
  try {
42353
- const dotenvPath = resolve(".env");
42954
+ const dotenvPath = resolve2(".env");
42354
42955
  if (!existsSync15(dotenvPath))
42355
42956
  return null;
42356
42957
  const parsed = import_dotenv.parse(readFileSync12(dotenvPath, "utf-8"));
@@ -42407,10 +43008,10 @@ class GeminiRequestQueue {
42407
43008
  log(`[GeminiQueue] Queue full (${this.queue.length}/${this.maxQueueSize}), rejecting request`);
42408
43009
  throw new Error("Gemini request queue full. Please retry later.");
42409
43010
  }
42410
- return new Promise((resolve2, reject) => {
43011
+ return new Promise((resolve3, reject) => {
42411
43012
  const queuedRequest = {
42412
43013
  fetchFn,
42413
- resolve: resolve2,
43014
+ resolve: resolve3,
42414
43015
  reject
42415
43016
  };
42416
43017
  this.queue.push(queuedRequest);
@@ -42466,7 +43067,7 @@ class GeminiRequestQueue {
42466
43067
  if (timeSinceLastRequest < delayMs) {
42467
43068
  const waitMs = delayMs - timeSinceLastRequest;
42468
43069
  log(`[GeminiQueue] Waiting ${waitMs}ms before next request`);
42469
- await new Promise((resolve2) => setTimeout(resolve2, waitMs));
43070
+ await new Promise((resolve3) => setTimeout(resolve3, waitMs));
42470
43071
  }
42471
43072
  }
42472
43073
  handleRateLimitResponse(errorText) {
@@ -43264,10 +43865,10 @@ class LocalModelQueue {
43264
43865
  }
43265
43866
  throw new Error(`Local model queue full (${this.queue.length}/${this.maxQueueSize}). GPU is overloaded. Please wait for current requests to complete.`);
43266
43867
  }
43267
- return new Promise((resolve2, reject) => {
43868
+ return new Promise((resolve3, reject) => {
43268
43869
  const queuedRequest = {
43269
43870
  fetchFn,
43270
- resolve: resolve2,
43871
+ resolve: resolve3,
43271
43872
  reject,
43272
43873
  providerId
43273
43874
  };
@@ -43362,7 +43963,7 @@ class LocalModelQueue {
43362
43963
  return parsed;
43363
43964
  }
43364
43965
  delay(ms) {
43365
- return new Promise((resolve2) => setTimeout(resolve2, ms));
43966
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
43366
43967
  }
43367
43968
  getStats() {
43368
43969
  return {
@@ -43664,10 +44265,10 @@ class OpenRouterRequestQueue {
43664
44265
  }
43665
44266
  throw new Error(`OpenRouter request queue full (${this.queue.length}/${this.maxQueueSize}). The API is rate-limited. Please wait and try again.`);
43666
44267
  }
43667
- return new Promise((resolve2, reject) => {
44268
+ return new Promise((resolve3, reject) => {
43668
44269
  const queuedRequest = {
43669
44270
  fetchFn,
43670
- resolve: resolve2,
44271
+ resolve: resolve3,
43671
44272
  reject
43672
44273
  };
43673
44274
  this.queue.push(queuedRequest);
@@ -43735,7 +44336,7 @@ class OpenRouterRequestQueue {
43735
44336
  if (getLogLevel() === "debug") {
43736
44337
  log(`[OpenRouterQueue] Waiting ${waitMs}ms before next request`);
43737
44338
  }
43738
- await new Promise((resolve2) => setTimeout(resolve2, waitMs));
44339
+ await new Promise((resolve3) => setTimeout(resolve3, waitMs));
43739
44340
  }
43740
44341
  }
43741
44342
  calculateDelay() {
@@ -44009,6 +44610,13 @@ async function createProxyServer(port, _openrouterApiKey, model, monitorMode = f
44009
44610
  } catch (err) {
44010
44611
  log(`[Proxy] customEndpoints load skipped: ${err instanceof Error ? err.message : String(err)}`);
44011
44612
  }
44613
+ try {
44614
+ const hookRules2 = await loadHookRules(parseBehaviorConfig(loadConfig().behavior).hooks);
44615
+ if (hookRules2.length > 0)
44616
+ registerHookRules(hookRules2);
44617
+ } catch (err) {
44618
+ log(`[Proxy] behavior hooks load skipped: ${err instanceof Error ? err.message : String(err)}`);
44619
+ }
44012
44620
  const nativeHandler = new NativeHandler(anthropicApiKey, options.advisorModels, options.advisorCollector);
44013
44621
  const openRouterHandlers = new Map;
44014
44622
  const localProviderHandlers = new Map;
@@ -44415,6 +45023,8 @@ var init_proxy_server = __esm(() => {
44415
45023
  init_model_loader();
44416
45024
  init_profile_config();
44417
45025
  init_api_key_map();
45026
+ init_behavior();
45027
+ init_hooks();
44418
45028
  init_custom_endpoints_loader();
44419
45029
  init_model_catalog_resolver();
44420
45030
  init_model_parser();
@@ -44457,9 +45067,9 @@ import {
44457
45067
  readdirSync as readdirSync2,
44458
45068
  writeFileSync as writeFileSync11
44459
45069
  } from "fs";
44460
- import { join as join20, resolve as resolve2 } from "path";
45070
+ import { join as join20, resolve as resolve3 } from "path";
44461
45071
  function validateSessionPath(sessionPath) {
44462
- const resolved = resolve2(sessionPath);
45072
+ const resolved = resolve3(sessionPath);
44463
45073
  const cwd = process.cwd();
44464
45074
  if (!resolved.startsWith(`${cwd}/`) && resolved !== cwd) {
44465
45075
  throw new Error(`Session path must be within current directory: ${sessionPath}`);
@@ -44571,7 +45181,7 @@ async function runModels(sessionPath, opts = {}) {
44571
45181
  });
44572
45182
  proc.stdin?.write(inputContent);
44573
45183
  proc.stdin?.end();
44574
- const completionPromise = new Promise((resolve3) => {
45184
+ const completionPromise = new Promise((resolve4) => {
44575
45185
  let exitCode = null;
44576
45186
  let resolved = false;
44577
45187
  const finish = () => {
@@ -44579,7 +45189,7 @@ async function runModels(sessionPath, opts = {}) {
44579
45189
  return;
44580
45190
  if (statusCache.models[anonId].state === "TIMEOUT") {
44581
45191
  resolved = true;
44582
- resolve3();
45192
+ resolve4();
44583
45193
  return;
44584
45194
  }
44585
45195
  resolved = true;
@@ -44599,14 +45209,14 @@ async function runModels(sessionPath, opts = {}) {
44599
45209
  } : undefined
44600
45210
  });
44601
45211
  opts.onStatusChange?.(anonId, statusCache.models[anonId]);
44602
- resolve3();
45212
+ resolve4();
44603
45213
  };
44604
45214
  outputStream.on("close", finish);
44605
45215
  proc.on("exit", (code) => {
44606
45216
  const current = statusCache.models[anonId];
44607
45217
  if (current?.state === "TIMEOUT") {
44608
45218
  resolved = true;
44609
- resolve3();
45219
+ resolve4();
44610
45220
  return;
44611
45221
  }
44612
45222
  if (stderr) {
@@ -44624,7 +45234,7 @@ async function runModels(sessionPath, opts = {}) {
44624
45234
  let timeoutHandle = null;
44625
45235
  await Promise.race([
44626
45236
  Promise.all(completionPromises),
44627
- new Promise((resolve3) => {
45237
+ new Promise((resolve4) => {
44628
45238
  timeoutHandle = setTimeout(() => {
44629
45239
  for (const [id, proc] of processes) {
44630
45240
  const current = statusCache.models[id];
@@ -44638,7 +45248,7 @@ async function runModels(sessionPath, opts = {}) {
44638
45248
  opts.onStatusChange?.(id, statusCache.models[id]);
44639
45249
  }
44640
45250
  }
44641
- resolve3();
45251
+ resolve4();
44642
45252
  }, timeoutMs);
44643
45253
  })
44644
45254
  ]);
@@ -47685,13 +48295,13 @@ var PromisePolyfill;
47685
48295
  var init_promise_polyfill = __esm(() => {
47686
48296
  PromisePolyfill = class PromisePolyfill extends Promise {
47687
48297
  static withResolver() {
47688
- let resolve3;
48298
+ let resolve4;
47689
48299
  let reject;
47690
48300
  const promise3 = new Promise((res, rej) => {
47691
- resolve3 = res;
48301
+ resolve4 = res;
47692
48302
  reject = rej;
47693
48303
  });
47694
- return { promise: promise3, resolve: resolve3, reject };
48304
+ return { promise: promise3, resolve: resolve4, reject };
47695
48305
  }
47696
48306
  };
47697
48307
  });
@@ -47728,7 +48338,7 @@ function createPrompt(view) {
47728
48338
  output
47729
48339
  });
47730
48340
  const screen = new ScreenManager(rl);
47731
- const { promise: promise3, resolve: resolve3, reject } = PromisePolyfill.withResolver();
48341
+ const { promise: promise3, resolve: resolve4, reject } = PromisePolyfill.withResolver();
47732
48342
  const cancel = () => reject(new CancelPromptError);
47733
48343
  if (signal) {
47734
48344
  const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
@@ -47755,7 +48365,7 @@ function createPrompt(view) {
47755
48365
  cycle(() => {
47756
48366
  try {
47757
48367
  const nextView = view(config3, (value) => {
47758
- setImmediate(() => resolve3(value));
48368
+ setImmediate(() => resolve4(value));
47759
48369
  });
47760
48370
  if (nextView === undefined) {
47761
48371
  const callerFilename = callSites[1]?.getFileName();
@@ -53596,7 +54206,7 @@ var require_lib2 = __commonJS((exports) => {
53596
54206
  return matches;
53597
54207
  };
53598
54208
  exports.analyse = analyse;
53599
- var detectFile = (filepath, opts = {}) => new Promise((resolve3, reject) => {
54209
+ var detectFile = (filepath, opts = {}) => new Promise((resolve4, reject) => {
53600
54210
  let fd;
53601
54211
  const fs = (0, node_1.default)();
53602
54212
  const handler = (err, buffer) => {
@@ -53606,7 +54216,7 @@ var require_lib2 = __commonJS((exports) => {
53606
54216
  if (err) {
53607
54217
  reject(err);
53608
54218
  } else if (buffer) {
53609
- resolve3((0, exports.detect)(buffer));
54219
+ resolve4((0, exports.detect)(buffer));
53610
54220
  } else {
53611
54221
  reject(new Error("No error and no buffer received"));
53612
54222
  }
@@ -58867,7 +59477,7 @@ __export(exports_config, {
58867
59477
  DEFAULT_PORT_RANGE: () => DEFAULT_PORT_RANGE
58868
59478
  });
58869
59479
  var DEFAULT_PORT_RANGE, ENV, OPENROUTER_API_URL2 = "https://openrouter.ai/api/v1/chat/completions", OPENROUTER_HEADERS;
58870
- var init_config = __esm(() => {
59480
+ var init_config2 = __esm(() => {
58871
59481
  DEFAULT_PORT_RANGE = { start: 3000, end: 9000 };
58872
59482
  ENV = {
58873
59483
  OPENROUTER_API_KEY: "OPENROUTER_API_KEY",
@@ -59162,62 +59772,6 @@ var init_model_discovery = __esm(() => {
59162
59772
  _cache2 = new Map;
59163
59773
  });
59164
59774
 
59165
- // src/providers/ollama-discovery.ts
59166
- function ollamaBaseUrl() {
59167
- return process.env.OLLAMA_HOST || process.env.OLLAMA_BASE_URL || "http://localhost:11434";
59168
- }
59169
- async function fetchOllamaModels2(options = {}) {
59170
- const { enrichCapabilities = true } = options;
59171
- const host = ollamaBaseUrl();
59172
- try {
59173
- const response = await fetch(`${host}/api/tags`, {
59174
- signal: AbortSignal.timeout(3000)
59175
- });
59176
- if (!response.ok)
59177
- return [];
59178
- const data = await response.json();
59179
- const models = data.models || [];
59180
- const enriched = await Promise.all(models.map(async (m) => {
59181
- let capabilities = [];
59182
- if (enrichCapabilities) {
59183
- try {
59184
- const showResponse = await fetch(`${host}/api/show`, {
59185
- method: "POST",
59186
- headers: { "Content-Type": "application/json" },
59187
- body: JSON.stringify({ name: m.name }),
59188
- signal: AbortSignal.timeout(2000)
59189
- });
59190
- if (showResponse.ok) {
59191
- const showData = await showResponse.json();
59192
- capabilities = showData.capabilities || [];
59193
- }
59194
- } catch {}
59195
- }
59196
- const nameLower = String(m.name).toLowerCase();
59197
- const supportsTools = capabilities.includes("tools");
59198
- const isEmbeddingModel = capabilities.includes("embedding") || nameLower.includes("embed");
59199
- const sizeInfo = m.details?.parameter_size || "unknown size";
59200
- const toolsIndicator = supportsTools ? "\u2713 tools" : "\u2717 no tools";
59201
- return {
59202
- id: `ollama/${m.name}`,
59203
- name: m.name,
59204
- description: `Local Ollama model (${sizeInfo}, ${toolsIndicator})`,
59205
- provider: "ollama",
59206
- pricing: { prompt: "0", completion: "0" },
59207
- isLocal: true,
59208
- supportsTools,
59209
- isEmbeddingModel,
59210
- capabilities,
59211
- details: m.details,
59212
- size: m.size
59213
- };
59214
- }));
59215
- return enriched.filter((m) => !m.isEmbeddingModel);
59216
- } catch {
59217
- return [];
59218
- }
59219
- }
59220
-
59221
59775
  // src/model-selector.ts
59222
59776
  var exports_model_selector = {};
59223
59777
  __export(exports_model_selector, {
@@ -59749,7 +60303,7 @@ async function selectModelFromProvider(provider, tierName, recommendedModels, _f
59749
60303
  }
59750
60304
  }
59751
60305
  if (provider === "ollama") {
59752
- const ollamaModels = await fetchOllamaModels2({ enrichCapabilities: false });
60306
+ const ollamaModels = await fetchOllamaModels({ enrichCapabilities: false });
59753
60307
  const chatModels = ollamaModels.map((m) => ({
59754
60308
  id: m.name,
59755
60309
  name: m.name,
@@ -62688,8 +63242,8 @@ async function startProbeTui(initial) {
62688
63242
  });
62689
63243
  const store = new ProbeStore(initial);
62690
63244
  let resolveQuit;
62691
- const quitPromise = new Promise((resolve3) => {
62692
- resolveQuit = resolve3;
63245
+ const quitPromise = new Promise((resolve4) => {
63246
+ resolveQuit = resolve4;
62693
63247
  });
62694
63248
  let quit = false;
62695
63249
  const onQuit = () => {
@@ -63300,7 +63854,7 @@ Local providers`);
63300
63854
  console.log(` ${"\u2500".repeat(70)}`);
63301
63855
  let ollamaLine = " Ollama: not running";
63302
63856
  try {
63303
- const ollamaModels = await fetchOllamaModels2();
63857
+ const ollamaModels = await fetchOllamaModels();
63304
63858
  if (ollamaModels.length > 0) {
63305
63859
  const toolCount = ollamaModels.filter((m) => m.supportsTools).length;
63306
63860
  ollamaLine = ` Ollama: ${ollamaModels.length} models installed (${toolCount} with tools) \u2014 use: claudish --model ollama@<name>`;
@@ -64358,7 +64912,7 @@ function printAvailableModels() {
64358
64912
  }
64359
64913
  var __filename3, __dirname3;
64360
64914
  var init_cli = __esm(() => {
64361
- init_config();
64915
+ init_config2();
64362
64916
  init_model_loader();
64363
64917
  init_model_selector();
64364
64918
  init_probe_results_printer();
@@ -64474,7 +65028,7 @@ async function fetchLatestVersionOrThrow(options = {}) {
64474
65028
  } catch (error46) {
64475
65029
  lastError = error46 instanceof Error && error46.name === "AbortError" ? new Error(`request timed out after ${timeoutMs}ms`) : error46 instanceof Error ? error46 : new Error(String(error46));
64476
65030
  if (attempt < retries) {
64477
- await new Promise((resolve3) => setTimeout(resolve3, 300 * (attempt + 1)));
65031
+ await new Promise((resolve4) => setTimeout(resolve4, 300 * (attempt + 1)));
64478
65032
  }
64479
65033
  } finally {
64480
65034
  clearTimeout(timeout);
@@ -70207,14 +70761,14 @@ function App({ requestLogin } = {}) {
70207
70761
  return resolveSdkAuth({
70208
70762
  interactive: true,
70209
70763
  configAccount: readOnepasswordAccount(),
70210
- onNeedsPicker: (accounts) => new Promise((resolve3) => {
70764
+ onNeedsPicker: (accounts) => new Promise((resolve4) => {
70211
70765
  setOpAccounts(accounts);
70212
70766
  setOpAccountCursor(0);
70213
70767
  opPickerResolver.current = (url2) => {
70214
70768
  if (url2?.trim())
70215
70769
  saveOnepasswordAccount(url2.trim(), "global");
70216
70770
  opPickerResolver.current = null;
70217
- resolve3(url2);
70771
+ resolve4(url2);
70218
70772
  };
70219
70773
  setMode("pick_op_account");
70220
70774
  })
@@ -71660,8 +72214,8 @@ async function startConfigTui() {
71660
72214
  const renderer = await createCliRenderer2({
71661
72215
  exitOnCtrlC: false
71662
72216
  });
71663
- await new Promise((resolve3) => {
71664
- renderer.once("destroy", () => resolve3());
72217
+ await new Promise((resolve4) => {
72218
+ renderer.once("destroy", () => resolve4());
71665
72219
  createRoot2(renderer).render(/* @__PURE__ */ jsxDEV17(App, {
71666
72220
  requestLogin
71667
72221
  }, undefined, false, undefined, this));
@@ -72211,10 +72765,10 @@ Or set CLAUDE_PATH to your custom installation:`);
72211
72765
  });
72212
72766
  }
72213
72767
  setupSignalHandlers(proc, tempSettingsPath, config3.quiet, onCleanup);
72214
- const exitCode = await new Promise((resolve3) => {
72768
+ const exitCode = await new Promise((resolve4) => {
72215
72769
  proc.on("exit", (code) => {
72216
72770
  setClaudeCodeRunning(false);
72217
- resolve3(code ?? 1);
72771
+ resolve4(code ?? 1);
72218
72772
  });
72219
72773
  });
72220
72774
  releaseTerminalIsolation();
@@ -72294,9 +72848,9 @@ async function findClaudeBinary() {
72294
72848
  proc.stdout?.on("data", (data) => {
72295
72849
  output += data.toString();
72296
72850
  });
72297
- const exitCode = await new Promise((resolve3) => {
72851
+ const exitCode = await new Promise((resolve4) => {
72298
72852
  proc.on("exit", (code) => {
72299
- resolve3(code ?? 1);
72853
+ resolve4(code ?? 1);
72300
72854
  });
72301
72855
  });
72302
72856
  if (exitCode === 0 && output.trim()) {
@@ -72319,7 +72873,7 @@ async function checkClaudeInstalled() {
72319
72873
  var restoreTerminal = null, MIN_AUTO_COMPACT_WINDOW = 200000;
72320
72874
  var init_claude_runner = __esm(() => {
72321
72875
  init_model_catalog();
72322
- init_config();
72876
+ init_config2();
72323
72877
  init_logger();
72324
72878
  init_profile_config();
72325
72879
  init_model_discovery();
@@ -72686,9 +73240,9 @@ async function subscribeToMagmux(sockPath, onEvent) {
72686
73240
  for (let attempt = 0;attempt < 40; attempt++) {
72687
73241
  if (existsSync24(sockPath)) {
72688
73242
  try {
72689
- client = await new Promise((resolve3, reject) => {
73243
+ client = await new Promise((resolve4, reject) => {
72690
73244
  const s = netConnect(sockPath);
72691
- s.once("connect", () => resolve3(s));
73245
+ s.once("connect", () => resolve4(s));
72692
73246
  s.once("error", reject);
72693
73247
  });
72694
73248
  break;
@@ -72699,7 +73253,7 @@ async function subscribeToMagmux(sockPath, onEvent) {
72699
73253
  if (!client) {
72700
73254
  return { results: null, client: null };
72701
73255
  }
72702
- return await new Promise((resolve3) => {
73256
+ return await new Promise((resolve4) => {
72703
73257
  let buf = "";
72704
73258
  let finalResults = null;
72705
73259
  client.on("data", (chunk) => {
@@ -72722,7 +73276,7 @@ async function subscribeToMagmux(sockPath, onEvent) {
72722
73276
  } catch {}
72723
73277
  }
72724
73278
  });
72725
- const done = () => resolve3({ results: finalResults, client });
73279
+ const done = () => resolve4({ results: finalResults, client });
72726
73280
  client.once("end", done);
72727
73281
  client.once("close", done);
72728
73282
  client.once("error", done);
@@ -72798,9 +73352,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
72798
73352
  });
72799
73353
  const sockPath = `/tmp/magmux-${proc.pid}.sock`;
72800
73354
  const subscription = subscribeToMagmux(sockPath);
72801
- const procExit = new Promise((resolve3) => {
72802
- proc.on("exit", () => resolve3());
72803
- proc.on("error", () => resolve3());
73355
+ const procExit = new Promise((resolve4) => {
73356
+ proc.on("exit", () => resolve4());
73357
+ proc.on("error", () => resolve4());
72804
73358
  });
72805
73359
  const [{ results }] = await Promise.all([subscription, procExit]);
72806
73360
  const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
@@ -72829,7 +73383,7 @@ init_op_source();
72829
73383
  init_startup_trace();
72830
73384
  var import_dotenv3 = __toESM(require_main(), 1);
72831
73385
  import { existsSync as existsSync25, readFileSync as readFileSync23 } from "fs";
72832
- import { join as join28, resolve as resolve3 } from "path";
73386
+ import { join as join28, resolve as resolve4 } from "path";
72833
73387
  import_dotenv3.config({ quiet: true });
72834
73388
  function classifyStartupKind() {
72835
73389
  const argv = process.argv.slice(2);
@@ -72927,7 +73481,7 @@ async function applyOpImport() {
72927
73481
  async function applyConfigOverride() {
72928
73482
  const { planConfigOverride: planConfigOverride2, setConfigFileOverride: setConfigFileOverride2 } = await Promise.resolve().then(() => exports_config_override);
72929
73483
  const plan = planConfigOverride2(process.argv.slice(2), process.env, {
72930
- resolve: resolve3,
73484
+ resolve: resolve4,
72931
73485
  exists: existsSync25
72932
73486
  });
72933
73487
  if (plan.kind === "none")
@@ -73036,7 +73590,7 @@ async function runCli() {
73036
73590
  const endImports = beginSpan("startup:cli-imports");
73037
73591
  const { checkClaudeInstalled: checkClaudeInstalled2, runClaudeWithProxy: runClaudeWithProxy2 } = await Promise.resolve().then(() => (init_claude_runner(), exports_claude_runner));
73038
73592
  const { parseArgs: parseArgs2, getVersion: getVersion4 } = await Promise.resolve().then(() => (init_cli(), exports_cli));
73039
- const { DEFAULT_PORT_RANGE: DEFAULT_PORT_RANGE2 } = await Promise.resolve().then(() => (init_config(), exports_config));
73593
+ const { DEFAULT_PORT_RANGE: DEFAULT_PORT_RANGE2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
73040
73594
  const { selectModel: selectModel2, promptForApiKey: promptForApiKey2 } = await Promise.resolve().then(() => (init_model_selector(), exports_model_selector));
73041
73595
  const {
73042
73596
  resolveModelProvider: resolveModelProvider2,
@@ -73129,11 +73683,11 @@ Team Status`);
73129
73683
  You can disable it anytime with: --no-auto-approve
73130
73684
 
73131
73685
  `);
73132
- const answer = await new Promise((resolve4) => {
73686
+ const answer = await new Promise((resolve5) => {
73133
73687
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
73134
73688
  rl.question("Enable auto-approve? [Y/n] ", (ans) => {
73135
73689
  rl.close();
73136
- resolve4(ans.trim().toLowerCase());
73690
+ resolve5(ans.trim().toLowerCase());
73137
73691
  });
73138
73692
  });
73139
73693
  const declined = answer === "n" || answer === "no";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "7.24.0",
3
+ "version": "7.25.0",
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",
@@ -60,10 +60,10 @@
60
60
  "ai"
61
61
  ],
62
62
  "optionalDependencies": {
63
- "@claudish/magmux-darwin-arm64": "7.24.0",
64
- "@claudish/magmux-darwin-x64": "7.24.0",
65
- "@claudish/magmux-linux-arm64": "7.24.0",
66
- "@claudish/magmux-linux-x64": "7.24.0"
63
+ "@claudish/magmux-darwin-arm64": "7.25.0",
64
+ "@claudish/magmux-darwin-x64": "7.25.0",
65
+ "@claudish/magmux-linux-arm64": "7.25.0",
66
+ "@claudish/magmux-linux-x64": "7.25.0"
67
67
  },
68
68
  "author": "Jack Rudenko <i@madappgang.com>",
69
69
  "license": "MIT",