jinzd-ai-cli 0.4.329 → 0.4.331

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 (44) hide show
  1. package/README.md +4 -4
  2. package/README.zh-CN.md +4 -4
  3. package/dist/{batch-NNYRIS4U.js → batch-OOHC5POO.js} +2 -2
  4. package/dist/{chunk-BRZZ3QAI.js → chunk-2BAFLDDL.js} +44 -7
  5. package/dist/{chunk-XF2S7LNM.js → chunk-56ME7PB2.js} +1 -1
  6. package/dist/{chunk-DRNRQ6SD.js → chunk-6BGW25LG.js} +1 -1
  7. package/dist/{chunk-E2W2MI6H.js → chunk-C6GA7H5D.js} +1 -72
  8. package/dist/{chunk-2CNJ25U7.js → chunk-CM5C7ZSM.js} +9 -3
  9. package/dist/{chunk-IUC3DRGB.js → chunk-DTVN2LDU.js} +1 -1
  10. package/dist/{chunk-ABIR2TGW.js → chunk-EWARNWJ4.js} +4 -4
  11. package/dist/{chunk-YDH5ZWOQ.js → chunk-IJNDIOOQ.js} +1 -1
  12. package/dist/{chunk-LSSOPLKJ.js → chunk-L3FDFFD2.js} +9 -9
  13. package/dist/{chunk-TYQODNI7.js → chunk-MJSK6ZYR.js} +4 -4
  14. package/dist/{chunk-C2Z77RTU.js → chunk-MN25ELTF.js} +15 -4
  15. package/dist/{chunk-72AR23R2.js → chunk-NOLK5C4U.js} +54 -2
  16. package/dist/{chunk-SS3PG7LP.js → chunk-Q26V2PNA.js} +1 -1
  17. package/dist/{chunk-I5KLH7TL.js → chunk-QFF2PTI3.js} +53 -43
  18. package/dist/{chunk-LKSMXCFL.js → chunk-R3XIU7NS.js} +1 -1
  19. package/dist/{chunk-MPCQG7LV.js → chunk-RZDRBFAB.js} +9 -7
  20. package/dist/{chunk-LRRBYGHE.js → chunk-TF2FNRDZ.js} +1 -1
  21. package/dist/chunk-W4P2KJZN.js +75 -0
  22. package/dist/{chunk-6UDKGZ3D.js → chunk-YCDF6FNR.js} +1 -1
  23. package/dist/{ci-ODKA57IG.js → ci-HNYUXJUE.js} +7 -6
  24. package/dist/{ci-format-QDC4XKYZ.js → ci-format-ZLBEAU7Z.js} +2 -2
  25. package/dist/{constants-MG7EGG5J.js → constants-4EH6Y6VI.js} +1 -1
  26. package/dist/{constants-BYWEJLAI.js → constants-JM6MK5SS.js} +1 -1
  27. package/dist/{doctor-cli-LYZSHYR6.js → doctor-cli-VCNERMX5.js} +7 -6
  28. package/dist/electron-server.js +292 -178
  29. package/dist/{hub-GRHYVLGV.js → hub-3OXLWTEP.js} +8 -18
  30. package/dist/{hub-server-JLC7CNP7.js → hub-server-LJWXNEWK.js} +2 -1
  31. package/dist/index.js +23 -21
  32. package/dist/{persistent-memory-OCDFR4BH.js → persistent-memory-LMVWIHMX.js} +2 -2
  33. package/dist/{persistent-memory-O6GWHGJH.js → persistent-memory-PAS7NAPQ.js} +2 -2
  34. package/dist/{pr-LDUIDU27.js → pr-IMR62OHN.js} +7 -6
  35. package/dist/{run-tests-YTRU22LY.js → run-tests-2TVEWCQI.js} +2 -2
  36. package/dist/{run-tests-LTYJACRX.js → run-tests-MQVGE6FK.js} +2 -2
  37. package/dist/{server-ZTFB3KMS.js → server-55X6MCGX.js} +7 -6
  38. package/dist/{server-2NZN22XP.js → server-6MS2NGEJ.js} +30 -20
  39. package/dist/{task-orchestrator-URFLWSPI.js → task-orchestrator-KIDJB7NP.js} +9 -7
  40. package/dist/{usage-IP6G4Q2K.js → usage-X2CQYAUA.js} +3 -3
  41. package/dist/web/client/app.js +19 -0
  42. package/dist/web/client/app.js.br +0 -0
  43. package/dist/web/client/app.js.gz +0 -0
  44. package/package.json +1 -1
@@ -55,7 +55,7 @@ import {
55
55
  touchMemoryReferences,
56
56
  updateMemoryApproval,
57
57
  updateMemoryEntry
58
- } from "./chunk-LKSMXCFL.js";
58
+ } from "./chunk-R3XIU7NS.js";
59
59
  import {
60
60
  DEFAULT_PATTERNS,
61
61
  redactJson,
@@ -81,7 +81,7 @@ import {
81
81
  recoverGbkMojibake,
82
82
  runTestsTool,
83
83
  shellInvocation
84
- } from "./chunk-LRRBYGHE.js";
84
+ } from "./chunk-TF2FNRDZ.js";
85
85
  import {
86
86
  getCurrentSessionKey,
87
87
  getExecutionConfigDir,
@@ -123,7 +123,7 @@ import {
123
123
  SUBAGENT_MAX_ROUNDS_LIMIT,
124
124
  VERSION,
125
125
  buildUserIdentityPrompt
126
- } from "./chunk-IUC3DRGB.js";
126
+ } from "./chunk-DTVN2LDU.js";
127
127
 
128
128
  // src/web/access.ts
129
129
  import { randomBytes, timingSafeEqual } from "crypto";
@@ -205,8 +205,8 @@ function imageValidationError(images) {
205
205
  }
206
206
 
207
207
  // src/web/server.ts
208
- import { join as join25, dirname as dirname12, resolve as resolve12, relative as relative6, sep as sep5, extname as extname3, resolve as resolvePath } from "path";
209
- import { existsSync as existsSync31, readFileSync as readFileSync22, readdirSync as readdirSync15, statSync as statSync16, realpathSync as realpathSync3, createReadStream } from "fs";
208
+ import { join as join26, dirname as dirname12, resolve as resolve12, relative as relative6, sep as sep5, extname as extname3, resolve as resolvePath } from "path";
209
+ import { existsSync as existsSync32, readFileSync as readFileSync23, readdirSync as readdirSync16, statSync as statSync16, realpathSync as realpathSync3, createReadStream } from "fs";
210
210
  import { networkInterfaces as networkInterfaces2 } from "os";
211
211
 
212
212
  // src/config/config-manager.ts
