claudish 7.47.0 → 7.49.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 (3) hide show
  1. package/bin/claudish.cjs +69 -25
  2. package/dist/index.js +1192 -438
  3. 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.47.0";
732
+ var VERSION = "7.49.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -28142,7 +28142,7 @@ var init_provider_definitions = __esm(() => {
28142
28142
  name: "minimax",
28143
28143
  displayName: "MiniMax",
28144
28144
  transport: "anthropic",
28145
- baseUrl: "https://api.minimax.io",
28145
+ baseUrl: "https://api.minimaxi.com",
28146
28146
  baseUrlEnvVars: ["MINIMAX_BASE_URL"],
28147
28147
  apiPath: "/anthropic/v1/messages",
28148
28148
  apiKeyEnvVar: "MINIMAX_API_KEY",
@@ -28579,6 +28579,25 @@ var init_provider_definitions = __esm(() => {
28579
28579
  isDirectApi: true,
28580
28580
  description: "Qwen Plan (qc@)"
28581
28581
  },
28582
+ {
28583
+ name: "qwen-payg",
28584
+ displayName: "Qwen PAYG",
28585
+ transport: "anthropic",
28586
+ baseUrl: "https://dashscope-intl.aliyuncs.com",
28587
+ baseUrlEnvVars: ["DASHSCOPE_BASE_URL"],
28588
+ apiPath: "/apps/anthropic/v1/messages",
28589
+ apiKeyEnvVar: "DASHSCOPE_API_KEY",
28590
+ apiKeyAliases: ["QWEN_API_KEY"],
28591
+ apiKeyDescription: "Alibaba Model Studio API Key (pay-as-you-go)",
28592
+ apiKeyUrl: "https://www.alibabacloud.com/help/en/model-studio/get-api-key",
28593
+ authScheme: "bearer",
28594
+ shortcuts: ["qp", "dashscope"],
28595
+ shortestPrefix: "qp",
28596
+ legacyPrefixes: [{ prefix: "qp/", stripPrefix: true }],
28597
+ modelDiscovery: { path: "/compatible-mode/v1/models", format: "openai-models-list" },
28598
+ isDirectApi: true,
28599
+ description: "Alibaba Model Studio pay-as-you-go (qp@)"
28600
+ },
28582
28601
  {
28583
28602
  name: "qwen",
28584
28603
  displayName: "Qwen",
@@ -29621,6 +29640,48 @@ var init_base_api_format = __esm(() => {
29621
29640
  });
29622
29641
 
29623
29642
  // src/adapters/anthropic-api-format.ts
29643
+ function systemText(content) {
29644
+ if (typeof content === "string")
29645
+ return content;
29646
+ if (Array.isArray(content)) {
29647
+ return content.map((block) => typeof block?.text === "string" ? block.text : "").filter(Boolean).join(`
29648
+ `);
29649
+ }
29650
+ return "";
29651
+ }
29652
+ function hoistInlineSystem(messages) {
29653
+ if (!Array.isArray(messages) || !messages.some((m) => m?.role === "system")) {
29654
+ return { messages, hoisted: [] };
29655
+ }
29656
+ const kept = [];
29657
+ const hoisted = [];
29658
+ for (const msg of messages) {
29659
+ if (msg?.role === "system") {
29660
+ const text = systemText(msg.content);
29661
+ if (text)
29662
+ hoisted.push(text);
29663
+ continue;
29664
+ }
29665
+ kept.push(msg);
29666
+ }
29667
+ return { messages: kept, hoisted };
29668
+ }
29669
+ function mergeSystem(existing, hoisted) {
29670
+ if (hoisted.length === 0)
29671
+ return existing;
29672
+ const merged = hoisted.join(`
29673
+
29674
+ `);
29675
+ if (existing === undefined || existing === null || existing === "")
29676
+ return merged;
29677
+ if (Array.isArray(existing))
29678
+ return [...existing, { type: "text", text: merged }];
29679
+ if (typeof existing === "string")
29680
+ return `${existing}
29681
+
29682
+ ${merged}`;
29683
+ return existing;
29684
+ }
29624
29685
  var AnthropicAPIFormat;
29625
29686
  var init_anthropic_api_format = __esm(() => {
29626
29687
  init_base_api_format();
@@ -29665,14 +29726,16 @@ var init_anthropic_api_format = __esm(() => {
29665
29726
  return claudeRequest.tools || [];
29666
29727
  }
29667
29728
  buildPayload(claudeRequest, messages, tools) {
29729
+ const { messages: cleanMessages, hoisted } = hoistInlineSystem(messages);
29668
29730
  const payload = {
29669
29731
  model: this.modelId,
29670
- messages,
29732
+ messages: cleanMessages,
29671
29733
  max_tokens: claudeRequest.max_tokens || 4096,
29672
29734
  stream: true
29673
29735
  };
29674
- if (claudeRequest.system) {
29675
- payload.system = claudeRequest.system;
29736
+ const system = mergeSystem(claudeRequest.system, hoisted);
29737
+ if (system !== undefined) {
29738
+ payload.system = system;
29676
29739
  }
29677
29740
  if (tools.length > 0) {
29678
29741
  payload.tools = tools;
@@ -34865,14 +34928,14 @@ var init_config = __esm(() => {
34865
34928
  });
34866
34929
 
34867
34930
  // src/behavior/harness.ts
34868
- function extractAvailableSkills(systemText) {
34869
- if (!systemText)
34931
+ function extractAvailableSkills(systemText2) {
34932
+ if (!systemText2)
34870
34933
  return [];
34871
- const start = SKILL_SECTION.exec(systemText);
34934
+ const start = SKILL_SECTION.exec(systemText2);
34872
34935
  if (!start)
34873
34936
  return [];
34874
34937
  const out = [];
34875
- const body = systemText.slice(start.index + start[0].length);
34938
+ const body = systemText2.slice(start.index + start[0].length);
34876
34939
  for (const line of body.split(`
34877
34940
  `)) {
34878
34941
  const trimmed2 = line.trim();
@@ -37579,7 +37642,8 @@ var init_telemetry = __esm(() => {
37579
37642
  "minimax-coding",
37580
37643
  "kimi-coding",
37581
37644
  "glm-coding",
37582
- "qwen-cloud"
37645
+ "qwen-cloud",
37646
+ "qwen-payg"
37583
37647
  ]);
37584
37648
  });
37585
37649
 
@@ -38004,6 +38068,170 @@ var init_anthropic_error = __esm(() => {
38004
38068
  CONTROL_CHARS = /[\x00-\x1F\x7F]/g;
38005
38069
  });
38006
38070
 
38071
+ // src/handlers/shared/collect-sse-message.ts
38072
+ function finalizeBlock(block) {
38073
+ if (block.type === "text") {
38074
+ return { type: "text", text: block.text ?? "" };
38075
+ }
38076
+ if (block.type === "thinking") {
38077
+ return {
38078
+ type: "thinking",
38079
+ thinking: block.thinking ?? "",
38080
+ ...block.signature ? { signature: block.signature } : {}
38081
+ };
38082
+ }
38083
+ let input = block.input ?? {};
38084
+ if (block.partialJson !== undefined && block.partialJson !== "") {
38085
+ try {
38086
+ input = JSON.parse(block.partialJson);
38087
+ } catch {
38088
+ log(`[CollectSSE] tool_use ${block.name ?? "?"} had unparseable input, emitting empty object`);
38089
+ input = {};
38090
+ }
38091
+ }
38092
+ return {
38093
+ type: "tool_use",
38094
+ id: block.id ?? "",
38095
+ name: block.name ?? "",
38096
+ input
38097
+ };
38098
+ }
38099
+ async function collectSseMessage(sse, fallbackModel, opts = {}) {
38100
+ const message = {
38101
+ id: `msg_${Date.now()}`,
38102
+ type: "message",
38103
+ role: "assistant",
38104
+ model: fallbackModel,
38105
+ content: [],
38106
+ stop_reason: null,
38107
+ stop_sequence: null,
38108
+ usage: { input_tokens: 0, output_tokens: 0 }
38109
+ };
38110
+ const blocks = new Map;
38111
+ const blockAt = (index) => {
38112
+ let block = blocks.get(index);
38113
+ if (!block) {
38114
+ block = { type: "text", text: "" };
38115
+ blocks.set(index, block);
38116
+ }
38117
+ return block;
38118
+ };
38119
+ if (!sse.body)
38120
+ return message;
38121
+ const reader = sse.body.getReader();
38122
+ const decoder = new TextDecoder;
38123
+ const stallMs = opts.stallTimeoutMs ?? STALL_TIMEOUT_MS;
38124
+ let buffer = "";
38125
+ const handle = (data) => {
38126
+ switch (data?.type) {
38127
+ case "message_start": {
38128
+ const m = data.message ?? {};
38129
+ if (m.id)
38130
+ message.id = m.id;
38131
+ if (m.model)
38132
+ message.model = m.model;
38133
+ if (m.usage?.input_tokens != null)
38134
+ message.usage.input_tokens = m.usage.input_tokens;
38135
+ if (m.usage?.output_tokens != null)
38136
+ message.usage.output_tokens = m.usage.output_tokens;
38137
+ break;
38138
+ }
38139
+ case "content_block_start": {
38140
+ const cb = data.content_block ?? {};
38141
+ const kind = cb.type === "thinking" ? "thinking" : cb.type === "tool_use" ? "tool_use" : "text";
38142
+ blocks.set(data.index, {
38143
+ type: kind,
38144
+ text: kind === "text" ? cb.text ?? "" : undefined,
38145
+ thinking: kind === "thinking" ? cb.thinking ?? "" : undefined,
38146
+ id: cb.id,
38147
+ name: cb.name,
38148
+ partialJson: kind === "tool_use" ? "" : undefined
38149
+ });
38150
+ break;
38151
+ }
38152
+ case "content_block_delta": {
38153
+ const block = blockAt(data.index);
38154
+ const d = data.delta ?? {};
38155
+ if (d.type === "text_delta")
38156
+ block.text = (block.text ?? "") + (d.text ?? "");
38157
+ else if (d.type === "thinking_delta")
38158
+ block.thinking = (block.thinking ?? "") + (d.thinking ?? "");
38159
+ else if (d.type === "signature_delta")
38160
+ block.signature = d.signature;
38161
+ else if (d.type === "input_json_delta")
38162
+ block.partialJson = (block.partialJson ?? "") + (d.partial_json ?? "");
38163
+ break;
38164
+ }
38165
+ case "message_delta": {
38166
+ if (data.delta?.stop_reason !== undefined)
38167
+ message.stop_reason = data.delta.stop_reason;
38168
+ if (data.delta?.stop_sequence !== undefined)
38169
+ message.stop_sequence = data.delta.stop_sequence;
38170
+ if (data.usage?.input_tokens != null)
38171
+ message.usage.input_tokens = data.usage.input_tokens;
38172
+ if (data.usage?.output_tokens != null)
38173
+ message.usage.output_tokens = data.usage.output_tokens;
38174
+ break;
38175
+ }
38176
+ default:
38177
+ break;
38178
+ }
38179
+ };
38180
+ try {
38181
+ while (true) {
38182
+ let timer;
38183
+ const chunk = await Promise.race([
38184
+ reader.read(),
38185
+ new Promise((resolve2) => {
38186
+ timer = setTimeout(() => resolve2("stalled"), stallMs);
38187
+ })
38188
+ ]);
38189
+ clearTimeout(timer);
38190
+ if (chunk === "stalled") {
38191
+ log(`[CollectSSE] no data for ${stallMs}ms \u2014 returning ${blocks.size} block(s) collected so far`);
38192
+ await reader.cancel().catch(() => {});
38193
+ break;
38194
+ }
38195
+ if (chunk.done)
38196
+ break;
38197
+ buffer += decoder.decode(chunk.value, { stream: true });
38198
+ const lines = buffer.split(`
38199
+ `);
38200
+ buffer = lines.pop() ?? "";
38201
+ for (const line of lines) {
38202
+ if (!line.startsWith("data:"))
38203
+ continue;
38204
+ const payload = line.slice(5).trim();
38205
+ if (!payload || payload === "[DONE]")
38206
+ continue;
38207
+ try {
38208
+ handle(JSON.parse(payload));
38209
+ } catch {}
38210
+ }
38211
+ }
38212
+ } catch (e) {
38213
+ log(`[CollectSSE] read failed, keeping ${blocks.size} block(s): ${e}`);
38214
+ }
38215
+ for (const index of Array.from(blocks.keys()).sort((a, b) => a - b)) {
38216
+ message.content.push(finalizeBlock(blocks.get(index)));
38217
+ }
38218
+ if (message.stop_reason === null) {
38219
+ message.stop_reason = message.content.some((b) => b.type === "tool_use") ? "tool_use" : "end_turn";
38220
+ }
38221
+ return message;
38222
+ }
38223
+ async function sseResponseToJson(sse, fallbackModel, opts = {}) {
38224
+ const message = await collectSseMessage(sse, fallbackModel, opts);
38225
+ return new Response(JSON.stringify(message), {
38226
+ status: 200,
38227
+ headers: { "Content-Type": "application/json" }
38228
+ });
38229
+ }
38230
+ var STALL_TIMEOUT_MS = 120000;
38231
+ var init_collect_sse_message = __esm(() => {
38232
+ init_logger();
38233
+ });
38234
+
38007
38235
  // src/handlers/shared/connection-error.ts
38008
38236
  function findConnectionCode(error46) {
38009
38237
  let e = error46;
@@ -38564,6 +38792,31 @@ function createAnthropicPassthroughStream(c, response, opts) {
38564
38792
  const filterThinking = opts.adapter?.shouldFilterThinking() ?? false;
38565
38793
  const interceptToolFrame = createToolRepairInterceptor(opts);
38566
38794
  let pendingEventLine = null;
38795
+ let inputTokens = 0;
38796
+ let outputTokens = 0;
38797
+ let stopReason = null;
38798
+ let sawMessageStart = false;
38799
+ let sawMessageStop = false;
38800
+ let openBlockIndex = null;
38801
+ const noteLifecycle = (data, emittedIndex) => {
38802
+ switch (data?.type) {
38803
+ case "message_start":
38804
+ sawMessageStart = true;
38805
+ break;
38806
+ case "message_stop":
38807
+ sawMessageStop = true;
38808
+ break;
38809
+ case "content_block_start":
38810
+ if (emittedIndex !== null)
38811
+ openBlockIndex = emittedIndex;
38812
+ break;
38813
+ case "content_block_stop":
38814
+ openBlockIndex = null;
38815
+ break;
38816
+ default:
38817
+ break;
38818
+ }
38819
+ };
38567
38820
  const flushPendingEvent = (controller) => {
38568
38821
  if (pendingEventLine !== null && !isClosed) {
38569
38822
  controller.enqueue(encoder.encode(`${pendingEventLine}
@@ -38581,6 +38834,48 @@ function createAnthropicPassthroughStream(c, response, opts) {
38581
38834
  }
38582
38835
  flushPendingEvent(controller);
38583
38836
  controller.enqueue(encoder.encode(out));
38837
+ noteLifecycle(data, typeof data?.index === "number" ? data.index : null);
38838
+ };
38839
+ const finalizeAbandonedStream = (controller) => {
38840
+ if (isClosed || sawMessageStop)
38841
+ return;
38842
+ const send = (event, data) => {
38843
+ controller.enqueue(encoder.encode(`event: ${event}
38844
+ data: ${JSON.stringify(data)}
38845
+
38846
+ `));
38847
+ };
38848
+ try {
38849
+ if (!sawMessageStart) {
38850
+ send("message_start", {
38851
+ type: "message_start",
38852
+ message: {
38853
+ id: `msg_${Date.now()}`,
38854
+ type: "message",
38855
+ role: "assistant",
38856
+ content: [],
38857
+ model: opts.modelName,
38858
+ stop_reason: null,
38859
+ stop_sequence: null,
38860
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens }
38861
+ }
38862
+ });
38863
+ }
38864
+ if (openBlockIndex !== null) {
38865
+ send("content_block_stop", { type: "content_block_stop", index: openBlockIndex });
38866
+ openBlockIndex = null;
38867
+ }
38868
+ send("message_delta", {
38869
+ type: "message_delta",
38870
+ delta: { stop_reason: stopReason ?? "end_turn", stop_sequence: null },
38871
+ usage: {
38872
+ ...inputTokens > 0 ? { input_tokens: inputTokens } : {},
38873
+ output_tokens: outputTokens
38874
+ }
38875
+ });
38876
+ send("message_stop", { type: "message_stop" });
38877
+ sawMessageStop = true;
38878
+ } catch {}
38584
38879
  };
38585
38880
  return c.body(new ReadableStream({
38586
38881
  async start(controller) {
@@ -38601,12 +38896,9 @@ data: {"type":"ping"}
38601
38896
  try {
38602
38897
  const reader = response.body.getReader();
38603
38898
  let buffer = "";
38604
- let inputTokens = 0;
38605
- let outputTokens = 0;
38606
38899
  let totalLines = 0;
38607
38900
  let textChunks = 0;
38608
38901
  let toolUseBlocks = 0;
38609
- let stopReason = null;
38610
38902
  let insideThinkingBlock = false;
38611
38903
  let thinkingBlocksSuppressed = 0;
38612
38904
  let suppressedFrame = false;
@@ -38671,6 +38963,7 @@ data: ${JSON.stringify({
38671
38963
  flushPendingEvent(controller);
38672
38964
  controller.enqueue(encoder.encode(`${modifiedLine}
38673
38965
  `));
38966
+ noteLifecycle(data, reindexed);
38674
38967
  }
38675
38968
  } else {
38676
38969
  enqueueData(controller, data, line);
@@ -38793,6 +39086,13 @@ data: ${JSON.stringify({
38793
39086
  }
38794
39087
  } catch (e) {
38795
39088
  log(`[AnthropicSSE] Stream error: ${e}`);
39089
+ finalizeAbandonedStream(controller);
39090
+ try {
39091
+ opts.onTurnEnd?.();
39092
+ } catch {}
39093
+ try {
39094
+ opts.onTokenUpdate?.(inputTokens, outputTokens);
39095
+ } catch {}
38796
39096
  if (!isClosed) {
38797
39097
  isClosed = true;
38798
39098
  if (pingInterval) {
@@ -40753,9 +41053,12 @@ class ComposedHandler {
40753
41053
  behaviorSession?.noteTurnComplete(this.tokenTracker.getInputTokens());
40754
41054
  } catch {}
40755
41055
  };
40756
- return this.handleStream(c, response, adapter, claudeRequest, toolNameMap, onStreamComplete, (code, message) => {
41056
+ const streamed = this.handleStream(c, response, adapter, claudeRequest, toolNameMap, onStreamComplete, (code, message) => {
40757
41057
  streamApiError = { code, message };
40758
41058
  }, behaviorSession);
41059
+ if (payload?.stream === true)
41060
+ return streamed;
41061
+ return sseResponseToJson(streamed, this.bareModelName);
40759
41062
  }
40760
41063
  async settleResponsesStreamHead(initial, reissue) {
40761
41064
  let response = initial;
@@ -41064,6 +41367,7 @@ var init_composed_handler = __esm(() => {
41064
41367
  init_telemetry();
41065
41368
  init_transform();
41066
41369
  init_anthropic_error();
41370
+ init_collect_sse_message();
41067
41371
  init_connection_error();
41068
41372
  init_devin_stream_head_sniffer();
41069
41373
  init_openai_compat();
@@ -41599,6 +41903,40 @@ function toRosterEntry(model) {
41599
41903
  const { id, ...rest } = model;
41600
41904
  return { wireId: id, ...rest };
41601
41905
  }
41906
+ function recordFailure(failure) {
41907
+ _failures.set(failure.provider, failure);
41908
+ log(`[model-discovery:${failure.provider}] ${describeDiscoveryFailure(failure)}`);
41909
+ return [];
41910
+ }
41911
+ function getDiscoveryFailure(provider) {
41912
+ return _failures.get(provider);
41913
+ }
41914
+ function hasAuthHeader(headers) {
41915
+ return Object.entries(headers).some(([name, value]) => /^(authorization|x-api-key|api-key)$/i.test(name) && (value ?? "").trim().length > 0);
41916
+ }
41917
+ function oneLine(text, max = 200) {
41918
+ const flat = text.replace(/\s+/g, " ").trim();
41919
+ return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
41920
+ }
41921
+ function describeDiscoveryFailure(failure) {
41922
+ const { kind, endpoint, status, detail } = failure;
41923
+ const at = endpoint ? ` at ${endpoint}` : "";
41924
+ const because = detail ? ` \u2014 ${detail}` : "";
41925
+ switch (kind) {
41926
+ case "no-credentials":
41927
+ return `no usable credentials${because}`;
41928
+ case "unauthorized":
41929
+ return `the API key was rejected (HTTP ${status})${at}${because}`;
41930
+ case "http-error":
41931
+ return `the model list returned HTTP ${status}${at}${because}`;
41932
+ case "unreachable":
41933
+ return `the model list was unreachable${at}${because}`;
41934
+ case "malformed":
41935
+ return `the model list was not valid JSON${at}`;
41936
+ case "empty-roster":
41937
+ return `the endpoint answered${at} but listed no models`;
41938
+ }
41939
+ }
41602
41940
  function resolveBaseUrl(catalogName) {
41603
41941
  const def = getProviderByName(catalogName);
41604
41942
  if (!def)
@@ -41662,14 +42000,14 @@ async function discoverProviderModels(providerName) {
41662
42000
  const { getServedDevinModels: getServedDevinModels2 } = await Promise.resolve().then(() => (init_devin_models(), exports_devin_models));
41663
42001
  const served = await getServedDevinModels2();
41664
42002
  if (served.length === 0) {
41665
- log(`[model-discovery:${providerName}] no models for this subscription`);
41666
- return [];
42003
+ return recordFailure({ kind: "empty-roster", provider: providerName });
41667
42004
  }
41668
42005
  const { devinRosterEntry: devinRosterEntry2 } = await Promise.resolve().then(() => (init_devin(), exports_devin));
41669
42006
  const models2 = served.map((model) => {
41670
42007
  const { wireId, ...rest } = devinRosterEntry2(model);
41671
42008
  return { id: wireId, ...rest };
41672
42009
  });
42010
+ _failures.delete(providerName);
41673
42011
  log(`[model-discovery:${providerName}] discovered ${models2.length} models`);
41674
42012
  _cache.set(providerName, { models: models2, expiresAt: Date.now() + CACHE_TTL_MS });
41675
42013
  return models2;
@@ -41677,13 +42015,15 @@ async function discoverProviderModels(providerName) {
41677
42015
  if (descriptor.format === "ollama-tags") {
41678
42016
  const { fetchOllamaModels: fetchOllamaModels2 } = await Promise.resolve().then(() => exports_ollama_discovery);
41679
42017
  const installed = await fetchOllamaModels2({ enrichCapabilities: false });
41680
- if (installed.length === 0)
41681
- return [];
42018
+ if (installed.length === 0) {
42019
+ return recordFailure({ kind: "empty-roster", provider: providerName });
42020
+ }
41682
42021
  const models2 = installed.map((model) => ({
41683
42022
  id: model.name,
41684
42023
  displayName: model.name,
41685
42024
  supportsTools: model.supportsTools
41686
42025
  }));
42026
+ _failures.delete(providerName);
41687
42027
  log(`[model-discovery:${providerName}] discovered ${models2.length} local models`);
41688
42028
  _cache.set(providerName, { models: models2, expiresAt: Date.now() + CACHE_TTL_MS });
41689
42029
  return models2;
@@ -41702,8 +42042,15 @@ async function discoverProviderModels(providerName) {
41702
42042
  const auth = await credentials.getRequestAuth(providerName, { model: "" });
41703
42043
  headers = { ...auth.headers };
41704
42044
  } catch (e) {
41705
- log(`[model-discovery:${providerName}] no credentials, skipping: ${e?.message}`);
41706
- return [];
42045
+ return recordFailure({
42046
+ kind: "no-credentials",
42047
+ provider: providerName,
42048
+ endpoint,
42049
+ detail: oneLine(e?.message ?? "")
42050
+ });
42051
+ }
42052
+ if (!hasAuthHeader(headers)) {
42053
+ return recordFailure({ kind: "no-credentials", provider: providerName, endpoint });
41707
42054
  }
41708
42055
  }
41709
42056
  let response;
@@ -41714,25 +42061,38 @@ async function discoverProviderModels(providerName) {
41714
42061
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
41715
42062
  });
41716
42063
  } catch (e) {
41717
- log(`[model-discovery:${providerName}] fetch failed: ${e?.message}`);
41718
- return [];
42064
+ return recordFailure({
42065
+ kind: "unreachable",
42066
+ provider: providerName,
42067
+ endpoint,
42068
+ detail: oneLine(e?.message ?? "")
42069
+ });
41719
42070
  }
41720
42071
  if (!response.ok) {
41721
- log(`[model-discovery:${providerName}] HTTP ${response.status} from ${endpoint}`);
41722
- return [];
42072
+ const unauthorized = response.status === 401 || response.status === 403;
42073
+ let detail = "";
42074
+ try {
42075
+ detail = oneLine(await response.text());
42076
+ } catch {}
42077
+ return recordFailure({
42078
+ kind: unauthorized ? "unauthorized" : "http-error",
42079
+ provider: providerName,
42080
+ endpoint,
42081
+ status: response.status,
42082
+ detail
42083
+ });
41723
42084
  }
41724
42085
  let body;
41725
42086
  try {
41726
42087
  body = await response.json();
41727
42088
  } catch {
41728
- log(`[model-discovery:${providerName}] response was not JSON`);
41729
- return [];
42089
+ return recordFailure({ kind: "malformed", provider: providerName, endpoint });
41730
42090
  }
41731
42091
  const models = parseOpenAIModelsList(body);
41732
42092
  if (models.length === 0) {
41733
- log(`[model-discovery:${providerName}] endpoint reachable but listed no models`);
41734
- return [];
42093
+ return recordFailure({ kind: "empty-roster", provider: providerName, endpoint });
41735
42094
  }
42095
+ _failures.delete(providerName);
41736
42096
  log(`[model-discovery:${providerName}] discovered ${models.length} models: ` + models.map((m) => `${m.id}(${m.contextWindow ?? "?"})`).join(", "));
41737
42097
  _cache.set(providerName, { models, expiresAt: Date.now() + CACHE_TTL_MS });
41738
42098
  return models;
@@ -41748,13 +42108,14 @@ function rankDiscoveredModels(models) {
41748
42108
  return diff !== 0 ? diff : a.id.localeCompare(b.id);
41749
42109
  });
41750
42110
  }
41751
- var CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, _cache, MIN_CREATED_SECONDS = 946684800, MAX_CREATED_SECONDS = 4102444800;
42111
+ var CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, _cache, _failures, MIN_CREATED_SECONDS = 946684800, MAX_CREATED_SECONDS = 4102444800;
41752
42112
  var init_model_discovery = __esm(() => {
41753
42113
  init_authority();
41754
42114
  init_logger();
41755
42115
  init_provider_definitions();
41756
42116
  CACHE_TTL_MS = 5 * 60 * 1000;
41757
42117
  _cache = new Map;
42118
+ _failures = new Map;
41758
42119
  });
41759
42120
 
41760
42121
  // src/providers/transport/probe-discovery.ts
@@ -42150,9 +42511,10 @@ class AnthropicProviderTransport {
42150
42511
  }
42151
42512
  const discovered = await discoverProviderModels(this.provider.name);
42152
42513
  if (discovered.length === 0) {
42514
+ const failure = getDiscoveryFailure(this.provider.name);
42153
42515
  return {
42154
42516
  model: null,
42155
- reason: `${this.displayName} listed no models at ${def.modelDiscovery.path} \u2014 check the API key and that the subscription is active`
42517
+ reason: failure ? `${this.displayName}: ${describeDiscoveryFailure(failure)}` : `${this.displayName} listed no models at ${def.modelDiscovery.path} \u2014 check the API key and that the subscription is active`
42156
42518
  };
42157
42519
  }
42158
42520
  const ranked = rankDiscoveredModels(discovered).map((m) => m.id).filter(isChatCapable);
@@ -42205,6 +42567,7 @@ class AnthropicProviderTransport {
42205
42567
  kimi: "Kimi",
42206
42568
  "kimi-coding": "Kimi Coding",
42207
42569
  "qwen-cloud": "Qwen Plan",
42570
+ "qwen-payg": "Qwen PAYG",
42208
42571
  moonshot: "Kimi",
42209
42572
  "z-ai": "Z.AI"
42210
42573
  };
@@ -42751,6 +43114,7 @@ var init_routing_hints = __esm(() => {
42751
43114
  sakana: { apiKeyEnvVar: "SAKANA_API_KEY" },
42752
43115
  "sakana-subscription": { apiKeyEnvVar: "SAKANA_SUBSCRIPTION_API_KEY" },
42753
43116
  "qwen-cloud": { apiKeyEnvVar: "QWEN_CLOUD_PLAN_API_KEY" },
43117
+ "qwen-payg": { apiKeyEnvVar: "DASHSCOPE_API_KEY" },
42754
43118
  ollamacloud: { apiKeyEnvVar: "OLLAMA_API_KEY" },
42755
43119
  "native-anthropic": { apiKeyEnvVar: "ANTHROPIC_API_KEY" },
42756
43120
  openrouter: { apiKeyEnvVar: "OPENROUTER_API_KEY" },
@@ -42799,7 +43163,7 @@ var init_default_routing_rules = __esm(() => {
42799
43163
  "k3*": ["kimi-coding", "opencode-zen-go", "kimi", "openrouter"],
42800
43164
  "minimax-*": ["minimax-coding", "opencode-zen-go", "minimax", "openrouter"],
42801
43165
  "glm-*": ["glm-coding", "opencode-zen-go", "glm", "openrouter"],
42802
- "qwen3.*": ["qwen-cloud", "opencode-zen-go", "openrouter"],
43166
+ "qwen3.*": ["qwen-cloud", "opencode-zen-go", "qwen-payg", "openrouter"],
42803
43167
  "z-ai-*": ["z-ai", "openrouter"],
42804
43168
  "deepseek-*": ["opencode-zen-go", "deepseek", "openrouter"],
42805
43169
  "mimo-*": ["opencode-zen-go", "openrouter"],
@@ -43949,6 +44313,83 @@ var init_channel = __esm(() => {
43949
44313
  init_session_manager();
43950
44314
  });
43951
44315
 
44316
+ // src/mcp/progress-heartbeat.ts
44317
+ function isProgressToken(v) {
44318
+ return typeof v === "string" || typeof v === "number" && Number.isFinite(v);
44319
+ }
44320
+ function clampInterval(ms) {
44321
+ if (ms < MIN_PROGRESS_INTERVAL_MS)
44322
+ return MIN_PROGRESS_INTERVAL_MS;
44323
+ if (ms > MAX_PROGRESS_INTERVAL_MS)
44324
+ return MAX_PROGRESS_INTERVAL_MS;
44325
+ return ms;
44326
+ }
44327
+ function resolveProgressIntervalMs(env = process.env) {
44328
+ const raw = env[PROGRESS_INTERVAL_ENV_VAR];
44329
+ if (raw === undefined || raw === null || raw.trim() === "")
44330
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44331
+ const parsed = Number(raw);
44332
+ if (!Number.isFinite(parsed) || parsed <= 0)
44333
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44334
+ return clampInterval(parsed);
44335
+ }
44336
+ function resolveExplicitIntervalMs(ms) {
44337
+ if (ms === undefined)
44338
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44339
+ if (!Number.isFinite(ms) || ms <= 0)
44340
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44341
+ return Math.min(ms, MAX_PROGRESS_INTERVAL_MS);
44342
+ }
44343
+ function startHeartbeat(opts) {
44344
+ const { token, send, label } = opts;
44345
+ if (!isProgressToken(token))
44346
+ return NOOP_HEARTBEAT;
44347
+ const intervalMs = resolveExplicitIntervalMs(opts.intervalMs);
44348
+ const startedAt = Date.now();
44349
+ let progress = 0;
44350
+ let stopped = false;
44351
+ const emit2 = (message) => {
44352
+ if (stopped)
44353
+ return;
44354
+ progress += 1;
44355
+ try {
44356
+ const elapsedSeconds = Math.round((Date.now() - startedAt) / 1000);
44357
+ const result = send({
44358
+ progressToken: token,
44359
+ progress,
44360
+ message: message ?? `${label}: working (${elapsedSeconds}s)`
44361
+ });
44362
+ if (result && typeof result.then === "function") {
44363
+ result.catch(() => {});
44364
+ }
44365
+ } catch {}
44366
+ };
44367
+ const timer = setInterval(() => emit2(), intervalMs);
44368
+ timer.unref?.();
44369
+ return {
44370
+ tick: (message) => emit2(message),
44371
+ stop: () => {
44372
+ stopped = true;
44373
+ clearInterval(timer);
44374
+ },
44375
+ get active() {
44376
+ return !stopped;
44377
+ },
44378
+ get emitted() {
44379
+ return progress;
44380
+ }
44381
+ };
44382
+ }
44383
+ var DEFAULT_PROGRESS_INTERVAL_MS = 1e4, MIN_PROGRESS_INTERVAL_MS = 1000, MAX_PROGRESS_INTERVAL_MS = 60000, PROGRESS_INTERVAL_ENV_VAR = "CLAUDISH_MCP_PROGRESS_INTERVAL_MS", NOOP_HEARTBEAT;
44384
+ var init_progress_heartbeat = __esm(() => {
44385
+ NOOP_HEARTBEAT = Object.freeze({
44386
+ tick(_message) {},
44387
+ stop() {},
44388
+ active: false,
44389
+ emitted: 0
44390
+ });
44391
+ });
44392
+
43952
44393
  // src/providers/cache-ttl.ts
43953
44394
  var FIREBASE_CACHE_TTL_HOURS = 24, FIREBASE_CACHE_TTL_MS;
43954
44395
  var init_cache_ttl = __esm(() => {
@@ -46812,6 +47253,34 @@ var init_native_handler_advisor = __esm(() => {
46812
47253
  advisorToolUseIds = new Set;
46813
47254
  });
46814
47255
 
47256
+ // src/handlers/shared/thinking-signature.ts
47257
+ function isUnsignedThinking(block) {
47258
+ if (!block || typeof block !== "object")
47259
+ return false;
47260
+ const b = block;
47261
+ if (b.type !== "thinking")
47262
+ return false;
47263
+ return typeof b.signature !== "string" || b.signature.length === 0;
47264
+ }
47265
+ function stripUnsignedThinkingBlocks(messages) {
47266
+ if (!Array.isArray(messages))
47267
+ return 0;
47268
+ let removed = 0;
47269
+ for (const message of messages) {
47270
+ if (!message || typeof message !== "object")
47271
+ continue;
47272
+ const content = message.content;
47273
+ if (!Array.isArray(content))
47274
+ continue;
47275
+ const kept = content.filter((block) => !isUnsignedThinking(block));
47276
+ if (kept.length !== content.length) {
47277
+ removed += content.length - kept.length;
47278
+ message.content = kept;
47279
+ }
47280
+ }
47281
+ return removed;
47282
+ }
47283
+
46815
47284
  // src/handlers/native-handler.ts
46816
47285
  async function resolveAdvisorKeys() {
46817
47286
  const keyFromAuthority = async (name) => {
@@ -46857,6 +47326,10 @@ class NativeHandler {
46857
47326
  async handle(c, payload) {
46858
47327
  const originalHeaders = c.req.header();
46859
47328
  const target = payload.model;
47329
+ const strippedThinking = stripUnsignedThinkingBlocks(payload.messages);
47330
+ if (strippedThinking > 0) {
47331
+ log(`[Native] stripped ${strippedThinking} unsigned thinking block(s) from history for ${target} (foreign-provider origin)`);
47332
+ }
46860
47333
  const advisorCfg = loadAdvisorSwapConfig(this.advisorModels, this.advisorCollector);
46861
47334
  let advisorSwapped = null;
46862
47335
  let advisorRewrittenIds = [];
@@ -47084,6 +47557,7 @@ var init_api_key_map = __esm(() => {
47084
47557
  aliases: ["SAKANA_CODING_API_KEY"]
47085
47558
  },
47086
47559
  "qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
47560
+ "qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
47087
47561
  ollamacloud: { envVar: "OLLAMA_API_KEY" },
47088
47562
  "opencode-zen": { envVar: "OPENCODE_API_KEY" },
47089
47563
  "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
@@ -47877,7 +48351,8 @@ var init_vertex_oauth = __esm(() => {
47877
48351
 
47878
48352
  // src/providers/provider-profiles.ts
47879
48353
  function requiresResponsesApi(modelName) {
47880
- return /^gpt-5\.6/.test(modelName.toLowerCase());
48354
+ const name = modelName.toLowerCase();
48355
+ return /^gpt-5\.6/.test(name) || name.includes("codex");
47881
48356
  }
47882
48357
  function createHandlerForProvider(ctx) {
47883
48358
  const profile = PROVIDER_PROFILES[ctx.provider.name] ?? getRuntimeProfiles().get(ctx.provider.name);
@@ -48132,6 +48607,7 @@ var init_provider_profiles = __esm(() => {
48132
48607
  kimi: anthropicCompatProfile,
48133
48608
  "kimi-coding": anthropicCompatProfile,
48134
48609
  "qwen-cloud": anthropicCompatProfile,
48610
+ "qwen-payg": anthropicCompatProfile,
48135
48611
  "z-ai": anthropicCompatProfile,
48136
48612
  glm: glmProfile,
48137
48613
  "glm-coding": glmProfile,
@@ -49638,7 +50114,7 @@ import {
49638
50114
  } from "fs";
49639
50115
  import { join as join28, resolve as resolve3 } from "path";
49640
50116
  function classifyRunOutput(opts) {
49641
- const { outputSize, stdoutTail, stderr, minOutputBytes } = opts;
50117
+ const { outputSize, stdoutTail, stderr, minOutputBytes, requirePattern, fullOutput } = opts;
49642
50118
  const apiError = API_ERROR_RE.exec(stdoutTail);
49643
50119
  if (apiError) {
49644
50120
  return {
@@ -49666,6 +50142,21 @@ function classifyRunOutput(opts) {
49666
50142
  detail: `Child exited 0 but produced only ${outputSize} B of stdout ` + `(caller required at least ${minOutputBytes} B).`
49667
50143
  };
49668
50144
  }
50145
+ if (requirePattern) {
50146
+ const haystack = fullOutput ?? stdoutTail;
50147
+ let re = null;
50148
+ try {
50149
+ re = new RegExp(requirePattern);
50150
+ } catch {
50151
+ re = null;
50152
+ }
50153
+ if (re && !re.test(haystack)) {
50154
+ return {
50155
+ reason: "shape_mismatch",
50156
+ detail: `Child exited 0 with ${outputSize} B, but the response does not match the ` + `required pattern /${requirePattern}/. This is the signature of a child that ` + "answered and then took one more turn: `claude -p` prints only the FINAL " + "assistant message, so a background task completing (or any late notification) " + "replaces the real answer with an epilogue about it. Check the child's " + "transcript \u2014 the answer was generated, it just was not the last thing said."
50157
+ };
50158
+ }
50159
+ }
49669
50160
  return null;
49670
50161
  }
49671
50162
  function persistErrorLog(errorLogPath, header, stderr, stdoutTail) {
@@ -49744,8 +50235,28 @@ function setupSession(sessionPath, models, input) {
49744
50235
  writeFileSync12(join28(sessionPath, "status.json"), JSON.stringify(status, null, 2), "utf-8");
49745
50236
  return manifest;
49746
50237
  }
50238
+ function assertValidRequirePattern(pattern) {
50239
+ if (pattern === undefined)
50240
+ return;
50241
+ try {
50242
+ new RegExp(pattern);
50243
+ } catch (err) {
50244
+ throw new Error(`Invalid requirePattern /${pattern}/: ${err instanceof Error ? err.message : String(err)}`);
50245
+ }
50246
+ }
50247
+ function readFullOutputIfNeeded(opts) {
50248
+ const { crashed, requirePattern, outputSize, outputPath } = opts;
50249
+ if (crashed || !requirePattern || outputSize <= STDOUT_TAIL_LIMIT)
50250
+ return;
50251
+ try {
50252
+ return readFileSync18(outputPath, "utf-8");
50253
+ } catch {
50254
+ return;
50255
+ }
50256
+ }
49747
50257
  async function runModels(sessionPath, opts = {}) {
49748
50258
  const timeoutMs = (opts.timeout ?? 300) * 1000;
50259
+ assertValidRequirePattern(opts.requirePattern);
49749
50260
  const manifest = JSON.parse(readFileSync18(join28(sessionPath, "manifest.json"), "utf-8"));
49750
50261
  const statusPath = join28(sessionPath, "status.json");
49751
50262
  const inputPath = join28(sessionPath, "input.md");
@@ -49757,6 +50268,7 @@ async function runModels(sessionPath, opts = {}) {
49757
50268
  writeFileSync12(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
49758
50269
  }
49759
50270
  const minOutputBytes = opts.minOutputBytes ?? DEFAULT_MIN_OUTPUT_BYTES;
50271
+ const requirePattern = opts.requirePattern;
49760
50272
  mkdirSync12(statsDir(sessionPath), { recursive: true });
49761
50273
  const processes = new Map;
49762
50274
  const runtimes = new Map;
@@ -49823,7 +50335,20 @@ async function runModels(sessionPath, opts = {}) {
49823
50335
  resolved = true;
49824
50336
  const outputSize = byteCount;
49825
50337
  const crashed = exitCode !== 0;
49826
- const degraded = crashed ? null : classifyRunOutput({ outputSize, stdoutTail, stderr, minOutputBytes });
50338
+ const fullOutput = readFullOutputIfNeeded({
50339
+ crashed,
50340
+ requirePattern,
50341
+ outputSize,
50342
+ outputPath
50343
+ });
50344
+ const degraded = crashed ? null : classifyRunOutput({
50345
+ outputSize,
50346
+ stdoutTail,
50347
+ stderr,
50348
+ minOutputBytes,
50349
+ requirePattern,
50350
+ fullOutput
50351
+ });
49827
50352
  const failed = crashed || degraded !== null;
49828
50353
  const state = crashed ? "FAILED" : degraded ? "EMPTY" : "COMPLETED";
49829
50354
  if (failed) {
@@ -50356,6 +50881,7 @@ ${block.join(`
50356
50881
  required: ["model", "prompt"]
50357
50882
  },
50358
50883
  group: "low-level",
50884
+ heartbeat: true,
50359
50885
  handler: async (args) => {
50360
50886
  try {
50361
50887
  const result = await runPromptViaProxy(args.model, args.prompt, args.system_prompt, args.max_tokens);
@@ -50576,7 +51102,8 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50576
51102
  required: ["models", "prompt"]
50577
51103
  },
50578
51104
  group: "low-level",
50579
- handler: async (args) => {
51105
+ heartbeat: true,
51106
+ handler: async (args, ctx) => {
50580
51107
  const modelIds = args.models;
50581
51108
  const prompt = args.prompt;
50582
51109
  const systemPrompt = args.system_prompt;
@@ -50593,6 +51120,7 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50593
51120
  error: error46 instanceof Error ? error46.message : String(error46)
50594
51121
  });
50595
51122
  }
51123
+ ctx.reportProgress(`compare_models: ${results.length}/${modelIds.length} models done`);
50596
51124
  }
50597
51125
  let output = `# Model Comparison
50598
51126
 
@@ -50658,12 +51186,21 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50658
51186
  type: "string",
50659
51187
  description: "Task prompt text (or place input.md in the session directory before calling)"
50660
51188
  },
50661
- timeout: { type: "number", description: "Per-model timeout in seconds (default: 300)" }
51189
+ timeout: { type: "number", description: "Per-model timeout in seconds (default: 300)" },
51190
+ require_pattern: {
51191
+ type: "string",
51192
+ description: "Regex the response MUST match, or the slot is reported FAILED (state EMPTY, " + "reason 'shape_mismatch') instead of succeeded. Strongly recommended whenever " + "your prompt mandates an output shape \u2014 e.g. '```vote' for a voting panel. " + "Exit code 0 is not a success oracle: a child that answers and then takes one " + "more turn (a background Task finishing, a late notification) has its real " + "answer replaced by a short epilogue, because print mode emits only the FINAL " + "assistant message. That lands as a few hundred bytes of plausible prose with " + "exit 0 and no error, and is otherwise indistinguishable from success."
51193
+ },
51194
+ min_output_bytes: {
51195
+ type: "number",
51196
+ description: "Report a slot FAILED if it produced fewer than this many bytes (default 0 = " + "off). A blunter instrument than require_pattern \u2014 short answers can be " + "legitimate \u2014 so prefer require_pattern when you know the expected shape."
51197
+ }
50662
51198
  },
50663
51199
  required: ["mode", "path"]
50664
51200
  },
50665
51201
  group: "agentic",
50666
- handler: async (args) => {
51202
+ heartbeat: true,
51203
+ handler: async (args, ctx) => {
50667
51204
  try {
50668
51205
  const mode = args.mode;
50669
51206
  const path = args.path;
@@ -50671,19 +51208,26 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50671
51208
  const judges = args.judges;
50672
51209
  const input = args.input;
50673
51210
  const timeout = args.timeout;
51211
+ const requirePattern = args.require_pattern;
51212
+ const minOutputBytes = args.min_output_bytes;
50674
51213
  const resolved = validateSessionPath(path);
50675
51214
  const teamSessionId = resolved.split("/").filter(Boolean).pop() ?? "team";
50676
51215
  const teamCreatedAt = new Date().toISOString();
50677
51216
  const runOpts = {
50678
51217
  timeout,
50679
- onProgress: (u) => notifyChannel({
50680
- content: u.rendered,
50681
- sessionId: teamSessionId,
50682
- event: u.phase === "settled" ? u.allFailed ? "failed" : "completed" : "running",
50683
- model: "team",
50684
- elapsedSeconds: (Date.now() - Date.parse(teamCreatedAt)) / 1000,
50685
- createdAt: teamCreatedAt
50686
- })
51218
+ requirePattern,
51219
+ minOutputBytes,
51220
+ onProgress: (u) => {
51221
+ ctx.reportProgress(`team: ${u.phase}`);
51222
+ notifyChannel({
51223
+ content: u.rendered,
51224
+ sessionId: teamSessionId,
51225
+ event: u.phase === "settled" ? u.allFailed ? "failed" : "completed" : "running",
51226
+ model: "team",
51227
+ elapsedSeconds: (Date.now() - Date.parse(teamCreatedAt)) / 1000,
51228
+ createdAt: teamCreatedAt
51229
+ });
51230
+ }
50687
51231
  };
50688
51232
  switch (mode) {
50689
51233
  case "run": {
@@ -51118,6 +51662,7 @@ To report this error, use the report_error tool with error_type: "provider_failu
51118
51662
  watchNotificationResult(result, { sessionId: p.sessionId, eventType: p.event });
51119
51663
  } catch {}
51120
51664
  };
51665
+ const progressIntervalMs = resolveProgressIntervalMs();
51121
51666
  const allTools = defineTools(sessionManager, notifyChannel);
51122
51667
  const enabledTools = allTools.filter((t) => enabledGroups.has(t.group));
51123
51668
  const toolMap = new Map(enabledTools.map((t) => [t.name, t]));
@@ -51129,7 +51674,7 @@ To report this error, use the report_error tool with error_type: "provider_failu
51129
51674
  inputSchema: t.inputSchema
51130
51675
  }))
51131
51676
  }));
51132
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
51677
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
51133
51678
  const { name, arguments: args } = request.params;
51134
51679
  const tool = toolMap.get(name);
51135
51680
  if (!tool) {
@@ -51138,8 +51683,15 @@ To report this error, use the report_error tool with error_type: "provider_failu
51138
51683
  isError: true
51139
51684
  };
51140
51685
  }
51686
+ const heartbeat = tool.heartbeat ? startHeartbeat({
51687
+ token: extra._meta?.progressToken,
51688
+ label: name,
51689
+ intervalMs: progressIntervalMs,
51690
+ send: (frame) => extra.sendNotification({ method: "notifications/progress", params: frame })
51691
+ }) : NOOP_HEARTBEAT;
51692
+ const ctx = { reportProgress: (message) => heartbeat.tick(message) };
51141
51693
  try {
51142
- return await tool.handler(args ?? {});
51694
+ return await tool.handler(args ?? {}, ctx);
51143
51695
  } catch (error46) {
51144
51696
  return {
51145
51697
  content: [
@@ -51150,6 +51702,8 @@ To report this error, use the report_error tool with error_type: "provider_failu
51150
51702
  ],
51151
51703
  isError: true
51152
51704
  };
51705
+ } finally {
51706
+ heartbeat.stop();
51153
51707
  }
51154
51708
  });
51155
51709
  const transport = new StdioServerTransport;
@@ -51197,6 +51751,7 @@ var init_mcp_server = __esm(() => {
51197
51751
  init_prehydrate();
51198
51752
  init_diagnostics();
51199
51753
  init_channel();
51754
+ init_progress_heartbeat();
51200
51755
  init_model_loader();
51201
51756
  init_port_manager();
51202
51757
  init_onepassword();
@@ -51215,7 +51770,8 @@ var init_mcp_server = __esm(() => {
51215
51770
  timeout: "raise `timeout`, or pick a faster model",
51216
51771
  api_error: "retry once, or route via a different provider (or@<model>)",
51217
51772
  background_task_ceiling: "set CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0 for children, or forbid background work in the prompt",
51218
- empty_output: "retry once; if it repeats, drop the model"
51773
+ empty_output: "retry once; if it repeats, drop the model",
51774
+ shape_mismatch: "the answer was generated but not captured \u2014 print mode keeps only the FINAL " + "assistant message, so a late background task or notification overwrote it. " + "Retry, and forbid background work in the prompt; do NOT count this slot as a vote"
51219
51775
  };
51220
51776
  sanitize = sanitizeForReport;
51221
51777
  EVENT_TO_TASK_STATUS = new Map([
@@ -51521,6 +52077,440 @@ var init_behavior_command = __esm(() => {
51521
52077
  init_profile_config();
51522
52078
  });
51523
52079
 
52080
+ // src/team-grid.ts
52081
+ var exports_team_grid = {};
52082
+ __export(exports_team_grid, {
52083
+ runWithGrid: () => runWithGrid
52084
+ });
52085
+ import { spawn as spawn3 } from "child_process";
52086
+ import { execSync } from "child_process";
52087
+ import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync15 } from "fs";
52088
+ import { connect as netConnect } from "net";
52089
+ import { dirname as dirname10, join as join30 } from "path";
52090
+ import { setTimeout as wait } from "timers/promises";
52091
+ import { fileURLToPath as fileURLToPath2 } from "url";
52092
+ function resolveRouteInfo(modelId) {
52093
+ const parsed = parseModelSpec(modelId);
52094
+ if (parsed.isExplicitProvider) {
52095
+ return { chain: [parsed.provider], source: "direct" };
52096
+ }
52097
+ const local = loadLocalConfig();
52098
+ if (local?.routing && Object.keys(local.routing).length > 0) {
52099
+ const matched2 = matchRoutingRule(parsed.model, local.routing);
52100
+ if (matched2) {
52101
+ const routes = buildRoutingChain(matched2, parsed.model);
52102
+ const pattern = Object.keys(local.routing).find((k) => {
52103
+ if (k === parsed.model)
52104
+ return true;
52105
+ if (k.includes("*")) {
52106
+ const star = k.indexOf("*");
52107
+ return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
52108
+ }
52109
+ return false;
52110
+ });
52111
+ return {
52112
+ chain: routes.map((r) => r.displayName),
52113
+ source: "project routing",
52114
+ sourceDetail: pattern
52115
+ };
52116
+ }
52117
+ }
52118
+ const global_ = loadConfig();
52119
+ if (global_.routing && Object.keys(global_.routing).length > 0) {
52120
+ const matched2 = matchRoutingRule(parsed.model, global_.routing);
52121
+ if (matched2) {
52122
+ const routes = buildRoutingChain(matched2, parsed.model);
52123
+ const pattern = Object.keys(global_.routing).find((k) => {
52124
+ if (k === parsed.model)
52125
+ return true;
52126
+ if (k.includes("*")) {
52127
+ const star = k.indexOf("*");
52128
+ return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
52129
+ }
52130
+ return false;
52131
+ });
52132
+ return {
52133
+ chain: routes.map((r) => r.displayName),
52134
+ source: "user routing",
52135
+ sourceDetail: pattern
52136
+ };
52137
+ }
52138
+ }
52139
+ const merged = loadRoutingRules();
52140
+ const matched = matchRoutingRule(parsed.model, merged);
52141
+ if (matched) {
52142
+ const routes = buildRoutingChain(matched, parsed.model);
52143
+ return {
52144
+ chain: routes.map((r) => r.displayName),
52145
+ source: "auto"
52146
+ };
52147
+ }
52148
+ return {
52149
+ chain: [],
52150
+ source: "auto"
52151
+ };
52152
+ }
52153
+ function pickBannerColor(model, used) {
52154
+ let hash2 = 0;
52155
+ for (let i = 0;i < model.length; i++)
52156
+ hash2 = (hash2 << 5) - hash2 + model.charCodeAt(i) | 0;
52157
+ const start = Math.abs(hash2) % BANNER_BG_COLORS.length;
52158
+ let idx = start;
52159
+ if (used.size < BANNER_BG_COLORS.length) {
52160
+ while (used.has(idx))
52161
+ idx = (idx + 1) % BANNER_BG_COLORS.length;
52162
+ }
52163
+ used.add(idx);
52164
+ return BANNER_BG_COLORS[idx];
52165
+ }
52166
+ function buildPaneHeader(model, prompt, bg) {
52167
+ const route2 = resolveRouteInfo(model);
52168
+ const esc2 = (s) => s.replace(/'/g, "'\\''");
52169
+ const chainStr = route2.chain.join(" \u2192 ");
52170
+ const sourceLabel = route2.sourceDetail ? `${route2.source}: ${route2.sourceDetail}` : route2.source;
52171
+ const lines = [];
52172
+ lines.push(`printf '\\033[1;97;${bg}m %s \\033[0m\\n' '${esc2(model)}';`);
52173
+ lines.push(`printf '\\033[2m route: ${esc2(chainStr)} (${esc2(sourceLabel)})\\033[0m\\n' ;`);
52174
+ lines.push(`printf '\\033[2m %s\\033[0m\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
52175
+ const promptForShell = esc2(prompt).replace(/\n/g, "\\n");
52176
+ lines.push(`printf '%b\\n' '${promptForShell}' | fold -s -w 78 | sed 's/^/ /';`);
52177
+ lines.push(`printf '\\033[2m %s\\033[0m\\n\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
52178
+ return lines.join(" ");
52179
+ }
52180
+ function findMagmuxBinary() {
52181
+ const thisFile = fileURLToPath2(import.meta.url);
52182
+ const thisDir = dirname10(thisFile);
52183
+ const pkgRoot = join30(thisDir, "..");
52184
+ const platform2 = process.platform;
52185
+ const arch = process.arch;
52186
+ const bundledMagmux = join30(pkgRoot, "native", `magmux-${platform2}-${arch}`);
52187
+ if (existsSync23(bundledMagmux))
52188
+ return bundledMagmux;
52189
+ try {
52190
+ const pkgName = `@claudish/magmux-${platform2}-${arch}`;
52191
+ let searchDir = pkgRoot;
52192
+ for (let i = 0;i < 5; i++) {
52193
+ const candidate = join30(searchDir, "node_modules", pkgName, "bin", "magmux");
52194
+ if (existsSync23(candidate))
52195
+ return candidate;
52196
+ const parent = dirname10(searchDir);
52197
+ if (parent === searchDir)
52198
+ break;
52199
+ searchDir = parent;
52200
+ }
52201
+ } catch {}
52202
+ try {
52203
+ const result = execSync("which magmux", { encoding: "utf-8" }).trim();
52204
+ if (result)
52205
+ return result;
52206
+ } catch {}
52207
+ throw new Error(`magmux not found. Install it:
52208
+ brew install MadAppGang/tap/magmux`);
52209
+ }
52210
+ async function subscribeToMagmux(sockPath, onEvent) {
52211
+ let client = null;
52212
+ for (let attempt = 0;attempt < 40; attempt++) {
52213
+ if (existsSync23(sockPath)) {
52214
+ try {
52215
+ client = await new Promise((resolve5, reject) => {
52216
+ const s = netConnect(sockPath);
52217
+ s.once("connect", () => resolve5(s));
52218
+ s.once("error", reject);
52219
+ });
52220
+ break;
52221
+ } catch {}
52222
+ }
52223
+ await wait(50);
52224
+ }
52225
+ if (!client) {
52226
+ return { results: null, client: null };
52227
+ }
52228
+ return await new Promise((resolve5) => {
52229
+ let buf = "";
52230
+ let finalResults = null;
52231
+ client.on("data", (chunk) => {
52232
+ buf += chunk.toString("utf-8");
52233
+ let nl = buf.indexOf(`
52234
+ `);
52235
+ while (nl >= 0) {
52236
+ const line = buf.slice(0, nl).trim();
52237
+ buf = buf.slice(nl + 1);
52238
+ nl = buf.indexOf(`
52239
+ `);
52240
+ if (!line)
52241
+ continue;
52242
+ try {
52243
+ const evt = JSON.parse(line);
52244
+ onEvent?.(evt);
52245
+ if (evt.type === "results") {
52246
+ finalResults = evt;
52247
+ }
52248
+ } catch {}
52249
+ }
52250
+ });
52251
+ const done = () => resolve5({ results: finalResults, client });
52252
+ client.once("end", done);
52253
+ client.once("close", done);
52254
+ client.once("error", done);
52255
+ });
52256
+ }
52257
+ function buildTeamStatus(manifest, startedAt, results) {
52258
+ const anonIds = Object.keys(manifest.models);
52259
+ const models = {};
52260
+ for (let i = 0;i < anonIds.length; i++) {
52261
+ const anonId = anonIds[i];
52262
+ const result = results?.find((r) => r.pane === i);
52263
+ if (!result) {
52264
+ models[anonId] = {
52265
+ state: "TIMEOUT",
52266
+ exitCode: null,
52267
+ startedAt,
52268
+ completedAt: null,
52269
+ outputSize: 0
52270
+ };
52271
+ continue;
52272
+ }
52273
+ let state;
52274
+ switch (result.state) {
52275
+ case "completed":
52276
+ case "awaiting_input":
52277
+ state = "COMPLETED";
52278
+ break;
52279
+ case "failed":
52280
+ state = "FAILED";
52281
+ break;
52282
+ default:
52283
+ state = "TIMEOUT";
52284
+ }
52285
+ models[anonId] = {
52286
+ state,
52287
+ exitCode: result.exitCode,
52288
+ startedAt: result.startedAt ?? startedAt,
52289
+ completedAt: result.completedAt ?? new Date().toISOString(),
52290
+ outputSize: result.response?.length ?? 0
52291
+ };
52292
+ }
52293
+ return { startedAt, models };
52294
+ }
52295
+ async function runWithGrid(sessionPath, models, input, opts) {
52296
+ const mode = opts?.mode ?? "default";
52297
+ const keep = opts?.keep ?? false;
52298
+ const manifest = setupSession(sessionPath, models, input);
52299
+ const startedAt = new Date().toISOString();
52300
+ const gridfilePath = join30(sessionPath, "gridfile.txt");
52301
+ const prompt = readFileSync22(join30(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
52302
+ const rawPrompt = readFileSync22(join30(sessionPath, "input.md"), "utf-8");
52303
+ const usedBannerColors = new Set;
52304
+ const gridLines = Object.entries(manifest.models).map(([anonId]) => {
52305
+ const model = manifest.models[anonId].model;
52306
+ if (mode === "interactive") {
52307
+ return `claudish --model ${model} -i --dangerously-skip-permissions '${prompt}'`;
52308
+ }
52309
+ const bg = pickBannerColor(model, usedBannerColors);
52310
+ const header = buildPaneHeader(model, rawPrompt, bg);
52311
+ return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
52312
+ });
52313
+ writeFileSync15(gridfilePath, `${gridLines.join(`
52314
+ `)}
52315
+ `, "utf-8");
52316
+ const magmuxPath = findMagmuxBinary();
52317
+ const spawnArgs = ["-g", gridfilePath];
52318
+ if (!keep && mode === "default") {
52319
+ spawnArgs.push("-w");
52320
+ }
52321
+ const proc = spawn3(magmuxPath, spawnArgs, {
52322
+ stdio: "inherit",
52323
+ env: { ...process.env }
52324
+ });
52325
+ const sockPath = `/tmp/magmux-${proc.pid}.sock`;
52326
+ const subscription = subscribeToMagmux(sockPath);
52327
+ const procExit = new Promise((resolve5) => {
52328
+ proc.on("exit", () => resolve5());
52329
+ proc.on("error", () => resolve5());
52330
+ });
52331
+ const [{ results }] = await Promise.all([subscription, procExit]);
52332
+ const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
52333
+ const statusPath = join30(sessionPath, "status.json");
52334
+ writeFileSync15(statusPath, JSON.stringify(status, null, 2), "utf-8");
52335
+ return status;
52336
+ }
52337
+ var BANNER_BG_COLORS;
52338
+ var init_team_grid = __esm(() => {
52339
+ init_profile_config();
52340
+ init_model_parser();
52341
+ init_routing_rules();
52342
+ init_team_orchestrator();
52343
+ BANNER_BG_COLORS = [
52344
+ "48;2;40;90;180",
52345
+ "48;2;140;60;160",
52346
+ "48;2;30;130;100",
52347
+ "48;2;160;80;40",
52348
+ "48;2;60;120;60",
52349
+ "48;2;160;50;70"
52350
+ ];
52351
+ });
52352
+
52353
+ // src/team-cli.ts
52354
+ var exports_team_cli = {};
52355
+ __export(exports_team_cli, {
52356
+ teamCommand: () => teamCommand
52357
+ });
52358
+ import { readFileSync as readFileSync23 } from "fs";
52359
+ import { join as join31 } from "path";
52360
+ function getFlag(args, flag) {
52361
+ const idx = args.indexOf(flag);
52362
+ if (idx === -1 || idx + 1 >= args.length)
52363
+ return;
52364
+ return args[idx + 1];
52365
+ }
52366
+ function hasFlag(args, flag) {
52367
+ return args.includes(flag);
52368
+ }
52369
+ function printStatus(status) {
52370
+ const modelIds = Object.keys(status.models).sort();
52371
+ console.log(`
52372
+ Team Status (started: ${status.startedAt})`);
52373
+ console.log("\u2500".repeat(60));
52374
+ for (const id of modelIds) {
52375
+ const m = status.models[id];
52376
+ const duration3 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : m.startedAt ? "running" : "pending";
52377
+ const size = m.outputSize > 0 ? ` (${m.outputSize} bytes)` : "";
52378
+ console.log(` ${id} ${m.state.padEnd(10)} ${duration3}${size}`);
52379
+ }
52380
+ console.log("");
52381
+ }
52382
+ function printHelp() {
52383
+ console.log(`
52384
+ Usage: claudish team <subcommand> [options]
52385
+
52386
+ Subcommands:
52387
+ run Run multiple models on a task in parallel
52388
+ judge Blind-judge existing model outputs
52389
+ run-and-judge Run models then judge their outputs
52390
+ status Show current session status
52391
+
52392
+ Options (run / run-and-judge):
52393
+ --path <dir> Session directory (default: .)
52394
+ --models <a,b,...> Comma-separated model IDs to run
52395
+ --input <text> Task prompt (or create input.md in --path beforehand)
52396
+ --timeout <secs> Timeout per model in seconds (default: 300)
52397
+ --grid Show all models in a magmux grid with live output + status bar
52398
+
52399
+ Options (judge / run-and-judge):
52400
+ --judges <a,b,...> Comma-separated judge model IDs (default: same as runners)
52401
+
52402
+ Options (status):
52403
+ --path <dir> Session directory (default: .)
52404
+
52405
+ Examples:
52406
+ claudish team run --path ./review --models minimax-m2.5,kimi-k2.5 --input "Review this code"
52407
+ claudish team run --grid --models kimi-k2.5,gpt-5.4,gemini-3.1-pro --input "Solve this"
52408
+ claudish team judge --path ./review
52409
+ claudish team run-and-judge --path ./review --models gpt-5.4,gemini-3.1-pro-preview --input "Evaluate this design"
52410
+ claudish team status --path ./review
52411
+ `);
52412
+ }
52413
+ async function teamCommand(args) {
52414
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
52415
+ printHelp();
52416
+ process.exit(0);
52417
+ }
52418
+ const firstArg = args[0] ?? "";
52419
+ const legacySubs = ["run", "judge", "run-and-judge", "status"];
52420
+ const subcommand = legacySubs.includes(firstArg) ? firstArg : "run";
52421
+ const rawSessionPath = getFlag(args, "--path") ?? ".";
52422
+ let sessionPath;
52423
+ try {
52424
+ sessionPath = validateSessionPath(rawSessionPath);
52425
+ } catch (err) {
52426
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
52427
+ process.exit(1);
52428
+ }
52429
+ const modelsRaw = getFlag(args, "--models");
52430
+ const judgesRaw = getFlag(args, "--judges");
52431
+ const mode = getFlag(args, "--mode") ?? "default";
52432
+ const timeoutStr = getFlag(args, "--timeout");
52433
+ const timeout = timeoutStr ? Number.parseInt(timeoutStr, 10) : 300;
52434
+ let input = getFlag(args, "--input");
52435
+ if (!input) {
52436
+ const flagsWithValues = ["--models", "--judges", "--mode", "--path", "--timeout", "--input"];
52437
+ const positionals = args.filter((a, i) => {
52438
+ if (legacySubs.includes(a) && i === 0)
52439
+ return false;
52440
+ if (a.startsWith("--"))
52441
+ return false;
52442
+ const prev = args[i - 1];
52443
+ if (prev && flagsWithValues.includes(prev))
52444
+ return false;
52445
+ return true;
52446
+ });
52447
+ if (positionals.length > 0)
52448
+ input = positionals.join(" ");
52449
+ }
52450
+ const models = modelsRaw ? modelsRaw.split(",").map((m) => m.trim()).filter(Boolean) : [];
52451
+ const judges = judgesRaw ? judgesRaw.split(",").map((m) => m.trim()).filter(Boolean) : undefined;
52452
+ const effectiveMode = hasFlag(args, "--interactive") ? "interactive" : hasFlag(args, "--grid") ? "default" : mode;
52453
+ switch (subcommand) {
52454
+ case "run": {
52455
+ if (models.length === 0) {
52456
+ console.error("Error: --models is required");
52457
+ printHelp();
52458
+ process.exit(1);
52459
+ }
52460
+ if (effectiveMode === "json") {
52461
+ setupSession(sessionPath, models, input);
52462
+ const runStatus = await runModels(sessionPath, {
52463
+ timeout,
52464
+ onStatusChange: (id, s) => {
52465
+ process.stderr.write(`[team] ${id}: ${s.state}
52466
+ `);
52467
+ }
52468
+ });
52469
+ printStatus(runStatus);
52470
+ } else {
52471
+ const { runWithGrid: runWithGrid2 } = await Promise.resolve().then(() => (init_team_grid(), exports_team_grid));
52472
+ const gridStatus = await runWithGrid2(sessionPath, models, input ?? "", {
52473
+ timeout,
52474
+ mode: effectiveMode === "interactive" ? "interactive" : "default"
52475
+ });
52476
+ printStatus(gridStatus);
52477
+ }
52478
+ break;
52479
+ }
52480
+ case "judge": {
52481
+ await judgeResponses(sessionPath, { judges });
52482
+ console.log(readFileSync23(join31(sessionPath, "verdict.md"), "utf-8"));
52483
+ break;
52484
+ }
52485
+ case "run-and-judge": {
52486
+ if (models.length === 0) {
52487
+ console.error("Error: --models is required");
52488
+ process.exit(1);
52489
+ }
52490
+ setupSession(sessionPath, models, input);
52491
+ const status = await runModels(sessionPath, {
52492
+ timeout,
52493
+ onStatusChange: (id, s) => {
52494
+ process.stderr.write(`[team] ${id}: ${s.state}
52495
+ `);
52496
+ }
52497
+ });
52498
+ printStatus(status);
52499
+ await judgeResponses(sessionPath, { judges });
52500
+ console.log(readFileSync23(join31(sessionPath, "verdict.md"), "utf-8"));
52501
+ break;
52502
+ }
52503
+ case "status": {
52504
+ const statusResult = getStatus(sessionPath);
52505
+ printStatus(statusResult);
52506
+ break;
52507
+ }
52508
+ }
52509
+ }
52510
+ var init_team_cli = __esm(() => {
52511
+ init_team_orchestrator();
52512
+ });
52513
+
51524
52514
  // src/auth/credentials/source.ts
51525
52515
  function describeSourceSync(p, config3) {
51526
52516
  if (p.isLocal)
@@ -62932,8 +63922,8 @@ var init_RemoveFileError = __esm(() => {
62932
63922
  });
62933
63923
 
62934
63924
  // ../../node_modules/.bun/@inquirer+external-editor@2.0.1+04f2146be16c61ef/node_modules/@inquirer/external-editor/dist/index.js
62935
- import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
62936
- import { readFileSync as readFileSync22, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "fs";
63925
+ import { spawn as spawn4, spawnSync as spawnSync2 } from "child_process";
63926
+ import { readFileSync as readFileSync24, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "fs";
62937
63927
  import path from "path";
62938
63928
  import os from "os";
62939
63929
  import { randomUUID as randomUUID5 } from "crypto";
@@ -63042,14 +64032,14 @@ class ExternalEditor {
63042
64032
  if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
63043
64033
  opt.mode = this.fileOptions.mode;
63044
64034
  }
63045
- writeFileSync15(this.tempFile, this.text, opt);
64035
+ writeFileSync16(this.tempFile, this.text, opt);
63046
64036
  } catch (createFileError) {
63047
64037
  throw new CreateFileError(createFileError);
63048
64038
  }
63049
64039
  }
63050
64040
  readTemporaryFile() {
63051
64041
  try {
63052
- const tempFileBuffer = readFileSync22(this.tempFile);
64042
+ const tempFileBuffer = readFileSync24(this.tempFile);
63053
64043
  if (tempFileBuffer.length === 0) {
63054
64044
  this.text = "";
63055
64045
  } else {
@@ -63080,7 +64070,7 @@ class ExternalEditor {
63080
64070
  }
63081
64071
  launchEditorAsync(callback) {
63082
64072
  try {
63083
- const editorProcess = spawn3(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" });
64073
+ const editorProcess = spawn4(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" });
63084
64074
  editorProcess.on("exit", (code) => {
63085
64075
  this.lastExitStatus = code;
63086
64076
  setImmediate(callback);
@@ -64030,9 +65020,9 @@ var init_dist16 = __esm(() => {
64030
65020
 
64031
65021
  // src/auth/antigravity-oauth.ts
64032
65022
  import { spawnSync as spawnSync3 } from "child_process";
64033
- import { existsSync as existsSync23, unlinkSync as unlinkSync6 } from "fs";
65023
+ import { existsSync as existsSync24, unlinkSync as unlinkSync6 } from "fs";
64034
65024
  import { homedir as homedir28 } from "os";
64035
- import { join as join30 } from "path";
65025
+ import { join as join32 } from "path";
64036
65026
  async function defaultSuggestModel() {
64037
65027
  try {
64038
65028
  const tok = readSharedAntigravityToken();
@@ -64153,8 +65143,8 @@ No session detected yet. Starting the Antigravity CLI interactively \u2014
64153
65143
  async logout(deps) {
64154
65144
  deleteSharedAntigravityToken(deps);
64155
65145
  try {
64156
- const tokenFile = join30(homedir28(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
64157
- if (existsSync23(tokenFile))
65146
+ const tokenFile = join32(homedir28(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
65147
+ if (existsSync24(tokenFile))
64158
65148
  unlinkSync6(tokenFile);
64159
65149
  } catch {}
64160
65150
  log("[AntigravityOAuth] Antigravity session cleared (keychain + agy token file)");
@@ -64684,6 +65674,7 @@ var init_model_catalog2 = __esm(() => {
64684
65674
  // src/model-selector.ts
64685
65675
  var exports_model_selector = {};
64686
65676
  __export(exports_model_selector, {
65677
+ warnDiscoveryFailure: () => warnDiscoveryFailure,
64687
65678
  selectProfile: () => selectProfile,
64688
65679
  selectModelsForProfile: () => selectModelsForProfile,
64689
65680
  selectModel: () => selectModel,
@@ -65235,6 +66226,26 @@ function describeOffer(offer) {
65235
66226
  });
65236
66227
  return `FREE until ${until}`;
65237
66228
  }
66229
+ function warnDiscoveryFailure(provider, displayName, def) {
66230
+ const failure = getDiscoveryFailure(provider);
66231
+ if (!failure || failure.kind === "empty-roster")
66232
+ return;
66233
+ process.stderr.write(`
66234
+ \u26A0 ${displayName} could not list its models: ${describeDiscoveryFailure(failure)}
66235
+ `);
66236
+ if (failure.kind === "unauthorized" || failure.kind === "no-credentials") {
66237
+ if (def.apiKeyEnvVar) {
66238
+ process.stderr.write(` Check ${def.apiKeyEnvVar} (a value in your shell overrides stored credentials).
66239
+ `);
66240
+ }
66241
+ if (def.apiKeyUrl)
66242
+ process.stderr.write(` Get a key: ${def.apiKeyUrl}
66243
+ `);
66244
+ }
66245
+ process.stderr.write(` Falling back to manual model entry.
66246
+
66247
+ `);
66248
+ }
65238
66249
  async function buildDiscoveredModelRows(provider, displayName, catalog) {
65239
66250
  const discovered = rankDiscoveredModels(await discoverProviderModels(provider)).filter((m) => isChatCapable(m.id));
65240
66251
  if (discovered.length === 0)
@@ -65285,6 +66296,8 @@ async function selectModelFromProvider(provider, tierName, recommendedModels, _f
65285
66296
  const picked = await pickModelFromList(provider, displayName, tierName, discoveredModels);
65286
66297
  if (picked)
65287
66298
  return picked;
66299
+ } else {
66300
+ warnDiscoveryFailure(provider, displayName, def);
65288
66301
  }
65289
66302
  }
65290
66303
  if (isUserDeployedProvider(provider)) {
@@ -65499,6 +66512,7 @@ var init_model_selector = __esm(() => {
65499
66512
  kimi: { name: "Kimi / Moonshot", description: "Direct API" },
65500
66513
  "kimi-coding": { name: "Kimi Coding", description: "Coding subscription" },
65501
66514
  "qwen-cloud": { name: "Qwen Plan", description: "Alibaba Model Studio subscription" },
66515
+ "qwen-payg": { name: "Qwen PAYG", description: "Alibaba Model Studio pay-as-you-go" },
65502
66516
  glm: { name: "GLM / Zhipu", description: "Direct API" },
65503
66517
  "glm-coding": { name: "GLM Coding Plan", description: "Coding subscription" },
65504
66518
  "z-ai": { name: "Z.AI", description: "Direct API" },
@@ -65528,6 +66542,7 @@ var init_model_selector = __esm(() => {
65528
66542
  "kimi",
65529
66543
  "kimi-coding",
65530
66544
  "qwen-cloud",
66545
+ "qwen-payg",
65531
66546
  "glm",
65532
66547
  "glm-coding",
65533
66548
  "z-ai",
@@ -68215,22 +69230,22 @@ __export(exports_cli, {
68215
69230
  });
68216
69231
  import {
68217
69232
  copyFileSync as copyFileSync2,
68218
- existsSync as existsSync24,
69233
+ existsSync as existsSync25,
68219
69234
  mkdirSync as mkdirSync14,
68220
- readFileSync as readFileSync23,
69235
+ readFileSync as readFileSync25,
68221
69236
  readdirSync as readdirSync5,
68222
69237
  unlinkSync as unlinkSync7,
68223
- writeFileSync as writeFileSync16
69238
+ writeFileSync as writeFileSync17
68224
69239
  } from "fs";
68225
69240
  import { homedir as homedir29 } from "os";
68226
- import { dirname as dirname10, join as join31 } from "path";
68227
- import { fileURLToPath as fileURLToPath2 } from "url";
69241
+ import { dirname as dirname11, join as join33 } from "path";
69242
+ import { fileURLToPath as fileURLToPath3 } from "url";
68228
69243
  function getVersion3() {
68229
69244
  return VERSION;
68230
69245
  }
68231
69246
  function clearAllModelCaches() {
68232
- const cacheDir = join31(homedir29(), ".claudish");
68233
- if (!existsSync24(cacheDir))
69247
+ const cacheDir = join33(homedir29(), ".claudish");
69248
+ if (!existsSync25(cacheDir))
68234
69249
  return;
68235
69250
  const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
68236
69251
  let cleared = 0;
@@ -68238,7 +69253,7 @@ function clearAllModelCaches() {
68238
69253
  const files = readdirSync5(cacheDir);
68239
69254
  for (const file2 of files) {
68240
69255
  if (cachePatterns.includes(file2)) {
68241
- unlinkSync7(join31(cacheDir, file2));
69256
+ unlinkSync7(join33(cacheDir, file2));
68242
69257
  cleared++;
68243
69258
  }
68244
69259
  }
@@ -68471,7 +69486,7 @@ async function parseArgs(args) {
68471
69486
  printVersion();
68472
69487
  process.exit(0);
68473
69488
  } else if (arg === "--help" || arg === "-h") {
68474
- printHelp();
69489
+ printHelp2();
68475
69490
  process.exit(0);
68476
69491
  } else if (arg === "--help-ai") {
68477
69492
  printAIAgentGuide();
@@ -68653,15 +69668,15 @@ Usage: claudish --models --provider <slug>`);
68653
69668
  });
68654
69669
  config3.resolvedDefaultProvider = resolved;
68655
69670
  if (resolved.legacyAutoPromoted && !config3.quiet) {
68656
- const markerFile = join31(homedir29(), ".claudish", ".legacy-litellm-hint-shown");
68657
- if (!existsSync24(markerFile)) {
69671
+ const markerFile = join33(homedir29(), ".claudish", ".legacy-litellm-hint-shown");
69672
+ if (!existsSync25(markerFile)) {
68658
69673
  const hint = buildLegacyHint(resolved);
68659
69674
  if (hint) {
68660
69675
  console.error(hint);
68661
69676
  }
68662
69677
  try {
68663
- mkdirSync14(dirname10(markerFile), { recursive: true });
68664
- writeFileSync16(markerFile, new Date().toISOString(), "utf-8");
69678
+ mkdirSync14(dirname11(markerFile), { recursive: true });
69679
+ writeFileSync17(markerFile, new Date().toISOString(), "utf-8");
68665
69680
  } catch {}
68666
69681
  }
68667
69682
  }
@@ -69106,6 +70121,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
69106
70121
  "kimi",
69107
70122
  "kimi-coding",
69108
70123
  "qwen-cloud",
70124
+ "qwen-payg",
69109
70125
  "z-ai"
69110
70126
  ];
69111
70127
  const isMinimaxModel = modelName.toLowerCase().includes("minimax");
@@ -69427,7 +70443,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
69427
70443
  await tui.shutdown();
69428
70444
  }
69429
70445
  }
69430
- function printHelp() {
70446
+ function printHelp2() {
69431
70447
  const useColor = !!process.stdout.isTTY && !process.env.NO_COLOR;
69432
70448
  const c = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
69433
70449
  const bold4 = c("1");
@@ -69730,8 +70746,8 @@ ${h("MORE INFO")}
69730
70746
  }
69731
70747
  function printAIAgentGuide() {
69732
70748
  try {
69733
- const guidePath = join31(__dirname3, "../AI_AGENT_GUIDE.md");
69734
- const guideContent = readFileSync23(guidePath, "utf-8");
70749
+ const guidePath = join33(__dirname3, "../AI_AGENT_GUIDE.md");
70750
+ const guideContent = readFileSync25(guidePath, "utf-8");
69735
70751
  console.log(guideContent);
69736
70752
  } catch (error46) {
69737
70753
  console.error("Error reading AI Agent Guide:");
@@ -69747,19 +70763,19 @@ async function initializeClaudishSkill() {
69747
70763
  console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
69748
70764
  `);
69749
70765
  const cwd = process.cwd();
69750
- const claudeDir = join31(cwd, ".claude");
69751
- const skillsDir = join31(claudeDir, "skills");
69752
- const claudishSkillDir = join31(skillsDir, "claudish-usage");
69753
- const skillFile = join31(claudishSkillDir, "SKILL.md");
69754
- if (existsSync24(skillFile)) {
70766
+ const claudeDir = join33(cwd, ".claude");
70767
+ const skillsDir = join33(claudeDir, "skills");
70768
+ const claudishSkillDir = join33(skillsDir, "claudish-usage");
70769
+ const skillFile = join33(claudishSkillDir, "SKILL.md");
70770
+ if (existsSync25(skillFile)) {
69755
70771
  console.log("\u2705 Claudish skill already installed at:");
69756
70772
  console.log(` ${skillFile}
69757
70773
  `);
69758
70774
  console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
69759
70775
  return;
69760
70776
  }
69761
- const sourceSkillPath = join31(__dirname3, "../skills/claudish-usage/SKILL.md");
69762
- if (!existsSync24(sourceSkillPath)) {
70777
+ const sourceSkillPath = join33(__dirname3, "../skills/claudish-usage/SKILL.md");
70778
+ if (!existsSync25(sourceSkillPath)) {
69763
70779
  console.error("\u274C Error: Claudish skill file not found in installation.");
69764
70780
  console.error(` Expected at: ${sourceSkillPath}`);
69765
70781
  console.error(`
@@ -69768,15 +70784,15 @@ async function initializeClaudishSkill() {
69768
70784
  process.exit(1);
69769
70785
  }
69770
70786
  try {
69771
- if (!existsSync24(claudeDir)) {
70787
+ if (!existsSync25(claudeDir)) {
69772
70788
  mkdirSync14(claudeDir, { recursive: true });
69773
70789
  console.log("\uD83D\uDCC1 Created .claude/ directory");
69774
70790
  }
69775
- if (!existsSync24(skillsDir)) {
70791
+ if (!existsSync25(skillsDir)) {
69776
70792
  mkdirSync14(skillsDir, { recursive: true });
69777
70793
  console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
69778
70794
  }
69779
- if (!existsSync24(claudishSkillDir)) {
70795
+ if (!existsSync25(claudishSkillDir)) {
69780
70796
  mkdirSync14(claudishSkillDir, { recursive: true });
69781
70797
  console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
69782
70798
  }
@@ -69848,8 +70864,8 @@ var init_cli = __esm(() => {
69848
70864
  init_provider_definitions();
69849
70865
  init_routing_rules();
69850
70866
  init_provider_resolver();
69851
- __filename3 = fileURLToPath2(import.meta.url);
69852
- __dirname3 = dirname10(__filename3);
70867
+ __filename3 = fileURLToPath3(import.meta.url);
70868
+ __dirname3 = dirname11(__filename3);
69853
70869
  });
69854
70870
 
69855
70871
  // src/update-checker.ts
@@ -69861,33 +70877,33 @@ __export(exports_update_checker, {
69861
70877
  clearCache: () => clearCache,
69862
70878
  checkForUpdates: () => checkForUpdates
69863
70879
  });
69864
- import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync8, writeFileSync as writeFileSync17 } from "fs";
70880
+ import { existsSync as existsSync26, mkdirSync as mkdirSync15, readFileSync as readFileSync26, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18 } from "fs";
69865
70881
  import { homedir as homedir30, platform as platform2, tmpdir } from "os";
69866
- import { join as join32 } from "path";
70882
+ import { join as join34 } from "path";
69867
70883
  function getCacheFilePath() {
69868
70884
  let cacheDir;
69869
70885
  if (isWindows) {
69870
- const localAppData = process.env.LOCALAPPDATA || join32(homedir30(), "AppData", "Local");
69871
- cacheDir = join32(localAppData, "claudish");
70886
+ const localAppData = process.env.LOCALAPPDATA || join34(homedir30(), "AppData", "Local");
70887
+ cacheDir = join34(localAppData, "claudish");
69872
70888
  } else {
69873
- cacheDir = join32(homedir30(), ".cache", "claudish");
70889
+ cacheDir = join34(homedir30(), ".cache", "claudish");
69874
70890
  }
69875
70891
  try {
69876
- if (!existsSync25(cacheDir)) {
70892
+ if (!existsSync26(cacheDir)) {
69877
70893
  mkdirSync15(cacheDir, { recursive: true });
69878
70894
  }
69879
- return join32(cacheDir, "update-check.json");
70895
+ return join34(cacheDir, "update-check.json");
69880
70896
  } catch {
69881
- return join32(tmpdir(), "claudish-update-check.json");
70897
+ return join34(tmpdir(), "claudish-update-check.json");
69882
70898
  }
69883
70899
  }
69884
70900
  function readCache() {
69885
70901
  try {
69886
70902
  const cachePath = getCacheFilePath();
69887
- if (!existsSync25(cachePath)) {
70903
+ if (!existsSync26(cachePath)) {
69888
70904
  return null;
69889
70905
  }
69890
- const data = JSON.parse(readFileSync24(cachePath, "utf-8"));
70906
+ const data = JSON.parse(readFileSync26(cachePath, "utf-8"));
69891
70907
  return data;
69892
70908
  } catch {
69893
70909
  return null;
@@ -69900,7 +70916,7 @@ function writeCache(latestVersion) {
69900
70916
  lastCheck: Date.now(),
69901
70917
  latestVersion
69902
70918
  };
69903
- writeFileSync17(cachePath, JSON.stringify(data), "utf-8");
70919
+ writeFileSync18(cachePath, JSON.stringify(data), "utf-8");
69904
70920
  } catch {}
69905
70921
  }
69906
70922
  function isCacheValid(cache2) {
@@ -69910,7 +70926,7 @@ function isCacheValid(cache2) {
69910
70926
  function clearCache() {
69911
70927
  try {
69912
70928
  const cachePath = getCacheFilePath();
69913
- if (existsSync25(cachePath)) {
70929
+ if (existsSync26(cachePath)) {
69914
70930
  unlinkSync8(cachePath);
69915
70931
  }
69916
70932
  } catch {}
@@ -69998,7 +71014,7 @@ var exports_update_command = {};
69998
71014
  __export(exports_update_command, {
69999
71015
  updateCommand: () => updateCommand
70000
71016
  });
70001
- import { execSync } from "child_process";
71017
+ import { execSync as execSync2 } from "child_process";
70002
71018
  function detectInstallationMethod() {
70003
71019
  const scriptPath = process.argv[1] || "";
70004
71020
  if (scriptPath.includes("/opt/homebrew/") || scriptPath.includes("/usr/local/Cellar/")) {
@@ -70026,7 +71042,7 @@ function getUpdateCommand(method) {
70026
71042
  }
70027
71043
  async function executeUpdate(command) {
70028
71044
  try {
70029
- execSync(command, {
71045
+ execSync2(command, {
70030
71046
  stdio: "inherit",
70031
71047
  shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh"
70032
71048
  });
@@ -70163,7 +71179,7 @@ ${BOLD2}Unable to detect installation method.${RESET2}`);
70163
71179
  }
70164
71180
  function fetchLatestVersionViaNpm() {
70165
71181
  try {
70166
- const output = execSync("npm view claudish version", {
71182
+ const output = execSync2("npm view claudish version", {
70167
71183
  encoding: "utf-8",
70168
71184
  timeout: 20000,
70169
71185
  stdio: ["ignore", "pipe", "ignore"]
@@ -70795,15 +71811,15 @@ var init_local_liveness = __esm(() => {
70795
71811
  });
70796
71812
 
70797
71813
  // src/providers/probe-catalog.ts
70798
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync18 } from "fs";
71814
+ import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync27, writeFileSync as writeFileSync19 } from "fs";
70799
71815
  import { homedir as homedir31 } from "os";
70800
- import { dirname as dirname11, join as join33 } from "path";
71816
+ import { dirname as dirname12, join as join35 } from "path";
70801
71817
  function readProbeModelsCache(path2 = PROBE_MODELS_CACHE_PATH) {
70802
- if (!existsSync26(path2))
71818
+ if (!existsSync27(path2))
70803
71819
  return null;
70804
71820
  let raw2;
70805
71821
  try {
70806
- raw2 = JSON.parse(readFileSync25(path2, "utf-8"));
71822
+ raw2 = JSON.parse(readFileSync27(path2, "utf-8"));
70807
71823
  } catch {
70808
71824
  return null;
70809
71825
  }
@@ -70812,8 +71828,8 @@ function readProbeModelsCache(path2 = PROBE_MODELS_CACHE_PATH) {
70812
71828
  return raw2;
70813
71829
  }
70814
71830
  function writeProbeModelsCache(data, path2 = PROBE_MODELS_CACHE_PATH) {
70815
- mkdirSync16(dirname11(path2), { recursive: true });
70816
- writeFileSync18(path2, JSON.stringify(data), "utf-8");
71831
+ mkdirSync16(dirname12(path2), { recursive: true });
71832
+ writeFileSync19(path2, JSON.stringify(data), "utf-8");
70817
71833
  }
70818
71834
  function isCacheFresh(data, ttlMs = CACHE_TTL_MS4) {
70819
71835
  if (!data?.generatedAt)
@@ -70932,7 +71948,7 @@ function isValidResponse(raw2) {
70932
71948
  var PROBE_MODELS_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/probeModels", CACHE_TTL_MS4, FETCH_TIMEOUT_MS3 = 15000, PROBE_MODELS_CACHE_PATH, _inFlight = null;
70933
71949
  var init_probe_catalog = __esm(() => {
70934
71950
  CACHE_TTL_MS4 = 60 * 60 * 1000;
70935
- PROBE_MODELS_CACHE_PATH = join33(homedir31(), ".claudish", "probe-models.json");
71951
+ PROBE_MODELS_CACHE_PATH = join35(homedir31(), ".claudish", "probe-models.json");
70936
71952
  });
70937
71953
 
70938
71954
  // src/tui/constants.ts
@@ -77279,20 +78295,20 @@ __export(exports_claude_runner, {
77279
78295
  MIN_AUTO_COMPACT_WINDOW: () => MIN_AUTO_COMPACT_WINDOW,
77280
78296
  CLAUDE_CODE_DEFAULT_MAX_CONTEXT: () => CLAUDE_CODE_DEFAULT_MAX_CONTEXT
77281
78297
  });
77282
- import { spawn as spawn4 } from "child_process";
78298
+ import { spawn as spawn5 } from "child_process";
77283
78299
  import {
77284
78300
  closeSync as closeSync4,
77285
- existsSync as existsSync27,
78301
+ existsSync as existsSync28,
77286
78302
  mkdirSync as mkdirSync17,
77287
78303
  openSync as openSync4,
77288
- readFileSync as readFileSync26,
78304
+ readFileSync as readFileSync28,
77289
78305
  readdirSync as readdirSync6,
77290
78306
  statSync as statSync5,
77291
78307
  unlinkSync as unlinkSync9,
77292
- writeFileSync as writeFileSync19
78308
+ writeFileSync as writeFileSync20
77293
78309
  } from "fs";
77294
78310
  import { homedir as homedir32, tmpdir as tmpdir2 } from "os";
77295
- import { dirname as dirname12, join as join34 } from "path";
78311
+ import { dirname as dirname13, join as join36 } from "path";
77296
78312
  import { isatty } from "tty";
77297
78313
  function releaseTerminalIsolation() {
77298
78314
  if (!restoreTerminal)
@@ -77327,14 +78343,14 @@ function isProxyAuthMode(config3) {
77327
78343
  }
77328
78344
  function managedSettingsPath() {
77329
78345
  if (isWindows2()) {
77330
- return join34(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
78346
+ return join36(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
77331
78347
  }
77332
78348
  if (process.platform === "darwin") {
77333
78349
  return "/Library/Application Support/ClaudeCode/managed-settings.json";
77334
78350
  }
77335
78351
  return "/etc/claude-code/managed-settings.json";
77336
78352
  }
77337
- function managedSettingsForcesClaudeAi(readFile3 = readFileSync26) {
78353
+ function managedSettingsForcesClaudeAi(readFile3 = readFileSync28) {
77338
78354
  try {
77339
78355
  const raw2 = readFile3(managedSettingsPath(), "utf-8");
77340
78356
  const parsed = JSON.parse(raw2);
@@ -77348,9 +78364,9 @@ function isWindows2() {
77348
78364
  }
77349
78365
  function createStatusLineScript(tokenFilePath) {
77350
78366
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
77351
- const claudishDir = join34(homeDir, ".claudish");
78367
+ const claudishDir = join36(homeDir, ".claudish");
77352
78368
  const timestamp = Date.now();
77353
- const scriptPath = join34(claudishDir, `status-${timestamp}.js`);
78369
+ const scriptPath = join36(claudishDir, `status-${timestamp}.js`);
77354
78370
  const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
77355
78371
  const script = `
77356
78372
  const fs = require('fs');
@@ -77477,13 +78493,13 @@ process.stdin.on('end', () => {
77477
78493
  }
77478
78494
  });
77479
78495
  `;
77480
- writeFileSync19(scriptPath, script, "utf-8");
78496
+ writeFileSync20(scriptPath, script, "utf-8");
77481
78497
  return scriptPath;
77482
78498
  }
77483
78499
  function initializeTokenFile(tokenFilePath) {
77484
78500
  try {
77485
- mkdirSync17(dirname12(tokenFilePath), { recursive: true });
77486
- writeFileSync19(tokenFilePath, JSON.stringify({
78501
+ mkdirSync17(dirname13(tokenFilePath), { recursive: true });
78502
+ writeFileSync20(tokenFilePath, JSON.stringify({
77487
78503
  input_tokens: 0,
77488
78504
  output_tokens: 0,
77489
78505
  total_tokens: 0,
@@ -77514,7 +78530,7 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
77514
78530
  if (!name.startsWith("tokens-") || !name.endsWith(".json"))
77515
78531
  continue;
77516
78532
  scanned++;
77517
- const full = join34(dir, name);
78533
+ const full = join36(dir, name);
77518
78534
  try {
77519
78535
  if (statSync5(full).mtimeMs >= cutoff)
77520
78536
  continue;
@@ -77531,7 +78547,7 @@ function parseSettingsArg(value) {
77531
78547
  if (value.trimStart().startsWith("{")) {
77532
78548
  return JSON.parse(value);
77533
78549
  }
77534
- return JSON.parse(readFileSync26(value, "utf-8"));
78550
+ return JSON.parse(readFileSync28(value, "utf-8"));
77535
78551
  }
77536
78552
  function parseSettingsArgSafe(value) {
77537
78553
  try {
@@ -77543,13 +78559,13 @@ function parseSettingsArgSafe(value) {
77543
78559
  }
77544
78560
  function userSettingsFileCandidates(cwd) {
77545
78561
  return [
77546
- join34(homedir32(), ".claude", "settings.json"),
77547
- join34(cwd, ".claude", "settings.json"),
77548
- join34(cwd, ".claude", "settings.local.json")
78562
+ join36(homedir32(), ".claude", "settings.json"),
78563
+ join36(cwd, ".claude", "settings.json"),
78564
+ join36(cwd, ".claude", "settings.local.json")
77549
78565
  ];
77550
78566
  }
77551
78567
  function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
77552
- const sources = userSettingsFileCandidates(cwd).filter((file2) => existsSync27(file2));
78568
+ const sources = userSettingsFileCandidates(cwd).filter((file2) => existsSync28(file2));
77553
78569
  const idx = claudeArgs.indexOf("--settings");
77554
78570
  const settingsArg = idx === -1 ? undefined : claudeArgs[idx + 1];
77555
78571
  if (settingsArg)
@@ -77586,13 +78602,13 @@ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
77586
78602
  }
77587
78603
  function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
77588
78604
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
77589
- const claudishDir = join34(homeDir, ".claudish");
78605
+ const claudishDir = join36(homeDir, ".claudish");
77590
78606
  try {
77591
78607
  mkdirSync17(claudishDir, { recursive: true });
77592
78608
  } catch {}
77593
78609
  const timestamp = Date.now();
77594
- const tempPath = join34(claudishDir, `settings-${timestamp}.json`);
77595
- const tokenFilePath = join34(claudishDir, `tokens-${port}.json`);
78610
+ const tempPath = join36(claudishDir, `settings-${timestamp}.json`);
78611
+ const tokenFilePath = join36(claudishDir, `tokens-${port}.json`);
77596
78612
  cleanupStaleTokenFiles(claudishDir);
77597
78613
  initializeTokenFile(tokenFilePath);
77598
78614
  let statusCommand;
@@ -77625,7 +78641,7 @@ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLi
77625
78641
  padding: 0
77626
78642
  };
77627
78643
  const settings = buildClaudishSettingsOverlay(statusLine, proxyAuthMode);
77628
- writeFileSync19(tempPath, JSON.stringify(settings, null, 2), "utf-8");
78644
+ writeFileSync20(tempPath, JSON.stringify(settings, null, 2), "utf-8");
77629
78645
  return { path: tempPath, statusLine, tokenFilePath };
77630
78646
  }
77631
78647
  function buildClaudishSettingsOverlay(statusLine, proxyAuthMode) {
@@ -77650,7 +78666,7 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine, proxy
77650
78666
  if (proxyAuthMode && !("forceLoginMethod" in userSettings)) {
77651
78667
  userSettings.forceLoginMethod = "console";
77652
78668
  }
77653
- writeFileSync19(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
78669
+ writeFileSync20(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
77654
78670
  } catch {
77655
78671
  if (!config3.quiet) {
77656
78672
  console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
@@ -77843,7 +78859,7 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
77843
78859
  console.error(`
77844
78860
  Or set CLAUDE_PATH to your custom installation:`);
77845
78861
  const home = homedir32();
77846
- const localPath = isWindows2() ? join34(home, ".claude", "local", "claude.exe") : join34(home, ".claude", "local", "claude");
78862
+ const localPath = isWindows2() ? join36(home, ".claude", "local", "claude.exe") : join36(home, ".claude", "local", "claude");
77847
78863
  console.error(` export CLAUDE_PATH=${localPath}`);
77848
78864
  process.exit(1);
77849
78865
  }
@@ -77867,7 +78883,7 @@ Or set CLAUDE_PATH to your custom installation:`);
77867
78883
  console.error("[claudish] An interactive session was requested but no terminal is attached (stdin and stdout are both non-TTY). Pass a prompt argument, or use --stdin / -p for non-interactive mode.");
77868
78884
  }
77869
78885
  const stdio = ttyFd !== undefined ? [0, ttyFd, ttyFd] : "inherit";
77870
- const proc = spawn4(spawnCommand, claudeArgs, {
78886
+ const proc = spawn5(spawnCommand, claudeArgs, {
77871
78887
  env,
77872
78888
  stdio,
77873
78889
  shell: needsShell
@@ -77923,23 +78939,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
77923
78939
  async function findClaudeBinary() {
77924
78940
  const isWindows3 = process.platform === "win32";
77925
78941
  if (process.env.CLAUDE_PATH) {
77926
- if (existsSync27(process.env.CLAUDE_PATH)) {
78942
+ if (existsSync28(process.env.CLAUDE_PATH)) {
77927
78943
  return process.env.CLAUDE_PATH;
77928
78944
  }
77929
78945
  }
77930
78946
  const home = homedir32();
77931
- const localPath = isWindows3 ? join34(home, ".claude", "local", "claude.exe") : join34(home, ".claude", "local", "claude");
77932
- if (existsSync27(localPath)) {
78947
+ const localPath = isWindows3 ? join36(home, ".claude", "local", "claude.exe") : join36(home, ".claude", "local", "claude");
78948
+ if (existsSync28(localPath)) {
77933
78949
  return localPath;
77934
78950
  }
77935
78951
  if (isWindows3) {
77936
78952
  const windowsPaths = [
77937
- join34(home, "AppData", "Roaming", "npm", "claude.cmd"),
77938
- join34(home, ".npm-global", "claude.cmd"),
77939
- join34(home, "node_modules", ".bin", "claude.cmd")
78953
+ join36(home, "AppData", "Roaming", "npm", "claude.cmd"),
78954
+ join36(home, ".npm-global", "claude.cmd"),
78955
+ join36(home, "node_modules", ".bin", "claude.cmd")
77940
78956
  ];
77941
78957
  for (const path2 of windowsPaths) {
77942
- if (existsSync27(path2)) {
78958
+ if (existsSync28(path2)) {
77943
78959
  return path2;
77944
78960
  }
77945
78961
  }
@@ -77947,21 +78963,21 @@ async function findClaudeBinary() {
77947
78963
  const commonPaths = [
77948
78964
  "/usr/local/bin/claude",
77949
78965
  "/opt/homebrew/bin/claude",
77950
- join34(home, ".npm-global/bin/claude"),
77951
- join34(home, ".local/bin/claude"),
77952
- join34(home, "node_modules/.bin/claude"),
78966
+ join36(home, ".npm-global/bin/claude"),
78967
+ join36(home, ".local/bin/claude"),
78968
+ join36(home, "node_modules/.bin/claude"),
77953
78969
  "/data/data/com.termux/files/usr/bin/claude",
77954
- join34(home, "../usr/bin/claude")
78970
+ join36(home, "../usr/bin/claude")
77955
78971
  ];
77956
78972
  for (const path2 of commonPaths) {
77957
- if (existsSync27(path2)) {
78973
+ if (existsSync28(path2)) {
77958
78974
  return path2;
77959
78975
  }
77960
78976
  }
77961
78977
  }
77962
78978
  try {
77963
78979
  const shellCommand = isWindows3 ? "where claude" : "command -v claude";
77964
- const proc = spawn4(shellCommand, [], {
78980
+ const proc = spawn5(shellCommand, [], {
77965
78981
  stdio: "pipe",
77966
78982
  shell: true
77967
78983
  });
@@ -78013,18 +79029,18 @@ __export(exports_diag_output, {
78013
79029
  NullDiagOutput: () => NullDiagOutput,
78014
79030
  LogFileDiagOutput: () => LogFileDiagOutput
78015
79031
  });
78016
- import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync18, unlinkSync as unlinkSync10, writeFileSync as writeFileSync20 } from "fs";
79032
+ import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync18, unlinkSync as unlinkSync10, writeFileSync as writeFileSync21 } from "fs";
78017
79033
  import { homedir as homedir33 } from "os";
78018
- import { join as join35 } from "path";
79034
+ import { join as join37 } from "path";
78019
79035
  function getClaudishDir() {
78020
- const dir = join35(homedir33(), ".claudish");
79036
+ const dir = join37(homedir33(), ".claudish");
78021
79037
  try {
78022
79038
  mkdirSync18(dir, { recursive: true });
78023
79039
  } catch {}
78024
79040
  return dir;
78025
79041
  }
78026
79042
  function getDiagLogPath() {
78027
- return join35(getClaudishDir(), `diag-${process.pid}.log`);
79043
+ return join37(getClaudishDir(), `diag-${process.pid}.log`);
78028
79044
  }
78029
79045
 
78030
79046
  class LogFileDiagOutput {
@@ -78033,7 +79049,7 @@ class LogFileDiagOutput {
78033
79049
  constructor() {
78034
79050
  this.logPath = getDiagLogPath();
78035
79051
  try {
78036
- writeFileSync20(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
79052
+ writeFileSync21(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
78037
79053
  `);
78038
79054
  } catch {}
78039
79055
  this.stream = createWriteStream3(this.logPath, { flags: "a" });
@@ -78220,279 +79236,6 @@ var init_catalog_warm = __esm(() => {
78220
79236
  ];
78221
79237
  });
78222
79238
 
78223
- // src/team-grid.ts
78224
- var exports_team_grid = {};
78225
- __export(exports_team_grid, {
78226
- runWithGrid: () => runWithGrid
78227
- });
78228
- import { spawn as spawn5 } from "child_process";
78229
- import { execSync as execSync2 } from "child_process";
78230
- import { existsSync as existsSync28, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
78231
- import { connect as netConnect } from "net";
78232
- import { dirname as dirname13, join as join36 } from "path";
78233
- import { setTimeout as wait } from "timers/promises";
78234
- import { fileURLToPath as fileURLToPath3 } from "url";
78235
- function resolveRouteInfo(modelId) {
78236
- const parsed = parseModelSpec(modelId);
78237
- if (parsed.isExplicitProvider) {
78238
- return { chain: [parsed.provider], source: "direct" };
78239
- }
78240
- const local = loadLocalConfig();
78241
- if (local?.routing && Object.keys(local.routing).length > 0) {
78242
- const matched2 = matchRoutingRule(parsed.model, local.routing);
78243
- if (matched2) {
78244
- const routes = buildRoutingChain(matched2, parsed.model);
78245
- const pattern = Object.keys(local.routing).find((k) => {
78246
- if (k === parsed.model)
78247
- return true;
78248
- if (k.includes("*")) {
78249
- const star = k.indexOf("*");
78250
- return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
78251
- }
78252
- return false;
78253
- });
78254
- return {
78255
- chain: routes.map((r) => r.displayName),
78256
- source: "project routing",
78257
- sourceDetail: pattern
78258
- };
78259
- }
78260
- }
78261
- const global_ = loadConfig();
78262
- if (global_.routing && Object.keys(global_.routing).length > 0) {
78263
- const matched2 = matchRoutingRule(parsed.model, global_.routing);
78264
- if (matched2) {
78265
- const routes = buildRoutingChain(matched2, parsed.model);
78266
- const pattern = Object.keys(global_.routing).find((k) => {
78267
- if (k === parsed.model)
78268
- return true;
78269
- if (k.includes("*")) {
78270
- const star = k.indexOf("*");
78271
- return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
78272
- }
78273
- return false;
78274
- });
78275
- return {
78276
- chain: routes.map((r) => r.displayName),
78277
- source: "user routing",
78278
- sourceDetail: pattern
78279
- };
78280
- }
78281
- }
78282
- const merged = loadRoutingRules();
78283
- const matched = matchRoutingRule(parsed.model, merged);
78284
- if (matched) {
78285
- const routes = buildRoutingChain(matched, parsed.model);
78286
- return {
78287
- chain: routes.map((r) => r.displayName),
78288
- source: "auto"
78289
- };
78290
- }
78291
- return {
78292
- chain: [],
78293
- source: "auto"
78294
- };
78295
- }
78296
- function pickBannerColor(model, used) {
78297
- let hash2 = 0;
78298
- for (let i = 0;i < model.length; i++)
78299
- hash2 = (hash2 << 5) - hash2 + model.charCodeAt(i) | 0;
78300
- const start = Math.abs(hash2) % BANNER_BG_COLORS.length;
78301
- let idx = start;
78302
- if (used.size < BANNER_BG_COLORS.length) {
78303
- while (used.has(idx))
78304
- idx = (idx + 1) % BANNER_BG_COLORS.length;
78305
- }
78306
- used.add(idx);
78307
- return BANNER_BG_COLORS[idx];
78308
- }
78309
- function buildPaneHeader(model, prompt, bg) {
78310
- const route2 = resolveRouteInfo(model);
78311
- const esc2 = (s) => s.replace(/'/g, "'\\''");
78312
- const chainStr2 = route2.chain.join(" \u2192 ");
78313
- const sourceLabel = route2.sourceDetail ? `${route2.source}: ${route2.sourceDetail}` : route2.source;
78314
- const lines = [];
78315
- lines.push(`printf '\\033[1;97;${bg}m %s \\033[0m\\n' '${esc2(model)}';`);
78316
- lines.push(`printf '\\033[2m route: ${esc2(chainStr2)} (${esc2(sourceLabel)})\\033[0m\\n' ;`);
78317
- lines.push(`printf '\\033[2m %s\\033[0m\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
78318
- const promptForShell = esc2(prompt).replace(/\n/g, "\\n");
78319
- lines.push(`printf '%b\\n' '${promptForShell}' | fold -s -w 78 | sed 's/^/ /';`);
78320
- lines.push(`printf '\\033[2m %s\\033[0m\\n\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
78321
- return lines.join(" ");
78322
- }
78323
- function findMagmuxBinary() {
78324
- const thisFile = fileURLToPath3(import.meta.url);
78325
- const thisDir = dirname13(thisFile);
78326
- const pkgRoot = join36(thisDir, "..");
78327
- const platform3 = process.platform;
78328
- const arch = process.arch;
78329
- const bundledMagmux = join36(pkgRoot, "native", `magmux-${platform3}-${arch}`);
78330
- if (existsSync28(bundledMagmux))
78331
- return bundledMagmux;
78332
- try {
78333
- const pkgName = `@claudish/magmux-${platform3}-${arch}`;
78334
- let searchDir = pkgRoot;
78335
- for (let i = 0;i < 5; i++) {
78336
- const candidate = join36(searchDir, "node_modules", pkgName, "bin", "magmux");
78337
- if (existsSync28(candidate))
78338
- return candidate;
78339
- const parent = dirname13(searchDir);
78340
- if (parent === searchDir)
78341
- break;
78342
- searchDir = parent;
78343
- }
78344
- } catch {}
78345
- try {
78346
- const result = execSync2("which magmux", { encoding: "utf-8" }).trim();
78347
- if (result)
78348
- return result;
78349
- } catch {}
78350
- throw new Error(`magmux not found. Install it:
78351
- brew install MadAppGang/tap/magmux`);
78352
- }
78353
- async function subscribeToMagmux(sockPath, onEvent) {
78354
- let client = null;
78355
- for (let attempt = 0;attempt < 40; attempt++) {
78356
- if (existsSync28(sockPath)) {
78357
- try {
78358
- client = await new Promise((resolve5, reject) => {
78359
- const s = netConnect(sockPath);
78360
- s.once("connect", () => resolve5(s));
78361
- s.once("error", reject);
78362
- });
78363
- break;
78364
- } catch {}
78365
- }
78366
- await wait(50);
78367
- }
78368
- if (!client) {
78369
- return { results: null, client: null };
78370
- }
78371
- return await new Promise((resolve5) => {
78372
- let buf = "";
78373
- let finalResults = null;
78374
- client.on("data", (chunk) => {
78375
- buf += chunk.toString("utf-8");
78376
- let nl = buf.indexOf(`
78377
- `);
78378
- while (nl >= 0) {
78379
- const line = buf.slice(0, nl).trim();
78380
- buf = buf.slice(nl + 1);
78381
- nl = buf.indexOf(`
78382
- `);
78383
- if (!line)
78384
- continue;
78385
- try {
78386
- const evt = JSON.parse(line);
78387
- onEvent?.(evt);
78388
- if (evt.type === "results") {
78389
- finalResults = evt;
78390
- }
78391
- } catch {}
78392
- }
78393
- });
78394
- const done = () => resolve5({ results: finalResults, client });
78395
- client.once("end", done);
78396
- client.once("close", done);
78397
- client.once("error", done);
78398
- });
78399
- }
78400
- function buildTeamStatus(manifest, startedAt, results) {
78401
- const anonIds = Object.keys(manifest.models);
78402
- const models = {};
78403
- for (let i = 0;i < anonIds.length; i++) {
78404
- const anonId = anonIds[i];
78405
- const result = results?.find((r) => r.pane === i);
78406
- if (!result) {
78407
- models[anonId] = {
78408
- state: "TIMEOUT",
78409
- exitCode: null,
78410
- startedAt,
78411
- completedAt: null,
78412
- outputSize: 0
78413
- };
78414
- continue;
78415
- }
78416
- let state;
78417
- switch (result.state) {
78418
- case "completed":
78419
- case "awaiting_input":
78420
- state = "COMPLETED";
78421
- break;
78422
- case "failed":
78423
- state = "FAILED";
78424
- break;
78425
- default:
78426
- state = "TIMEOUT";
78427
- }
78428
- models[anonId] = {
78429
- state,
78430
- exitCode: result.exitCode,
78431
- startedAt: result.startedAt ?? startedAt,
78432
- completedAt: result.completedAt ?? new Date().toISOString(),
78433
- outputSize: result.response?.length ?? 0
78434
- };
78435
- }
78436
- return { startedAt, models };
78437
- }
78438
- async function runWithGrid(sessionPath, models, input, opts) {
78439
- const mode = opts?.mode ?? "default";
78440
- const keep = opts?.keep ?? false;
78441
- const manifest = setupSession(sessionPath, models, input);
78442
- const startedAt = new Date().toISOString();
78443
- const gridfilePath = join36(sessionPath, "gridfile.txt");
78444
- const prompt = readFileSync27(join36(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
78445
- const rawPrompt = readFileSync27(join36(sessionPath, "input.md"), "utf-8");
78446
- const usedBannerColors = new Set;
78447
- const gridLines = Object.entries(manifest.models).map(([anonId]) => {
78448
- const model = manifest.models[anonId].model;
78449
- if (mode === "interactive") {
78450
- return `claudish --model ${model} -i --dangerously-skip-permissions '${prompt}'`;
78451
- }
78452
- const bg = pickBannerColor(model, usedBannerColors);
78453
- const header = buildPaneHeader(model, rawPrompt, bg);
78454
- return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
78455
- });
78456
- writeFileSync21(gridfilePath, `${gridLines.join(`
78457
- `)}
78458
- `, "utf-8");
78459
- const magmuxPath = findMagmuxBinary();
78460
- const spawnArgs = ["-g", gridfilePath];
78461
- if (!keep && mode === "default") {
78462
- spawnArgs.push("-w");
78463
- }
78464
- const proc = spawn5(magmuxPath, spawnArgs, {
78465
- stdio: "inherit",
78466
- env: { ...process.env }
78467
- });
78468
- const sockPath = `/tmp/magmux-${proc.pid}.sock`;
78469
- const subscription = subscribeToMagmux(sockPath);
78470
- const procExit = new Promise((resolve5) => {
78471
- proc.on("exit", () => resolve5());
78472
- proc.on("error", () => resolve5());
78473
- });
78474
- const [{ results }] = await Promise.all([subscription, procExit]);
78475
- const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
78476
- const statusPath = join36(sessionPath, "status.json");
78477
- writeFileSync21(statusPath, JSON.stringify(status, null, 2), "utf-8");
78478
- return status;
78479
- }
78480
- var BANNER_BG_COLORS;
78481
- var init_team_grid = __esm(() => {
78482
- init_profile_config();
78483
- init_model_parser();
78484
- init_routing_rules();
78485
- init_team_orchestrator();
78486
- BANNER_BG_COLORS = [
78487
- "48;2;40;90;180",
78488
- "48;2;140;60;160",
78489
- "48;2;30;130;100",
78490
- "48;2;160;80;40",
78491
- "48;2;60;120;60",
78492
- "48;2;160;50;70"
78493
- ];
78494
- });
78495
-
78496
79239
  // src/tui/viz/text.ts
78497
79240
  function columns(n, fn, arg = "width") {
78498
79241
  if (!Number.isFinite(n))
@@ -78895,7 +79638,7 @@ __export(exports_session_discovery, {
78895
79638
  import { execFile, execFileSync as execFileSync2 } from "child_process";
78896
79639
  import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
78897
79640
  import { homedir as homedir34 } from "os";
78898
- import { basename, join as join37 } from "path";
79641
+ import { basename, join as join38 } from "path";
78899
79642
  function slugForPath(absPath) {
78900
79643
  return absPath.replace(/[/.]/g, "-");
78901
79644
  }
@@ -78944,7 +79687,7 @@ function projectDirs() {
78944
79687
  }
78945
79688
  }
78946
79689
  function sessionsIn(dirName) {
78947
- const dir = join37(PROJECTS_DIR, dirName);
79690
+ const dir = join38(PROJECTS_DIR, dirName);
78948
79691
  let names;
78949
79692
  try {
78950
79693
  names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
@@ -78953,7 +79696,7 @@ function sessionsIn(dirName) {
78953
79696
  }
78954
79697
  const rows = [];
78955
79698
  for (const n of names) {
78956
- const file2 = join37(dir, n);
79699
+ const file2 = join38(dir, n);
78957
79700
  try {
78958
79701
  const st = statSync6(file2);
78959
79702
  if (st.size === 0)
@@ -79312,7 +80055,7 @@ function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
79312
80055
  }
79313
80056
  var ENTRYPOINT_BYTES = 8192, PROJECTS_DIR, ACTIVE_WINDOW_MS = 120000, HEAD_BYTES, TAIL_BYTES, HARNESS_ENVELOPES, DEEP_TAIL_BYTES, RECENT_AI_TURNS = 5, RECENT_USER_TURNS = 1;
79314
80057
  var init_session_discovery = __esm(() => {
79315
- PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
80058
+ PROJECTS_DIR = join38(homedir34(), ".claude", "projects");
79316
80059
  HEAD_BYTES = 64 * 1024;
79317
80060
  TAIL_BYTES = 128 * 1024;
79318
80061
  HARNESS_ENVELOPES = [
@@ -81004,22 +81747,26 @@ __export(exports_session_stats, {
81004
81747
  readSessionStats: () => readSessionStats,
81005
81748
  computeSavings: () => computeSavings
81006
81749
  });
81007
- import { readFileSync as readFileSync28 } from "fs";
81750
+ import { readFileSync as readFileSync29 } from "fs";
81008
81751
  import { homedir as homedir35 } from "os";
81009
- import { join as join38 } from "path";
81752
+ import { join as join39 } from "path";
81010
81753
  function tokenFilePath(port) {
81011
- return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
81754
+ return process.env.CLAUDISH_TOKEN_FILE || join39(homedir35(), ".claudish", `tokens-${port}.json`);
81012
81755
  }
81013
- function readSessionStats(port) {
81756
+ function readSessionStats(port, opts) {
81014
81757
  let raw2;
81015
81758
  try {
81016
- raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
81759
+ raw2 = JSON.parse(readFileSync29(tokenFilePath(port), "utf-8"));
81017
81760
  } catch {
81018
81761
  return null;
81019
81762
  }
81020
81763
  if (!raw2 || typeof raw2 !== "object")
81021
81764
  return null;
81022
81765
  const d = raw2;
81766
+ const processStartMs = opts?.processStartMs ?? Date.now() - Math.round(process.uptime() * 1000);
81767
+ const trackerStartedAt = num(d.started_at);
81768
+ if (trackerStartedAt <= 0 || trackerStartedAt < processStartMs)
81769
+ return null;
81023
81770
  const inputTokens = num(d.input_tokens);
81024
81771
  const outputTokens = num(d.output_tokens);
81025
81772
  if (inputTokens <= 0 && outputTokens <= 0)
@@ -81349,8 +82096,8 @@ var init_session_summary = __esm(() => {
81349
82096
  init_op_source();
81350
82097
  init_startup_trace();
81351
82098
  var import_dotenv3 = __toESM(require_main(), 1);
81352
- import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
81353
- import { join as join39, resolve as resolve5 } from "path";
82099
+ import { existsSync as existsSync29, readFileSync as readFileSync30 } from "fs";
82100
+ import { join as join40, resolve as resolve5 } from "path";
81354
82101
  import_dotenv3.config({ quiet: true });
81355
82102
  function classifyStartupKind() {
81356
82103
  const argv = process.argv.slice(2);
@@ -81484,6 +82231,7 @@ var isConfigCommand = firstPositional === "config";
81484
82231
  var isServeCommand = firstPositional === "serve";
81485
82232
  var isProvidersCommand = firstPositional === "providers";
81486
82233
  var isBehaviorCommand = firstPositional === "behavior";
82234
+ var isTeamCommand = firstPositional === "team";
81487
82235
  var isLoginCommand = firstPositional === "login";
81488
82236
  var isLogoutCommand = firstPositional === "logout";
81489
82237
  var isQuotaCommand = firstPositional === "quota" || firstPositional === "usage";
@@ -81509,6 +82257,12 @@ if (isMcpMode) {
81509
82257
  console.error(`[claudish behavior] ${e instanceof Error ? e.message : String(e)}`);
81510
82258
  process.exit(1);
81511
82259
  }));
82260
+ } else if (isTeamCommand) {
82261
+ const teamArgIndex = args.indexOf("team");
82262
+ Promise.resolve().then(() => (init_team_cli(), exports_team_cli)).then((m) => m.teamCommand(args.slice(teamArgIndex + 1)).catch((e) => {
82263
+ console.error(`[claudish team] ${e instanceof Error ? e.message : String(e)}`);
82264
+ process.exit(1);
82265
+ }));
81512
82266
  } else if (isProvidersCommand) {
81513
82267
  const json2 = args.includes("--json");
81514
82268
  Promise.resolve().then(() => (init_providers_command(), exports_providers_command)).then((m) => m.providersCommand({ json: json2 }).catch((e) => {
@@ -81593,14 +82347,14 @@ async function runCli() {
81593
82347
  if (cliConfig.team && cliConfig.team.length > 0) {
81594
82348
  let prompt = cliConfig.claudeArgs.join(" ");
81595
82349
  if (cliConfig.inputFile) {
81596
- prompt = readFileSync29(cliConfig.inputFile, "utf-8");
82350
+ prompt = readFileSync30(cliConfig.inputFile, "utf-8");
81597
82351
  }
81598
82352
  if (!prompt.trim()) {
81599
82353
  console.error("Error: --team requires a prompt (positional args or -f <file>)");
81600
82354
  process.exit(1);
81601
82355
  }
81602
82356
  const mode = cliConfig.teamMode ?? "default";
81603
- const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
82357
+ const sessionPath = join40(process.cwd(), `.claudish-team-${Date.now()}`);
81604
82358
  if (mode === "json") {
81605
82359
  const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
81606
82360
  setupSession2(sessionPath, cliConfig.team, prompt);
@@ -81610,9 +82364,9 @@ async function runCli() {
81610
82364
  });
81611
82365
  const result = { ...status2, responses: {} };
81612
82366
  for (const anonId of Object.keys(status2.models)) {
81613
- const responsePath = join39(sessionPath, `response-${anonId}.md`);
82367
+ const responsePath = join40(sessionPath, `response-${anonId}.md`);
81614
82368
  try {
81615
- const raw2 = readFileSync29(responsePath, "utf-8").trim();
82369
+ const raw2 = readFileSync30(responsePath, "utf-8").trim();
81616
82370
  try {
81617
82371
  result.responses[anonId] = JSON.parse(raw2);
81618
82372
  } catch {