@@ -691,8 +691,14 @@ ${err}`
691
691
  "deepseek-v4-flash-vision-exp"
692
692
  ]);
693
693
  const DEEPSEEK_TARGET = "deepseek-flash";
694
- const LEGACY_KIMI = /* @__PURE__ */ new Set(["kimi-k2-0711-preview", "kimi-k2-turbo-preview"]);
695
- const KIMI_TARGET = "kimi-k2.6";
694
+ const LEGACY_KIMI = /* @__PURE__ */ new Set([
695
+ "kimi-k2-0711-preview",
696
+ "kimi-k2-turbo-preview",
697
+ "kimi-k2.5",
698
+ "kimi-k2-thinking",
699
+ "kimi-k2-thinking-turbo"
700
+ ]);
701
+ const KIMI_TARGET = "kimi-k2.7-code";
696
702
  const LEGACY_CLAUDE = {
697
703
  "claude-sonnet-4-5-20250929": "claude-sonnet-5-5",
698
704
  "claude-sonnet-4-5": "claude-sonnet-5-5",
@@ -921,7 +927,6 @@ function deepSeekCnyPricing(cacheRead, input, output) {
921
927
  output: output / CNY_PER_USD
922
928
  };
923
929
  }
924
- var DEEPSEEK_V4_PRO_ROUTED_TO_FLASH_AT = /* @__PURE__ */ new Date("2026-09-14T04:00:00.000Z");
925
930
  var DEEPSEEK_V41_FLASH_ROUTING_CONFIRMED_AT = /* @__PURE__ */ new Date("2026-09-11T00:00:00.000Z");
926
931
  function isDeepSeekPeakHour(at) {
927
932
  const weekday = at.getUTCDay();
@@ -937,7 +942,6 @@ function getDeepSeekV4Pricing(model, at) {
937
942
  const isPro = model.startsWith("deepseek-v4-pro");
938
943
  const isFlash = model.startsWith("deepseek-v4-flash");
939
944
  if (!isPro && !isFlash) return null;
940
- if (isPro && at >= DEEPSEEK_V4_PRO_ROUTED_TO_FLASH_AT) return deepSeekFlash41Pricing(at);
941
945
  if (isFlash && at >= DEEPSEEK_V41_FLASH_ROUTING_CONFIRMED_AT) return deepSeekFlash41Pricing(at);
942
946
  if (at < DEEPSEEK_TIME_PRICING_EFFECTIVE_AT) {
943
947
  return isPro ? deepSeekCnyPricing(0.025, 3, 6) : deepSeekCnyPricing(0.02, 1, 2);
@@ -1016,6 +1020,12 @@ var PRICING_TABLE = {
1016
1020
  // Kimi K3(2026-07 旗舰,1M 上下文)官方定价 platform.kimi.ai/pricing/chat-k3:
1017
1021
  // 输入 cache-miss $3.00 / cache-hit $0.30,输出 $15.00(/MTok)。
1018
1022
  "kimi-k3": { input: 3, output: 15, cacheRead: 0.3 },
1023
+ // K2.6 / K2.7 Code:官方 platform.kimi.ai/docs/pricing/chat(2026-10-18 核对,USD/MTok)。
1024
+ // 国内站人民币价 ¥6.5/¥27(缓存 ¥1.1 / ¥1.3)按 ¥6.9 折算与之相差 <3%,沿用 K3 那行的美元口径。
1025
+ // 此前 K2.6 没有单列,前缀落到 'kimi-k2'($0.6/$2.5),输出低报约 37%。
1026
+ "kimi-k2.6": { input: 0.95, output: 4, cacheRead: 0.16 },
1027
+ "kimi-k2.7-code": { input: 0.95, output: 4, cacheRead: 0.19 },
1028
+ "kimi-k2.7-code-highspeed": { input: 1.9, output: 8, cacheRead: 0.38 },
1019
1029
  // ── Zhipu GLM ─────────────────────────────────────────────────
1020
1030
  "glm-4-plus": { input: 0.7, output: 0.7 },
1021
1031
  "glm-4": { input: 0.14, output: 0.14 },
@@ -1037,6 +1047,11 @@ var PRICING_TABLE = {
1037
1047
  // 没公布截止日期,静态表表达不了这个时间维度;高估比低估安全——预算告警早触发
1038
1048
  // 好过晚触发。折扣期内 `/cost` 会比实际偏高约一倍,这是刻意的取舍。
1039
1049
  "glm-5.3-flash": { input: 0.116, output: 0.406, cacheRead: 0.033 },
1050
+ // GLM-5.3-FlashX:官方定价页(docs.bigmodel.cn/cn/guide/start/pricing,2026-10-18 浏览器读取)
1051
+ // 输入 2 元、输出 7 元、缓存命中 0.57 元。必须单列——否则前缀匹配会落到上面的 glm-5.3-flash,低报 2.5 倍。
1052
+ "glm-5.3-flashx": { input: 2 / CNY_PER_USD, output: 7 / CNY_PER_USD, cacheRead: 0.57 / CNY_PER_USD },
1053
+ // GLM-4.7-Flash:官方免费(输入/输出/缓存全免)。必须单列——否则前缀落到 'glm-4'($0.14)。
1054
+ "glm-4.7-flash": { input: 0, output: 0, cacheRead: 0 },
1040
1055
  // GLM-5.2:与 GLM-5.3 同价(同一定价页:8 / 28 / 缓存命中 2 元),口径修正同上。
1041
1056
  "glm-5.2": { input: 8 / CNY_PER_USD, output: 28 / CNY_PER_USD, cacheRead: 2 / CNY_PER_USD },
1042
1057
  // GLM-5V-Turbo(2026-04 视觉编程旗舰)官方直连 API 定价 $1.20 / $4.00。
@@ -1046,8 +1061,26 @@ var PRICING_TABLE = {
1046
1061
  "glm-z1-air": { input: 0.2, output: 0.6 },
1047
1062
  "glm-z1-flash": { input: 0, output: 0 },
1048
1063
  // ── MiniMax (海螺) ────────────────────────────────────────────
1049
- // GAP (v0.4.184): MiniMax-M3 / M2.x are unpriced — no verified rate anchor yet.
1050
- // Unpriced → `aicli usage` shows "—" (unknown), not a false $0.00. Add when confirmed.
1064
+ // 官方定价页 platform.minimax.cn/docs/guides/pricing(2026-10-18 核对,元/百万 tokens),按 ¥6.9 折算。
1065
+ // 本 provider 打的是国内站 api.minimaxi.com,按人民币计费。
1066
+ // M3 记的是「永久五折」后的价(官方原文标注永久,不同于智谱的限时折扣),≤512K 输入档;
1067
+ // >512K 那档见 computeCost 的 LONG_PROMPT_TIERS。M3 未公布缓存写入价,缺省按输入价(偏高估)。
1068
+ "minimax-m3": { input: 2.1 / CNY_PER_USD, output: 8.4 / CNY_PER_USD, cacheRead: 0.42 / CNY_PER_USD },
1069
+ "minimax-m2.7": { input: 2.1 / CNY_PER_USD, output: 8.4 / CNY_PER_USD, cacheRead: 0.42 / CNY_PER_USD, cacheWrite: 2.625 / CNY_PER_USD },
1070
+ "minimax-m2.7-highspeed": { input: 4.2 / CNY_PER_USD, output: 16.8 / CNY_PER_USD, cacheRead: 0.42 / CNY_PER_USD, cacheWrite: 2.625 / CNY_PER_USD },
1071
+ // M2.5 / M2.1 / M2(历史模型)同价;highspeed 变体输入输出翻倍、缓存同价。
1072
+ "minimax-m2": { input: 2.1 / CNY_PER_USD, output: 8.4 / CNY_PER_USD, cacheRead: 0.21 / CNY_PER_USD, cacheWrite: 2.625 / CNY_PER_USD },
1073
+ "minimax-m2.5-highspeed": { input: 4.2 / CNY_PER_USD, output: 16.8 / CNY_PER_USD, cacheRead: 0.21 / CNY_PER_USD, cacheWrite: 2.625 / CNY_PER_USD },
1074
+ "minimax-m2.1-highspeed": { input: 4.2 / CNY_PER_USD, output: 16.8 / CNY_PER_USD, cacheRead: 0.21 / CNY_PER_USD, cacheWrite: 2.625 / CNY_PER_USD },
1075
+ // ── Qwen(阿里云百炼,北京地域)───────────────────────────────────
1076
+ // 官方 help.aliyun.com/zh/model-studio/model-pricing(2026-10-18 核对,元/百万 tokens,原价),按 ¥6.9 折算。
1077
+ // 思考与非思考输出同价。qwen3.7-plus 当前限时 8 折,记原价(高估方向,同智谱 glm-5.3-flash 的取舍)。
1078
+ // 官方未给出各模型的缓存单价,不填 cacheRead,命中按输入全价算(高估方向)。
1079
+ // 长提示分档(qwen3.7-plus >256K、qwen-flash >128K / >256K)见 LONG_PROMPT_TIERS;此处为最低档。
1080
+ "qwen3.8-max": { input: 12 / CNY_PER_USD, output: 36 / CNY_PER_USD },
1081
+ "qwen3.7-plus": { input: 2 / CNY_PER_USD, output: 8 / CNY_PER_USD },
1082
+ "qwen3.8-flash": { input: 0.8 / CNY_PER_USD, output: 2.7 / CNY_PER_USD },
1083
+ "qwen-flash": { input: 0.15 / CNY_PER_USD, output: 1.5 / CNY_PER_USD },
1051
1084
  // ── OpenRouter (pass-through — actual cost depends on underlying model) ──
1052
1085
  // 绝大多数 id 仍**刻意留空**:真实价由底层模型 + OpenRouter 加价决定,静态表覆盖不了
1053
1086
  // 396 个模型,未收录的照旧显示 "—"(未知)而不是假的 $0.00。
@@ -1102,11 +1135,21 @@ function getPricing(provider, model, at = /* @__PURE__ */ new Date()) {
1102
1135
  return null;
1103
1136
  }
1104
1137
  var CLAUDE_HAIKU_55_LONG = { input: 0.5, output: 2.5, cacheWrite: 0.625, cacheRead: 0.05 };
1105
- var CLAUDE_HAIKU_55_TIER_TOKENS = 1e5;
1138
+ var LONG_PROMPT_TIERS = [
1139
+ { prefix: "claude-haiku-5-5", overTokens: 1e5, pricing: CLAUDE_HAIKU_55_LONG },
1140
+ {
1141
+ prefix: "minimax-m3",
1142
+ overTokens: 512e3,
1143
+ pricing: { input: 4.2 / CNY_PER_USD, output: 16.8 / CNY_PER_USD, cacheRead: 0.84 / CNY_PER_USD }
1144
+ },
1145
+ { prefix: "qwen3.7-plus", overTokens: 256e3, pricing: { input: 6 / CNY_PER_USD, output: 24 / CNY_PER_USD } },
1146
+ { prefix: "qwen-flash", overTokens: 256e3, pricing: { input: 1.2 / CNY_PER_USD, output: 12 / CNY_PER_USD } },
1147
+ { prefix: "qwen-flash", overTokens: 128e3, pricing: { input: 0.6 / CNY_PER_USD, output: 6 / CNY_PER_USD } }
1148
+ ];
1106
1149
  function longPromptTier(model, usage) {
1107
- if (!model.toLowerCase().startsWith("claude-haiku-5-5")) return null;
1150
+ const key = model.toLowerCase();
1108
1151
  const prompt = usage.inputTokens + (usage.cacheCreationTokens ?? 0) + (usage.cacheReadTokens ?? 0);
1109
- return prompt > CLAUDE_HAIKU_55_TIER_TOKENS ? CLAUDE_HAIKU_55_LONG : null;
1152
+ return LONG_PROMPT_TIERS.find((t) => key.startsWith(t.prefix) && prompt > t.overTokens)?.pricing ?? null;
1110
1153
  }
1111
1154
  function computeCost(provider, model, usage, at = /* @__PURE__ */ new Date()) {
1112
1155
  const p = longPromptTier(model, usage) ?? getPricing(provider, model, at);
@@ -2816,12 +2859,12 @@ function findPhantomClaims(content, extraMessages) {
2816
2859
  const claimed = extractClaimedFilePaths(content);
2817
2860
  if (claimed.length === 0) return [];
2818
2861
  const normalize = (p) => p.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
2819
- const basename11 = (p) => {
2862
+ const basename12 = (p) => {
2820
2863
  const parts = normalize(p).split("/");
2821
2864
  return parts[parts.length - 1] ?? "";
2822
2865
  };
2823
2866
  const written = extractWrittenFilePaths(extraMessages).map(normalize);
2824
- const writtenBases = new Set(written.map(basename11));
2867
+ const writtenBases = new Set(written.map(basename12));
2825
2868
  const writtenFull = new Set(written);
2826
2869
  const bashText = extractBashCommands(extraMessages).map((c) => c.replace(/\\/g, "/").toLowerCase()).join("\n");
2827
2870
  return claimed.filter((raw) => {
@@ -2830,8 +2873,8 @@ function findPhantomClaims(content, extraMessages) {
2830
2873
  for (const w of writtenFull) {
2831
2874
  if (w.endsWith("/" + norm2) || norm2.endsWith("/" + w)) return false;
2832
2875
  }
2833
- if (writtenBases.has(basename11(norm2))) return false;
2834
- const base = basename11(norm2);
2876
+ if (writtenBases.has(basename12(norm2))) return false;
2877
+ const base = basename12(norm2);
2835
2878
  if (base && bashText.includes(base)) return false;
2836
2879
  return true;
2837
2880
  });
@@ -4447,7 +4490,9 @@ var DeepSeekProvider = class extends OpenAICompatibleProvider {
4447
4490
  },
4448
4491
  {
4449
4492
  id: "deepseek-v4-pro",
4450
- displayName: "DeepSeek V4 Pro (text-only; routes to V4.1 Flash from 2026-09-14)",
4493
+ // 2026-09-11 官方撤回了「9-14 起路由到 Flash」,Pro 照常服务、按 Pro 价计费(见 pricing.ts)。
4494
+ // 价格约为 Flash 的 4 倍且不收图片,但 Flash 替代不了它的能力档,故保留。
4495
+ displayName: "DeepSeek V4 Pro (0813, text-only)",
4451
4496
  contextWindow: 1048576,
4452
4497
  supportsStreaming: true,
4453
4498
  supportsThinking: true
@@ -4506,20 +4551,24 @@ var DeepSeekProvider = class extends OpenAICompatibleProvider {
4506
4551
  var ZHIPU_CONFIG = {
4507
4552
  id: "zhipu",
4508
4553
  displayName: "Zhipu (GLM)",
4509
- defaultModel: "glm-4.6",
4554
+ defaultModel: "glm-5.3-flash",
4510
4555
  apiKeyEnvVar: "AICLI_API_KEY_ZHIPU",
4511
4556
  baseUrl: "https://open.bigmodel.cn/api/paas/v4",
4512
4557
  defaultTimeout: 3e5,
4513
4558
  models: [
4514
4559
  { id: "glm-5.3", displayName: "GLM-5.3 (2026-08 Flagship, 1M, Coding/Security, \u59CB\u7EC8\u601D\u8003)", contextWindow: 1048576, supportsStreaming: true, supportsThinking: true },
4560
+ // GLM-5.3-FlashX(2026-10):Flash 与 5.3 之间的一档,¥2/¥7、缓存命中 ¥0.57(官方定价页)。
4561
+ // 2026-10-18 实测:同样始终思考(`disabled` → 400/1210),`low` 档 200,base64 图片可识别。
4562
+ { id: "glm-5.3-flashx", displayName: "GLM-5.3 FlashX (\u539F\u751F\u591A\u6A21\u6001, 1M, \u4EF7\u683C\u7EA6\u4E3A 5.3 \u7684 1/4, \u59CB\u7EC8\u601D\u8003)", contextWindow: 1048576, supportsStreaming: true, supportsThinking: true },
4515
4563
  { id: "glm-5.3-flash", displayName: "GLM-5.3 Flash (\u539F\u751F\u591A\u6A21\u6001, 1M, \u8F93\u5165\u7EA6\u4E3A 5.3 \u7684 1/10, \u59CB\u7EC8\u601D\u8003)", contextWindow: 1048576, supportsStreaming: true, supportsThinking: true },
4516
- { id: "glm-5.2", displayName: "GLM-5.2 (2026-06 Flagship, 1M, Coding)", contextWindow: 1048576, supportsStreaming: true, supportsThinking: true },
4517
- { id: "glm-5v-turbo", displayName: "GLM-5V-Turbo (Multimodal Vision + Coding)", contextWindow: 202752, supportsStreaming: true },
4518
- // 2026-10-07 清理:移除 glm-5.1-reasoning / glm-5.1-air(官方模型总览与发布记录里**从未出现过**,
4519
- // 当初取自第三方标注,同 v0.4.198 的 `glm-5.2[1m]`)与 glm-5 / glm-5.1(已被 glm-5.2 / 5.3 取代,
4520
- // 要便宜有 glm-5.3-flash;glm-5 这里标的 128K 与官方 200K 也不符)。后两者端点仍可用,`/model` 照收,
4521
- // pricing.ts 保留其费率供历史成本回算。
4522
- { id: "glm-4.6", displayName: "GLM-4.6 (200K, \u4E2D\u6587\u5199\u4F5C\u63A8\u8350)", contextWindow: 204800, supportsStreaming: true }
4564
+ // 免费(输入/输出/缓存全免,官方定价页「更多文本模型」)。2026-10-18 实测 200,但慢(一句 ok 约 15s)。
4565
+ { id: "glm-4.7-flash", displayName: "GLM-4.7 Flash (\u514D\u8D39, 200K)", contextWindow: 2e5, supportsStreaming: true }
4566
+ // 2026-10-18 按「老模型没有价格优势就下架」清理(端点仍可用,`/model` 照收,pricing.ts 保留费率供历史回算):
4567
+ // - glm-5.2:与 glm-5.3 同价(¥8/¥28/缓存 ¥2),已被取代
4568
+ // - glm-5v-turbo:¥5/¥22 起,比同样原生多模态的 glm-5.3-flash(¥0.8/¥2.8)贵 6–8 倍
4569
+ // - glm-4.6:已不在官方定价页上,且旧价高于 glm-5.3-flash
4570
+ // 更早的 2026-10-07 清理:glm-5.1-reasoning / glm-5.1-air 官方从未存在(取自第三方标注,同 v0.4.198
4571
+ // 的 `glm-5.2[1m]`),glm-5 / glm-5.1 已被取代。
4523
4572
  ]
4524
4573
  };
4525
4574
  var REASONING_EFFORT_MODELS = /^glm-5/i;
@@ -4585,7 +4634,10 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
4585
4634
  */
4586
4635
  static TEMP_1_MODELS = /* @__PURE__ */ new Set([
4587
4636
  "kimi-k3",
4637
+ "kimi-k2.7-code",
4638
+ "kimi-k2.7-code-highspeed",
4588
4639
  "kimi-k2.6",
4640
+ // 已 404 的旧 code 仍留在这里:config 自愈迁移之外的显式 `/model kimi-k2.5` 不该先撞 temperature 400
4589
4641
  "kimi-k2.5",
4590
4642
  "kimi-k2-thinking",
4591
4643
  "kimi-k2-thinking-turbo"
@@ -4606,7 +4658,7 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
4606
4658
  info = {
4607
4659
  id: "kimi",
4608
4660
  displayName: "Kimi (Moonshot AI)",
4609
- defaultModel: "kimi-k2.6",
4661
+ defaultModel: "kimi-k2.7-code",
4610
4662
  apiKeyEnvVar: "AICLI_API_KEY_KIMI",
4611
4663
  requiresApiKey: true,
4612
4664
  baseUrl: this.defaultBaseUrl,
@@ -4616,39 +4668,33 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
4616
4668
  // - contextWindow 1M(1048576);code 就是 `kimi-k3`(UI 名 "K3 · Max" ≠ API code)
4617
4669
  // - temperature 固定 1.0(已并入 TEMP_1_MODELS)
4618
4670
  // - vision 走 base64 / ms://file-id(不收公网 URL)——本版未接图片输入路径,仅文本/工具
4619
- // 默认仍保持 kimi-k2.6(K3 输出 $15/MTok 偏贵,按需 /model kimi-k3)。
4671
+ // 默认用 kimi-k2.7-code(K3 输出 $15/MTok,约为它的 4 倍,按需 /model kimi-k3)。
4620
4672
  {
4621
4673
  id: "kimi-k3",
4622
4674
  displayName: "Kimi K3",
4623
4675
  contextWindow: 1048576,
4624
4676
  supportsStreaming: true
4625
4677
  },
4626
- // Kimi K2.6 / K2.5 系列(最新一代,推荐)
4627
- {
4628
- id: "kimi-k2.6",
4629
- displayName: "Kimi K2.6",
4630
- contextWindow: 262144,
4631
- supportsStreaming: true
4632
- },
4678
+ // Kimi K2.7 Code(官方 platform.kimi.com/docs/guide/kimi-k2-7-code-quickstart,2026-10-18 核对):
4679
+ // - 256K 上下文;虽名为 Coding 模型,官方写明也支持对话与 Agent 任务
4680
+ // - 思考默认开启且**关不掉**(关闭报错)——本 provider 不发 thinking 字段,正好不踩
4681
+ // - temperature 固定 1.0(已并入 TEMP_1_MODELS);tool_choice 只收 auto / none
4682
+ // - 价格与 K2.6 相同($0.95/$4.00),各项基准提升 10–30%,故取代 K2.6 成为默认
4633
4683
  {
4634
- id: "kimi-k2.5",
4635
- displayName: "Kimi K2.5",
4684
+ id: "kimi-k2.7-code",
4685
+ displayName: "Kimi K2.7 Code",
4636
4686
  contextWindow: 262144,
4637
4687
  supportsStreaming: true
4638
4688
  },
4639
- // Kimi K2 Thinking 系列(支持多步推理 + 工具调用)
4689
+ // 同一模型的高速版:输出约 5–6 倍速,价格翻倍。
4640
4690
  {
4641
- id: "kimi-k2-thinking",
4642
- displayName: "Kimi K2 Thinking",
4643
- contextWindow: 262144,
4644
- supportsStreaming: true
4645
- },
4646
- {
4647
- id: "kimi-k2-thinking-turbo",
4648
- displayName: "Kimi K2 Thinking Turbo",
4691
+ id: "kimi-k2.7-code-highspeed",
4692
+ displayName: "Kimi K2.7 Code Highspeed (2x price, ~5x speed)",
4649
4693
  contextWindow: 262144,
4650
4694
  supportsStreaming: true
4651
4695
  }
4696
+ // 2026-10-18 下架:kimi-k2.6 与 K2.7 Code 同价、已被取代(端点仍可用,`/model` 照收);
4697
+ // kimi-k2.5 / kimi-k2-thinking / kimi-k2-thinking-turbo 端点已 404,由 config 自愈迁移到 kimi-k2.7-code。
4652
4698
  ]
4653
4699
  };
4654
4700
  /**
@@ -4760,11 +4806,10 @@ var MINIMAX_CONFIG = {
4760
4806
  apiKeyEnvVar: "AICLI_API_KEY_MINIMAX",
4761
4807
  baseUrl: "https://api.minimaxi.com/v1",
4762
4808
  models: [
4763
- { id: "MiniMax-M3", displayName: "MiniMax M3\uFF08\u65D7\u8230\uFF0C1M \u4E0A\u4E0B\u6587\uFF09", contextWindow: 1e6, supportsStreaming: true },
4764
- { id: "MiniMax-M2.7", displayName: "MiniMax M2.7", contextWindow: 1e6, supportsStreaming: true },
4765
- { id: "MiniMax-M2.7-highspeed", displayName: "MiniMax M2.7 Highspeed", contextWindow: 1e6, supportsStreaming: true },
4766
- { id: "MiniMax-M2.5", displayName: "MiniMax M2.5", contextWindow: 1e6, supportsStreaming: true },
4767
- { id: "MiniMax-M2.5-highspeed", displayName: "MiniMax M2.5 Highspeed", contextWindow: 1e6, supportsStreaming: true }
4809
+ { id: "MiniMax-M3", displayName: "MiniMax M3\uFF08\u65D7\u8230\uFF0C1M \u4E0A\u4E0B\u6587\uFF09", contextWindow: 1e6, supportsStreaming: true }
4810
+ // 2026-10-18 按「老模型没有价格优势就下架」清理(官方定价页 platform.minimax.cn/docs/guides/pricing):
4811
+ // M3「永久五折」后 ¥2.1/¥8.4,与 M2.7 同价、比 M2.x highspeed 便宜一半;M2.5 / M2.1 / M2 官方已归入
4812
+ // 「历史模型」。端点仍可用,`/model` 照收,pricing.ts 保留费率供历史回算。
4768
4813
  ]
4769
4814
  };
4770
4815
  var MiniMaxProvider = createStaticProviderClass(MINIMAX_CONFIG);
@@ -4773,14 +4818,20 @@ var MiniMaxProvider = createStaticProviderClass(MINIMAX_CONFIG);
4773
4818
  var QWEN_CONFIG = {
4774
4819
  id: "qwen",
4775
4820
  displayName: "Qwen (Alibaba Cloud)",
4776
- defaultModel: "qwen-plus",
4821
+ defaultModel: "qwen3.8-flash",
4777
4822
  apiKeyEnvVar: "AICLI_API_KEY_QWEN",
4778
4823
  baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
4824
+ // 2026-10-18 按百炼官方文档更新(help.aliyun.com/zh/model-studio/models、/model-pricing、/deep-thinking)。
4825
+ // **未经真实端点实测**(本机无 Qwen key)。三款 qwen3.x 默认开启思考、支持非流式调用、纯文本输入;
4826
+ // 思考与非思考输出同价。上下文按官方价格阶梯的上限 1M 记。
4779
4827
  models: [
4780
- { id: "qwen-plus", displayName: "Qwen Plus", contextWindow: 131072, supportsStreaming: true },
4781
- { id: "qwen-max", displayName: "Qwen Max", contextWindow: 131072, supportsStreaming: true },
4782
- { id: "qwen-turbo", displayName: "Qwen Turbo", contextWindow: 131072, supportsStreaming: true },
4783
- { id: "qwen-coder-plus", displayName: "Qwen Coder Plus", contextWindow: 131072, supportsStreaming: true }
4828
+ { id: "qwen3.8-max", displayName: "Qwen3.8 Max\uFF08\u65D7\u8230\uFF0C1M\uFF0C\xA512/\xA536\uFF09", contextWindow: 1e6, supportsStreaming: true },
4829
+ { id: "qwen3.7-plus", displayName: "Qwen3.7 Plus\uFF081M\uFF0C\u2264256K \xA52/\xA58\uFF09", contextWindow: 1e6, supportsStreaming: true },
4830
+ { id: "qwen3.8-flash", displayName: "Qwen3.8 Flash\uFF081M\uFF0C\xA50.8/\xA52.7\uFF09", contextWindow: 1e6, supportsStreaming: true },
4831
+ // 老模型里唯一有价格优势的:≤128K ¥0.15/¥1.5,最便宜。默认不开思考。
4832
+ { id: "qwen-flash", displayName: "Qwen Flash\uFF08\u6700\u4FBF\u5B9C\uFF0C\u2264128K \xA50.15/\xA51.5\uFF09", contextWindow: 1e6, supportsStreaming: true }
4833
+ // 下架(端点若仍可用,`/model` 照收):qwen-max ¥2.4/¥9.6 比 qwen3.7-plus 还贵;qwen-plus 价格不如
4834
+ // qwen-flash、能力不如 qwen3.8-flash;qwen-turbo / qwen-coder-plus 已不在官方价格页上。
4784
4835
  ]
4785
4836
  };
4786
4837
  var QwenProvider = createStaticProviderClass(QWEN_CONFIG);
@@ -15953,8 +16004,12 @@ function stripConvergedMarker(content) {
15953
16004
 
15954
16005
  // src/hub/agent.ts
15955
16006
  var PASS_MARKER = "[PASS]";
16007
+ function stripThinkBlocks(raw) {
16008
+ const filter = new ThinkTagFilter();
16009
+ return filter.push(raw) + filter.flush();
16010
+ }
15956
16011
  function parseTurn(raw) {
15957
- const trimmed = raw.trim();
16012
+ const trimmed = stripThinkBlocks(raw).trim();
15958
16013
  const upper = trimmed.toUpperCase();
15959
16014
  if (upper.startsWith(PASS_MARKER) || upper === PASS_MARKER) {
15960
16015
  return { content: "", passed: true, converged: false };
@@ -16034,6 +16089,7 @@ var HubAgent = class {
16034
16089
  const systemPrompt = this.buildSystemPrompt(topic, round, maxRounds, opts?.voteConverge);
16035
16090
  const messages = this.buildMessages(history);
16036
16091
  let raw = "";
16092
+ const thinkFilter = new ThinkTagFilter();
16037
16093
  const stream = provider.chatStream({
16038
16094
  messages,
16039
16095
  model: this.modelId,
@@ -16048,13 +16104,16 @@ var HubAgent = class {
16048
16104
  for await (const chunk of stream) {
16049
16105
  if (chunk.delta) {
16050
16106
  raw += chunk.delta;
16051
- onToken?.(chunk.delta);
16107
+ const visible = thinkFilter.push(chunk.delta);
16108
+ if (visible) onToken?.(visible);
16052
16109
  }
16053
16110
  if (opts?.signal?.aborted) break;
16054
16111
  }
16055
16112
  } catch (error) {
16056
16113
  if (!opts?.signal?.aborted) throw error;
16057
16114
  }
16115
+ const tail = thinkFilter.flush();
16116
+ if (tail) onToken?.(tail);
16058
16117
  const { content, passed, converged } = parseTurn(raw);
16059
16118
  return {
16060
16119
  speaker: this.role.id,
@@ -16089,7 +16148,7 @@ var HubAgent = class {
16089
16148
  maxTokens: 4096,
16090
16149
  ...this.thinking ?? {}
16091
16150
  });
16092
- return response.content.trim();
16151
+ return stripThinkBlocks(response.content).trim();
16093
16152
  }
16094
16153
  // ── Private ──────────────────────────────────────────────────────
16095
16154
  buildSystemPrompt(topic, round, maxRounds, voteConverge) {
@@ -16170,7 +16229,7 @@ Produce a structured, decision-oriented synthesis with these exact sections:
16170
16229
  4. **Action Items** \u2014 concrete next steps as a checklist. For each, suggest which role/expertise should own it, e.g. "- [ ] (\u67B6\u6784\u5E08) \u8BC4\u4F30\u670D\u52A1\u62C6\u5206\u8FB9\u754C".
16171
16230
  5. **Risks & Open Questions** \u2014 unresolved issues and what to watch.
16172
16231
 
16173
- Keep it tight (within ~600 words). Use the same language as the discussion.`;
16232
+ Keep it tight (within ~600 words). Use the same language as the discussion \u2014 including the section headings (translate them, e.g. \u51B3\u7B56 / \u5EFA\u8BAE\u3001\u5173\u952E\u5171\u8BC6\u3001\u5206\u6B67\u70B9\u3001\u884C\u52A8\u9879\u3001\u98CE\u9669\u4E0E\u5F00\u653E\u95EE\u9898 for a Chinese discussion).`;
16174
16233
  }
16175
16234
  };
16176
16235
 
@@ -16546,8 +16605,57 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
16546
16605
  return { roles: outRoles, assignments, warnings };
16547
16606
  }
16548
16607
 
16608
+ // src/hub/custom-presets.ts
16609
+ import { existsSync as existsSync25, readdirSync as readdirSync12, readFileSync as readFileSync18 } from "fs";
16610
+ import { join as join18, basename as basename10 } from "path";
16611
+ var CUSTOM_PRESET_PREFIX = "custom:";
16612
+ var HUB_ROLES_DIRNAME = "hub-roles";
16613
+ function parseRolesJson(parsed) {
16614
+ const rawRoles = Array.isArray(parsed) ? parsed : parsed?.roles;
16615
+ if (!Array.isArray(rawRoles) || rawRoles.length === 0) {
16616
+ throw new Error("roles must be a non-empty array");
16617
+ }
16618
+ return rawRoles.map((r) => ({
16619
+ id: r.id ?? r.name?.toLowerCase().replace(/\s+/g, "-") ?? "agent",
16620
+ name: r.name ?? r.id ?? "Agent",
16621
+ persona: r.persona ?? r.description ?? `You are ${r.name ?? "an AI assistant"}.`,
16622
+ ...r.provider && { provider: r.provider },
16623
+ ...r.model && { model: r.model },
16624
+ ...r.color && { color: r.color }
16625
+ }));
16626
+ }
16627
+ function loadCustomPresets(configDir) {
16628
+ const dir = join18(configDir, HUB_ROLES_DIRNAME);
16629
+ const presets = [];
16630
+ const warnings = [];
16631
+ if (!existsSync25(dir)) return { presets, warnings };
16632
+ let files;
16633
+ try {
16634
+ files = readdirSync12(dir).filter((f) => f.toLowerCase().endsWith(".json")).sort();
16635
+ } catch (err) {
16636
+ return { presets, warnings: [`${dir}: ${err.message}`] };
16637
+ }
16638
+ for (const file of files) {
16639
+ const stem = basename10(file, file.slice(file.lastIndexOf(".")));
16640
+ try {
16641
+ const parsed = JSON.parse(readFileSync18(join18(dir, file), "utf-8").replace(/^/, ""));
16642
+ const roles = parseRolesJson(parsed);
16643
+ const meta = Array.isArray(parsed) ? {} : parsed;
16644
+ presets.push({
16645
+ id: `${CUSTOM_PRESET_PREFIX}${stem}`,
16646
+ name: typeof meta.name === "string" && meta.name.trim() ? meta.name.trim() : stem,
16647
+ description: typeof meta.description === "string" && meta.description.trim() ? meta.description.trim() : roles.map((r) => r.name).join("\xB7"),
16648
+ roles
16649
+ });
16650
+ } catch (err) {
16651
+ warnings.push(`${HUB_ROLES_DIRNAME}/${file}: ${err.message}`);
16652
+ }
16653
+ }
16654
+ return { presets, warnings };
16655
+ }
16656
+
16549
16657
  // src/hub/persist.ts
16550
- import { join as join18 } from "path";
16658
+ import { join as join19 } from "path";
16551
16659
  function discussionToMessages(state2) {
16552
16660
  const out = [];
16553
16661
  const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
@@ -16585,7 +16693,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
16585
16693
  session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
16586
16694
  session.titleAiGenerated = true;
16587
16695
  await sm.save();
16588
- return { id: session.id, path: join18(config.getHistoryDir(), `${session.id}.json`) };
16696
+ return { id: session.id, path: join19(config.getHistoryDir(), `${session.id}.json`) };
16589
16697
  }
16590
16698
 
16591
16699
  // src/core/think-command.ts
@@ -17046,8 +17154,8 @@ function costSourceNote(source2) {
17046
17154
  }
17047
17155
 
17048
17156
  // src/web/commands/_shared.ts
17049
- import { existsSync as existsSync27, readFileSync as readFileSync19, writeFileSync as writeFileSync3, mkdirSync as mkdirSync13, readdirSync as readdirSync13, statSync as statSync14, createWriteStream } from "fs";
17050
- import { join as join21, resolve as resolve9, dirname as dirname9 } from "path";
17157
+ import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as writeFileSync3, mkdirSync as mkdirSync13, readdirSync as readdirSync14, statSync as statSync14, createWriteStream } from "fs";
17158
+ import { join as join22, resolve as resolve9, dirname as dirname9 } from "path";
17051
17159
 
17052
17160
  // src/cli/review-prompts.ts
17053
17161
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -17119,47 +17227,47 @@ function readGitDiff(options = {}) {
17119
17227
  }
17120
17228
 
17121
17229
  // src/diagnostics/doctor-report.ts
17122
- import { existsSync as existsSync26, statSync as statSync13 } from "fs";
17123
- import { join as join20 } from "path";
17230
+ import { existsSync as existsSync27, statSync as statSync13 } from "fs";
17231
+ import { join as join21 } from "path";
17124
17232
  import { arch as arch2, platform as platform5, release as release2 } from "os";
17125
17233
 
17126
17234
  // src/diagnostics/crash-log.ts
17127
17235
  import {
17128
- existsSync as existsSync25,
17236
+ existsSync as existsSync26,
17129
17237
  mkdirSync as mkdirSync12,
17130
- readdirSync as readdirSync12,
17131
- readFileSync as readFileSync18,
17238
+ readdirSync as readdirSync13,
17239
+ readFileSync as readFileSync19,
17132
17240
  statSync as statSync12,
17133
17241
  unlinkSync as unlinkSync6,
17134
17242
  writeFileSync as writeFileSync2
17135
17243
  } from "fs";
17136
- import { join as join19 } from "path";
17244
+ import { join as join20 } from "path";
17137
17245
  import { homedir as homedir7, platform as platform4, release, arch } from "os";
17138
17246
  var LOGS_DIR_NAME = "logs";
17139
17247
  var CRASH_LOG_PREFIX = "crash-";
17140
17248
  function getLogsDir(configDir) {
17141
- const base = configDir ?? join19(homedir7(), CONFIG_DIR_NAME);
17142
- return join19(base, LOGS_DIR_NAME);
17249
+ const base = configDir ?? join20(homedir7(), CONFIG_DIR_NAME);
17250
+ return join20(base, LOGS_DIR_NAME);
17143
17251
  }
17144
17252
  function truncate(s, n) {
17145
17253
  return s.length <= n ? s : s.slice(0, n) + "\u2026";
17146
17254
  }
17147
17255
  function listRecentCrashes(limit = 10, configDir) {
17148
17256
  const dir = getLogsDir(configDir);
17149
- if (!existsSync25(dir)) return [];
17257
+ if (!existsSync26(dir)) return [];
17150
17258
  const files = [];
17151
17259
  let names;
17152
17260
  try {
17153
- names = readdirSync12(dir);
17261
+ names = readdirSync13(dir);
17154
17262
  } catch {
17155
17263
  return [];
17156
17264
  }
17157
17265
  for (const name of names) {
17158
17266
  if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
17159
- const full = join19(dir, name);
17267
+ const full = join20(dir, name);
17160
17268
  try {
17161
17269
  const st = statSync12(full);
17162
- const head = readFileSync18(full, "utf-8").split("\n").slice(0, 20);
17270
+ const head = readFileSync19(full, "utf-8").split("\n").slice(0, 20);
17163
17271
  const tsLine = head.find((l) => l.startsWith("timestamp:"));
17164
17272
  const kindLine = head.find((l) => l.startsWith("kind:"));
17165
17273
  const msgIdx = head.findIndex((l) => l.trim() === "## message");
@@ -17178,18 +17286,18 @@ function listRecentCrashes(limit = 10, configDir) {
17178
17286
  return files.slice(0, limit);
17179
17287
  }
17180
17288
  function getConfigDirUsage(configDir) {
17181
- const base = configDir ?? join19(homedir7(), CONFIG_DIR_NAME);
17182
- if (!existsSync25(base)) return { totalBytes: 0, entries: [] };
17289
+ const base = configDir ?? join20(homedir7(), CONFIG_DIR_NAME);
17290
+ if (!existsSync26(base)) return { totalBytes: 0, entries: [] };
17183
17291
  const entries = [];
17184
17292
  let total = 0;
17185
17293
  let names;
17186
17294
  try {
17187
- names = readdirSync12(base);
17295
+ names = readdirSync13(base);
17188
17296
  } catch {
17189
17297
  return { totalBytes: 0, entries: [] };
17190
17298
  }
17191
17299
  for (const name of names) {
17192
- const full = join19(base, name);
17300
+ const full = join20(base, name);
17193
17301
  const bytes = dirSize(full);
17194
17302
  entries.push({ name, bytes });
17195
17303
  total += bytes;
@@ -17203,8 +17311,8 @@ function dirSize(path3) {
17203
17311
  if (st.isFile()) return st.size;
17204
17312
  if (!st.isDirectory()) return 0;
17205
17313
  let sum = 0;
17206
- for (const name of readdirSync12(path3)) {
17207
- sum += dirSize(join19(path3, name));
17314
+ for (const name of readdirSync13(path3)) {
17315
+ sum += dirSize(join20(path3, name));
17208
17316
  }
17209
17317
  return sum;
17210
17318
  } catch {
@@ -17214,7 +17322,7 @@ function dirSize(path3) {
17214
17322
 
17215
17323
  // src/diagnostics/doctor-report.ts
17216
17324
  function checkFile(label, path3) {
17217
- const exists = existsSync26(path3);
17325
+ const exists = existsSync27(path3);
17218
17326
  let sizeBytes = null;
17219
17327
  if (exists) {
17220
17328
  try {
@@ -17257,12 +17365,12 @@ function buildDoctorReport(options) {
17257
17365
  configDir,
17258
17366
  providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
17259
17367
  files: [
17260
- checkFile("config.json", join20(configDir, "config.json")),
17261
- checkFile("memory.md", join20(configDir, MEMORY_FILE_NAME)),
17262
- checkFile("memory.jsonl", join20(configDir, "memory.jsonl")),
17263
- checkFile("dev-state.md", join20(configDir, DEV_STATE_FILE_NAME)),
17264
- checkFile("hooks-trust.json", join20(configDir, "hooks-trust.json")),
17265
- checkFile("plugin-state.json", join20(configDir, "plugins", "plugin-state.json"))
17368
+ checkFile("config.json", join21(configDir, "config.json")),
17369
+ checkFile("memory.md", join21(configDir, MEMORY_FILE_NAME)),
17370
+ checkFile("memory.jsonl", join21(configDir, "memory.jsonl")),
17371
+ checkFile("dev-state.md", join21(configDir, DEV_STATE_FILE_NAME)),
17372
+ checkFile("hooks-trust.json", join21(configDir, "hooks-trust.json")),
17373
+ checkFile("plugin-state.json", join21(configDir, "plugins", "plugin-state.json"))
17266
17374
  ],
17267
17375
  context: {
17268
17376
  enabled: contextSetting !== false,
@@ -17297,7 +17405,7 @@ function buildDoctorReport(options) {
17297
17405
  disabled: hooks.filter((h) => h.disabled).length
17298
17406
  },
17299
17407
  plugins: {
17300
- root: join20(configDir, "plugins"),
17408
+ root: join21(configDir, "plugins"),
17301
17409
  installed: plugins.length,
17302
17410
  active: activeAssets.plugins.length,
17303
17411
  invalid: plugins.filter((p) => !p.valid).length,
@@ -17312,7 +17420,7 @@ function buildDoctorReport(options) {
17312
17420
  mcp: {
17313
17421
  enabled: config.get("mcpEnabled"),
17314
17422
  configured: Object.keys(mcpServers).length,
17315
- projectConfigExists: existsSync26(join20(projectRoot, MCP_PROJECT_CONFIG_NAME)),
17423
+ projectConfigExists: existsSync27(join21(projectRoot, MCP_PROJECT_CONFIG_NAME)),
17316
17424
  statuses: options.mcpStatuses ?? []
17317
17425
  },
17318
17426
  recentCrashes: listRecentCrashes(5, configDir),
@@ -17834,7 +17942,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
17834
17942
  Cost: ${formatCost(cost)}` : "";
17835
17943
  let memoryLine = "";
17836
17944
  try {
17837
- const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-OCDFR4BH.js");
17945
+ const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-LMVWIHMX.js");
17838
17946
  const pending = countPendingMemories2(ctx.config.getConfigDir());
17839
17947
  if (pending > 0) memoryLine = `
17840
17948
  Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
@@ -18575,7 +18683,7 @@ async function handlePlugin(args, ctx) {
18575
18683
  lines.push("");
18576
18684
  }
18577
18685
  if (pluginTools.length > 0) {
18578
- const legacyDir = join21(configDir, PLUGINS_DIR_NAME);
18686
+ const legacyDir = join22(configDir, PLUGINS_DIR_NAME);
18579
18687
  lines.push(`**Legacy JS tools** (${pluginTools.length}):`);
18580
18688
  lines.push(`Dir: ${legacyDir}`);
18581
18689
  for (const t of pluginTools) lines.push(` \u2022 ${t.definition.name} \u2014 ${t.definition.description}`);
@@ -18715,14 +18823,14 @@ ${note}` : head });
18715
18823
  }
18716
18824
  async function handleCommands(_args, ctx) {
18717
18825
  const configDir = ctx.config.getConfigDir();
18718
- const commandsDir = join21(configDir, CUSTOM_COMMANDS_DIR_NAME);
18719
- if (!existsSync27(commandsDir)) {
18826
+ const commandsDir = join22(configDir, CUSTOM_COMMANDS_DIR_NAME);
18827
+ if (!existsSync28(commandsDir)) {
18720
18828
  ctx.send({ type: "info", message: `No custom commands directory.
18721
18829
  Create: ${commandsDir}/ with .md files.` });
18722
18830
  return;
18723
18831
  }
18724
18832
  try {
18725
- const files = readdirSync13(commandsDir).filter((f) => f.endsWith(".md"));
18833
+ const files = readdirSync14(commandsDir).filter((f) => f.endsWith(".md"));
18726
18834
  if (files.length === 0) {
18727
18835
  ctx.send({ type: "info", message: `No custom commands found in ${commandsDir}
18728
18836
  Add .md files to create commands.` });
@@ -18822,7 +18930,7 @@ async function handleTest(args, ctx) {
18822
18930
  const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
18823
18931
  testArgs = isCommand ? { command: argStr } : { filter: argStr };
18824
18932
  }
18825
- const runTests = ctx.runTests ?? (await import("./run-tests-LTYJACRX.js")).executeTests;
18933
+ const runTests = ctx.runTests ?? (await import("./run-tests-MQVGE6FK.js")).executeTests;
18826
18934
  const report = await runTests(testArgs);
18827
18935
  ctx.send({ type: "info", message: report });
18828
18936
  } catch (err) {
@@ -18846,9 +18954,9 @@ async function handleDiff(args, ctx) {
18846
18954
  let modifiedFiles = 0;
18847
18955
  const diffLines2 = [];
18848
18956
  for (const [filePath, { earliest }] of fileMap) {
18849
- const currentContent = existsSync27(filePath) ? (() => {
18957
+ const currentContent = existsSync28(filePath) ? (() => {
18850
18958
  try {
18851
- return readFileSync19(filePath, "utf-8");
18959
+ return readFileSync20(filePath, "utf-8");
18852
18960
  } catch {
18853
18961
  return null;
18854
18962
  }
@@ -18883,9 +18991,9 @@ async function handleDiff(args, ctx) {
18883
18991
  }
18884
18992
  async function handleInit(args, ctx) {
18885
18993
  const cwd = process.cwd();
18886
- const targetPath = join21(cwd, "AICLI.md");
18994
+ const targetPath = join22(cwd, "AICLI.md");
18887
18995
  const force = args.includes("--force");
18888
- if (existsSync27(targetPath) && !force) {
18996
+ if (existsSync28(targetPath) && !force) {
18889
18997
  ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
18890
18998
  Use /init --force to overwrite.` });
18891
18999
  return;
@@ -19213,7 +19321,7 @@ async function handleMemory(args, ctx) {
19213
19321
  }
19214
19322
  if (intent.kind === "status") {
19215
19323
  const { getChatIndexStatus: getChatIndexStatus2 } = await import("./chat-index-RLVLQRRC.js");
19216
- const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-OCDFR4BH.js");
19324
+ const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-LMVWIHMX.js");
19217
19325
  const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
19218
19326
  const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
19219
19327
  const view = buildMemoryStatusView({
@@ -19236,7 +19344,7 @@ async function handleMemory(args, ctx) {
19236
19344
  return;
19237
19345
  }
19238
19346
  if (intent.kind === "recall") {
19239
- const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-OCDFR4BH.js");
19347
+ const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-LMVWIHMX.js");
19240
19348
  const { searchChatMemory: searchChatMemory2 } = await import("./chat-index-RLVLQRRC.js");
19241
19349
  const persistentHits = searchPersistentMemories2(configDir, intent.query, process.cwd(), 3);
19242
19350
  if (persistentHits.length > 0) {
@@ -19357,7 +19465,7 @@ async function handleMemory(args, ctx) {
19357
19465
  ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
19358
19466
  } else if (intent.kind === "export") {
19359
19467
  if (args[1] === "md") {
19360
- const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-OCDFR4BH.js");
19468
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-LMVWIHMX.js");
19361
19469
  ctx.send({
19362
19470
  type: "export_data",
19363
19471
  format: "md",
@@ -19395,7 +19503,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
19395
19503
  return;
19396
19504
  }
19397
19505
  const dirPath = resolve9(sub);
19398
- if (!existsSync27(dirPath)) {
19506
+ if (!existsSync28(dirPath)) {
19399
19507
  ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
19400
19508
  return;
19401
19509
  }
@@ -20360,16 +20468,16 @@ No tools match "${filter}".
20360
20468
  usage: "/mcp [reconnect [serverId] | trust-project]",
20361
20469
  async execute(args, ctx) {
20362
20470
  if (args[0] === "trust-project") {
20363
- const { join: join26 } = await import("path");
20364
- const { existsSync: existsSync32 } = await import("fs");
20471
+ const { join: join27 } = await import("path");
20472
+ const { existsSync: existsSync33 } = await import("fs");
20365
20473
  const { getGitRoot: getGitRoot2 } = await import("./git-context-VNCA2GU7.js");
20366
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-BYWEJLAI.js");
20474
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-JM6MK5SS.js");
20367
20475
  const { approveProject, hashMcpFile } = await import("./project-trust-6CJB5ZMX.js");
20368
20476
  const cwd = process.cwd();
20369
20477
  const projectRoot = getGitRoot2(cwd) ?? cwd;
20370
- const mcpPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME2);
20478
+ const mcpPath = join27(projectRoot, MCP_PROJECT_CONFIG_NAME2);
20371
20479
  console.log();
20372
- if (!existsSync32(mcpPath)) {
20480
+ if (!existsSync33(mcpPath)) {
20373
20481
  console.log(theme.dim(` No .mcp.json in ${projectRoot}.`));
20374
20482
  console.log();
20375
20483
  return;
@@ -20804,12 +20912,12 @@ ${outcome.summary}
20804
20912
  if (scanAll) {
20805
20913
  const metas = ctx.sessions.listSessions();
20806
20914
  console.log(theme.info(` Scanning ${metas.length} session(s)\u2026`));
20807
- const { readFileSync: readFileSync23 } = await import("fs");
20808
- const { join: join26 } = await import("path");
20915
+ const { readFileSync: readFileSync24 } = await import("fs");
20916
+ const { join: join27 } = await import("path");
20809
20917
  const historyDir = ctx.config.getHistoryDir();
20810
20918
  for (const m of metas) {
20811
20919
  try {
20812
- const content = readFileSync23(join26(historyDir, `${m.id}.json`), "utf-8");
20920
+ const content = readFileSync24(join27(historyDir, `${m.id}.json`), "utf-8");
20813
20921
  const hits2 = scanString(content, opts);
20814
20922
  if (hits2.length) {
20815
20923
  filesWithHits++;
@@ -20856,8 +20964,8 @@ ${outcome.summary}
20856
20964
  }
20857
20965
 
20858
20966
  // src/repl/commands/review-commands.ts
20859
- import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as writeFileSync4 } from "fs";
20860
- import { join as join22 } from "path";
20967
+ import { existsSync as existsSync29, readFileSync as readFileSync21, writeFileSync as writeFileSync4 } from "fs";
20968
+ import { join as join23 } from "path";
20861
20969
  function createReviewCommands() {
20862
20970
  return [
20863
20971
  {
@@ -21216,9 +21324,9 @@ function createReviewCommands() {
21216
21324
  usage: "/init [--force]",
21217
21325
  async execute(args, ctx) {
21218
21326
  const cwd = process.cwd();
21219
- const targetPath = join22(cwd, "AICLI.md");
21327
+ const targetPath = join23(cwd, "AICLI.md");
21220
21328
  const force = args.includes("--force");
21221
- if (existsSync28(targetPath) && !force) {
21329
+ if (existsSync29(targetPath) && !force) {
21222
21330
  ctx.renderer.printInfo(`AICLI.md already exists at ${targetPath}`);
21223
21331
  ctx.renderer.printInfo("Use /init --force to overwrite, or edit it manually.");
21224
21332
  return;
@@ -21282,7 +21390,7 @@ function createReviewCommands() {
21282
21390
  usage: "/test [command|filter]",
21283
21391
  async execute(args, ctx) {
21284
21392
  try {
21285
- const { executeTests } = await import("./run-tests-LTYJACRX.js");
21393
+ const { executeTests } = await import("./run-tests-MQVGE6FK.js");
21286
21394
  const argStr = args.join(" ").trim();
21287
21395
  let testArgs = {};
21288
21396
  if (argStr) {
@@ -21317,9 +21425,9 @@ function createReviewCommands() {
21317
21425
  let newFiles = 0;
21318
21426
  let modifiedFiles = 0;
21319
21427
  for (const [filePath, { earliest }] of fileMap) {
21320
- const currentContent = existsSync28(filePath) ? (() => {
21428
+ const currentContent = existsSync29(filePath) ? (() => {
21321
21429
  try {
21322
- return readFileSync20(filePath, "utf-8");
21430
+ return readFileSync21(filePath, "utf-8");
21323
21431
  } catch {
21324
21432
  return null;
21325
21433
  }
@@ -22481,17 +22589,17 @@ function createSessionCommands() {
22481
22589
  }
22482
22590
 
22483
22591
  // src/repl/commands/misc-commands.ts
22484
- import { basename as basename10 } from "path";
22592
+ import { basename as basename11 } from "path";
22485
22593
  import { platform as platform8 } from "os";
22486
22594
 
22487
22595
  // src/repl/clipboard.ts
22488
22596
  import { execSync as execSync3 } from "child_process";
22489
- import { existsSync as existsSync29, statSync as statSync15, unlinkSync as unlinkSync7 } from "fs";
22597
+ import { existsSync as existsSync30, statSync as statSync15, unlinkSync as unlinkSync7 } from "fs";
22490
22598
  import { tmpdir as tmpdir3 } from "os";
22491
- import { join as join23 } from "path";
22599
+ import { join as join24 } from "path";
22492
22600
  var CLIPBOARD_TIMEOUT = 5e3;
22493
22601
  function readClipboardImage() {
22494
- const outPath = join23(tmpdir3(), `aicli-paste-${Date.now()}.png`);
22602
+ const outPath = join24(tmpdir3(), `aicli-paste-${Date.now()}.png`);
22495
22603
  try {
22496
22604
  if (process.platform === "win32") {
22497
22605
  _readWin32(outPath);
@@ -22504,7 +22612,7 @@ function readClipboardImage() {
22504
22612
  _cleanup(outPath);
22505
22613
  return null;
22506
22614
  }
22507
- if (!existsSync29(outPath) || statSync15(outPath).size === 0) {
22615
+ if (!existsSync30(outPath) || statSync15(outPath).size === 0) {
22508
22616
  _cleanup(outPath);
22509
22617
  return null;
22510
22618
  }
@@ -22557,7 +22665,7 @@ function _readLinux(outPath) {
22557
22665
  }
22558
22666
  function _cleanup(path3) {
22559
22667
  try {
22560
- if (existsSync29(path3)) unlinkSync7(path3);
22668
+ if (existsSync30(path3)) unlinkSync7(path3);
22561
22669
  } catch {
22562
22670
  }
22563
22671
  }
@@ -22827,7 +22935,7 @@ ${hint}` : "")
22827
22935
  return;
22828
22936
  }
22829
22937
  const description = args.join(" ").trim() || "Please describe this image";
22830
- ctx.renderer.printSuccess(`\u{1F4CB} Image loaded: ${basename10(imgPath)}`);
22938
+ ctx.renderer.printSuccess(`\u{1F4CB} Image loaded: ${basename11(imgPath)}`);
22831
22939
  await ctx.sendAsChat(`@${imgPath} ${description}`);
22832
22940
  }
22833
22941
  },
@@ -23383,6 +23491,11 @@ var SessionHandler = class _SessionHandler {
23383
23491
  costCnyPerUsd: CNY_PER_USD,
23384
23492
  costPricingUnknown,
23385
23493
  providers: providerList,
23494
+ hubPresets: loadCustomPresets(this.config.getConfigDir()).presets.map((p) => ({
23495
+ id: p.id,
23496
+ name: p.name,
23497
+ description: p.description
23498
+ })),
23386
23499
  branches,
23387
23500
  activeBranchId: sess?.activeBranchId ?? "main",
23388
23501
  agents: listAgentRuns().slice(0, 20).map((run) => ({
@@ -23636,7 +23749,8 @@ var SessionHandler = class _SessionHandler {
23636
23749
  this.send({ type: "error", message: "Hub topic is required." });
23637
23750
  return;
23638
23751
  }
23639
- const preset = getPreset(msg.preset ?? "brainstorm");
23752
+ const presetId = msg.preset ?? "brainstorm";
23753
+ const preset = presetId.startsWith(CUSTOM_PRESET_PREFIX) ? loadCustomPresets(this.config.getConfigDir()).presets.find((p) => p.id === presetId) : getPreset(presetId);
23640
23754
  if (!preset) {
23641
23755
  this.send({ type: "error", message: `Unknown preset "${msg.preset}".` });
23642
23756
  return;
@@ -24839,8 +24953,8 @@ async function setupProxy(configProxy) {
24839
24953
  }
24840
24954
 
24841
24955
  // src/web/auth.ts
24842
- import { existsSync as existsSync30, readFileSync as readFileSync21, mkdirSync as mkdirSync16, readdirSync as readdirSync14, openSync, closeSync, unlinkSync as unlinkSync8 } from "fs";
24843
- import { isAbsolute as isAbsolute3, join as join24, relative as relative5 } from "path";
24956
+ import { existsSync as existsSync31, readFileSync as readFileSync22, mkdirSync as mkdirSync16, readdirSync as readdirSync15, openSync, closeSync, unlinkSync as unlinkSync8 } from "fs";
24957
+ import { isAbsolute as isAbsolute3, join as join25, relative as relative5 } from "path";
24844
24958
  import { createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2, pbkdf2Sync, pbkdf2 } from "crypto";
24845
24959
  var USERS_FILE = "users.json";
24846
24960
  var TOKEN_EXPIRY_HOURS = 24;
@@ -24856,7 +24970,7 @@ var AuthManager = class {
24856
24970
  loadedRaw;
24857
24971
  constructor(baseDir) {
24858
24972
  this.baseDir = baseDir;
24859
- this.usersFile = join24(baseDir, USERS_FILE);
24973
+ this.usersFile = join25(baseDir, USERS_FILE);
24860
24974
  this.db = this.loadOrCreate();
24861
24975
  }
24862
24976
  // ── Public API ─────────────────────────────────────────────────
@@ -24898,10 +25012,10 @@ var AuthManager = class {
24898
25012
  }
24899
25013
  const salt = prepared?.salt ?? randomBytes2(16).toString("hex");
24900
25014
  const passwordHash = prepared?.hash ?? this.hashPassword(password, salt);
24901
- const dataDir = join24(USERS_DIR, username);
24902
- const fullDataDir = join24(this.baseDir, dataDir);
25015
+ const dataDir = join25(USERS_DIR, username);
25016
+ const fullDataDir = join25(this.baseDir, dataDir);
24903
25017
  mkdirSync16(fullDataDir, { recursive: true, mode: 448 });
24904
- mkdirSync16(join24(fullDataDir, "history"), { recursive: true, mode: 448 });
25018
+ mkdirSync16(join25(fullDataDir, "history"), { recursive: true, mode: 448 });
24905
25019
  const user = {
24906
25020
  username,
24907
25021
  passwordHash,
@@ -25061,7 +25175,7 @@ var AuthManager = class {
25061
25175
  this.refresh();
25062
25176
  const user = this.db.users.find((u) => u.username === username);
25063
25177
  if (!user) throw new Error(`User not found: ${username}`);
25064
- return join24(this.baseDir, user.dataDir);
25178
+ return join25(this.baseDir, user.dataDir);
25065
25179
  }
25066
25180
  /** List all usernames */
25067
25181
  listUsers() {
@@ -25100,30 +25214,30 @@ var AuthManager = class {
25100
25214
  const err = this.register(username, password);
25101
25215
  if (err) return err;
25102
25216
  const userDir = this.getUserDataDir(username);
25103
- const globalConfig = join24(this.baseDir, "config.json");
25104
- if (existsSync30(globalConfig)) {
25217
+ const globalConfig = join25(this.baseDir, "config.json");
25218
+ if (existsSync31(globalConfig)) {
25105
25219
  try {
25106
- const content = readFileSync21(globalConfig, "utf-8");
25107
- atomicWriteFileSync(join24(userDir, "config.json"), content, { mode: 384 });
25220
+ const content = readFileSync22(globalConfig, "utf-8");
25221
+ atomicWriteFileSync(join25(userDir, "config.json"), content, { mode: 384 });
25108
25222
  } catch {
25109
25223
  }
25110
25224
  }
25111
- const globalMemory = join24(this.baseDir, "memory.md");
25112
- if (existsSync30(globalMemory)) {
25225
+ const globalMemory = join25(this.baseDir, "memory.md");
25226
+ if (existsSync31(globalMemory)) {
25113
25227
  try {
25114
- const content = readFileSync21(globalMemory, "utf-8");
25115
- atomicWriteFileSync(join24(userDir, "memory.md"), content, { mode: 384 });
25228
+ const content = readFileSync22(globalMemory, "utf-8");
25229
+ atomicWriteFileSync(join25(userDir, "memory.md"), content, { mode: 384 });
25116
25230
  } catch {
25117
25231
  }
25118
25232
  }
25119
- const globalHistory = join24(this.baseDir, "history");
25120
- if (existsSync30(globalHistory)) {
25233
+ const globalHistory = join25(this.baseDir, "history");
25234
+ if (existsSync31(globalHistory)) {
25121
25235
  try {
25122
- const files = readdirSync14(globalHistory).filter((f) => f.endsWith(".json"));
25123
- const userHistory = join24(userDir, "history");
25236
+ const files = readdirSync15(globalHistory).filter((f) => f.endsWith(".json"));
25237
+ const userHistory = join25(userDir, "history");
25124
25238
  for (const f of files) {
25125
25239
  try {
25126
- atomicWriteFileSync(join24(userHistory, f), readFileSync21(join24(globalHistory, f), "utf8"), { mode: 384 });
25240
+ atomicWriteFileSync(join25(userHistory, f), readFileSync22(join25(globalHistory, f), "utf8"), { mode: 384 });
25127
25241
  } catch {
25128
25242
  }
25129
25243
  }
@@ -25134,16 +25248,16 @@ var AuthManager = class {
25134
25248
  }
25135
25249
  // ── Private methods ────────────────────────────────────────────
25136
25250
  refresh() {
25137
- const raw = readFileSync21(this.usersFile, "utf8");
25251
+ const raw = readFileSync22(this.usersFile, "utf8");
25138
25252
  const parsed = JSON.parse(raw);
25139
25253
  if (!this.isValidUsersDB(parsed)) throw new Error("Authentication database is invalid; access refused");
25140
25254
  this.db = parsed;
25141
25255
  this.loadedRaw = raw;
25142
25256
  }
25143
25257
  loadOrCreate() {
25144
- if (existsSync30(this.usersFile)) {
25258
+ if (existsSync31(this.usersFile)) {
25145
25259
  try {
25146
- const raw = readFileSync21(this.usersFile, "utf-8");
25260
+ const raw = readFileSync22(this.usersFile, "utf-8");
25147
25261
  const parsed = JSON.parse(raw);
25148
25262
  if (!this.isValidUsersDB(parsed)) {
25149
25263
  throw new Error("database structure is invalid");
@@ -25179,7 +25293,7 @@ var AuthManager = class {
25179
25293
  if (typeof record.username !== "string" || !/^[a-z0-9_-]{2,32}$/.test(record.username) || usernames.has(record.username) || typeof record.passwordHash !== "string" || record.passwordHash.length === 0 || typeof record.salt !== "string" || record.salt.length === 0 || typeof record.createdAt !== "string" || typeof record.dataDir !== "string" || record.dataDir.length === 0 || isAbsolute3(record.dataDir)) {
25180
25294
  return false;
25181
25295
  }
25182
- const dataDirFromBase = relative5(this.baseDir, join24(this.baseDir, record.dataDir));
25296
+ const dataDirFromBase = relative5(this.baseDir, join25(this.baseDir, record.dataDir));
25183
25297
  if (dataDirFromBase.startsWith("..") || isAbsolute3(dataDirFromBase)) return false;
25184
25298
  if (record.hashVersion !== void 0 && record.hashVersion !== 1 && record.hashVersion !== 2) return false;
25185
25299
  if (record.tokensRevokedBefore !== void 0 && !Number.isFinite(record.tokensRevokedBefore)) return false;
@@ -25195,7 +25309,7 @@ var AuthManager = class {
25195
25309
  const lock = this.usersFile + ".lock";
25196
25310
  const fd = openSync(lock, "wx", 384);
25197
25311
  try {
25198
- const current = existsSync30(this.usersFile) ? readFileSync21(this.usersFile, "utf8") : void 0;
25312
+ const current = existsSync31(this.usersFile) ? readFileSync22(this.usersFile, "utf8") : void 0;
25199
25313
  if (current !== this.loadedRaw) throw new Error("Authentication database changed concurrently; retry the operation");
25200
25314
  const raw = JSON.stringify(db, null, 2);
25201
25315
  atomicWriteFileSync(this.usersFile, raw, { mode: 384 });
@@ -25329,8 +25443,8 @@ async function startWebServer(options = {}) {
25329
25443
  }
25330
25444
  }
25331
25445
  let skillManager = null;
25332
- const skillsDir = join25(config.getConfigDir(), SKILLS_DIR_NAME);
25333
- if (existsSync31(skillsDir)) {
25446
+ const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
25447
+ if (existsSync32(skillsDir)) {
25334
25448
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
25335
25449
  skillManager.loadSkills(pluginAssets.skillDirs);
25336
25450
  const count = skillManager.listSkills().length;
@@ -25423,15 +25537,15 @@ async function startWebServer(options = {}) {
25423
25537
  next();
25424
25538
  };
25425
25539
  const moduleDir = getModuleDir();
25426
- let clientDir = join25(moduleDir, "web", "client");
25427
- if (!existsSync31(clientDir)) {
25428
- clientDir = join25(moduleDir, "client");
25540
+ let clientDir = join26(moduleDir, "web", "client");
25541
+ if (!existsSync32(clientDir)) {
25542
+ clientDir = join26(moduleDir, "client");
25429
25543
  }
25430
- if (!existsSync31(clientDir)) {
25431
- clientDir = join25(moduleDir, "..", "..", "src", "web", "client");
25544
+ if (!existsSync32(clientDir)) {
25545
+ clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
25432
25546
  }
25433
- if (!existsSync31(clientDir)) {
25434
- clientDir = join25(process.cwd(), "src", "web", "client");
25547
+ if (!existsSync32(clientDir)) {
25548
+ clientDir = join26(process.cwd(), "src", "web", "client");
25435
25549
  }
25436
25550
  console.log(` Static files: ${clientDir}`);
25437
25551
  const PRECOMPRESSED_TYPES = {
@@ -25451,14 +25565,14 @@ async function startWebServer(options = {}) {
25451
25565
  const encoding = /\bbr\b/.test(accept) ? "br" : /\bgzip\b/.test(accept) ? "gzip" : null;
25452
25566
  if (!encoding) return next();
25453
25567
  const target = resolvePath(clientDir, `.${req.path}`) + (encoding === "br" ? ".br" : ".gz");
25454
- if (!target.startsWith(clientDir) || !existsSync31(target)) return next();
25568
+ if (!target.startsWith(clientDir) || !existsSync32(target)) return next();
25455
25569
  res.setHeader("Content-Type", contentType);
25456
25570
  res.setHeader("Content-Encoding", encoding);
25457
25571
  res.setHeader("Vary", "Accept-Encoding");
25458
25572
  if (req.path.startsWith("/vendor/")) res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
25459
25573
  createReadStream(target).pipe(res);
25460
25574
  });
25461
- app.use("/vendor", express.static(join25(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
25575
+ app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
25462
25576
  app.use(express.static(clientDir));
25463
25577
  app.get("/api/status", (_req, res) => {
25464
25578
  res.json({
@@ -25549,7 +25663,7 @@ async function startWebServer(options = {}) {
25549
25663
  app.get("/api/files", requireAuth, (req, res) => {
25550
25664
  const cwd = process.cwd();
25551
25665
  const prefix = req.query.prefix || "";
25552
- const targetDir = join25(cwd, prefix);
25666
+ const targetDir = join26(cwd, prefix);
25553
25667
  try {
25554
25668
  const canonicalTarget = realpathSync3(resolve12(targetDir));
25555
25669
  const canonicalCwd = realpathSync3(resolve12(cwd));
@@ -25563,10 +25677,10 @@ async function startWebServer(options = {}) {
25563
25677
  }
25564
25678
  try {
25565
25679
  const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "dist-cjs", "release", "__pycache__", ".next", ".nuxt", "coverage", ".cache"]);
25566
- const entries = readdirSync15(targetDir, { withFileTypes: true });
25680
+ const entries = readdirSync16(targetDir, { withFileTypes: true });
25567
25681
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
25568
25682
  name: e.name,
25569
- path: relative6(cwd, join25(targetDir, e.name)).replace(/\\/g, "/"),
25683
+ path: relative6(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
25570
25684
  isDir: e.isDirectory()
25571
25685
  }));
25572
25686
  res.json({ files });
@@ -25606,8 +25720,8 @@ async function startWebServer(options = {}) {
25606
25720
  try {
25607
25721
  const authUser = req._authUser;
25608
25722
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
25609
- const filePath = join25(histDir, `${id}.json`);
25610
- if (!existsSync31(filePath)) {
25723
+ const filePath = join26(histDir, `${id}.json`);
25724
+ if (!existsSync32(filePath)) {
25611
25725
  res.status(404).json({ error: "Session not found" });
25612
25726
  return;
25613
25727
  }
@@ -25622,7 +25736,7 @@ async function startWebServer(options = {}) {
25622
25736
  res.status(404).json({ error: "Session not found" });
25623
25737
  return;
25624
25738
  }
25625
- const data = JSON.parse(readFileSync22(filePath, "utf-8"));
25739
+ const data = JSON.parse(readFileSync23(filePath, "utf-8"));
25626
25740
  res.json({ session: data });
25627
25741
  } catch (err) {
25628
25742
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
@@ -25635,7 +25749,7 @@ async function startWebServer(options = {}) {
25635
25749
  return;
25636
25750
  }
25637
25751
  const cwd = process.cwd();
25638
- const fullPath = resolve12(join25(cwd, filePath));
25752
+ const fullPath = resolve12(join26(cwd, filePath));
25639
25753
  try {
25640
25754
  const canonicalFull = realpathSync3(fullPath);
25641
25755
  assertReadablePath(canonicalFull);
@@ -25654,7 +25768,7 @@ async function startWebServer(options = {}) {
25654
25768
  res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
25655
25769
  return;
25656
25770
  }
25657
- const content = readFileSync22(fullPath, "utf-8");
25771
+ const content = readFileSync23(fullPath, "utf-8");
25658
25772
  res.json({ content, size: stat.size });
25659
25773
  } catch {
25660
25774
  res.json({ error: "Cannot read file" });
@@ -25991,17 +26105,17 @@ function resolveProjectMcpPath() {
25991
26105
  const cwd = process.cwd();
25992
26106
  const gitRoot = getGitRoot(cwd);
25993
26107
  const projectRoot = gitRoot ?? cwd;
25994
- const configPath = join25(projectRoot, MCP_PROJECT_CONFIG_NAME);
25995
- return existsSync31(configPath) ? configPath : null;
26108
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
26109
+ return existsSync32(configPath) ? configPath : null;
25996
26110
  }
25997
26111
  function loadProjectMcpConfig() {
25998
26112
  const cwd = process.cwd();
25999
26113
  const gitRoot = getGitRoot(cwd);
26000
26114
  const projectRoot = gitRoot ?? cwd;
26001
- const configPath = join25(projectRoot, MCP_PROJECT_CONFIG_NAME);
26002
- if (!existsSync31(configPath)) return null;
26115
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
26116
+ if (!existsSync32(configPath)) return null;
26003
26117
  try {
26004
- const raw = JSON.parse(readFileSync22(configPath, "utf-8"));
26118
+ const raw = JSON.parse(readFileSync23(configPath, "utf-8"));
26005
26119
  return raw.mcpServers ?? raw;
26006
26120
  } catch {
26007
26121
  return null;