deepseeker-code 1.0.49 → 1.0.51

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/README.md +10 -9
  2. package/dist/cli.mjs +899 -317
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -130,6 +130,7 @@ var init_strings = __esm({
130
130
  approvalDeny: "\u62D2\u7EDD",
131
131
  errAborted: "\u5DF2\u4E2D\u6B62\u5F53\u524D\u8F6E\u3002",
132
132
  busyBlockSend: "\u23F3 \u6B63\u5728\u751F\u6210\uFF0C\u8BF7\u7B49\u5F85\u6216\u6309 Esc \u4E2D\u6B62\u540E\u518D\u53D1\u9001\u3002",
133
+ escInterrupt: "Esc \u4E2D\u6B62",
133
134
  inboxQueued: "\u{1F4EE} \u5DF2\u6392\u961F\uFF0C\u5C06\u5728\u56DE\u5408\u8FB9\u754C\u9001\u8FBE\u6A21\u578B\uFF08Esc \u4ECD\u53EF\u4E2D\u6B62\uFF09\u3002",
134
135
  inboxClaimed: (n) => `\u{1F4EC} \u5DF2\u9001\u8FBE\u6A21\u578B\uFF08${n} \u6761\u8865\u5145\u8F93\u5165\uFF09`,
135
136
  inboxCarryover: (n) => `\u{1F4EC} \u672C\u8F6E\u7ED3\u675F\u65F6 ${n} \u6761\u8865\u5145\u8F93\u5165\u672A\u5728\u56DE\u5408\u8FB9\u754C\u9001\u8FBE\uFF0C\u5DF2\u5199\u5165\u4F1A\u8BDD\u5386\u53F2\uFF0C\u4E0B\u8F6E\u5BF9\u8BDD\u81EA\u52A8\u5E26\u5165\u3002`,
@@ -161,7 +162,8 @@ ${dim} \u8FD0\u884C npm i -g deepseeker-code@latest \u66F4\u65B0${reset}
161
162
  cmdStatus: "\u67E5\u770B\u5F53\u524D\u6A21\u578B/\u4F1A\u8BDD/\u6A21\u5F0F",
162
163
  cmdPlan: "\u5207\u6362\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u8BFB\u8C03\u7814\u2192\u5BA1\u6279\u2192\u5B9E\u73B0\uFF09",
163
164
  cmdAuto: "\u5207\u6362\u81EA\u52A8\u6A21\u5F0F\uFF08\u6587\u4EF6\u7F16\u8F91\u5206\u7C7B\u5668\u81EA\u52A8\u653E\u884C\uFF0C\u9AD8\u5371\u8F6C\u4EBA\u5DE5\uFF09",
164
- cmdModel: "\u5207\u6362\u6A21\u578B\uFF1A/model <deepseek-v4|deepseek-v4-flash|\u2026>",
165
+ cmdModel: "\u5207\u6362\u6A21\u578B\uFF08\u76F4\u8F93\uFF09\uFF1A/model <\u6A21\u578Bid>\uFF0C\u65E0\u53C2\u67E5\u770B\u5F53\u524D\u6A21\u578B",
166
+ cmdSwitch: "\u5F39\u51FA\u5019\u9009\u6A21\u578B\u9009\u62E9\u5668\uFF08\u2191\u2193 \u9009\u62E9\uFF09",
165
167
  cmdThinking: "\u5207\u6362\u601D\u8003\u7B49\u7EA7\uFF1A/thinking <off|high|max>",
166
168
  cmdLang: "\u5207\u6362\u754C\u9762\u8BED\u8A00\uFF1A/lang <zh|en>",
167
169
  cmdOutputStyle: "\u5207\u6362\u8F93\u51FA\u98CE\u683C\uFF1A/output-style <name|off>",
@@ -185,6 +187,12 @@ ${dim} \u8FD0\u884C npm i -g deepseeker-code@latest \u66F4\u65B0${reset}
185
187
  forkEmpty: "\uFF08\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u53EF\u5206\u53C9\u7684\u68C0\u67E5\u70B9\uFF09",
186
188
  forkDone: (id, roundNo) => `\u5DF2\u4ECE\u7B2C ${roundNo} \u8F6E\u5206\u53C9 \u2192 \u65B0\u4F1A\u8BDD ${id}\uFF08\u5DF2\u8F7D\u5165\uFF0C\u540E\u7EED\u5BF9\u8BDD\u5199\u5165\u65B0\u4F1A\u8BDD\uFF0C\u539F\u4F1A\u8BDD\u4E0D\u53D8\uFF09`,
187
189
  forkFailed: (e) => `\u5206\u53C9\u5931\u8D25\uFF1A${e}`,
190
+ modelUsage: (current) => `\u5F53\u524D\u6A21\u578B\uFF1A${current || "\u9ED8\u8BA4\uFF08DEEP_SEEK_MODEL\uFF09"}
191
+ \u7528\u6CD5\uFF1A/model <\u6A21\u578Bid> \u76F4\u8F93\u5207\u6362\uFF0C\u6216 /switch \u5F39\u51FA\u5019\u9009\u9009\u62E9\u5668`,
192
+ modelPickerTitle: (current) => `\u{1F9E0} \u9009\u62E9\u6A21\u578B\uFF08\u5F53\u524D\uFF1A${current || "\u9ED8\u8BA4"}\uFF09`,
193
+ modelPickerPrompt: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u5207\u6362 \xB7 Esc \u53D6\u6D88\uFF08\u5176\u5B83\u6A21\u578B\uFF1A/model <\u6A21\u578Bid>\uFF09",
194
+ modelCurrentTag: "\u5F53\u524D",
195
+ modelSwitched: (m) => `\u6A21\u578B\u5DF2\u5207\u6362\uFF1A${m}\uFF08\u4E0B\u6B21\u56DE\u590D\u751F\u6548\uFF0C\u91CD\u542F\u540E\u4FDD\u6301\uFF09`,
188
196
  relTime: (iso) => {
189
197
  const t = Date.parse(iso);
190
198
  if (!t) return "";
@@ -213,10 +221,11 @@ ${cwd}
213
221
  DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u548C\u6267\u884C\u6587\u4EF6\u3002`,
214
222
  optTrust: "\u4FE1\u4EFB\u6B64\u76EE\u5F55",
215
223
  optExit: "\u9000\u51FA",
216
- helpText: (model2, thinking, locale2) => [
224
+ helpText: (model, thinking, locale2) => [
217
225
  "/help \xB7 /status \xB7 /clear \xB7 /exit",
218
226
  "/plan \u2014 \u5207\u6362\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u8BFB\u8C03\u7814 \u2192 \u65B9\u6848\u5BA1\u6279 \u2192 \u5B9E\u73B0\uFF09",
219
- `/model [deepseek-v4|deepseek-v4-flash|<\u4EFB\u610F>] \u2014 \u5207\u6362\u6A21\u578B\uFF08\u5F53\u524D ${model2}\uFF09`,
227
+ `/model <\u6A21\u578Bid> \u2014 \u5207\u6362\u6A21\u578B\uFF08\u76F4\u8F93\uFF0C\u5F53\u524D ${model}\uFF09`,
228
+ "/switch \u2014 \u5F39\u51FA\u5019\u9009\u6A21\u578B\u9009\u62E9\u5668",
220
229
  `/thinking [off|high|max] \u2014 \u5207\u6362\u601D\u8003\u7B49\u7EA7\uFF08\u5F53\u524D ${thinking}\uFF09`,
221
230
  `/lang [zh|en] \u2014 \u5207\u6362\u754C\u9762\u8BED\u8A00\uFF08\u5F53\u524D ${locale2}\uFF09`,
222
231
  "/sessions \u2014 \u9009\u62E9\u5E76\u8F7D\u5165\u5386\u53F2\u4F1A\u8BDD\uFF08\u7EED\u63A5\u5BF9\u8BDD\uFF09",
@@ -224,8 +233,8 @@ DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u54
224
233
  "/trust \u2014 \u7BA1\u7406\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF08\u9879\u76EE\u7EA7 hooks/skills \u7B49\u4EC5\u5728\u4FE1\u4EFB\u76EE\u5F55\u52A0\u8F7D\uFF1BCI \u7528 --trust \u663E\u5F0F\u4FE1\u4EFB\uFF09",
225
234
  "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62/\u6E05\u8F93\u5165 \xB7 Ctrl+G \u4E2D\u6B62 \xB7 Ctrl+T \u5C55\u5F00/\u6536\u8D77\u601D\u8003"
226
235
  ].join("\n"),
227
- statusText: (model2, thinking, planOn, locale2, cwd) => [
228
- `\u6A21\u578B\uFF1A${model2}`,
236
+ statusText: (model, thinking, planOn, locale2, cwd) => [
237
+ `\u6A21\u578B\uFF1A${model}`,
229
238
  `\u601D\u8003\u7B49\u7EA7\uFF1A${thinking}`,
230
239
  `\u8BA1\u5212\u6A21\u5F0F\uFF1A${planOn ? "\u5F00" : "\u5173"}`,
231
240
  `\u754C\u9762\u8BED\u8A00\uFF1A${locale2}`,
@@ -270,6 +279,7 @@ DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u54
270
279
  approvalDeny: "Deny",
271
280
  errAborted: "Aborted current turn.",
272
281
  busyBlockSend: "\u23F3 Still generating\u2014wait or press Esc to abort before sending.",
282
+ escInterrupt: "esc to interrupt",
273
283
  inboxQueued: "\u{1F4EE} Queued\u2014will reach the model at the next round boundary (Esc still aborts).",
274
284
  inboxClaimed: (n) => `\u{1F4EC} Delivered to the model (${n} supplemental input${n > 1 ? "s" : ""})`,
275
285
  inboxCarryover: (n) => `\u{1F4EC} ${n} supplemental input${n > 1 ? "s" : ""} not claimed at a round boundary before this run ended; saved to session history and carried into the next turn.`,
@@ -301,7 +311,8 @@ ${dim} Run npm i -g deepseeker-code@latest to update${reset}
301
311
  cmdStatus: "Show current model/session/mode",
302
312
  cmdPlan: "Toggle plan mode (read-only research \u2192 review \u2192 implement)",
303
313
  cmdAuto: "Toggle auto mode (classifier auto-approves file edits, escalates risky)",
304
- cmdModel: "Switch model: /model <deepseek-v4|deepseek-v4-flash|\u2026>",
314
+ cmdModel: "Switch model (direct): /model <model-id>; no arg shows current",
315
+ cmdSwitch: "Open the model picker (\u2191\u2193 to pick)",
305
316
  cmdThinking: "Switch thinking level: /thinking <off|high|max>",
306
317
  cmdLang: "Switch interface language: /lang <zh|en>",
307
318
  cmdOutputStyle: "Switch output style: /output-style <name|off>",
@@ -325,6 +336,12 @@ ${dim} Run npm i -g deepseeker-code@latest to update${reset}
325
336
  forkEmpty: "(no checkpoints to fork from in this session)",
326
337
  forkDone: (id, roundNo) => `Forked from round ${roundNo} \u2192 new session ${id} (loaded; new messages go to the fork, source untouched)`,
327
338
  forkFailed: (e) => `Fork failed: ${e}`,
339
+ modelUsage: (current) => `Current model: ${current || "default (DEEP_SEEK_MODEL)"}
340
+ Usage: /model <model-id> to switch directly, or /switch for the picker`,
341
+ modelPickerTitle: (current) => `\u{1F9E0} Pick a model (current: ${current || "default"})`,
342
+ modelPickerPrompt: "\u2191\u2193 pick \xB7 Enter switch \xB7 Esc cancel (any other model: /model <id>)",
343
+ modelCurrentTag: "current",
344
+ modelSwitched: (m) => `Model switched: ${m} (takes effect on next reply, persists across restarts)`,
328
345
  relTime: (iso) => {
329
346
  const t = Date.parse(iso);
330
347
  if (!t) return "";
@@ -353,10 +370,11 @@ ${cwd}
353
370
  DeepSeeker-Code will read, edit, and execute files here.`,
354
371
  optTrust: "Yes, I trust this folder",
355
372
  optExit: "No, exit",
356
- helpText: (model2, thinking, locale2) => [
373
+ helpText: (model, thinking, locale2) => [
357
374
  "/help \xB7 /status \xB7 /clear \xB7 /exit",
358
375
  "/plan \u2014 Toggle plan mode (read-only research \u2192 review \u2192 implement)",
359
- `/model [deepseek-v4|deepseek-v4-flash|<any>] \u2014 Switch model (current ${model2})`,
376
+ `/model <model-id> \u2014 Switch model (direct input; current ${model})`,
377
+ "/switch \u2014 Open the model picker",
360
378
  `/thinking [off|high|max] \u2014 Switch thinking level (current ${thinking})`,
361
379
  `/lang [zh|en] \u2014 Switch interface language (current ${locale2})`,
362
380
  "/sessions \u2014 Pick a past session to resume",
@@ -364,8 +382,8 @@ DeepSeeker-Code will read, edit, and execute files here.`,
364
382
  "/trust \u2014 Manage trusted dirs (project hooks/skills load only when trusted; CI uses --trust)",
365
383
  "Ctrl+C exit \xB7 Esc abort/clear input \xB7 Ctrl+G abort \xB7 Ctrl+T toggle thinking"
366
384
  ].join("\n"),
367
- statusText: (model2, thinking, planOn, locale2, cwd) => [
368
- `Model: ${model2}`,
385
+ statusText: (model, thinking, planOn, locale2, cwd) => [
386
+ `Model: ${model}`,
369
387
  `Thinking: ${thinking}`,
370
388
  `Plan mode: ${planOn ? "on" : "off"}`,
371
389
  `Language: ${locale2}`,
@@ -381,7 +399,7 @@ DeepSeeker-Code will read, edit, and execute files here.`,
381
399
  S = new Proxy({}, {
382
400
  get: (_t, k) => typeof k === "string" ? STRINGS[locale][k] : void 0
383
401
  });
384
- LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "thinking", "lang", "output-style", "sessions", "fork", "usage", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
402
+ LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "switch", "thinking", "lang", "output-style", "sessions", "fork", "usage", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
385
403
  }
386
404
  });
387
405
 
@@ -690,7 +708,7 @@ ${body}`) return;
690
708
  // src/prefs.ts
691
709
  import path4 from "path";
692
710
  import fs2 from "fs/promises";
693
- var PREFS_FILE, readPrefs, readLocale, writePrefs, writeLocale;
711
+ var PREFS_FILE, readPrefs, readModelPref, readLocale, writePrefs, writeLocale;
694
712
  var init_prefs = __esm({
695
713
  "src/prefs.ts"() {
696
714
  "use strict";
@@ -701,6 +719,10 @@ var init_prefs = __esm({
701
719
  const p = await readJSONFile(PREFS_FILE);
702
720
  return p && typeof p === "object" ? p : {};
703
721
  };
722
+ readModelPref = async () => {
723
+ const { model } = await readPrefs();
724
+ return typeof model === "string" && model.trim() ? model.trim() : null;
725
+ };
704
726
  readLocale = async () => {
705
727
  const { locale: locale2 } = await readPrefs();
706
728
  return locale2 === "zh" || locale2 === "en" ? locale2 : null;
@@ -784,22 +806,23 @@ var init_type = __esm({
784
806
 
785
807
  // ../core/src/llm/providers/deepseek/client.ts
786
808
  import OpenAI from "openai";
787
- var model, MODEL_NAME, AUX_MODEL_NAME, REASONING_EFFORTS, clampReasoningEffort, MODEL_REASONING_EFFORT, MODEL_THINKING_ENABLED;
809
+ var _model, getModel, MODEL_NAME, AUX_MODEL_NAME, REASONING_EFFORTS, clampReasoningEffort, MODEL_REASONING_EFFORT, MODEL_THINKING_ENABLED;
788
810
  var init_client = __esm({
789
811
  "../core/src/llm/providers/deepseek/client.ts"() {
790
812
  "use strict";
791
- model = new OpenAI({
813
+ getModel = () => _model ??= new OpenAI({
792
814
  baseURL: process.env.DEEP_SEEK_API_URL || "https://api.deepseek.com",
793
815
  apiKey: process.env.DEEP_SEEK_API_KEY,
794
816
  // ★ 瞬态错误自动指数退避重试(SDK 内置:408/409/429/500/502/503/504 + 连接错误),
795
- // 避免单次 429 限流 / 网关抖动 / 网络超时直接终止整轮 agent(长任务几十轮工具调用体验极差)。
796
- // 注:SDK 重试已覆盖瞬态错误,runAgent 不再叠加第二层重试(否则会放大负载)。
817
+ // 覆盖非流式 helper(summarize / classifyRisk)。★ 流式对话不在此列:stream.ts 按
818
+ // maxRetries: 0 关掉 SDK 层,瞬态重试单层归 streamInference 应用层(Retry-After 感知 +
819
+ // text.reset/abort/超长降级语义,SDK 层不具备)——两层同开会把 429 放大成 5×3=15 次请求。
797
820
  maxRetries: 4,
798
821
  timeout: 12e4
799
822
  // 单次请求 120s 兜底(长上下文 / 长生成场景)
800
823
  });
801
- MODEL_NAME = process.env.DEEP_SEEK_MODEL || "deepseek-v4-flash";
802
- AUX_MODEL_NAME = process.env.DEEP_SEEK_AUX_MODEL || "deepseek-v4-flash";
824
+ MODEL_NAME = process.env.DEEP_SEEK_MODEL || "deepseek-flash";
825
+ AUX_MODEL_NAME = process.env.DEEP_SEEK_AUX_MODEL || "deepseek-flash";
803
826
  REASONING_EFFORTS = ["high", "max"];
804
827
  clampReasoningEffort = (raw) => raw && REASONING_EFFORTS.includes(raw) ? raw : "high";
805
828
  MODEL_REASONING_EFFORT = clampReasoningEffort(process.env.DEEP_SEEK_REASONING_EFFORT);
@@ -808,18 +831,37 @@ var init_client = __esm({
808
831
  });
809
832
 
810
833
  // ../core/src/llm/providers/deepseek/stream.ts
811
- var STREAM_IDLE_TIMEOUT_MS, streamChat;
834
+ var STREAM_IDLE_TIMEOUT_MS, stripHistoricalReasoning, streamChat;
812
835
  var init_stream = __esm({
813
836
  "../core/src/llm/providers/deepseek/stream.ts"() {
814
837
  "use strict";
815
838
  init_client();
816
839
  STREAM_IDLE_TIMEOUT_MS = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
840
+ stripHistoricalReasoning = (messages) => {
841
+ if (process.env.DEEP_SEEK_REASONING_PASSTHROUGH === "1") return messages;
842
+ let lastNonSystem = null;
843
+ for (let i = messages.length - 1; i >= 0; i--) {
844
+ if (messages[i]?.role !== "system") {
845
+ lastNonSystem = messages[i];
846
+ break;
847
+ }
848
+ }
849
+ if (lastNonSystem?.role === "assistant") return messages;
850
+ return messages.map((m) => {
851
+ const wire = m;
852
+ if (wire?.role !== "assistant" || typeof wire.reasoning_content !== "string") return m;
853
+ const copy = { ...wire };
854
+ delete copy.reasoning_content;
855
+ return copy;
856
+ });
857
+ };
817
858
  streamChat = async function* (messages, tools, opts) {
818
859
  const level = opts.thinkingLevel;
819
860
  const thinkingType = (level === void 0 ? MODEL_THINKING_ENABLED : level !== "off") ? "enabled" : "disabled";
820
861
  const effort = level === "max" ? "max" : level === "high" ? "high" : level === "off" ? "high" : MODEL_REASONING_EFFORT;
821
862
  const requestBody = {
822
- messages,
863
+ messages: stripHistoricalReasoning(messages),
864
+ // ★ 出口剥离历史思考记录(见函数注释;默认开)
823
865
  model: opts.model ?? MODEL_NAME,
824
866
  // per-agent 覆盖(声明式子 Agent);缺省回退全局
825
867
  tool_choice: "auto",
@@ -835,8 +877,9 @@ var init_stream = __esm({
835
877
  const idleAc = new AbortController();
836
878
  const extSignal = opts.signal;
837
879
  const combinedSignal = extSignal ? AbortSignal.any([idleAc.signal, extSignal]) : idleAc.signal;
838
- const stream = await model.chat.completions.create(requestBody, {
839
- signal: combinedSignal
880
+ const stream = await getModel().chat.completions.create(requestBody, {
881
+ signal: combinedSignal,
882
+ maxRetries: 0
840
883
  });
841
884
  const reader = stream[Symbol.asyncIterator]();
842
885
  try {
@@ -903,7 +946,8 @@ var init_deepseek = __esm({
903
946
  summarize = async (messages, tools, opts) => {
904
947
  try {
905
948
  const requestBody = {
906
- messages,
949
+ messages: stripHistoricalReasoning(messages),
950
+ // ★ 出口剥离历史思考记录(压缩输入全价计费,省得最实)
907
951
  model: AUX_MODEL_NAME,
908
952
  tool_choice: "auto",
909
953
  tools,
@@ -911,7 +955,7 @@ var init_deepseek = __esm({
911
955
  thinking: { type: "disabled" },
912
956
  stream: false
913
957
  };
914
- const completion = await model.chat.completions.create(requestBody, {
958
+ const completion = await getModel().chat.completions.create(requestBody, {
915
959
  signal: opts.signal
916
960
  });
917
961
  if (completion && "choices" in completion) return completion;
@@ -926,7 +970,7 @@ var init_deepseek = __esm({
926
970
  const timer = setTimeout(() => ac.abort(), 1e4);
927
971
  if (signal) signal.addEventListener("abort", () => ac.abort(), { once: true });
928
972
  try {
929
- const completion = await model.chat.completions.create({
973
+ const completion = await getModel().chat.completions.create({
930
974
  messages: [
931
975
  { role: "system", content: "\u4F60\u662F\u5DE5\u5177\u8C03\u7528\u98CE\u9669\u5206\u7C7B\u5668\u3002\u53EA\u56DE SAFE \u6216 RISKY\uFF0C\u4E0D\u8981\u4EFB\u4F55\u89E3\u91CA\u3002\u6309\u5DE5\u5177\u7C7B\u578B\u5224\u5B9A\uFF1A\u2460\u6587\u4EF6(edit/write/create/move_file/delete_path)\u2014\u2014\u5DE5\u4F5C\u533A\u5185\u5E38\u89C4\u6587\u4EF6\u64CD\u4F5C(\u542B\u79FB\u52A8/\u91CD\u547D\u540D)=SAFE\uFF0C\u8986\u76D6/\u5220\u9664/\u79FB\u52A8\u654F\u611F\u6587\u4EF6(.env/.git/.ssh/.aws/\u5BC6\u94A5/credentials/settings.json)\u6216\u5DE5\u4F5C\u533A\u5916=RISKY\uFF1B\u2461\u547D\u4EE4(run_command/run_in_background)\u2014\u2014\u53EA\u8BFB/\u6784\u5EFA/\u6D4B\u8BD5/\u67E5\u770B\u7C7B(ls/cat/git status/npm test/pnpm build/node -v/tsc/lint)=SAFE\uFF0C\u7834\u574F\u6027/\u5916\u5411/\u63D0\u6743(rm -rf\u3001\u683C\u5F0F\u5316\u3001curl|sh\u3001\u5916\u4F20\u6587\u4EF6\u3001chmod 777\u3001shutdown\u3001\u5199\u7CFB\u7EDF\u76EE\u5F55\u3001\u5B89\u88C5\u964C\u751F\u5305)=RISKY\uFF1B\u2462\u7F51\u7EDC(web_fetch/web_search)\u2014\u2014\u6293\u53D6\u516C\u5F00\u6587\u6863/\u5E38\u89C4 URL=SAFE\uFF0C\u5185\u7F51/\u53EF\u7591/\u672A\u77E5 URL=RISKY\uFF1B\u2463git_commit\u2014\u2014\u5E38\u89C4\u63D0\u4EA4=SAFE\uFF0C\u542B\u6076\u610F\u811A\u672C/\u5F02\u5E38\u6539\u52A8=RISKY\uFF1B\u2464MCP(mcp__*)\u2014\u2014\u9ED8\u8BA4 RISKY(\u9ED1\u76D2\u5DE5\u5177\u65E0\u6CD5\u5224\u5B9A\u5185\u90E8\u884C\u4E3A)\u3002\u4EFB\u4F55\u4E0D\u786E\u5B9A\u4E00\u5F8B RISKY\u3002" },
932
976
  { role: "user", content: `\u5DE5\u5177:${toolName}
@@ -980,11 +1024,22 @@ var init_deepseek = __esm({
980
1024
  }
981
1025
  });
982
1026
 
1027
+ // ../core/src/llm/createModel.ts
1028
+ var SELECTABLE_MODELS;
1029
+ var init_createModel = __esm({
1030
+ "../core/src/llm/createModel.ts"() {
1031
+ "use strict";
1032
+ init_client();
1033
+ SELECTABLE_MODELS = ["deepseek-flash", "deepseek-v4-pro", "deepseek-flash-vision-exp"];
1034
+ }
1035
+ });
1036
+
983
1037
  // ../core/src/session/contentParts.ts
984
- var MAX_IMAGE_BYTES, msgText, imageUrlsOf, hasImagePart, isVisionEnabled, estimateImageTokens, toWireUserContent, replaceImageParts, collapseToText, degradeImagesForAux;
1038
+ var MAX_IMAGE_BYTES, msgText, imageUrlsOf, hasImagePart, modelSupportsVision, isVisionEnabled, estimateImageTokens, toWireUserContent, toIngestContents, replaceImageParts, collapseToText, degradeImagesForAux;
985
1039
  var init_contentParts = __esm({
986
1040
  "../core/src/session/contentParts.ts"() {
987
1041
  "use strict";
1042
+ init_createModel();
988
1043
  MAX_IMAGE_BYTES = 8 * 1024 * 1024;
989
1044
  msgText = (content) => {
990
1045
  if (typeof content === "string") return content;
@@ -998,9 +1053,13 @@ var init_contentParts = __esm({
998
1053
  return content.filter((p) => p?.type === "image_url" && typeof p?.image_url?.url === "string").map((p) => p.image_url.url);
999
1054
  };
1000
1055
  hasImagePart = (content) => imageUrlsOf(content).length > 0;
1001
- isVisionEnabled = () => {
1056
+ modelSupportsVision = (modelId) => /vision|vlm|(^|[-_.])vl([-_.0-9]|$)/i.test(modelId || "");
1057
+ isVisionEnabled = (modelId) => {
1002
1058
  const v = process.env.DEEP_SEEK_VISION;
1003
- return v === "1" || v === "true";
1059
+ if (v === "1" || v === "true") return true;
1060
+ if (v === "0" || v === "false") return false;
1061
+ const effective = typeof modelId === "string" && modelId.trim() ? modelId.trim() : MODEL_NAME;
1062
+ return modelSupportsVision(effective);
1004
1063
  };
1005
1064
  estimateImageTokens = () => {
1006
1065
  const n = Number(process.env.DEEP_SEEK_IMAGE_TOKENS);
@@ -1039,6 +1098,22 @@ ${footer.join("\n")}` : text;
1039
1098
  ];
1040
1099
  return parts;
1041
1100
  };
1101
+ toIngestContents = (text, attachments, modelId) => {
1102
+ const archive = toWireUserContent(text, attachments);
1103
+ if (!Array.isArray(attachments) || attachments.length === 0) return { wire: archive, archive };
1104
+ if (isVisionEnabled(modelId)) return { wire: archive, archive };
1105
+ const names = attachments.map((a) => a?.name || "image").join("\u3001");
1106
+ const paths = attachments.map((a) => typeof a?.path === "string" && a.path ? ` ${a?.name || "image"} \u2192 ${a.path}` : "").filter(Boolean);
1107
+ const relay = paths.length ? `\u56FE\u7247\u5DF2\u5B58\u5DE5\u4F5C\u533A\uFF1A
1108
+ ${paths.join("\n")}
1109
+ \u82E5\u5DF2\u914D\u7F6E\u56FE\u50CF\u8BC6\u522B MCP \u5DE5\u5177\uFF0C\u53EF\u8BFB\u53D6\u4E0A\u8FF0\u8DEF\u5F84\u7406\u89E3\u56FE\u7247\u5185\u5BB9\u3002` : "\u82E5\u6D88\u606F\u4E2D\u5305\u542B\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84\u4E14\u5DF2\u914D\u7F6E\u56FE\u50CF\u8BC6\u522B MCP \u5DE5\u5177\uFF0C\u53EF\u8BFB\u53D6\u8BE5\u8DEF\u5F84\u7406\u89E3\u56FE\u7247\u3002";
1110
+ return {
1111
+ wire: `${text}
1112
+
1113
+ \u{1F5BC} \u6536\u5230\u56FE\u7247\u9644\u4EF6\uFF08${names}\uFF09\uFF1A\u5F53\u524D\u6A21\u578B\u65E0\u89C6\u89C9\u80FD\u529B\uFF0C\u65E0\u6CD5\u76F4\u63A5\u67E5\u770B\u56FE\u7247\u5185\u5BB9\uFF08\u53EF\u5207\u6362 vision \u6A21\u578B\u6216\u8BBE DEEP_SEEK_VISION=1 \u5F3A\u5F00\uFF09\uFF1B${relay}`,
1114
+ archive
1115
+ };
1116
+ };
1042
1117
  replaceImageParts = (m, note) => {
1043
1118
  if (!m || !Array.isArray(m.content)) return m;
1044
1119
  let touched = false;
@@ -1719,7 +1794,7 @@ var init_shellExecutor = __esm({
1719
1794
  };
1720
1795
  executeHookCommand = (input) => {
1721
1796
  return new Promise((resolve) => {
1722
- const timeoutMs = Math.min(input.timeoutMs ?? DEFAULT_TIMEOUT_MS, HARD_TIMEOUT_CAP_MS);
1797
+ const timeoutMs2 = Math.min(input.timeoutMs ?? DEFAULT_TIMEOUT_MS, HARD_TIMEOUT_CAP_MS);
1723
1798
  const childEnv = {};
1724
1799
  for (const k of ENV_WHITELIST) {
1725
1800
  const v = process.env[k];
@@ -1755,7 +1830,7 @@ var init_shellExecutor = __esm({
1755
1830
  const timer = setTimeout(() => {
1756
1831
  timedOut = true;
1757
1832
  void killTree(child);
1758
- }, timeoutMs);
1833
+ }, timeoutMs2);
1759
1834
  child.stdout?.on("data", (d) => {
1760
1835
  if (stdout.length < MAX_OUTPUT) stdout += d.toString();
1761
1836
  });
@@ -1787,7 +1862,7 @@ var init_shellExecutor = __esm({
1787
1862
  });
1788
1863
 
1789
1864
  // ../core/src/hooks/registry.ts
1790
- var rules, matches, registerHooks, listHooks, isPlainObject, dispatch, runPreHooks, HOOK_RESULT_MAX, truncateForHook, runPostHooks;
1865
+ var rules, matches, DECLARATIVE_RULE, replaceDeclarativeHooks, listHooks, isPlainObject, dispatch, runPreHooks, HOOK_RESULT_MAX, truncateForHook, runPostHooks;
1791
1866
  var init_registry = __esm({
1792
1867
  "../core/src/hooks/registry.ts"() {
1793
1868
  "use strict";
@@ -1796,7 +1871,12 @@ var init_registry = __esm({
1796
1871
  init_config();
1797
1872
  rules = [];
1798
1873
  matches = (matcher, toolName) => {
1799
- if (typeof matcher === "string") return matcher === "*" || matcher === toolName;
1874
+ if (typeof matcher === "string") {
1875
+ if (matcher === "*") return true;
1876
+ if (matcher === "mcp_call") return toolName.startsWith("mcp__");
1877
+ if (matcher.endsWith("*")) return toolName.startsWith(matcher.slice(0, -1));
1878
+ return matcher === toolName;
1879
+ }
1800
1880
  if (matcher instanceof RegExp) return matcher.test(toolName);
1801
1881
  try {
1802
1882
  return matcher(toolName);
@@ -1804,8 +1884,15 @@ var init_registry = __esm({
1804
1884
  return false;
1805
1885
  }
1806
1886
  };
1807
- registerHooks = (rs) => {
1808
- for (const r of rs) rules.push(r);
1887
+ DECLARATIVE_RULE = Symbol("dsc.hook.declarative");
1888
+ replaceDeclarativeHooks = (rs) => {
1889
+ for (let i = rules.length - 1; i >= 0; i--) {
1890
+ if (rules[i][DECLARATIVE_RULE]) rules.splice(i, 1);
1891
+ }
1892
+ for (const r of rs) {
1893
+ r[DECLARATIVE_RULE] = true;
1894
+ rules.push(r);
1895
+ }
1809
1896
  };
1810
1897
  listHooks = () => {
1811
1898
  const strMatcher = (m) => {
@@ -1923,15 +2010,14 @@ function stripAnsi(text) {
1923
2010
  }
1924
2011
  function microcompactTextContent(raw) {
1925
2012
  let s = stripAnsi(raw || "");
1926
- s = s.replace(/<!--[\s\S]*?-->/g, "");
1927
2013
  s = relativizeWorkspacePathsInText(s);
1928
- s = s.replace(/\n{3,}/g, "\n\n").replace(/[ \t]{3,}/g, " ").trim();
2014
+ s = s.replace(/\n{3,}/g, "\n\n").trim();
1929
2015
  return s;
1930
2016
  }
1931
2017
  function isAsyncGenerator(x) {
1932
2018
  return x != null && typeof x[Symbol.asyncIterator] === "function";
1933
2019
  }
1934
- var ANSI_ESCAPE, getFileAccessRoots, relativizeWorkspacePathsInText, truncateToolResult, truncateApprovalDetail, compactBatch, ARCHIVE_INDEX_MAX_ENTRIES, ARCHIVE_INDEX_BATCH_CAP, extractArchiveEntities, parseSummarySlot, mergeSummarySlot, compactSlotNarrative, compactToLine, ensureSummarySlot, SUMMARY_SELF_COMPACT_THRESHOLD, ensureFitsWindow, collectToolResult;
2020
+ var ANSI_ESCAPE, getFileAccessRoots, relativizeWorkspacePathsInText, truncateToolResult, truncateApprovalDetail, batchSummaryPrompt, compactBatch, ARCHIVE_INDEX_MAX_ENTRIES, ARCHIVE_INDEX_BATCH_CAP, extractArchiveEntities, parseSummarySlot, mergeSummarySlot, CHECKPOINT_MAX_CHARS, SLOT_PLACEHOLDER, CHECKPOINT_FORMAT, checkpointRules, checkpointPrompt, checkpointDirectPrompt, assembleSlot, parseSlotNarrativeState, synthesizeSlotNarrative, synthesizeSlotNarrativeFromBatch, compactSlotNarrative, splitIntoBatches, compactBatches, ensureSummarySlot, SUMMARY_SELF_COMPACT_THRESHOLD, persistCompaction, ensureFitsWindow, collectToolResult;
1935
2021
  var init_truncate = __esm({
1936
2022
  "../core/src/agent/truncate.ts"() {
1937
2023
  "use strict";
@@ -2003,10 +2089,11 @@ var init_truncate = __esm({
2003
2089
 
2004
2090
  ${tail}`;
2005
2091
  };
2006
- compactBatch = async (batch, signal) => {
2092
+ batchSummaryPrompt = (tailContext) => "\u6982\u62EC\u4EE5\u4E0A\u5BF9\u8BDD\u4E0E\u5DE5\u5177\u8C03\u7528\uFF0C\u6309\u300C\u5B8C\u6210\u4E86\u4EC0\u4E48 \u2192 \u6B63\u5728\u505A\u4EC0\u4E48 \u2192 \u4E0B\u4E00\u6B65/\u53D7\u963B\u300D\u7684\u987A\u5E8F\u5199\u6210\u4E00\u6BB5\u3002\u5FC5\u987B\u539F\u6837\u4FDD\u7559\u5173\u952E\u5B9E\u4F53\u540D\uFF08\u6587\u4EF6\u8DEF\u5F84\u3001\u51FD\u6570/\u7C7B\u540D\u3001\u62A5\u9519\u5173\u952E\u8BCD\uFF09\u4EE5\u4FBF\u540E\u7EED\u68C0\u7D22\uFF1B" + (tailContext ? "\u8FD9\u6279\u6458\u8981\u7D27\u90BB\u672A\u538B\u7F29\u7684\u4FDD\u7559\u533A\uFF0C\u8BF7\u989D\u5916\u5199\u660E\u5176\u4E2D\u5BF9\u7D27\u968F\u5176\u540E\u7684\u8FD1\u671F\u5DE5\u4F5C\u6709\u7528\u7684\u80CC\u666F\uFF08\u5DF2\u786E\u8BA4\u7684\u7EA6\u675F\u3001\u6570\u636E\u3001\u7ED3\u8BBA\uFF09\u3002" : "") + '\u5BF9\u8BB0\u4E0D\u51C6\u7684\u7EC6\u8282\u4E0D\u8981\u81C6\u6D4B\uFF0C\u6807\u6CE8"(\u7EC6\u8282\u5DF2\u5F52\u6863)"\u5373\u53EF\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\u3002';
2093
+ compactBatch = async (batch, signal, tailContext) => {
2007
2094
  const resp = await chatWithModelWithSummary(
2008
2095
  // ★ 辅助模型是 text-only:送前把 image part 降级为占位符——base64 绝不能进摘要批(烧钱且无意义)
2009
- [...batch.map(degradeImagesForAux), { role: "user", content: '\u7528\u4E00\u884C\u8BDD\u6982\u62EC\u4EE5\u4E0A\u5BF9\u8BDD\u4E0E\u5DE5\u5177\u8C03\u7528\uFF1A\u4EFB\u52A1\u76EE\u6807\u3001\u5173\u952E\u51B3\u7B56\u3001\u52A8\u8FC7\u7684\u6587\u4EF6\u3001\u5F53\u524D\u8FDB\u5EA6\u3002\u5FC5\u987B\u539F\u6837\u4FDD\u7559\u5173\u952E\u5B9E\u4F53\u540D\uFF08\u6587\u4EF6\u8DEF\u5F84\u3001\u51FD\u6570/\u7C7B\u540D\u3001\u62A5\u9519\u5173\u952E\u8BCD\uFF09\u4EE5\u4FBF\u540E\u7EED\u68C0\u7D22\u3002\u5BF9\u8BB0\u4E0D\u51C6\u7684\u7EC6\u8282\u4E0D\u8981\u81C6\u6D4B\uFF0C\u6807\u6CE8"(\u7EC6\u8282\u5DF2\u5F52\u6863)"\u5373\u53EF\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\u3002' }],
2096
+ [...batch.map(degradeImagesForAux), { role: "user", content: batchSummaryPrompt(tailContext) }],
2010
2097
  [],
2011
2098
  { signal }
2012
2099
  );
@@ -2026,6 +2113,8 @@ ${tail}`;
2026
2113
  const push = (raw) => {
2027
2114
  const s = raw.trim();
2028
2115
  if (!s || s.length < 3 || s.length > 60 || seen.has(s)) return;
2116
+ const lastSeg = s.split(/[\\/]/).pop() ?? "";
2117
+ if (/^[\d.]+$/.test(lastSeg)) return;
2029
2118
  seen.add(s);
2030
2119
  found.push(s);
2031
2120
  };
@@ -2054,8 +2143,8 @@ ${tail}`;
2054
2143
  merged.push(e);
2055
2144
  if (merged.length >= ARCHIVE_INDEX_MAX_ENTRIES) break;
2056
2145
  }
2057
- const newNotes = notes ? `${notes}
2058
- ${noteLine}` : noteLine;
2146
+ const newNotes = noteLine ? notes ? `${notes}
2147
+ ${noteLine}` : noteLine : notes;
2059
2148
  return [
2060
2149
  "\u27E6DSC:ARCHIVE-INDEX\u27E7 \u7CBE\u786E\u7EC6\u8282\u53EF\u7528 recall \u5DE5\u5177\u68C0\u7D22\u672C\u4F1A\u8BDD\u5168\u91CF\u5386\u53F2\uFF1B\u4EE5\u4E0B\u4E3A\u5F52\u6863\u5B9E\u4F53\u7D22\u5F15\uFF08\u68C0\u7D22\u5173\u952E\u8BCD\u7EBF\u7D22\uFF09\uFF1A",
2061
2150
  merged.join(" | "),
@@ -2063,17 +2152,57 @@ ${noteLine}` : noteLine;
2063
2152
  newNotes
2064
2153
  ].join("\n");
2065
2154
  };
2066
- compactSlotNarrative = async (slotContent, modelWindow, signal) => {
2067
- const { index } = parseSummarySlot(slotContent);
2068
- const compactedNotes = await compactToLine([{ role: "system", content: slotContent }], modelWindow, signal);
2069
- return [
2070
- "\u27E6DSC:ARCHIVE-INDEX\u27E7 \u7CBE\u786E\u7EC6\u8282\u53EF\u7528 recall \u5DE5\u5177\u68C0\u7D22\u672C\u4F1A\u8BDD\u5168\u91CF\u5386\u53F2\uFF1B\u4EE5\u4E0B\u4E3A\u5F52\u6863\u5B9E\u4F53\u7D22\u5F15\uFF08\u68C0\u7D22\u5173\u952E\u8BCD\u7EBF\u7D22\uFF09\uFF1A",
2071
- index.join(" | "),
2072
- "\u27E6DSC:ARCHIVE-NOTES\u27E7",
2073
- compactedNotes
2074
- ].join("\n");
2155
+ CHECKPOINT_MAX_CHARS = 800;
2156
+ SLOT_PLACEHOLDER = "SYSTEM_ROLLING_SUMMARY_SLOT";
2157
+ CHECKPOINT_FORMAT = '\u4E25\u683C\u6309\u4EE5\u4E0B\u683C\u5F0F\u8F93\u51FA\uFF1A\n\n## \u76EE\u6807\n[\u7528\u6237\u8981\u5B8C\u6210\u4EC0\u4E48\uFF1B\u591A\u4EFB\u52A1\u5206\u6BB5\u5217\u51FA]\n## \u7EA6\u675F\u4E0E\u504F\u597D\n- [\u7528\u6237\u660E\u793A\u7684\u7EA6\u675F\u3001\u504F\u597D\u4E0E\u6280\u672F\u8981\u6C42\uFF1B\u65E0\u5219\u5199"(\u65E0)"]\n## \u8FDB\u5EA6\n### \u5DF2\u5B8C\u6210\n- [x] [\u5DF2\u5B8C\u6210\u7684\u4E8B\u9879]\n### \u8FDB\u884C\u4E2D\n- [ ] [\u8FDB\u884C\u4E2D\u7684\u4E8B\u9879\u53CA\u5176\u6700\u65B0\u72B6\u6001]\n### \u53D7\u963B\n- [\u5361\u70B9\uFF1B\u65E0\u5219\u5199"(\u65E0)"]\n## \u5173\u952E\u51B3\u7B56\n- **[\u51B3\u7B56]**: [\u7B80\u8981\u7406\u7531]\n## \u4E0B\u4E00\u6B65\n1. [\u6309\u987A\u5E8F\u5217\u51FA\u63A5\u4E0B\u6765\u8BE5\u505A\u4EC0\u4E48]\n## \u5173\u952E\u4E0A\u4E0B\u6587\n- [\u7EED\u63A5\u6240\u9700\u7684\u6570\u636E\u3001\u7ED3\u8BBA\u4E0E\u53C2\u7167\uFF1B\u65E0\u5219\u5199"(\u65E0)"]\n\n';
2158
+ checkpointRules = (hasPrevious, isRaw = false) => "\u89C4\u5219\uFF1A" + (hasPrevious ? `\u4FDD\u7559\u65E2\u6709\u68C0\u67E5\u70B9\u4E0E\u65B0\u5F52\u6863${isRaw ? "\u539F\u6587" : "\u884C"}\u4E2D\u7684\u5168\u90E8\u6709\u6548\u4FE1\u606F\uFF0C\u53EA\u6574\u7406\u3001\u4E0D\u6539\u5199\u4E8B\u5B9E\uFF1B\u5DF2\u5B8C\u6210\u4E8B\u9879\u79FB\u5165"\u5DF2\u5B8C\u6210"\uFF0C\u8FDB\u884C\u4E2D\u4E8B\u9879\u5237\u65B0\u5230\u6700\u65B0\u72B6\u6001\uFF1B\u88AB\u53D6\u4EE3\u7684\u8FC7\u671F\u7EC6\u8282\u53EF\u5220\u9664\uFF08\u7EC6\u8282\u53EF\u7ECF recall \u68C0\u7D22\uFF0C\u65E0\u9700\u604B\u6218\uFF09\uFF1B` : '\u5BF9\u8BB0\u4E0D\u51C6\u7684\u7EC6\u8282\u4E0D\u8981\u81C6\u6D4B\uFF0C\u6807\u6CE8"(\u7EC6\u8282\u5DF2\u5F52\u6863)"\u5373\u53EF\uFF08\u7EC6\u8282\u53EF\u7ECF recall \u68C0\u7D22\uFF09\uFF1B') + "\u539F\u6837\u4FDD\u7559\u6587\u4EF6\u8DEF\u5F84\u3001\u51FD\u6570/\u7C7B\u540D\u3001\u62A5\u9519\u5173\u952E\u8BCD\u7B49\u5B9E\u4F53\u540D\uFF1B\u5168\u6587\u4E0D\u8D85\u8FC7 " + CHECKPOINT_MAX_CHARS + " \u5B57\uFF1B\u76F4\u63A5\u8F93\u51FA\u68C0\u67E5\u70B9\uFF0C\u4E0D\u8981\u4EFB\u4F55\u89E3\u91CA\u3002";
2159
+ checkpointPrompt = (hasPrevious) => (hasPrevious ? "\u4EE5\u4E0A\u662F\u672C\u4F1A\u8BDD\u7684\u5F52\u6863\u6863\u6848\uFF1A\u27E6DSC:ARCHIVE-INDEX\u27E7 \u5B9E\u4F53\u7D22\u5F15\uFF08\u68C0\u7D22\u7EBF\u7D22\uFF0C\u4E0D\u8981\u6539\u52A8\u5176\u5185\u5BB9\uFF09\u4E0E \u27E6DSC:ARCHIVE-NOTES\u27E7 \u65E2\u6709\u68C0\u67E5\u70B9\u3002\u8BF7\u628A\u65E2\u6709\u68C0\u67E5\u70B9\u4E0E\u7528\u6237\u6D88\u606F\u4E2D\u7684\u672C\u8F6E\u65B0\u5F52\u6863\u6458\u8981\u884C\u5408\u5E76\u6539\u5199\u4E3A\u4E00\u4EFD\u65B0\u7684\u7ED3\u6784\u5316\u68C0\u67E5\u70B9\uFF0C\u4F9B\u540E\u7EED\u6A21\u578B\u7EED\u63A5\u5DE5\u4F5C\u3002" : "\u7528\u6237\u6D88\u606F\u662F\u672C\u4F1A\u8BDD\u672C\u8F6E\u5F52\u6863\u51FA\u7684\u6458\u8981\u884C\u3002\u8BF7\u628A\u5B83\u4EEC\u6574\u7406\u4E3A\u4E00\u4EFD\u7ED3\u6784\u5316\u68C0\u67E5\u70B9\uFF0C\u4F9B\u540E\u7EED\u6A21\u578B\u7EED\u63A5\u5DE5\u4F5C\uFF08\u5B9E\u4F53\u7D22\u5F15\u7531\u7CFB\u7EDF\u53E6\u884C\u786E\u5B9A\u6027\u7EF4\u62A4\uFF0C\u65E0\u9700\u4F60\u8F93\u51FA\uFF09\u3002") + CHECKPOINT_FORMAT + checkpointRules(hasPrevious);
2160
+ checkpointDirectPrompt = (hasPrevious) => (hasPrevious ? "system \u6D88\u606F\u662F\u672C\u4F1A\u8BDD\u7684\u5F52\u6863\u6863\u6848\uFF1A\u27E6DSC:ARCHIVE-INDEX\u27E7 \u5B9E\u4F53\u7D22\u5F15\uFF08\u68C0\u7D22\u7EBF\u7D22\uFF0C\u4E0D\u8981\u6539\u52A8\u5176\u5185\u5BB9\uFF09\u4E0E \u27E6DSC:ARCHIVE-NOTES\u27E7 \u65E2\u6709\u68C0\u67E5\u70B9\uFF1B\u5176\u540E\u7684\u5BF9\u8BDD\u6D88\u606F\u662F\u672C\u8F6E\u65B0\u5F52\u6863\u7684\u4F1A\u8BDD\u539F\u6587\uFF08\u672A\u7ECF\u884C\u5F0F\u538B\u7F29\uFF09\u3002\u8BF7\u628A\u65E2\u6709\u68C0\u67E5\u70B9\u4E0E\u8FD9\u4E9B\u539F\u6587\u5408\u5E76\u6539\u5199\u4E3A\u4E00\u4EFD\u65B0\u7684\u7ED3\u6784\u5316\u68C0\u67E5\u70B9\uFF0C\u4F9B\u540E\u7EED\u6A21\u578B\u7EED\u63A5\u5DE5\u4F5C\u3002" : "\u672C\u8F6E\u5F52\u6863\u51FA\u7684\u4F1A\u8BDD\u539F\u6587\u4EE5\u5BF9\u8BDD\u6D88\u606F\u5F62\u5F0F\u9644\u540E\uFF08\u672A\u7ECF\u884C\u5F0F\u538B\u7F29\uFF09\u3002\u8BF7\u628A\u5B83\u4EEC\u6574\u7406\u4E3A\u4E00\u4EFD\u7ED3\u6784\u5316\u68C0\u67E5\u70B9\uFF0C\u4F9B\u540E\u7EED\u6A21\u578B\u7EED\u63A5\u5DE5\u4F5C\uFF08\u5B9E\u4F53\u7D22\u5F15\u7531\u7CFB\u7EDF\u53E6\u884C\u786E\u5B9A\u6027\u7EF4\u62A4\uFF0C\u65E0\u9700\u4F60\u8F93\u51FA\uFF09\u3002") + "\u8FD9\u6279\u539F\u6587\u7D27\u90BB\u672A\u538B\u7F29\u7684\u4FDD\u7559\u533A\uFF0C\u8BF7\u989D\u5916\u5199\u660E\u5176\u4E2D\u5BF9\u7D27\u968F\u5176\u540E\u7684\u8FD1\u671F\u5DE5\u4F5C\u6709\u7528\u7684\u80CC\u666F\uFF08\u5DF2\u786E\u8BA4\u7684\u7EA6\u675F\u3001\u6570\u636E\u3001\u7ED3\u8BBA\uFF09\u3002" + CHECKPOINT_FORMAT + checkpointRules(hasPrevious, true);
2161
+ assembleSlot = (index, newNotes) => [
2162
+ "\u27E6DSC:ARCHIVE-INDEX\u27E7 \u7CBE\u786E\u7EC6\u8282\u53EF\u7528 recall \u5DE5\u5177\u68C0\u7D22\u672C\u4F1A\u8BDD\u5168\u91CF\u5386\u53F2\uFF1B\u4EE5\u4E0B\u4E3A\u5F52\u6863\u5B9E\u4F53\u7D22\u5F15\uFF08\u68C0\u7D22\u5173\u952E\u8BCD\u7EBF\u7D22\uFF09\uFF1A",
2163
+ index.join(" | "),
2164
+ "\u27E6DSC:ARCHIVE-NOTES\u27E7",
2165
+ newNotes
2166
+ ].join("\n");
2167
+ parseSlotNarrativeState = (slotContent) => {
2168
+ const { index, notes } = parseSummarySlot(slotContent);
2169
+ const trimmedNotes = notes.trim();
2170
+ const hasPrevious = trimmedNotes.length > 0 && trimmedNotes !== SLOT_PLACEHOLDER;
2171
+ return { index, hasPrevious };
2172
+ };
2173
+ synthesizeSlotNarrative = async (slotContent, newLines, signal) => {
2174
+ const { index, hasPrevious } = parseSlotNarrativeState(slotContent);
2175
+ const lines = newLines.trim();
2176
+ const resp = await chatWithModelWithSummary(
2177
+ [
2178
+ { role: "system", content: slotContent },
2179
+ { role: "user", content: `${lines ? `\u672C\u8F6E\u65B0\u5F52\u6863\u7684\u6458\u8981\u884C\uFF1A
2180
+ ${lines}
2181
+
2182
+ ` : ""}${checkpointPrompt(hasPrevious)}` }
2183
+ ],
2184
+ [],
2185
+ { signal }
2186
+ );
2187
+ const newNotes = (resp.choices[0].message.content || "").trim();
2188
+ return assembleSlot(index, newNotes);
2189
+ };
2190
+ synthesizeSlotNarrativeFromBatch = async (slotContent, batch, signal) => {
2191
+ const { index, hasPrevious } = parseSlotNarrativeState(slotContent);
2192
+ const resp = await chatWithModelWithSummary(
2193
+ [
2194
+ { role: "system", content: slotContent },
2195
+ ...batch.map(degradeImagesForAux),
2196
+ { role: "user", content: checkpointDirectPrompt(hasPrevious) }
2197
+ ],
2198
+ [],
2199
+ { signal }
2200
+ );
2201
+ const newNotes = (resp.choices[0].message.content || "").trim();
2202
+ return assembleSlot(index, newNotes);
2075
2203
  };
2076
- compactToLine = async (toCompact, _modelWindow, signal) => {
2204
+ compactSlotNarrative = async (slotContent, _modelWindow, signal) => synthesizeSlotNarrative(slotContent, "", signal);
2205
+ splitIntoBatches = (toCompact) => {
2077
2206
  const MAX_BATCH_TOKENS = 16e3;
2078
2207
  const units = groupUnits(toCompact);
2079
2208
  const batches = [];
@@ -2090,7 +2219,10 @@ ${noteLine}` : noteLine;
2090
2219
  batchTokens += size;
2091
2220
  }
2092
2221
  if (batch.length > 0) batches.push(batch);
2093
- const lines = await Promise.all(batches.map((b) => compactBatch(b, signal)));
2222
+ return batches;
2223
+ };
2224
+ compactBatches = async (batches, signal) => {
2225
+ const lines = await Promise.all(batches.map((b, i) => compactBatch(b, signal, i === batches.length - 1)));
2094
2226
  return lines.join("\n");
2095
2227
  };
2096
2228
  ensureSummarySlot = (messageArr) => {
@@ -2098,13 +2230,27 @@ ${noteLine}` : noteLine;
2098
2230
  messageArr.unshift({ role: "system", content: "SYSTEM_META_CONTEXT_START" });
2099
2231
  }
2100
2232
  if (messageArr.length < 2 || messageArr[1]?.role !== "system") {
2101
- messageArr.splice(1, 0, { role: "system", content: "SYSTEM_ROLLING_SUMMARY_SLOT" });
2233
+ messageArr.splice(1, 0, { role: "system", content: SLOT_PLACEHOLDER });
2102
2234
  }
2103
2235
  };
2104
2236
  SUMMARY_SELF_COMPACT_THRESHOLD = 2e3;
2237
+ persistCompaction = async (sessionId, archivedMessageCount, summary) => {
2238
+ await setRollingState(sessionId, {
2239
+ archivedMessageCount,
2240
+ rollingSummary: summary,
2241
+ consecutiveFailures: 0,
2242
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2243
+ });
2244
+ await appendEvent(sessionId, {
2245
+ dscEvent: "compaction",
2246
+ archivedMessageCount,
2247
+ summary
2248
+ });
2249
+ };
2105
2250
  ensureFitsWindow = async (event) => {
2106
2251
  const correctionRatio = event.correctionRatio ?? 1.4;
2107
- const estReal = (arr) => estimateTokens(arr) * correctionRatio;
2252
+ const toolsTokens = event.toolsTokens ?? 0;
2253
+ const estReal = (arr) => estimateTokens(arr) * correctionRatio + toolsTokens;
2108
2254
  const hitRate = event.lastRealPromptTokens && event.lastCachedTokens != null && event.lastRealPromptTokens > 0 ? event.lastCachedTokens / event.lastRealPromptTokens : null;
2109
2255
  const cacheFactor = hitRate == null ? 1 : Math.min(1.1, Math.max(0.9, 0.9 + hitRate * 0.2));
2110
2256
  const effectiveRatio = Math.min(event.compactRatio * cacheFactor, 0.82);
@@ -2126,8 +2272,9 @@ ${noteLine}` : noteLine;
2126
2272
  const { toCompact, keepRecent } = splitUntils(active, keep);
2127
2273
  try {
2128
2274
  if (toCompact.length > 0) {
2129
- const line = await compactToLine(toCompact, event.modelWindow, event.signal);
2130
- summaryMsg.content = mergeSummarySlot(summaryMsg?.content || "", line, extractArchiveEntities(toCompact));
2275
+ summaryMsg.content = mergeSummarySlot(summaryMsg?.content || "", "", extractArchiveEntities(toCompact));
2276
+ const batches = splitIntoBatches(toCompact);
2277
+ summaryMsg.content = batches.length === 1 ? await synthesizeSlotNarrativeFromBatch(summaryMsg.content, batches[0], event.signal) : await synthesizeSlotNarrative(summaryMsg.content, await compactBatches(batches, event.signal), event.signal);
2131
2278
  if (estimateTokens([summaryMsg]) > SUMMARY_SELF_COMPACT_THRESHOLD) {
2132
2279
  summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal);
2133
2280
  }
@@ -2151,17 +2298,7 @@ ${noteLine}` : noteLine;
2151
2298
  });
2152
2299
  const store = await getRollingState(event.sessionId);
2153
2300
  store.archivedMessageCount = (store.archivedMessageCount || 0) + toCompact.length;
2154
- await setRollingState(event.sessionId, {
2155
- archivedMessageCount: store.archivedMessageCount,
2156
- rollingSummary: summaryMsg.content,
2157
- consecutiveFailures: 0,
2158
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2159
- });
2160
- await appendEvent(event.sessionId, {
2161
- dscEvent: "compaction",
2162
- archivedMessageCount: store.archivedMessageCount,
2163
- summary: summaryMsg.content
2164
- });
2301
+ await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
2165
2302
  } else if (keep > 1) {
2166
2303
  keep--;
2167
2304
  continue;
@@ -2170,17 +2307,7 @@ ${noteLine}` : noteLine;
2170
2307
  event.messageArr.length = 0;
2171
2308
  event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
2172
2309
  const store = await getRollingState(event.sessionId);
2173
- await setRollingState(event.sessionId, {
2174
- archivedMessageCount: store.archivedMessageCount,
2175
- rollingSummary: summaryMsg.content,
2176
- consecutiveFailures: 0,
2177
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2178
- });
2179
- await appendEvent(event.sessionId, {
2180
- dscEvent: "compaction",
2181
- archivedMessageCount: store.archivedMessageCount,
2182
- summary: summaryMsg.content
2183
- });
2310
+ await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
2184
2311
  } else {
2185
2312
  break;
2186
2313
  }
@@ -2263,12 +2390,12 @@ ${noteLine}` : noteLine;
2263
2390
  }
2264
2391
  return full;
2265
2392
  }
2266
- const timeoutMs = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
2393
+ const timeoutMs2 = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
2267
2394
  let timer;
2268
2395
  const raced = await Promise.race([
2269
2396
  Promise.resolve(ret).then((v) => ({ kind: "ok", value: typeof v === "string" ? v : JSON.stringify(v) })),
2270
2397
  new Promise((resolve) => {
2271
- timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs);
2398
+ timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs2);
2272
2399
  }),
2273
2400
  ...signal ? [new Promise((resolve) => {
2274
2401
  signal.addEventListener("abort", () => resolve({ kind: "abort" }), { once: true });
@@ -2276,7 +2403,7 @@ ${noteLine}` : noteLine;
2276
2403
  ]);
2277
2404
  if (timer) clearTimeout(timer);
2278
2405
  if (raced.kind === "timeout") {
2279
- return `[\u23F3 \u5DE5\u5177\u6267\u884C\u8D85\u65F6\uFF08${Math.round(timeoutMs / 1e3)}s \u672A\u8FD4\u56DE\uFF09\uFF0C\u5DF2\u7194\u65AD\u8DF3\u8FC7\u3002\u8BE5\u5DE5\u5177\u53EF\u80FD hang \u6216\u4E0D\u54CD\u5E94\u4E2D\u6B62\u4FE1\u53F7\u3002]`;
2406
+ return `[\u23F3 \u5DE5\u5177\u6267\u884C\u8D85\u65F6\uFF08${Math.round(timeoutMs2 / 1e3)}s \u672A\u8FD4\u56DE\uFF09\uFF0C\u5DF2\u7194\u65AD\u8DF3\u8FC7\u3002\u8BE5\u5DE5\u5177\u53EF\u80FD hang \u6216\u4E0D\u54CD\u5E94\u4E2D\u6B62\u4FE1\u53F7\u3002]`;
2280
2407
  }
2281
2408
  if (raced.kind === "abort") {
2282
2409
  throw new Error("aborted");
@@ -4038,19 +4165,19 @@ var init_httpExecutor = __esm({
4038
4165
  };
4039
4166
  executeHttpHook = async (input) => {
4040
4167
  const method = (input.method || "POST").toUpperCase();
4041
- const timeoutMs = input.timeoutMs ?? 3e4;
4168
+ const timeoutMs2 = input.timeoutMs ?? 3e4;
4042
4169
  try {
4043
4170
  const res = await fetch(input.url, {
4044
4171
  method,
4045
4172
  headers: { "content-type": "application/json", ...input.headers ?? {} },
4046
4173
  body: input.body !== void 0 ? JSON.stringify(input.body) : void 0,
4047
- signal: AbortSignal.timeout(timeoutMs)
4174
+ signal: AbortSignal.timeout(timeoutMs2)
4048
4175
  });
4049
4176
  const text = await res.text();
4050
4177
  return { ok: true, status: res.status, responseBody: truncateBody(text) };
4051
4178
  } catch (e) {
4052
4179
  const name = e?.name ?? "";
4053
- if (name === "TimeoutError") return { ok: false, status: 0, responseBody: "", error: `\u8BF7\u6C42\u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09` };
4180
+ if (name === "TimeoutError") return { ok: false, status: 0, responseBody: "", error: `\u8BF7\u6C42\u8D85\u65F6\uFF08>${timeoutMs2}ms\uFF09` };
4054
4181
  if (name === "AbortError") return { ok: false, status: 0, responseBody: "", error: "\u8BF7\u6C42\u88AB\u4E2D\u6B62" };
4055
4182
  return { ok: false, status: 0, responseBody: "", error: e?.message ?? String(e) };
4056
4183
  }
@@ -4351,7 +4478,7 @@ var init_registry3 = __esm({
4351
4478
  getSkillCatalog = () => {
4352
4479
  const all = listSkills();
4353
4480
  if (all.length === 0) return "";
4354
- return all.map((s) => `- ${s.name}\uFF1A${s.description}${s.triggers.length ? `\uFF08\u89E6\u53D1\uFF1A${s.triggers.join("\u3001 ")}\uFF09` : ""}`).join("\n");
4481
+ return all.slice().sort((a, b) => a.name.localeCompare(b.name)).map((s) => `- ${s.name}\uFF1A${s.description}${s.triggers.length ? `\uFF08\u89E6\u53D1\uFF1A${s.triggers.join("\u3001 ")}\uFF09` : ""}`).join("\n");
4355
4482
  };
4356
4483
  }
4357
4484
  });
@@ -4391,7 +4518,7 @@ var init_registry4 = __esm({
4391
4518
  getAgentCatalog = () => {
4392
4519
  const all = listAgents();
4393
4520
  if (all.length === 0) return "";
4394
- return all.map((a) => `- ${a.name}\uFF1A${a.description}${a.role ? `\uFF08\u89D2\u8272\uFF1A${a.role}\uFF09` : ""}`).join("\n");
4521
+ return all.slice().sort((a, b) => a.name.localeCompare(b.name)).map((a) => `- ${a.name}\uFF1A${a.description}${a.role ? `\uFF08\u89D2\u8272\uFF1A${a.role}\uFF09` : ""}`).join("\n");
4395
4522
  };
4396
4523
  }
4397
4524
  });
@@ -4593,6 +4720,7 @@ var init_systemInjections = __esm({
4593
4720
  type: t.type,
4594
4721
  function: { name: t.function.name, description: t.function.description, parameters: t.function.parameters }
4595
4722
  }));
4723
+ const toolsTokens = Math.round(JSON.stringify(cleanedToolSchemas).length / 4);
4596
4724
  ensureSummarySlot(message);
4597
4725
  const LOCALE_HINTS = {
4598
4726
  zh: "\u59CB\u7EC8\u7528\u4E2D\u6587\u8F93\u51FA\u6240\u6709\u9762\u5411\u7528\u6237\u7684\u5185\u5BB9\uFF1A\u4F60\u7684\u56DE\u590D\u3001\u4E2D\u95F4\u53D9\u8FF0\u3001todo \u4EFB\u52A1\u9879\u6807\u9898\u3001\u901A\u8FC7 exit_plan_mode \u63D0\u4EA4\u7684\u65B9\u6848\u6587\u672C\u3001\u4EE3\u7801\u6CE8\u91CA\u4E0E\u63D0\u4EA4\u4FE1\u606F\u3002\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u539F\u6837\uFF1B\u5DE5\u5177\u7ED3\u679C\u4E0E\u7CFB\u7EDF\u6307\u4EE4\u53EF\u80FD\u5939\u6742\u5176\u4ED6\u8BED\u8A00\u2014\u2014\u5FC5\u8981\u65F6\u7167\u5F15\u539F\u6587\uFF0C\u4F46\u4F60\u81EA\u5DF1\u7684\u53D9\u8FF0\u4E0E\u89E3\u91CA\u4E00\u5F8B\u7528\u4E2D\u6587\u3002",
@@ -4616,7 +4744,7 @@ var init_systemInjections = __esm({
4616
4744
  injectAgentCatalog(message);
4617
4745
  injectProjectGuide(message);
4618
4746
  injectMemory(message);
4619
- return { rawTools, cleanedToolSchemas };
4747
+ return { rawTools, cleanedToolSchemas, toolsTokens };
4620
4748
  };
4621
4749
  }
4622
4750
  });
@@ -4633,14 +4761,14 @@ var init_agent = __esm({
4633
4761
  type: "function",
4634
4762
  function: {
4635
4763
  name: "spawn_agent",
4636
- description: "\u521B\u5EFA\u4E00\u4E2A\u62E5\u6709\u72EC\u7ACB\u6267\u884C\u73AF\u5883\u7684\u5B50 agent \u5904\u7406\u9694\u79BB\u7684\u7279\u5B9A\u5B50\u4EFB\u52A1\uFF0C\u5B50\u4EFB\u52A1\u5B8C\u6210\u540E\u81EA\u52A8\u6C47\u603B\u6539\u52A8\u70B9\u4E0E\u7ED3\u8BBA\u8FD4\u56DE\u3002\u53EF\u901A\u8FC7 name \u6307\u5B9A\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF09\u4EE5\u5957\u7528\u5176\u4E13\u957F\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF1B\u4E0D\u4F20 name \u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002\u652F\u6301\u7EED\u8DD1\uFF1A\u4F20 resume_session_id\uFF08\u6B64\u524D\u8FD4\u56DE\u7684 agent_id\uFF09\u590D\u7528\u8BE5\u5B50 Agent \u7684\u5B8C\u6574\u5386\u53F2\u8BB0\u5FC6\uFF0C\u9002\u5408\u8FFD\u95EE\u3001\u7EE7\u7EED\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u3002",
4764
+ description: "\u6D3E\u751F\u62E5\u6709\u72EC\u7ACB\u6267\u884C\u73AF\u5883\u7684\u5B50 agent \u5904\u7406\u9694\u79BB\u5B50\u4EFB\u52A1\uFF0C\u5B8C\u6210\u540E\u81EA\u52A8\u6C47\u62A5\u6539\u52A8\u70B9\u4E0E\u7ED3\u8BBA\u3002name \u53EF\u6307\u5B9A\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF0C\u5957\u7528\u5176\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF09\uFF1B\u7F3A\u7701\u8D70\u901A\u7528\u6A21\u677F\u3002\u7EED\u8DD1\uFF1A\u4F20 resume_session_id\uFF08\u6B64\u524D\u8FD4\u56DE\u7684 agent_id\uFF09\u590D\u7528\u8BE5\u5B50 Agent \u5B8C\u6574\u5386\u53F2\u8BB0\u5FC6\uFF0C\u9002\u5408\u8FFD\u95EE/\u7EE7\u7EED\u672A\u5B8C\u4EFB\u52A1\u3002",
4637
4765
  parameters: {
4638
4766
  type: "object",
4639
4767
  properties: {
4640
- task: { type: "string", description: "\u4EA4\u7ED9\u5B50 agent \u7684\u5177\u4F53\u5FAE\u89C2\u4EFB\u52A1\u63CF\u8FF0\uFF08\u5982 '\u7F16\u5199 Button \u7EC4\u4EF6\u7684\u5355\u5143\u6D4B\u8BD5'\uFF09\uFF1B\u7EED\u8DD1\u65F6\u4E3A\u65B0\u6307\u4EE4\uFF08\u5982 '\u7EE7\u7EED\u5B8C\u6210\u5269\u4F59\u7528\u4F8B'\uFF09" },
4641
- name: { type: "string", description: "\u58F0\u660E\u5F0F\u5B50 Agent \u540D\u79F0\uFF08\u987B\u4E0E\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\u4E00\u81F4\uFF09\u3002\u63D0\u4F9B\u65F6\u6309\u5176\u58F0\u660E\u52A0\u8F7D\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF1B\u4E0D\u63D0\u4F9B\u5219\u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002" },
4642
- role: { type: "string", description: "\u5B50 agent \u7684\u89D2\u8272/\u4E13\u957F\uFF08\u53EF\u9009\uFF0C\u5982 '\u6D4B\u8BD5\u4E13\u5BB6'\u3001'\u91CD\u6784\u5148\u950B'\uFF09\u3002\u58F0\u660E\u5F0F agent \u81EA\u5E26 role \u65F6\u4F5C\u8865\u5145\u3002" },
4643
- resume_session_id: { type: "string", description: "\u7EED\u8DD1\u65E2\u6709\u5B50 Agent\uFF1A\u4F20\u6B64\u524D spawn_agent \u8FD4\u56DE\u7684 agent_id\uFF0C\u5176\u78C1\u76D8\u4E0A\u7684\u5386\u53F2\u4E0E\u8BB0\u5FC6\u5C06\u5B8C\u6574\u6062\u590D\u3002\u6CE8\u610F\uFF1A\u5B50 Agent \u72B6\u6001\u4E0D\u968F\u4E3B\u4F1A\u8BDD fork \u56DE\u6EDA\u2014\u2014fork \u53EA\u5206\u53C9\u5BF9\u8BDD\uFF0C\u7EED\u8DD1\u62FF\u5230\u7684\u662F\u5B50\u4F1A\u8BDD\u78C1\u76D8\u6700\u65B0\u72B6\u6001\u3002\u7F3A\u7701\u5219\u65B0\u5EFA\u5B50 Agent\u3002" }
4768
+ task: { type: "string", description: "\u5B50 agent \u7684\u5177\u4F53\u4EFB\u52A1\uFF08\u5982 '\u7F16\u5199 Button \u7EC4\u4EF6\u7684\u5355\u5143\u6D4B\u8BD5'\uFF09\uFF1B\u7EED\u8DD1\u65F6\u4E3A\u65B0\u6307\u4EE4" },
4769
+ name: { type: "string", description: "\u58F0\u660E\u5F0F\u5B50 Agent \u540D\uFF08\u987B\u4E0E\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\u4E00\u81F4\uFF09\uFF1B\u7F3A\u7701\u901A\u7528\u5B50 agent" },
4770
+ role: { type: "string", description: "\u89D2\u8272/\u4E13\u957F\u8865\u5145\uFF08\u53EF\u9009\uFF09\uFF1B\u58F0\u660E\u5F0F agent \u81EA\u5E26 role \u65F6\u4F5C\u8865\u5145" },
4771
+ resume_session_id: { type: "string", description: "\u7EED\u8DD1\u65E2\u6709\u5B50 Agent\uFF1A\u4F20\u6B64\u524D\u8FD4\u56DE\u7684 agent_id\uFF08\u6062\u590D\u5176\u78C1\u76D8\u5386\u53F2\u4E0E\u8BB0\u5FC6\uFF09\u3002\u6CE8\u610F\u5B50 Agent \u72B6\u6001\u4E0D\u968F\u4E3B\u4F1A\u8BDD fork \u56DE\u6EDA\u3002\u7F3A\u7701\u65B0\u5EFA" }
4644
4772
  },
4645
4773
  required: ["task"]
4646
4774
  },
@@ -4907,13 +5035,9 @@ var init_workflow = __esm({
4907
5035
  function: {
4908
5036
  name: "run_workflow",
4909
5037
  description: [
4910
- "\u591A\u5B50 agent \u5E76\u884C\u7F16\u6392\uFF08P0-3\uFF09\uFF1A\u628A\u4E00\u4E2A\u53EF\u5206\u89E3\u7684\u5927\u4EFB\u52A1\u4E00\u6B21\u6027\u6D3E\u7ED9\u591A\u4E2A\u72EC\u7ACB\u5B50 agent \u5E76\u53D1\u6267\u884C\uFF0C\u6216\u4E32\u6210\u6570\u636E\u6D41\u6C34\u7EBF\u9010\u7EA7\u7CBE\u70BC\u3002\u7A81\u7834 spawn_agent \u53EA\u80FD\u4E32\u884C\u5355\u6D3E\u751F\u7684\u4E0A\u9650\uFF0C\u9002\u7528\u4E8E\u5927\u89C4\u6A21\u4EE3\u7801\u5BA1\u67E5\u3001\u8FC1\u79FB\u5206\u6790\u3001\u591A\u65B9\u6848\u5E76\u884C\u8C03\u7814\u7B49\u3002",
4911
- "\u4E24\u79CD\u6A21\u5F0F\uFF1A",
4912
- "\u2022 parallel\uFF08\u9ED8\u8BA4\uFF09\uFF1A\u5404\u6B65\u9AA4\u4E92\u4E0D\u4F9D\u8D56\u3001\u5E76\u53D1\u6267\u884C\uFF0C\u7ED3\u679C\u6309\u5E8F\u805A\u5408\u3002\u9002\u5408\u4E92\u76F8\u72EC\u7ACB\u7684\u53EA\u8BFB/\u5206\u6790\u4EFB\u52A1\uFF08\u5982\u5BA1\u67E5 5 \u4E2A\u6A21\u5757\u3001\u5BF9 3 \u4E2A\u6280\u672F\u65B9\u6848\u5404\u6D3E\u4E00\u4E2A\u8C03\u7814 agent\uFF09\u3002\u5F3A\u70C8\u5EFA\u8BAE parallel \u7528\u4E8E\u8BFB/\u5206\u6790\u7C7B\u4EFB\u52A1\uFF08\u5E76\u884C\u6539\u540C\u4E00\u6279\u6587\u4EF6\u6613\u51B2\u7A81\uFF09\u3002",
4913
- "\u2022 pipeline\uFF1A\u6B65\u9AA4\u4E32\u884C\uFF0C\u9636\u6BB5 N \u81EA\u52A8\u63A5\u6536\u9636\u6BB5 N-1 \u7684\u4EA7\u51FA\u4F5C\u4E3A\u8F93\u5165\u4E0A\u4E0B\u6587\uFF0C\u9010\u7EA7\u7CBE\u70BC\u3002\u9002\u5408\u5206\u9636\u6BB5\u52A0\u5DE5\uFF08\u5982\uFF1A\u9636\u6BB51 \u62BD\u53D6\u8981\u70B9 \u2192 \u9636\u6BB52 \u5F52\u7C7B \u2192 \u9636\u6BB53 \u6C47\u603B\u7ED3\u8BBA\uFF09\u3002",
4914
- "\u6BCF\u4E2A\u6B65\u9AA4\u53EF\u6307\u5B9A name \u5957\u7528\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF09\u3001role \u8865\u5145\u89D2\u8272\u3001key \u4F5C\u4E3A\u7ED3\u679C\u6807\u7B7E\u3002",
4915
- '\u5E76\u884C\u5199\u5B89\u5168\uFF1Aparallel \u6A21\u5F0F\u9ED8\u8BA4\u5171\u4EAB\u5DE5\u4F5C\u533A\uFF08\u5E76\u884C\u6539\u540C\u4E00\u6279\u6587\u4EF6\u4F1A\u51B2\u7A81\uFF0C\u5EFA\u8BAE\u4EC5\u7528\u4E8E\u8BFB/\u5206\u6790\uFF09\u3002\u8BBE isolation="worktree" \u540E\uFF0C\u6BCF\u4E2A\u6B65\u9AA4\u5728\u72EC\u7ACB git worktree \u91CC\u6267\u884C\uFF0C\u4E92\u4E0D\u6C61\u67D3\u2014\u2014\u7ED3\u675F\u540E\u6536\u5272\u5404 worktree \u7684 diff \u4F9B\u5408\u5E76\uFF0C\u5B9E\u73B0\u5B89\u5168\u7684\u5E76\u884C\u8FC1\u79FB/\u6539\u9020\u3002',
4916
- "\u4E0E spawn_agent \u7684\u5173\u7CFB\uFF1Aspawn_agent = \u4E32\u884C\u6D3E\u751F\u5355\u4E2A\uFF1Brun_workflow = \u5E76\u884C/\u6D41\u6C34\u7EBF\u7F16\u6392\u591A\u4E2A\u3002\u4EFB\u52A1\u53EF\u5206\u89E3\u3001\u5F7C\u6B64\u72EC\u7ACB\u6216\u5448\u6D41\u6C34\u7EBF\u65F6\u7528\u672C\u5DE5\u5177\uFF0C\u5426\u5219\u7528 spawn_agent\u3002"
5038
+ "\u591A\u5B50 agent \u7F16\u6392\uFF1A\u628A\u53EF\u5206\u89E3\u7684\u5927\u4EFB\u52A1\u6D3E\u7ED9\u591A\u4E2A\u72EC\u7ACB\u5B50 agent\u3002parallel\uFF08\u9ED8\u8BA4\uFF09=\u5404\u6B65\u9AA4\u5E76\u53D1\u6267\u884C\u3001\u7ED3\u679C\u6309\u5E8F\u805A\u5408\uFF0C\u9002\u5408\u4E92\u76F8\u72EC\u7ACB\u7684\u53EA\u8BFB/\u5206\u6790\u4EFB\u52A1\uFF1Bpipeline=\u6B65\u9AA4\u4E32\u884C\uFF0C\u524D\u9636\u6BB5\u4EA7\u51FA\u81EA\u52A8\u4F5C\u4E3A\u540E\u9636\u6BB5\u8F93\u5165\uFF0C\u9002\u5408\u5206\u9636\u6BB5\u52A0\u5DE5\uFF08\u62BD\u53D6\u2192\u5F52\u7C7B\u2192\u6C47\u603B\uFF09\u3002",
5039
+ '\u6B65\u9AA4\u53EF\u7528 name \u5957\u7528\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF09\u3001key \u4F5C\u7ED3\u679C\u6807\u7B7E\u3002parallel \u9ED8\u8BA4\u5171\u4EAB\u5DE5\u4F5C\u533A\uFF0C\u4EC5\u9002\u5408\u5E76\u884C\u8BFB/\u5206\u6790\uFF1B\u5E76\u884C\u5199/\u8FC1\u79FB\u7528 isolation="worktree"\uFF08\u6BCF\u6B65\u72EC\u7ACB git worktree\uFF0C\u7ED3\u675F\u540E\u6536\u5272\u5404\u6B65 diff\uFF09\u3002',
5040
+ "\u4E0E spawn_agent \u7684\u5206\u5DE5\uFF1A\u5355\u4EFB\u52A1\u4E32\u884C\u59D4\u6D3E\u7528 spawn_agent\uFF1B\u4EFB\u52A1\u53EF\u5206\u89E3\u3001\u591A\u5206\u652F\u6216\u6D41\u6C34\u7EBF\u5F0F\u65F6\u7528\u672C\u5DE5\u5177\u3002"
4917
5041
  ].join("\n"),
4918
5042
  parameters: {
4919
5043
  type: "object",
@@ -4921,18 +5045,18 @@ var init_workflow = __esm({
4921
5045
  mode: {
4922
5046
  type: "string",
4923
5047
  enum: ["parallel", "pipeline"],
4924
- description: "\u7F16\u6392\u6A21\u5F0F\uFF1Aparallel=\u5E76\u53D1\u6247\u51FA\uFF08\u9ED8\u8BA4\uFF0C\u4E92\u4E0D\u4F9D\u8D56\u7684\u4EFB\u52A1\uFF09\uFF1Bpipeline=\u4E32\u884C\u6D41\u6C34\u7EBF\uFF08\u9636\u6BB5\u95F4\u6709\u6570\u636E\u4F9D\u8D56\uFF0C\u9010\u7EA7\u7CBE\u70BC\uFF09\u3002"
5048
+ description: "parallel=\u5E76\u53D1\u6247\u51FA\uFF08\u9ED8\u8BA4\uFF0C\u6B65\u9AA4\u4E92\u4E0D\u4F9D\u8D56\uFF09\uFF1Bpipeline=\u4E32\u884C\u6D41\u6C34\u7EBF\uFF08\u524D\u9636\u6BB5\u4EA7\u51FA\u4F5C\u540E\u9636\u6BB5\u8F93\u5165\uFF09\u3002"
4925
5049
  },
4926
5050
  steps: {
4927
5051
  type: "array",
4928
- description: "\u7F16\u6392\u6B65\u9AA4\u5217\u8868\uFF08parallel\uFF1A\u6BCF\u4E2A\u662F\u4E00\u4E2A\u72EC\u7ACB\u5B50 agent \u4EFB\u52A1\uFF1Bpipeline\uFF1A\u6309\u987A\u5E8F\u4E3A\u5404\u9636\u6BB5\uFF0C\u524D\u9636\u6BB5\u4EA7\u51FA\u81EA\u52A8\u4F5C\u4E3A\u540E\u9636\u6BB5\u8F93\u5165\uFF09\u3002",
5052
+ description: "\u6B65\u9AA4\u5217\u8868\uFF08parallel=\u6BCF\u4E2A\u662F\u72EC\u7ACB\u5B50 agent \u4EFB\u52A1\uFF1Bpipeline=\u6309\u5E8F\u5404\u9636\u6BB5\uFF09\u3002",
4929
5053
  items: {
4930
5054
  type: "object",
4931
5055
  properties: {
4932
- task: { type: "string", description: "\u4EA4\u7ED9\u8BE5\u5B50 agent \u7684\u5177\u4F53\u4EFB\u52A1\u63CF\u8FF0\uFF08parallel \u5FC5\u987B\u81EA\u5305\u542B\u3001\u4E0D\u4F9D\u8D56\u5176\u5B83\u6B65\u9AA4\uFF1Bpipeline \u53EA\u5199\u672C\u9636\u6BB5\u804C\u8D23\uFF0C\u524D\u9636\u6BB5\u4EA7\u51FA\u4F1A\u81EA\u52A8\u62FC\u5165\uFF09" },
4933
- name: { type: "string", description: "\u58F0\u660E\u5F0F\u5B50 Agent \u540D\u79F0\uFF08\u53EF\u9009\uFF0C\u5957\u7528\u5176\u4E13\u957F\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF09" },
4934
- role: { type: "string", description: "\u89D2\u8272/\u4E13\u957F\u8865\u5145\uFF08\u53EF\u9009\uFF09" },
4935
- key: { type: "string", description: "\u7ED3\u679C\u6807\u7B7E\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u7528\u4EFB\u52A1\u9996\u53E5\uFF1Bparallel \u7ED3\u679C\u6309\u6B64\u6807\u8BC6\u5404\u5B50 agent \u4EA7\u51FA\uFF09" }
5056
+ task: { type: "string", description: "\u5B50 agent \u4EFB\u52A1\u63CF\u8FF0\uFF08parallel \u987B\u81EA\u5305\u542B\uFF1Bpipeline \u53EA\u5199\u672C\u9636\u6BB5\u804C\u8D23\uFF09" },
5057
+ name: { type: "string", description: "\u58F0\u660E\u5F0F\u5B50 Agent \u540D\uFF08\u53EF\u9009\uFF0C\u5957\u7528\u5176\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF09" },
5058
+ role: { type: "string", description: "\u89D2\u8272\u8865\u5145\uFF08\u53EF\u9009\uFF09" },
5059
+ key: { type: "string", description: "\u7ED3\u679C\u6807\u7B7E\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u53D6\u4EFB\u52A1\u9996\u53E5\uFF09" }
4936
5060
  },
4937
5061
  required: ["task"]
4938
5062
  },
@@ -4940,12 +5064,12 @@ var init_workflow = __esm({
4940
5064
  },
4941
5065
  concurrency: {
4942
5066
  type: "number",
4943
- description: "parallel \u6A21\u5F0F\u6700\u5927\u5E76\u53D1\u5B50 agent \u6570\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 4\uFF0C\u4E0A\u9650\u53D7\u5168\u5C40\u914D\u7F6E\u7EA6\u675F\uFF09\u3002\u4EFB\u52A1\u5F7C\u6B64\u72EC\u7ACB\u65F6\u8C03\u5927\u53EF\u52A0\u901F\uFF1B\u4E3A 1 \u5219\u9000\u5316\u4E3A\u4E32\u884C\u3002"
5067
+ description: "parallel \u6700\u5927\u5E76\u53D1\u6570\uFF08\u9ED8\u8BA4 4\uFF0C\u53D7\u5168\u5C40\u4E0A\u9650\u7EA6\u675F\uFF1B1=\u4E32\u884C\uFF09\u3002"
4944
5068
  },
4945
5069
  isolation: {
4946
5070
  type: "string",
4947
5071
  enum: ["none", "worktree"],
4948
- description: "\u9694\u79BB\u6A21\u5F0F\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 none\uFF09\uFF1Anone=\u5171\u4EAB\u5DE5\u4F5C\u533A\uFF08\u4EC5\u9002\u5408\u8BFB/\u5206\u6790\uFF09\uFF1Bworktree=\u6BCF\u6B65\u72EC\u7ACB git worktree\uFF08\u9002\u5408\u5E76\u884C\u5199/\u8FC1\u79FB\uFF0C\u4E92\u4E0D\u6C61\u67D3\uFF0C\u7ED3\u675F\u540E\u6536\u5272\u5404 worktree diff\uFF09\u3002\u4EC5 parallel \u6A21\u5F0F\u652F\u6301 worktree\u3002"
5072
+ description: "none=\u5171\u4EAB\u5DE5\u4F5C\u533A\uFF08\u9ED8\u8BA4\uFF0C\u53EA\u8BFB/\u5206\u6790\uFF09\uFF1Bworktree=\u6BCF\u6B65\u72EC\u7ACB git worktree\uFF08\u5E76\u884C\u5199/\u8FC1\u79FB\uFF09\u3002\u4EC5 parallel \u652F\u6301\u3002"
4949
5073
  }
4950
5074
  },
4951
5075
  required: ["steps"]
@@ -5270,7 +5394,7 @@ var init_fs = __esm({
5270
5394
  return lines.map((l) => l.length === 0 ? l : l.replace(re, "")).join("\n");
5271
5395
  };
5272
5396
  maskSecretsInContent = (_args, output) => {
5273
- return output.replace(/((?:api[_-]?key|secret|password|passwd|token|authorization|auth[_-]?token|access[_-]?key|secret[_-]?key|private[_-]?key)\s*[:=]\s*['"]?)[A-Za-z0-9_\-+/=.]{8,}(['"]?)/gi, "$1[MASKED_SECRET]$2").replace(/-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]*?-----END [A-Z ]+PRIVATE KEY-----/g, "[MASKED_SECRET (private key block)]");
5397
+ return output.replace(/((?:api[_-]?key|secret|password|passwd|token|authorization|auth[_-]?token|access[_-]?key|secret[_-]?key|private[_-]?key)\s*[:=]\s*)(['"])[A-Za-z0-9_\-+/=.]{8,}\2/gi, "$1$2[MASKED_SECRET]$2").replace(/((?:authorization|auth[_-]?token)\s*[:=]\s*Bearer\s+)[A-Za-z0-9_\-+/=.]{8,}/gi, "$1[MASKED_SECRET]").replace(/-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]*?-----END [A-Z ]+PRIVATE KEY-----/g, "[MASKED_SECRET (private key block)]");
5274
5398
  };
5275
5399
  makeTmpPath = (absPath) => `${absPath}.${Date.now()}.${Math.random().toString(36).slice(2, 7)}.tmp`;
5276
5400
  ATOMIC_TMP_RE = /\.\d+\.[a-z0-9]{1,12}\.tmp$/i;
@@ -5602,23 +5726,23 @@ ${treeResult}`;
5602
5726
  type: "function",
5603
5727
  function: {
5604
5728
  name: "edit_file",
5605
- description: "\u9488\u5BF9\u6307\u5B9A\u7684\u6587\u4EF6\u8FDB\u884C\u5C40\u90E8\u7CBE\u51C6\u4FEE\u6539\u3002\u2605 \u540C\u4E00\u6587\u4EF6\u6709\u591A\u5904\u8981\u6539\u65F6\uFF0C\u52A1\u5FC5\u4E00\u6B21\u8C03\u7528\u4F20 edits \u6570\u7EC4\u5168\u90E8\u5B8C\u6210\uFF08\u6309\u5E8F\u5E94\u7528\u3001\u6574\u4F53\u539F\u5B50\uFF1A\u4EFB\u4E00\u5904\u5931\u8D25\u5219\u5168\u90E8\u4E0D\u5199\u5165\uFF09\uFF0C\u52FF\u9010\u5904\u591A\u6B21\u8C03\u7528\u3002old_str \u5FC5\u987B\u4E0E\u6587\u4EF6\u4E2D\u7684\u539F\u59CB\u4EE3\u7801\u9010\u5B57\u7B26\u4E00\u81F4\uFF1A\u8BF7\u53BB\u6389 read_file \u8FD4\u56DE\u7684\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\uFF0C\u5E76\u4FDD\u7559\u539F\u6709\u7F29\u8FDB\uFF08Tab/\u7A7A\u683C\uFF09\u4E0E\u884C\u5C3E\u7A7A\u767D\uFF1B\u9ED8\u8BA4\u8981\u6C42\u5728\u5168\u6587\u4E2D\u552F\u4E00\uFF0C\u82E5\u8BBE replace_all=true \u5219\u66FF\u6362\u5168\u90E8\u5339\u914D\u5904\uFF08\u9002\u5408\u6279\u91CF\u91CD\u547D\u540D/\u7EDF\u4E00\u6539\u5199\uFF09\u3002",
5729
+ description: "\u5BF9\u6587\u4EF6\u505A\u5C40\u90E8\u7CBE\u51C6\u4FEE\u6539\u3002\u2605 \u540C\u4E00\u6587\u4EF6\u591A\u5904\u8981\u6539\u65F6\u4E00\u6B21\u4F20 edits \u6570\u7EC4\u5168\u90E8\u5B8C\u6210\uFF08\u6309\u5E8F\u5E94\u7528\u3001\u6574\u4F53\u539F\u5B50\uFF1A\u4EFB\u4E00\u5904\u5931\u8D25\u5168\u90E8\u4E0D\u5199\u5165\uFF09\uFF0C\u52FF\u9010\u5904\u591A\u6B21\u8C03\u7528\u3002old_str \u987B\u4E0E\u6587\u4EF6\u539F\u6587\u9010\u5B57\u7B26\u4E00\u81F4\uFF1A\u53BB\u6389 read_file \u8FD4\u56DE\u7684\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\uFF0C\u4FDD\u7559\u539F\u7F29\u8FDB\uFF08Tab/\u7A7A\u683C\uFF09\u4E0E\u884C\u5C3E\u7A7A\u767D\u3002\u9ED8\u8BA4\u987B\u5168\u6587\u552F\u4E00\uFF1Breplace_all=true \u66FF\u6362\u5168\u90E8\u5339\u914D\uFF08\u6279\u91CF\u91CD\u547D\u540D/\u7EDF\u4E00\u6539\u5199\uFF09\u3002",
5606
5730
  parameters: {
5607
5731
  type: "object",
5608
5732
  properties: {
5609
- path: { type: "string", description: "\u51C6\u5907\u4FEE\u6539\u7684\u6587\u4EF6\u76F8\u5BF9\u8DEF\u5F84" },
5610
- old_str: { type: "string", description: "\u6587\u4EF6\u4E2D\u73B0\u6709\u7684\u5B8C\u6574\u65E7\u4EE3\u7801\u5757\uFF08\u5355\u5904\u4FEE\u6539\u7528\uFF1B\u591A\u5904\u4FEE\u6539\u8BF7\u6539\u7528 edits \u6570\u7EC4\u4E00\u6B21\u5B8C\u6210\uFF09" },
5611
- new_str: { type: "string", description: "\u51C6\u5907\u66FF\u6362\u8FDB\u53BB\u7684\u65B0\u4EE3\u7801\u5757\uFF08\u5355\u5904\u4FEE\u6539\u7528\uFF09" },
5612
- replace_all: { type: "boolean", description: "\u662F\u5426\u66FF\u6362\u5168\u6587\u6240\u6709\u5339\u914D\u5904\uFF08\u9ED8\u8BA4 false \u4EC5\u66FF\u6362\u552F\u4E00\u5339\u914D\uFF1B\u6279\u91CF\u91CD\u547D\u540D/\u7EDF\u4E00\u6539\u5199\u65F6\u8BBE true\uFF09\u3002\u9876\u5C42\u53C2\u6570\u4EC5\u4F5C\u7528\u4E8E\u5355\u6761 old_str/new_str\uFF1Bedits \u5185\u6BCF\u6761\u53EF\u72EC\u7ACB\u8BBE\u7F6E" },
5733
+ path: { type: "string", description: "\u76EE\u6807\u6587\u4EF6\u8DEF\u5F84" },
5734
+ old_str: { type: "string", description: "\u73B0\u6709\u65E7\u4EE3\u7801\u5757\uFF08\u5355\u5904\u4FEE\u6539\u7528\uFF1B\u591A\u5904\u6539\u7528 edits\uFF09" },
5735
+ new_str: { type: "string", description: "\u66FF\u6362\u540E\u7684\u65B0\u4EE3\u7801\u5757\uFF08\u5355\u5904\u4FEE\u6539\u7528\uFF09" },
5736
+ replace_all: { type: "boolean", description: "\u66FF\u6362\u5168\u6587\u6240\u6709\u5339\u914D\uFF08\u9ED8\u8BA4 false \u4EC5\u552F\u4E00\u5339\u914D\uFF09\u3002\u9876\u5C42\u4EC5\u4F5C\u7528\u5355\u6761 old_str/new_str\uFF1Bedits \u5185\u6BCF\u6761\u53EF\u72EC\u7ACB\u8BBE\u7F6E" },
5613
5737
  edits: {
5614
5738
  type: "array",
5615
- description: "\u2605 \u540C\u4E00\u6587\u4EF6\u591A\u5904\u4FEE\u6539\u65F6\u4F18\u5148\u4F7F\u7528\uFF1A\u4E00\u6B21\u8C03\u7528\u6309\u5E8F\u5B8C\u6210\u5168\u90E8\u66FF\u6362\uFF0C\u7701\u53BB\u9010\u5904\u5F80\u8FD4\u3002\u7B2C N \u6761\u7684 old_str \u987B\u57FA\u4E8E\u524D N-1 \u6761\u5E94\u7528\u540E\u7684\u6587\u4EF6\u5185\u5BB9\u6784\u9020\uFF1B\u4EFB\u4E00\u6761\u672A\u547D\u4E2D\u5219\u6574\u4F53\u4E0D\u5199\u5165\uFF08\u539F\u5B50\uFF09\uFF0C\u4FEE\u6B63\u8BE5\u6761\u540E\u6574\u7EC4\u91CD\u8BD5",
5739
+ description: "\u2605 \u540C\u6587\u4EF6\u591A\u5904\u4FEE\u6539\u9996\u9009\uFF1A\u4E00\u6B21\u6309\u5E8F\u5B8C\u6210\u5168\u90E8\u66FF\u6362\u3002\u7B2C N \u6761 old_str \u57FA\u4E8E\u524D N-1 \u6761\u5E94\u7528\u540E\u7684\u5185\u5BB9\u6784\u9020\uFF1B\u4EFB\u4E00\u6761\u672A\u547D\u4E2D\u5219\u6574\u7EC4\u4E0D\u5199\u5165\uFF08\u539F\u5B50\uFF09\uFF0C\u4FEE\u6B63\u540E\u6574\u7EC4\u91CD\u8BD5",
5616
5740
  items: {
5617
5741
  type: "object",
5618
5742
  properties: {
5619
- old_str: { type: "string", description: "\u6587\u4EF6\u4E2D\u73B0\u6709\u7684\u5B8C\u6574\u65E7\u4EE3\u7801\u5757" },
5620
- new_str: { type: "string", description: "\u51C6\u5907\u66FF\u6362\u8FDB\u53BB\u7684\u65B0\u4EE3\u7801\u5757" },
5621
- replace_all: { type: "boolean", description: "\u8BE5\u6761\u662F\u5426\u66FF\u6362\u5168\u6587\u6240\u6709\u5339\u914D\u5904\uFF08\u9ED8\u8BA4 false\uFF09" }
5743
+ old_str: { type: "string", description: "\u73B0\u6709\u65E7\u4EE3\u7801\u5757" },
5744
+ new_str: { type: "string", description: "\u65B0\u4EE3\u7801\u5757" },
5745
+ replace_all: { type: "boolean", description: "\u66FF\u6362\u5168\u90E8\u5339\u914D\uFF08\u9ED8\u8BA4 false\uFF09" }
5622
5746
  },
5623
5747
  required: ["old_str", "new_str"]
5624
5748
  }
@@ -5784,7 +5908,7 @@ ${summaries.join("\n")}`;
5784
5908
  type: "function",
5785
5909
  function: {
5786
5910
  name: "write_file",
5787
- description: "\u5C06\u5B8C\u6574\u5185\u5BB9\u5168\u91CF\u5199\u5165\u6307\u5B9A\u6587\u4EF6\uFF08\u8986\u76D6\uFF09\u3002\u6587\u4EF6\u4E0D\u5B58\u5728\u5219\u65B0\u5EFA\uFF08\u542B\u7236\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B58\u5728\u5219\u6574\u4F53\u8986\u76D6\u3002\u2605 \u4EC5\u7528\u4E8E\u3010\u65B0\u5EFA\u6587\u4EF6\u3011\u6216\u3010\u5F7B\u5E95\u91CD\u5199\u6574\u4E2A\u6587\u4EF6\u3011\u2014\u2014\u4FEE\u6539\u65E2\u6709\u6587\u4EF6\u4E00\u5F8B\u6539\u7528 edit_file\uFF08\u7CBE\u51C6\u5C40\u90E8\u66FF\u6362\u3001\u4FDD\u7559\u539F\u7F29\u8FDB\u3001\u6700\u5C0F diff\uFF09\u3002\u7528 write_file \u8986\u76D6\u65E2\u6709\u6587\u4EF6\u4F1A\u4E22\u5931\u539F\u6709 Tab/\u7A7A\u683C\u7F29\u8FDB\uFF0C\u4EA7\u751F\u5927\u91CF\u65E0\u5173 diff\uFF0C\u7528\u6237\u5F3A\u70C8\u53CD\u611F\u3002",
5911
+ description: "\u5C06\u5B8C\u6574\u5185\u5BB9\u5168\u91CF\u5199\u5165\u6307\u5B9A\u6587\u4EF6\uFF08\u8986\u76D6\uFF09\u3002\u6587\u4EF6\u4E0D\u5B58\u5728\u5219\u65B0\u5EFA\uFF08\u542B\u7236\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B58\u5728\u5219\u6574\u4F53\u8986\u76D6\u3002\u2605 \u4EC5\u7528\u4E8E\u3010\u65B0\u5EFA\u6587\u4EF6\u3011\u6216\u3010\u5F7B\u5E95\u91CD\u5199\u6574\u4E2A\u6587\u4EF6\u3011\uFF1B\u4FEE\u6539\u65E2\u6709\u6587\u4EF6\u4E00\u5F8B\u6539\u7528 edit_file\uFF08\u5C40\u90E8\u7CBE\u51C6\u66FF\u6362\u3001\u6700\u5C0F diff\uFF09\u3002",
5788
5912
  parameters: {
5789
5913
  type: "object",
5790
5914
  properties: {
@@ -5970,14 +6094,14 @@ var init_search = __esm({
5970
6094
  type: "function",
5971
6095
  function: {
5972
6096
  name: "search_grep",
5973
- description: "\u5728\u5DE5\u4F5C\u533A\u7684\u6240\u6709\u6587\u4EF6\u4E2D\u68C0\u7D22\u5339\u914D\u7684\u4EE3\u7801\u884C\uFF0C\u6BCF\u4E2A\u547D\u4E2D\u9ED8\u8BA4\u5E26\u524D\u540E\u5404 2 \u884C\u4E0A\u4E0B\u6587\uFF08\u53EF\u76F4\u63A5\u5224\u65AD\u7528\u6CD5\u3001\u514D\u7D27\u8DDF\u4E00\u6B21 read_file\uFF1B\u6784\u9020 edit_file \u7684 old_str \u524D\u53EF\u7528 context \u53C2\u6570\u8C03\u5927\u4E00\u6B21\u62FF\u8DB3\uFF09\uFF1B\u9ED8\u8BA4\u6309\u5B57\u9762\u91CF\u5173\u952E\u8BCD\u5339\u914D\uFF0C\u5982\u9700\u6B63\u5219\u4F20 is_regex=true\u3002\u2605 \u68C0\u7D22\u591A\u4E2A\u5173\u952E\u8BCD\u65F6\uFF0C\u7528 is_regex=true \u4EE5 | \u5408\u5E76\uFF08\u5982 'uploadVisible|uploadTip'\uFF09\u4E00\u6B21\u67E5\u5B8C\uFF0C\u52FF\u9010\u4E2A\u5173\u952E\u8BCD\u591A\u6B21\u8C03\u7528\u3002path \u53EF\u9650\u5B9A\u5230\u76EE\u5F55\u6216\u5177\u4F53\u6587\u4EF6\uFF08\u5728\u5355\u4E2A\u5927\u6587\u4EF6\u5185\u591A\u5904\u5B9A\u4F4D\u65F6\u4F20\u6587\u4EF6\u8DEF\u5F84\u6700\u9AD8\u6548\uFF09\u3002\u591A\u6839\u5DE5\u4F5C\u533A\u4E0B\uFF1A\u53EF\u4F20\u9879\u76EE\u76EE\u5F55\u540D\uFF08\u5982 'frontend'\uFF09\u81EA\u52A8\u5339\u914D\u5DE5\u4F5C\u533A\u6839\uFF0C\u6216\u4F20\u7EDD\u5BF9\u8DEF\u5F84 / \u76F8\u5BF9\u9ED8\u8BA4\u6839\u7684 ../\u5144\u5F1F\u76EE\u5F55\u3002\u2605 \u5F53\u7528\u6237\u5728\u5BF9\u8BDD\u91CC\u6307\u660E\u4E86\u67D0\u4E2A\u9879\u76EE\u65F6\uFF0C\u52A1\u5FC5\u628A\u8BE5\u9879\u76EE\u4F5C\u4E3A path \u4F20\u5165\u2014\u2014\u5426\u5219\u53EA\u4F1A\u641C\u7D22 IDE \u5934\u90E8\u6240\u793A\u7684\u6D3B\u52A8\u6839\uFF0C\u5E38\u4E0E\u7528\u6237\u610F\u56FE\u4E0D\u7B26\u3002",
6097
+ description: "\u5728\u5DE5\u4F5C\u533A\u6587\u4EF6\u4E2D\u68C0\u7D22\u5339\u914D\u7684\u4EE3\u7801\u884C\uFF0C\u6BCF\u4E2A\u547D\u4E2D\u9ED8\u8BA4\u5E26\u524D\u540E\u5404 2 \u884C\u4E0A\u4E0B\u6587\uFF08\u53EF\u76F4\u63A5\u5224\u65AD\u7528\u6CD5\uFF1B\u6784\u9020 edit_file \u7684 old_str \u524D\u53EF\u7528 context \u8C03\u5927\u4E00\u6B21\u62FF\u8DB3\uFF09\u3002\u9ED8\u8BA4\u5B57\u9762\u91CF\u5339\u914D\uFF0C\u6B63\u5219\u4F20 is_regex=true\u3002\u2605 \u591A\u5173\u952E\u8BCD\u7528 is_regex=true \u4EE5 | \u5408\u5E76\uFF08\u5982 'uploadVisible|uploadTip'\uFF09\u4E00\u6B21\u67E5\u5B8C\uFF0C\u52FF\u9010\u8BCD\u591A\u6B21\u8C03\u7528\u3002path \u53EF\u9650\u5B9A\u76EE\u5F55\u6216\u6587\u4EF6\uFF08\u5355\u5927\u6587\u4EF6\u591A\u5904\u5B9A\u4F4D\u4F20\u6587\u4EF6\u8DEF\u5F84\u6700\u9AD8\u6548\uFF09\u3002\u591A\u6839\u5DE5\u4F5C\u533A\uFF1Apath \u53EF\u4F20\u9879\u76EE\u76EE\u5F55\u540D\uFF08\u5982 'frontend'\uFF09\u3001\u7EDD\u5BF9\u8DEF\u5F84\u6216 ../\u5144\u5F1F\u76EE\u5F55\u3002\u2605 \u7528\u6237\u6307\u660E\u4E86\u9879\u76EE\u65F6\u52A1\u5FC5\u628A\u8BE5\u9879\u76EE\u4F5C\u4E3A path \u4F20\u5165\u2014\u2014\u7F3A\u7701\u53EA\u641C\u6D3B\u52A8\u6839\uFF0C\u5E38\u4E0E\u610F\u56FE\u4E0D\u7B26\u3002",
5974
6098
  parameters: {
5975
6099
  type: "object",
5976
6100
  properties: {
5977
- query: { type: "string", description: "\u68C0\u7D22\u5185\u5BB9\u3002\u9ED8\u8BA4\u4E3A\u5B57\u9762\u91CF\u5173\u952E\u8BCD\uFF08\u5982 'function runAgent'\uFF09\uFF1Bis_regex=true \u65F6\u6309\u6B63\u5219\u89E3\u6790\uFF08\u5982 'function\\s+runAgent'\uFF0C\u591A\u5173\u952E\u8BCD\u7528 | \u5408\u5E76\uFF09" },
5978
- is_regex: { type: "boolean", description: "\u662F\u5426\u5C06 query \u4F5C\u4E3A\u6B63\u5219\u8868\u8FBE\u5F0F\u89E3\u6790\uFF0C\u9ED8\u8BA4 false\uFF08\u5B57\u9762\u91CF\u5339\u914D\uFF0C\u81EA\u52A8\u8F6C\u4E49\u7279\u6B8A\u5B57\u7B26\uFF09" },
5979
- path: { type: "string", description: "\u9650\u5B9A\u641C\u7D22\u7684\u76EE\u5F55\u6216\u5177\u4F53\u6587\u4EF6\uFF08\u53EF\u9009\uFF09\u3002\u4F20\u5177\u4F53\u6587\u4EF6\u8DEF\u5F84\u65F6\u76F8\u5F53\u4E8E\u8BE5\u6587\u4EF6\u5185\u5168\u6587\u68C0\u7D22\uFF08\u5927\u6587\u4EF6\u591A\u5904\u5B9A\u4F4D\u9996\u9009\uFF09\u3002\u9ED8\u8BA4\u641C\u7D22 IDE \u5934\u90E8\u6240\u793A\u6D3B\u52A8\u6839\u3002\u591A\u6839\u573A\u666F\u4E0B\uFF1A\u4F20\u9879\u76EE\u76EE\u5F55\u540D\uFF08\u5982 'frontend'\uFF09\u81EA\u52A8\u5339\u914D\u5DE5\u4F5C\u533A\u6839\uFF1B\u6216\u4F20\u7EDD\u5BF9\u8DEF\u5F84 / \u76F8\u5BF9\u9ED8\u8BA4\u6839\u7684 ../<\u5144\u5F1F\u76EE\u5F55>\uFF1B\u5747\u7ECF\u6C99\u7BB1\u6821\u9A8C\u3002\u7528\u6237\u63D0\u5230\u5177\u4F53\u9879\u76EE\u65F6\u5FC5\u586B\u3002" },
5980
- context: { type: "number", description: "\u6BCF\u4E2A\u547D\u4E2D\u9644\u5E26\u7684\u524D\u540E\u4E0A\u4E0B\u6587\u884C\u6570\uFF080-10\uFF0C\u9ED8\u8BA4 2\uFF09\u3002\u9700\u8981\u62FF\u8DB3\u4E00\u6BB5\u5B8C\u6574\u4EE3\u7801\u4F5C edit_file \u7684 old_str \u65F6\u53EF\u8C03\u5927\uFF08\u5982 5-8\uFF09\uFF0C\u514D\u5F97\u4E8C\u6B21\u68C0\u7D22" }
6101
+ query: { type: "string", description: "\u68C0\u7D22\u8BCD\u3002\u9ED8\u8BA4\u5B57\u9762\u91CF\uFF08\u5982 'function runAgent'\uFF09\uFF1Bis_regex=true \u65F6\u6309\u6B63\u5219\uFF08\u591A\u5173\u952E\u8BCD\u7528 | \u5408\u5E76\uFF09" },
6102
+ is_regex: { type: "boolean", description: "query \u6309\u6B63\u5219\u89E3\u6790\uFF08\u9ED8\u8BA4 false\uFF0C\u5B57\u9762\u91CF\u81EA\u52A8\u8F6C\u4E49\u7279\u6B8A\u5B57\u7B26\uFF09" },
6103
+ path: { type: "string", description: "\u9650\u5B9A\u641C\u7D22\u7684\u76EE\u5F55\u6216\u6587\u4EF6\uFF08\u53EF\u9009\uFF09\u3002\u4F20\u6587\u4EF6=\u8BE5\u6587\u4EF6\u5185\u68C0\u7D22\uFF08\u5927\u6587\u4EF6\u591A\u5904\u5B9A\u4F4D\u9996\u9009\uFF09\u3002\u9ED8\u8BA4\u6D3B\u52A8\u6839\uFF1B\u591A\u6839\u4E0B\u53EF\u4F20\u9879\u76EE\u76EE\u5F55\u540D\uFF08\u5982 'frontend'\uFF09/\u7EDD\u5BF9\u8DEF\u5F84/../\u5144\u5F1F\u76EE\u5F55\u3002\u7528\u6237\u63D0\u5230\u5177\u4F53\u9879\u76EE\u65F6\u5FC5\u586B\u3002" },
6104
+ context: { type: "number", description: "\u6BCF\u547D\u4E2D\u9644\u5E26\u4E0A\u4E0B\u6587\u884C\u6570\uFF080-10\uFF0C\u9ED8\u8BA4 2\uFF09\u3002\u8981\u62FF\u8DB3\u5B8C\u6574\u4EE3\u7801\u4F5C old_str \u65F6\u8C03\u5927\uFF08\u5982 5-8\uFF09" }
5981
6105
  },
5982
6106
  required: ["query"]
5983
6107
  },
@@ -6121,7 +6245,7 @@ ${content}`;
6121
6245
 
6122
6246
  // ../core/src/tool/registry/command.ts
6123
6247
  import { spawn as spawn4 } from "child_process";
6124
- var EXIT_SENTINEL, EXIT_SENTINEL_RE, RUN_COMMAND_MAX_CHARS, commandTools;
6248
+ var EXIT_SENTINEL, EXIT_SENTINEL_RE, AUTO_BG_NOTICE, AUTO_BG_SENTINEL_RE, RUN_COMMAND_MAX_CHARS, commandTools;
6125
6249
  var init_command = __esm({
6126
6250
  "../core/src/tool/registry/command.ts"() {
6127
6251
  "use strict";
@@ -6132,6 +6256,8 @@ var init_command = __esm({
6132
6256
  EXIT_SENTINEL = (code) => `
6133
6257
  \u27E6DSC_EXIT:${code}\u27E7`;
6134
6258
  EXIT_SENTINEL_RE = /⟦DSC_EXIT:(-?\d+)⟧/g;
6259
+ AUTO_BG_NOTICE = "\u27E6DSC_BG\u27E7";
6260
+ AUTO_BG_SENTINEL_RE = /⟦DSC_BG⟧\s*$/;
6135
6261
  RUN_COMMAND_MAX_CHARS = 2e4;
6136
6262
  commandTools = [
6137
6263
  {
@@ -6156,8 +6282,13 @@ var init_command = __esm({
6156
6282
  // 💡 我们将在 execute 内部真正落地这个长度限制
6157
6283
  verifyResult: (rawOutput) => {
6158
6284
  const matches2 = [...rawOutput.matchAll(EXIT_SENTINEL_RE)];
6159
- const code = matches2.length ? parseInt(matches2[matches2.length - 1][1], 10) : 0;
6285
+ const last = matches2[matches2.length - 1];
6286
+ const code = last ? parseInt(last[1], 10) : null;
6160
6287
  if (code === 0) return { status: "success" /* SUCCESS */ };
6288
+ if (code === null) {
6289
+ if (AUTO_BG_SENTINEL_RE.test(rawOutput.trimEnd())) return { status: "success" /* SUCCESS */ };
6290
+ return { status: "failed" /* FAILED */, summary: "\u8F93\u51FA\u4E2D\u7F3A\u5C11\u9000\u51FA\u7801\u54E8\u5175\u2014\u2014\u8FDB\u7A0B\u88AB\u5F3A\u5236\u7EC8\u6B62\u6216\u8F93\u51FA\u4E0D\u5B8C\u6574\uFF0C\u7ED3\u679C\u4E0D\u53EF\u4FE1\uFF0C\u6309\u5931\u8D25\u5904\u7406\u3002" };
6291
+ }
6161
6292
  const hint = code === 9009 ? "\uFF08Windows\uFF1A\u547D\u4EE4\u672A\u88AB\u8BC6\u522B\u2014\u2014\u547D\u4EE4\u4E0D\u5B58\u5728\u6216 PATH \u7F3A\u5931\uFF09" : code === 127 ? "\uFF08\u547D\u4EE4\u672A\u627E\u5230\u2014\u2014\u68C0\u67E5\u547D\u4EE4\u540D/PATH\uFF09" : code === 255 ? "\uFF08255 \u5E38\u89C1\u4E8E Unix \u547D\u4EE4\u5728\u975E POSIX shell \u4E0B\u7F3A\u5931\uFF0C\u6216\u8FDB\u7A0B\u663E\u5F0F exit(-1/255)\u3001\u8FDE\u63A5\u5931\u8D25\uFF09" : code === 4294967295 || code === -1 ? "\uFF08\u8FDB\u7A0B\u9000\u51FA\u7801 -1/\u672A\u6355\u83B7\u5F02\u5E38\u88AB\u6740\uFF09" : "";
6162
6293
  return { status: "failed" /* FAILED */, summary: `\u547D\u4EE4\u9000\u51FA\u7801\u975E\u96F6\uFF1A${code}${hint}` };
6163
6294
  },
@@ -6346,6 +6477,7 @@ ${tail}`,
6346
6477
  \u23F3 [\u81EA\u52A8\u8F6C\u540E\u53F0]\uFF1A\u547D\u4EE4 [${args.command}] ${why}\uFF0C\u5DF2\u81EA\u52A8\u8F6C\u4E3A\u540E\u53F0\u4EFB\u52A1\uFF08\u524D\u53F0\u6D41\u5F0F\u7ED3\u675F\uFF0C\u672A\u6D88\u8D39\u7684\u5269\u4F59\u8F93\u51FA\u5DF2\u5E76\u5165\u540E\u53F0\u7F13\u51B2\uFF09\u3002
6347
6478
  task_id: ${taskId}
6348
6479
  \u26A0\uFE0F \u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF0C\u9000\u51FA\u7801\u672A\u77E5\uFF08\u4E0D\u5224\u5B9A\u6210\u529F/\u5931\u8D25\uFF09\u2014\u2014\u7528 get_background_output(task_id="${taskId}", wait_seconds=\u2026) \u7B49\u5F85/\u67E5\u770B\u7ED3\u679C\uFF1B\u82E5\u786E\u8BA4\u65E0\u9700\u7EE7\u7EED\uFF0C\u7528 stop_background_task(task_id="${taskId}") \u7EC8\u6B62\u3002
6480
+ ${AUTO_BG_NOTICE}
6349
6481
  `;
6350
6482
  } else if (overLimit) {
6351
6483
  yield EXIT_SENTINEL(-1);
@@ -7267,9 +7399,9 @@ var init_http = __esm({
7267
7399
  function: {
7268
7400
  name: "http_request",
7269
7401
  description: [
7270
- "\u5168\u65B9\u6CD5 HTTP \u5BA2\u6237\u7AEF\uFF0C\u7528\u4E8E\u672C\u5730\u524D\u540E\u7AEF\u8054\u8C03\u3001API \u6D4B\u8BD5\u3001\u8C03\u7528 REST \u63A5\u53E3\u3002\u652F\u6301 GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS\uFF0C\u53EF\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\u4E0E\u8BF7\u6C42\u4F53\uFF0C\u8FD4\u56DE\u539F\u59CB\u54CD\u5E94\uFF08HTTP \u72B6\u6001\u7801 + \u54CD\u5E94\u5934 + body \u539F\u6587\uFF0C\u4E0D\u8F6C Markdown\uFF09\u3002",
7271
- "\u4E0E web_fetch \u7684\u533A\u522B\uFF1Aweb_fetch \u662F GET \u6293\u7F51\u9875\u8F6C Markdown\uFF08\u67E5\u6587\u6863\u7528\u3001\u9ED8\u8BA4\u62E6\u5185\u7F51\uFF09\uFF1B\u672C\u5DE5\u5177\u662F\u8C03\u63A5\u53E3\u53D6\u539F\u59CB\u54CD\u5E94\uFF08\u8054\u8C03\u7528\u3001\u9ED8\u8BA4\u653E\u884C localhost/\u5185\u7F51\uFF09\u3002",
7272
- "SSRF\uFF1A\u9ED8\u8BA4\u653E\u884C loopback \u4E0E\u5185\u7F51\u6BB5\uFF08\u672C\u5730\u8054\u8C03\u5FC5\u9700\uFF0C\u542B docker/k8s \u5185\u7F51\uFF09\uFF0C\u4F46\u59CB\u7EC8\u62E6\u622A\u4E91\u5143\u6570\u636E\u7AEF\u70B9 169.254.169.254\uFF1B\u4EC5 http/https\u3002"
7402
+ "\u5168\u65B9\u6CD5 HTTP \u5BA2\u6237\u7AEF\uFF1A\u672C\u5730\u524D\u540E\u7AEF\u8054\u8C03\u3001API \u6D4B\u8BD5\u3001\u8C03 REST \u63A5\u53E3\u3002\u8FD4\u56DE\u539F\u59CB\u54CD\u5E94\uFF08\u72B6\u6001\u7801+\u54CD\u5E94\u5934+body \u539F\u6587\uFF0C\u4E0D\u8F6C Markdown\uFF09\u3002",
7403
+ "\u4E0E web_fetch \u533A\u522B\uFF1Aweb_fetch GET \u6293\u7F51\u9875\u8F6C Markdown\uFF08\u67E5\u6587\u6863\u3001\u9ED8\u8BA4\u62E6\u5185\u7F51\uFF09\uFF1B\u672C\u5DE5\u5177\u8C03\u63A5\u53E3\u53D6\u539F\u59CB\u54CD\u5E94\uFF08\u8054\u8C03\u3001\u9ED8\u8BA4\u653E\u884C localhost/\u5185\u7F51\uFF09\u3002",
7404
+ "SSRF\uFF1A\u59CB\u7EC8\u62E6\u4E91\u5143\u6570\u636E\u7AEF\u70B9 169.254.169.254\uFF1B\u4EC5 http/https\u3002"
7273
7405
  ].join(" "),
7274
7406
  parameters: {
7275
7407
  type: "object",
@@ -7365,7 +7497,7 @@ var init_http = __esm({
7365
7497
  // ../core/src/tool/registry/typescript.ts
7366
7498
  import fs16 from "fs/promises";
7367
7499
  import path20 from "path";
7368
- var MAX_FILE_BYTES, toDisplayPath, precheck, MAX_DIAG_ENTRIES, MAX_DIAG_FILES, diagnoseOneFile, typescriptTools;
7500
+ var MAX_FILE_BYTES, toDisplayPath, isLibFile, precheck, MAX_DIAG_ENTRIES, MAX_DIAG_FILES, MAX_REF_ENTRIES, REF_SNIPPET_MAX, diagnoseOneFile, typescriptTools;
7369
7501
  var init_typescript = __esm({
7370
7502
  "../core/src/tool/registry/typescript.ts"() {
7371
7503
  "use strict";
@@ -7379,6 +7511,7 @@ var init_typescript = __esm({
7379
7511
  if (rel && !rel.startsWith("..") && !path20.isAbsolute(rel)) return rel.replace(/\\/g, "/");
7380
7512
  return abs.replace(/\\/g, "/").split("/").slice(-3).join("/");
7381
7513
  };
7514
+ isLibFile = (abs) => /(?:^|\/)node_modules\//.test(abs.replace(/\\/g, "/")) || /\.d\.ts$/i.test(abs);
7382
7515
  precheck = async (displayPath, absPath) => {
7383
7516
  const readBlock = await assertReadable(absPath, displayPath);
7384
7517
  if (readBlock) return readBlock;
@@ -7392,6 +7525,8 @@ var init_typescript = __esm({
7392
7525
  };
7393
7526
  MAX_DIAG_ENTRIES = 300;
7394
7527
  MAX_DIAG_FILES = 20;
7528
+ MAX_REF_ENTRIES = 200;
7529
+ REF_SNIPPET_MAX = 160;
7395
7530
  diagnoseOneFile = async (TS, displayPath, checkJs) => {
7396
7531
  try {
7397
7532
  const absPath = realpathNative(resolveReadablePath(displayPath));
@@ -7446,7 +7581,7 @@ ${body}${tail}` };
7446
7581
  type: "function",
7447
7582
  function: {
7448
7583
  name: "get_diagnostics",
7449
- description: "\u5BF9\u4E00\u4E2A\u6216\u591A\u4E2A TS/JS \u6587\u4EF6\u53D6 TypeScript \u8BED\u6CD5 + \u8BED\u4E49\u8BCA\u65AD\uFF08\u7C7B\u578B\u9519\u8BEF\u3001\u672A\u7528\u53D8\u91CF\u3001\u4E0D\u53EF\u8FBE\u4EE3\u7801\u7B49\uFF09\uFF0C\u8F93\u51FA\u5E26\u884C\u53F7/\u5217/\u4E25\u91CD\u5EA6/TS \u9519\u8BEF\u7801\uFF0C\u683C\u5F0F\u5BF9\u9F50 tsc\u3002\u7528\u4E8E\u7CBE\u51C6\u5B9A\u4F4D\u7F16\u8BD1/\u7C7B\u578B\u95EE\u9898\uFF0C\u66FF\u4EE3\u624B\u52A8\u8DD1 tsc \u518D\u89E3\u6790\u8F93\u51FA\u3002\u2605 \u6539\u5B8C\u591A\u4E2A\u6587\u4EF6\u540E\u9A8C\u8BC1\uFF1A\u4F20 paths \u6570\u7EC4\u4E00\u6B21\u8BCA\u65AD\u5168\u90E8\uFF08\u6700\u591A 20 \u4E2A\uFF09\uFF0C\u52FF\u9010\u6587\u4EF6\u591A\u6B21\u8C03\u7528\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0D\u652F\u6301\uFF08\u9700 vue-tsc\uFF09\uFF0C\u5176\u4F59\u6269\u5C55\u540D\uFF08.java/.py/.go \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u9879\u76EE\u7EA7\u5168\u91CF\u8BCA\u65AD\u8BF7\u7528 run_command \u8DD1 `tsc --noEmit`\u3002\u4F9D\u8D56 typescript \u6A21\u5757\uFF08VSCode \u6269\u5C55\u5185\u53EF\u7528\uFF1B\u7F3A\u5931\u5219\u672C\u5DE5\u5177\u81EA\u52A8\u9690\u85CF\uFF09\u3002",
7584
+ description: "\u5BF9\u4E00\u4E2A\u6216\u591A\u4E2A TS/JS \u6587\u4EF6\u53D6\u7C7B\u578B+\u8BED\u4E49\u8BCA\u65AD\uFF08\u7C7B\u578B\u9519\u8BEF/\u672A\u7528\u53D8\u91CF/\u4E0D\u53EF\u8FBE\u4EE3\u7801\uFF09\uFF0C\u8F93\u51FA\u884C:\u5217/\u4E25\u91CD\u5EA6/TS \u9519\u8BEF\u7801\uFF0C\u683C\u5F0F\u5BF9\u9F50 tsc\u3002\u2605 \u6539\u5B8C\u591A\u4E2A\u6587\u4EF6\u540E\u9A8C\u8BC1\uFF1A\u4F20 paths \u6570\u7EC4\u4E00\u6B21\u8BCA\u65AD\u5168\u90E8\uFF08\u6700\u591A 20 \u4E2A\uFF09\uFF0C\u52FF\u9010\u6587\u4EF6\u591A\u6B21\u8C03\u7528\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\uFF08.java/.py \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u9879\u76EE\u7EA7\u5168\u91CF\u8BCA\u65AD\u7528 run_command \u8DD1 `tsc --noEmit`\u3002",
7450
7585
  parameters: {
7451
7586
  type: "object",
7452
7587
  properties: {
@@ -7501,7 +7636,7 @@ ${sections.join("\n\n")}`;
7501
7636
  type: "function",
7502
7637
  function: {
7503
7638
  name: "goto_definition",
7504
- description: "\u8DF3\u8F6C\u5230\u6307\u5B9A TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5B9A\u4E49\u4F4D\u7F6E\uFF0C\u8FD4\u56DE rel/path:line:col\uFF08\u53EF\u591A\u5904\uFF09\u3002\u7528\u4E8E\u8DE8\u6587\u4EF6\u8FFD\u8E2A\u51FD\u6570/\u7C7B\u578B/\u53D8\u91CF\u7684\u6765\u6E90\uFF0C\u66FF\u4EE3 grep \u731C\u6D4B\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0D\u652F\u6301\uFF0C\u5176\u4F59\u6269\u5C55\u540D\uFF08.java/.py/.go \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u843D\u70B9\u5728 node_modules \u6216 .d.ts \u65F6\u6807\u6CE8 (declaration/library)\u3002\u884C/\u5217\u4E3A 1-based\uFF08\u4E0E\u7F16\u8F91\u5668\u4E00\u81F4\uFF09\u3002\u4F9D\u8D56 typescript \u6A21\u5757\uFF08VSCode \u6269\u5C55\u5185\u53EF\u7528\uFF1B\u7F3A\u5931\u5219\u672C\u5DE5\u5177\u81EA\u52A8\u9690\u85CF\uFF09\u3002",
7639
+ description: "\u8DF3\u8F6C\u5230 TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5B9A\u4E49\u4F4D\u7F6E\uFF0C\u8FD4\u56DE rel/path:line:col\uFF08\u53EF\u591A\u5904\uFF09\u3002\u8DE8\u6587\u4EF6\u8FFD\u8E2A\u51FD\u6570/\u7C7B\u578B/\u53D8\u91CF\u7684\u6765\u6E90\uFF0C\u66FF\u4EE3 grep \u731C\u6D4B\u3002\u843D\u70B9\u5728 node_modules/.d.ts \u65F6\u6807\u6CE8 (declaration/library)\u3002\u884C\u5217 1-based\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002",
7505
7640
  parameters: {
7506
7641
  type: "object",
7507
7642
  properties: {
@@ -7541,7 +7676,7 @@ ${sections.join("\n\n")}`;
7541
7676
  const lc = posToLineCol(TS, defSf, def.textSpan.start);
7542
7677
  loc = `${toDisplayPath(wsRoot, def.fileName)}:${lc.line}:${lc.column}`;
7543
7678
  }
7544
- const isLib = /(?:^|\/)node_modules\//.test(def.fileName.replace(/\\/g, "/")) || /\.d\.ts$/i.test(def.fileName);
7679
+ const isLib = isLibFile(def.fileName);
7545
7680
  const name = def.name || "(anonymous)";
7546
7681
  const kind = def.kind ? ` [${def.kind}]` : "";
7547
7682
  return `${name}${kind} \u2192 ${loc}${isLib ? " (declaration/library)" : ""}`;
@@ -7553,6 +7688,97 @@ ${out.join("\n")}`;
7553
7688
  }
7554
7689
  }
7555
7690
  }
7691
+ },
7692
+ {
7693
+ type: "function",
7694
+ function: {
7695
+ name: "find_references",
7696
+ description: "\u67E5\u627E TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5168\u9879\u76EE\u5F15\u7528\uFF08\u8C03\u7528/\u5BFC\u5165/\u8BFB\u5199\uFF09\uFF0C\u8F93\u51FA rel/path:line:col + \u8BFB\u5199\u6807\u6CE8 + \u884C\u6458\u5F55\u3002\u7C7B\u578B\u611F\u77E5\uFF1A\u53EA\u8FD4\u56DE\u771F\u5B9E\u7ED1\u5B9A\uFF0C\u4E0D\u542B\u6CE8\u91CA/\u5B57\u7B26\u4E32\u91CC\u7684\u540C\u540D\u8BCD\uFF08grep \u6838\u5FC3\u8BEF\u62A5\u6E90\uFF09\u3002\u7528\u4E8E\u6539\u7B7E\u540D/\u91CD\u547D\u540D\u524D\u7684\u5F71\u54CD\u9762\u6392\u67E5\u3001\u8FFD\u8E2A\u8C03\u7528\u65B9\uFF1B\u4E0E goto_definition \u4E92\u4E3A\u53CD\u5411\u3002\u8986\u76D6\u4EE5 program \u5185\u6587\u4EF6\u4E3A\u754C\uFF08tsconfig include + import \u94FE\uFF09\uFF1B\u5355\u7B26\u53F7\u8D85 200 \u5904\u5F15\u7528\u622A\u65AD\u3002\u884C\u5217 1-based\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002",
7697
+ parameters: {
7698
+ type: "object",
7699
+ properties: {
7700
+ path: { type: "string", description: "\u76EE\u6807\u6587\u4EF6\u76F8\u5BF9\u8DEF\u5F84\uFF08\u5982 'src/index.ts'\uFF09" },
7701
+ line: { type: "integer", description: "\u7B26\u53F7\u6240\u5728\u884C\u53F7\uFF081-based\uFF09" },
7702
+ column: { type: "integer", description: "\u7B26\u53F7\u6240\u5728\u5217\u53F7\uFF081-based\uFF09" }
7703
+ },
7704
+ required: ["path", "line", "column"]
7705
+ },
7706
+ safetyLevel: "safe" /* SAFE */,
7707
+ isSync: true,
7708
+ maxOutputCharacters: 48e3,
7709
+ // 200 条引用 × ~160 字符摘录的理论上界之内;中心 truncateToolResult 仍是最终兜底
7710
+ privacyMaskingRules: maskSecretsInContent,
7711
+ validateEnvironment: async () => !!await getTs(),
7712
+ async execute(args, _ctx) {
7713
+ const { path: displayPath, line, column } = args;
7714
+ try {
7715
+ const TS = await getTs();
7716
+ if (!TS) return `\u26A0\uFE0F [\u67E5\u627E\u5F15\u7528\u4E0D\u53EF\u7528]\uFF1Atypescript \u6A21\u5757\u672A\u52A0\u8F7D\uFF08VSCode \u6269\u5C55\u5185\u53EF\u7528\uFF1BCLI \u73AF\u5883\u672A\u5FC5\u5B89\u88C5 typescript\uFF09\u3002`;
7717
+ const absPath = realpathNative(resolveReadablePath(displayPath));
7718
+ const block = await precheck(displayPath, absPath);
7719
+ if (block) return block;
7720
+ const { ls } = await getLanguageService(TS, absPath);
7721
+ const program = ls.getProgram();
7722
+ if (!program) return `\u274C \u65E0\u6CD5\u53D6\u5F97 TS program\uFF08\u5185\u90E8\u9519\u8BEF\uFF09\u3002`;
7723
+ const sf = program.getSourceFile(absPath);
7724
+ if (!sf) return `\u274C \u6587\u4EF6\u672A\u7EB3\u5165 program\uFF1A${displayPath}\uFF08\u53EF\u80FD\u65E0\u5BF9\u5E94 tsconfig \u6216\u89E3\u6790\u5931\u8D25\uFF09\u3002`;
7725
+ const pos = lineColToPos(TS, sf, line, column);
7726
+ const groups = ls.findReferences(absPath, pos);
7727
+ if (!groups || groups.length === 0) {
7728
+ return `No references found at ${displayPath}:${line}:${column}\uFF08\u786E\u8BA4\u5B9A\u4F4D\u5728\u6807\u8BC6\u7B26\u4E0A\uFF0C\u975E\u7A7A\u767D/\u5B57\u9762\u91CF/\u5173\u952E\u5B57\uFF09\u3002`;
7729
+ }
7730
+ const wsRoot = getContainingRoot(absPath);
7731
+ const lineTextCache = /* @__PURE__ */ new Map();
7732
+ const lineSnippet = async (abs, line1) => {
7733
+ let lines = lineTextCache.get(abs);
7734
+ if (!lines) {
7735
+ try {
7736
+ lines = (await fs16.readFile(abs, "utf-8")).split(/\r?\n/);
7737
+ } catch {
7738
+ lines = [];
7739
+ }
7740
+ lineTextCache.set(abs, lines);
7741
+ }
7742
+ const text = (lines[line1 - 1] ?? "").trim();
7743
+ if (!text) return "";
7744
+ return " " + (text.length > REF_SNIPPET_MAX ? text.slice(0, REF_SNIPPET_MAX) + "\u2026" : text);
7745
+ };
7746
+ const sections = [];
7747
+ let shown = 0, overflow = 0;
7748
+ for (const g of groups) {
7749
+ const defSf = program.getSourceFile(g.definition.fileName);
7750
+ let defLoc = toDisplayPath(wsRoot, g.definition.fileName);
7751
+ if (defSf && g.definition.textSpan) {
7752
+ const lc = posToLineCol(TS, defSf, g.definition.textSpan.start);
7753
+ defLoc = `${defLoc}:${lc.line}:${lc.column}`;
7754
+ }
7755
+ const defLib = isLibFile(g.definition.fileName) ? " (declaration/library)" : "";
7756
+ const defName = (g.definition.name || "(anonymous)").replace(/\s+/g, " ");
7757
+ const header = `${defName}${g.definition.kind ? ` [${g.definition.kind}]` : ""} \u2014 ${g.references.length} \u5904\u5F15\u7528\uFF08\u58F0\u660E ${defLoc}\uFF09${defLib}`;
7758
+ const bodyLines = [];
7759
+ for (const r of g.references) {
7760
+ if (shown >= MAX_REF_ENTRIES) {
7761
+ overflow++;
7762
+ continue;
7763
+ }
7764
+ shown++;
7765
+ const refSf = program.getSourceFile(r.fileName);
7766
+ const lc = refSf ? posToLineCol(TS, refSf, r.textSpan.start) : { line: 0, column: 0 };
7767
+ const tag = r.isDefinition ? "[def]" : r.isWriteAccess ? "(write)" : "(read)";
7768
+ const snippet = r.isDefinition ? "" : await lineSnippet(r.fileName, lc.line);
7769
+ bodyLines.push(` ${toDisplayPath(wsRoot, r.fileName)}:${lc.line}:${lc.column} ${tag}${snippet}`);
7770
+ }
7771
+ sections.push([header, ...bodyLines].join("\n"));
7772
+ }
7773
+ const tail = overflow > 0 ? `
7774
+ \u2026(\u53E6\u6709 ${overflow} \u5904\u5F15\u7528\u672A\u663E\u793A\uFF1B\u7B26\u53F7\u8FC7\u70ED\u53EF\u6539\u7528 grep \u6309\u8C03\u7528\u540D\u626B\u63CF\uFF0C\u6216\u5206\u6587\u4EF6\u7F29\u5C0F\u8303\u56F4)\u2026` : "";
7775
+ return `[References: ${displayPath}:${line}:${column}]
7776
+ ${sections.join("\n")}${tail}`;
7777
+ } catch (e) {
7778
+ return `\u274C \u67E5\u627E\u5F15\u7528\u5931\u8D25 [${displayPath}:${line}:${column}]: ${e?.message ?? e}`;
7779
+ }
7780
+ }
7781
+ }
7556
7782
  }
7557
7783
  ];
7558
7784
  }
@@ -8528,10 +8754,9 @@ var init_ask = __esm({
8528
8754
  function: {
8529
8755
  name: "ask_question",
8530
8756
  description: [
8531
- "\u5411\u7528\u6237\u63D0\u51FA\u4E00\u4E2A\u591A\u9009\u95EE\u9898\u5E76\u7B49\u5F85\u4F5C\u7B54\u3002\u7528\u4E8E\u9700\u8981\u7528\u6237\u62CD\u677F\u7684\u51B3\u7B56\uFF08\u5982\u6280\u672F\u65B9\u6848\u4E8C\u9009\u4E00\u3001\u5B9E\u73B0\u8DEF\u5F84\u53D6\u820D\u3001\u8303\u56F4\u786E\u8BA4\uFF09\u6216\u6F84\u6E05\u6B67\u4E49\u2014\u2014\u5207\u52FF\u7528\u4E8E\u80FD\u4ECE\u4EE3\u7801/\u4E0A\u4E0B\u6587\u81EA\u884C\u786E\u5B9A\u7684\u95EE\u9898\u3002",
8532
- "\u63D0\u4F9B 2-4 \u4E2A\u9009\u9879\uFF0C\u6BCF\u4E2A\u542B\u7B80\u77ED label \u4E0E\u53EF\u9009 description\u3002multiSelect=true \u5141\u8BB8\u591A\u9009\uFF08\u9ED8\u8BA4\u5355\u9009\uFF09\u3002",
8533
- "\u7528\u6237\u7684\u9009\u62E9\u4F1A\u4F5C\u4E3A\u7ED3\u679C\u8FD4\u56DE\uFF1B\u82E5\u7528\u6237\u53D6\u6D88\uFF0C\u8FD4\u56DE\u7A7A\u9009\u62E9\uFF08\u6B64\u65F6\u5E94\u6539\u7528\u9ED8\u8BA4\u6216\u91CD\u65B0\u63D0\u95EE\uFF09\u3002",
8534
- "\u6CE8\u610F\uFF1A\u4EC5\u5728\u4EA4\u4E92\u5F0F\u73AF\u5883\u53EF\u7528\uFF1B\u82E5\u7ED3\u679C\u63D0\u793A\u300C\u4E0D\u652F\u6301\u300D\uFF0C\u6539\u7528\u7EAF\u6587\u672C\u5411\u7528\u6237\u63D0\u95EE\u3002"
8757
+ "\u5411\u7528\u6237\u63D0\u51FA\u591A\u9009\u95EE\u9898\u5E76\u7B49\u5F85\u4F5C\u7B54\uFF1A\u6280\u672F\u65B9\u6848\u4E8C\u9009\u4E00\u3001\u8DEF\u5F84\u53D6\u820D\u3001\u8303\u56F4\u786E\u8BA4\u7B49\u9700\u7528\u6237\u62CD\u677F\u7684\u51B3\u7B56\u6216\u6B67\u4E49\u6F84\u6E05\u2014\u2014\u52FF\u7528\u4E8E\u80FD\u4ECE\u4EE3\u7801/\u4E0A\u4E0B\u6587\u81EA\u884C\u786E\u5B9A\u7684\u95EE\u9898\u3002",
8758
+ "2-4 \u4E2A\u9009\u9879\uFF08label + \u53EF\u9009 description\uFF09\uFF1BmultiSelect=true \u591A\u9009\uFF08\u9ED8\u8BA4\u5355\u9009\uFF09\u3002\u7528\u6237\u53D6\u6D88\u8FD4\u56DE\u7A7A\u9009\u62E9\uFF08\u6539\u7528\u9ED8\u8BA4\u6216\u91CD\u65B0\u63D0\u95EE\uFF09\u3002",
8759
+ "\u4EC5\u4EA4\u4E92\u5F0F\u73AF\u5883\u53EF\u7528\uFF1B\u7ED3\u679C\u63D0\u793A\u300C\u4E0D\u652F\u6301\u300D\u65F6\u6539\u7528\u7EAF\u6587\u672C\u63D0\u95EE\u3002"
8535
8760
  ].join("\n"),
8536
8761
  parameters: {
8537
8762
  type: "object",
@@ -8974,30 +9199,30 @@ ${body.trim()}
8974
9199
  type: "function",
8975
9200
  function: {
8976
9201
  name: "memory_save",
8977
- description: "\u4FDD\u5B58\u4E00\u6761\u6301\u4E45\u8BB0\u5FC6\uFF08\u8DE8\u4F1A\u8BDD\u4FDD\u7559\uFF09\u3002\u7528\u4E8E\u8BB0\u5F55\u975E\u663E\u7136\u7684\u4E8B\u5B9E\u3001\u7528\u6237\u504F\u597D\u3001\u5DE5\u4F5C\u65B9\u5F0F\u53CD\u9988\u3001\u9879\u76EE\u7EA6\u675F\u6216\u5916\u90E8\u8D44\u6E90\u6307\u9488\u2014\u2014\u5373\u90A3\u4E9B\u300C\u4E0D\u5E94\u6BCF\u6B21\u91CD\u65B0\u53D1\u73B0\u300D\u300C\u4EE3\u7801/git \u4E0D\u4F1A\u76F4\u63A5\u544A\u8BC9\u4F60\u300D\u7684\u4FE1\u606F\u3002\u52FF\u4FDD\u5B58\u4F1A\u8BDD\u5185\u5373\u629B\u7684\u4E34\u65F6\u4FE1\u606F\u3002\u4FDD\u5B58\u540E\u7CFB\u7EDF\u63D0\u793A\u8BCD\u7684\u8BB0\u5FC6\u7D22\u5F15\u4F1A\u81EA\u52A8\u66F4\u65B0\u3002\u540C\u540D\u8BB0\u5FC6\u4F1A\u88AB\u8986\u76D6\u3002",
9202
+ description: "\u4FDD\u5B58\u4E00\u6761\u6301\u4E45\u8BB0\u5FC6\uFF08\u8DE8\u4F1A\u8BDD\u4FDD\u7559\uFF09\uFF1A\u975E\u663E\u7136\u7684\u4E8B\u5B9E\u3001\u7528\u6237\u504F\u597D\u3001\u5DE5\u4F5C\u65B9\u5F0F\u53CD\u9988\u3001\u9879\u76EE\u7EA6\u675F\u3001\u5916\u90E8\u8D44\u6E90\u6307\u9488\u2014\u2014\u300C\u4E0D\u5E94\u6BCF\u6B21\u91CD\u65B0\u53D1\u73B0\u300D\u300C\u4EE3\u7801/git \u4E0D\u4F1A\u76F4\u63A5\u544A\u8BC9\u4F60\u300D\u7684\u4FE1\u606F\u3002\u52FF\u5B58\u4F1A\u8BDD\u5185\u5373\u629B\u7684\u4E34\u65F6\u4FE1\u606F\u3002\u4FDD\u5B58\u540E\u8BB0\u5FC6\u7D22\u5F15\u81EA\u52A8\u66F4\u65B0\uFF1B\u540C\u540D\u8BB0\u5FC6\u88AB\u8986\u76D6\u3002",
8978
9203
  parameters: {
8979
9204
  type: "object",
8980
9205
  properties: {
8981
9206
  name: {
8982
9207
  type: "string",
8983
- description: "\u8BB0\u5FC6\u552F\u4E00\u952E\uFF0C\u4EC5\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26\uFF08\u5982 user-prefers-arrow-fns\uFF09\u3002\u5EFA\u8BAE\u5177\u63CF\u8FF0\u6027\u7684 kebab-case\u3002"
9208
+ description: "\u552F\u4E00\u952E\uFF0C\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26\uFF08\u63CF\u8FF0\u6027 kebab-case\uFF0C\u5982 user-prefers-arrow-fns\uFF09"
8984
9209
  },
8985
9210
  description: {
8986
9211
  type: "string",
8987
- description: "\u4E00\u53E5\u8BDD\u6982\u62EC\u672C\u6761\u8BB0\u5FC6\u5185\u5BB9\uFF08\u6CE8\u5165\u8BB0\u5FC6\u7D22\u5F15\u65F6\u5C55\u793A\uFF0C\u4F9B\u65E5\u540E\u5224\u65AD\u662F\u5426\u9700\u8981\u53EC\u56DE\uFF09\u3002\u52FF\u6362\u884C\u3002"
9212
+ description: "\u4E00\u53E5\u8BDD\u6982\u62EC\uFF08\u6CE8\u5165\u7D22\u5F15\u4F9B\u65E5\u540E\u5224\u65AD\u662F\u5426\u53EC\u56DE\uFF09\u3002\u52FF\u6362\u884C"
8988
9213
  },
8989
9214
  body: {
8990
9215
  type: "string",
8991
- description: "\u8BB0\u5FC6\u6B63\u6587\uFF08\u4E8B\u5B9E\u672C\u8EAB\uFF09\u3002\u53EF\u591A\u884C\u3002\u4E0A\u9650 64KB\u3002"
9216
+ description: "\u8BB0\u5FC6\u6B63\u6587\uFF0C\u53EF\u591A\u884C\uFF0C\u4E0A\u9650 64KB"
8992
9217
  },
8993
9218
  type: {
8994
9219
  type: "string",
8995
9220
  enum: ["user", "feedback", "project", "reference"],
8996
- description: "user=\u7528\u6237\u753B\u50CF/\u504F\u597D\uFF1Bfeedback=\u5DE5\u4F5C\u65B9\u5F0F\u53CD\u9988\uFF08\u542B\u539F\u56E0\u4E0E\u505A\u6CD5\uFF09\uFF1Bproject=\u9879\u76EE\u76EE\u6807/\u7EA6\u675F\uFF1Breference=\u5916\u90E8\u8D44\u6E90\u6307\u9488\uFF08URL/dashboard/ticket\uFF09\u3002\u9ED8\u8BA4 reference\u3002"
9221
+ description: "user=\u7528\u6237\u753B\u50CF/\u504F\u597D\uFF1Bfeedback=\u5DE5\u4F5C\u65B9\u5F0F\u53CD\u9988\uFF08\u542B\u539F\u56E0\u4E0E\u505A\u6CD5\uFF09\uFF1Bproject=\u9879\u76EE\u76EE\u6807/\u7EA6\u675F\uFF1Breference=\u5916\u90E8\u8D44\u6E90\u6307\u9488\u3002\u9ED8\u8BA4 reference"
8997
9222
  },
8998
9223
  project: {
8999
9224
  type: "boolean",
9000
- description: "\u662F\u5426\u5199\u5165\u9879\u76EE\u8BB0\u5FC6\u76EE\u5F55\uFF08<\u9879\u76EE>/.deepseeker-code/memory\uFF0C\u53EF\u63D0\u4EA4 git \u56E2\u961F\u5171\u4EAB\uFF09\u3002\u9ED8\u8BA4 false\uFF08\u5199\u5168\u5C40 ~/.deepseeker-code/memory\uFF0C\u4EC5\u672C\u673A\uFF09\u3002"
9225
+ description: "true=\u5199\u5165\u9879\u76EE .deepseeker-code/memory\uFF08\u53EF\u63D0\u4EA4 git \u5171\u4EAB\uFF09\uFF1B\u9ED8\u8BA4 false\uFF08\u5168\u5C40\uFF0C\u4EC5\u672C\u673A\uFF09"
9001
9226
  }
9002
9227
  },
9003
9228
  required: ["name", "description", "body"]
@@ -9150,7 +9375,7 @@ var init_recall = __esm({
9150
9375
  type: "function",
9151
9376
  function: {
9152
9377
  name: "recall",
9153
- description: "\u68C0\u7D22\u3010\u672C\u4F1A\u8BDD\u3011\u5168\u91CF\u5386\u53F2\u8F6C\u5F55\uFF08transcript\uFF09\uFF0C\u5305\u62EC\u5DF2\u88AB\u4E0A\u4E0B\u6587\u538B\u7F29\u5F52\u6863\u7684\u539F\u59CB\u6D88\u606F\u4E0E\u5DE5\u5177\u7ED3\u679C\u3002\u4F7F\u7528\u65F6\u673A\uFF1A\u6EDA\u52A8\u6458\u8981\uFF08\u27E6DSC:ARCHIVE-INDEX\u27E7/\u27E6DSC:ARCHIVE-NOTES\u27E7\uFF09\u4E0D\u8DB3\u4EE5\u56DE\u7B54\u7EC6\u8282\u65F6\u2014\u2014\u5982\u5F52\u6863\u524D\u7684\u6587\u4EF6\u5185\u5BB9\u3001\u62A5\u9519\u539F\u6587\u3001\u7528\u6237\u65E9\u524D\u7684\u8981\u6C42\u2014\u2014\u5148\u7528\u672C\u5DE5\u5177\u68C0\u7D22\uFF0C\u4E0D\u8981\u51ED\u6458\u8981\u81C6\u6D4B\uFF0C\u4E5F\u4E0D\u8981\u91CD\u8DD1\u5DE5\u5177\u3002\u2605 \u9996\u67E5\u7B56\u7565\uFF1Aquery \u5FC5\u987B\u7528\u5177\u4F53\u5B9E\u4F53\u8BCD\u2014\u2014\u62A5\u9519\u5173\u952E\u8BCD\uFF08\u5982 TypeError\uFF09\u3001\u6587\u4EF6\u8DEF\u5F84\u3001\u51FD\u6570/\u7B26\u53F7\u540D\uFF08\u6458\u8981\u5B9E\u4F53\u7D22\u5F15\u91CC\u51FA\u73B0\u8FC7\u4EC0\u4E48\u5C31\u7528\u4EC0\u4E48\uFF09\uFF1B\u4E0D\u8981\u7528\u5DE5\u5177\u540D\uFF08read_file \u7B49\uFF09\u6216\u6CDB\u8BCD\uFF08\u4EE3\u7801/\u6587\u4EF6/\u95EE\u9898\uFF09\u4F5C query\uFF0C\u4F1A\u547D\u4E2D\u5927\u91CF\u65E0\u5173\u5DE5\u5177\u884C\u3002\u4E00\u6B21\u6CA1\u4E2D\u5C31\u6362\u66F4\u5177\u4F53\u7684\u8BCD\u6216\u7528 is_regex \u4EE5 | \u5408\u5E76\u591A\u4E2A\u5019\u9009\u8BCD\uFF0C\u901A\u5E38 1-2 \u6B21\u5185\u547D\u4E2D\u3002\u8FD4\u56DE\u6309\u65F6\u95F4\u65E7\u2192\u65B0\uFF08\u4F18\u5148\u5F52\u6863\u533A\uFF09\uFF1B\u6587\u4EF6\u7C7B\u5DE5\u5177\u7ED3\u679C\u9644\u5B9E\u65F6\u6821\u9A8C\uFF1A\u26A0\uFE0F \u5DF2\u4FEE\u6539/\u5DF2\u4E0D\u5B58\u5728 \u7684\u5FC5\u987B\u91CD\u65B0 read_file \u9A8C\u8BC1\u540E\u624D\u80FD\u636E\u4EE5\u4FEE\u6539\uFF0C\u2713 \u672A\u53D8 \u65B9\u53EF\u76F4\u63A5\u5F15\u7528\u3002\u7ED3\u679C\u66FE\u88AB\u622A\u65AD\u7684\u4F1A\u6807\u6CE8 with_full \u53EF\u53D6\u5B58\u6863\u5168\u6587\u3002\u6458\u8981\u5DF2\u591F\u7528\u65F6\u52FF\u8C03\u7528\uFF08\u7701 token\uFF09\u3002",
9378
+ description: "\u68C0\u7D22\u3010\u672C\u4F1A\u8BDD\u3011\u5168\u91CF\u5386\u53F2\uFF08\u542B\u5DF2\u88AB\u538B\u7F29\u5F52\u6863\u7684\u539F\u59CB\u6D88\u606F\u4E0E\u5DE5\u5177\u7ED3\u679C\uFF09\u3002\u65F6\u673A\uFF1A\u6EDA\u52A8\u6458\u8981\u4E0D\u8DB3\u4EE5\u56DE\u7B54\u7EC6\u8282\u65F6\uFF08\u5F52\u6863\u524D\u7684\u6587\u4EF6\u5185\u5BB9/\u62A5\u9519\u539F\u6587/\u7528\u6237\u65E9\u524D\u8981\u6C42\uFF09\u2014\u2014\u5148\u68C0\u7D22\uFF0C\u4E0D\u8981\u51ED\u6458\u8981\u81C6\u6D4B\uFF0C\u4E5F\u4E0D\u8981\u91CD\u8DD1\u5DE5\u5177\u3002query \u7528\u5177\u4F53\u5B9E\u4F53\u8BCD\uFF1A\u62A5\u9519\u5173\u952E\u8BCD\u3001\u6587\u4EF6\u8DEF\u5F84\u3001\u51FD\u6570/\u7B26\u53F7\u540D\uFF08\u4F18\u5148\u53D6\u6458\u8981\u5B9E\u4F53\u7D22\u5F15 \u27E6DSC:ARCHIVE-INDEX\u27E7 \u91CC\u51FA\u73B0\u8FC7\u7684\uFF09\uFF1B\u5FCC\u7528\u5DE5\u5177\u540D\u6216\u6CDB\u8BCD\uFF08\u4EE3\u7801/\u6587\u4EF6/\u95EE\u9898\uFF09\u3002\u672A\u547D\u4E2D\u5C31\u6362\u66F4\u5177\u4F53\u7684\u8BCD\uFF0C\u6216 is_regex=true \u7528 | \u5408\u5E76\u591A\u5019\u9009\u8BCD\uFF0C\u901A\u5E38 1-2 \u6B21\u5185\u547D\u4E2D\u3002\u8FD4\u56DE\u6309\u65F6\u95F4\u65E7\u2192\u65B0\uFF1B\u6587\u4EF6\u7C7B\u7ED3\u679C\u9644\u5B9E\u65F6\u6821\u9A8C\uFF1A\u26A0\uFE0F \u5DF2\u4FEE\u6539/\u5DF2\u4E0D\u5B58\u5728\u5FC5\u987B\u91CD\u65B0 read_file \u540E\u624D\u80FD\u636E\u4EE5\u4FEE\u6539\uFF0C\u2713 \u672A\u53D8\u53EF\u76F4\u63A5\u5F15\u7528\u3002\u66FE\u88AB\u622A\u65AD\u7684\u7ED3\u679C\u4F1A\u6807\u6CE8 with_full \u53D6\u5B58\u6863\u5168\u6587\u3002\u6458\u8981\u5DF2\u591F\u65F6\u52FF\u8C03\u7528\uFF08\u7701 token\uFF09\u3002",
9154
9379
  parameters: {
9155
9380
  type: "object",
9156
9381
  properties: {
@@ -9630,7 +9855,8 @@ async function runBackgroundTool(gen, lockKey, toolName, signal) {
9630
9855
  if (lockKey && !acquireLock(lockKey)) {
9631
9856
  try {
9632
9857
  await gen.return(void 0);
9633
- } catch {
9858
+ } catch (e) {
9859
+ console.warn("\u26A0\uFE0F \u5173\u95ED\u672A\u6D88\u8D39\u7684\u540E\u53F0 generator \u5931\u8D25:", e instanceof Error ? e.message : e);
9634
9860
  }
9635
9861
  return `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u9501 [${lockKey}] \u5DF2\u88AB\u5360\u7528\uFF0C[${toolName}] \u672A\u542F\u52A8\u3002`;
9636
9862
  }
@@ -9681,7 +9907,7 @@ var init_backgroundTool = __esm({
9681
9907
 
9682
9908
  // ../core/src/tool/autoPermission.ts
9683
9909
  import path30 from "path";
9684
- var AUTO_SCOPE, AUTO_DEFAULT_EXTRA, AUTO_AGGRESSIVE_EXTRA, isMcpTool, BUILTIN_AUTO_DENY, matchBuiltinDeny, COMMAND_DENY, matchCommandDeny, hasShellMetachars, READONLY_HEADS, hasWriteModifier, SENSITIVE_FILE_ARG_RE, hasSensitiveFileArg, isReadOnlyCommand, isWithinWorkspace, runAutoCheck;
9910
+ var AUTO_SCOPE, AUTO_DEFAULT_EXTRA, AUTO_AGGRESSIVE_EXTRA, isMcpTool, BUILTIN_AUTO_DENY, matchBuiltinDeny, COMMAND_DENY, matchCommandDeny, hasShellMetachars, READONLY_HEADS, PKG_MGR, SCRIPT_RUNNER_HEADS, SCRIPT_RUNNER_READONLY, isScriptRunnerCommand, hasWriteModifier, SENSITIVE_FILE_ARG_RE, hasSensitiveFileArg, isReadOnlyCommand, isWithinWorkspace, runAutoCheck;
9685
9911
  var init_autoPermission = __esm({
9686
9912
  "../core/src/tool/autoPermission.ts"() {
9687
9913
  "use strict";
@@ -9788,12 +10014,25 @@ var init_autoPermission = __esm({
9788
10014
  // —— 版本号 ——
9789
10015
  /^(node|npm|pnpm|npx|git|tsc|python|python3)\s+(-v|-V|--version)\b/,
9790
10016
  // —— 验证类(跑项目代码但不改源码)——
9791
- /^npm test\b/,
9792
- /^npm run (test|lint|typecheck|type-check)\b/,
9793
- /^pnpm (test|lint|typecheck)\b/,
9794
- /^npx (vitest|jest|eslint|tsc)\b/,
10017
+ // ★ npm/pnpm/npx/yarn 系「跑脚本」命令已整类移出免审清单(isScriptRunnerCommand 单独治理,见下方):
10018
+ // package.json scripts 是仓库作者的任意代码、npx 未装包时还会从 registry 拉取执行——
10019
+ // hasShellMetachars 检查的是命令串本身,拦不到脚本内容(恶意 repo 投毒面)。
10020
+ // 改走「首次人工确认 + allow-always 按精确命令串记住」路径(toolExecution 的 scriptRunnerBlocked 门)。
9795
10021
  /^tsc --noEmit\b/
9796
10022
  ];
10023
+ PKG_MGR = String.raw`\s*([^\s"'\`]*[\\/])?(npx|npm|pnpm|yarn|corepack)(\.cmd|\.exe|\.ps1|\.bat)?\b`;
10024
+ SCRIPT_RUNNER_HEADS = [
10025
+ new RegExp(`^${PKG_MGR}`, "i")
10026
+ ];
10027
+ SCRIPT_RUNNER_READONLY = [
10028
+ new RegExp(`^${PKG_MGR}\\s+(-v|-V|--version)\\b`, "i")
10029
+ ];
10030
+ isScriptRunnerCommand = (cmd) => {
10031
+ const c = (cmd || "").trim();
10032
+ if (!c || hasShellMetachars(c)) return false;
10033
+ if (SCRIPT_RUNNER_READONLY.some((re) => re.test(c))) return false;
10034
+ return SCRIPT_RUNNER_HEADS.some((re) => re.test(c));
10035
+ };
9797
10036
  hasWriteModifier = (cmd) => {
9798
10037
  if (!cmd) return false;
9799
10038
  if (/^sed\b/.test(cmd) && /(^|\s)-i\b/.test(cmd)) return true;
@@ -9950,7 +10189,7 @@ var init_toolExecution = __esm({
9950
10189
  preHooksAdvanced = true;
9951
10190
  let veto;
9952
10191
  try {
9953
- veto = await runPreHooks(calledName, calledArgs, toolCtx);
10192
+ veto = await runPreHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx);
9954
10193
  } catch (e) {
9955
10194
  veto = { deny: true, reason: `\u274C [Pre-hook \u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002` };
9956
10195
  }
@@ -9982,7 +10221,20 @@ var init_toolExecution = __esm({
9982
10221
  } catch {
9983
10222
  perm = null;
9984
10223
  }
9985
- if (perm === null && needApproval && !denied && calledName === "run_command" && isReadOnlyCommand(typeof calledArgs?.command === "string" ? calledArgs.command : "")) {
10224
+ const cmdIsScriptRunner = (calledName === "run_command" || calledName === "run_in_background") && isScriptRunnerCommand(typeof calledArgs?.command === "string" ? calledArgs.command : "");
10225
+ const argCwd = typeof calledArgs?.cwd === "string" ? calledArgs.cwd.trim() : "";
10226
+ const samePath = (a, b) => {
10227
+ try {
10228
+ const ra = path31.resolve(a), rb = path31.resolve(b);
10229
+ return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
10230
+ } catch {
10231
+ return false;
10232
+ }
10233
+ };
10234
+ const scriptRunnerCwdDrift = cmdIsScriptRunner && argCwd !== "" && !samePath(argCwd, toolCtx.cwd ?? process.cwd());
10235
+ const scriptRunnerBlocked = cmdIsScriptRunner && (perm === null || scriptRunnerCwdDrift);
10236
+ if (scriptRunnerCwdDrift) needApproval = true;
10237
+ if (perm === null && needApproval && !denied && !scriptRunnerBlocked && calledName === "run_command" && isReadOnlyCommand(typeof calledArgs?.command === "string" ? calledArgs.command : "")) {
9986
10238
  needApproval = false;
9987
10239
  }
9988
10240
  if ((calledName === "run_command" || calledName === "run_in_background") && matchCommandDeny(typeof calledArgs?.command === "string" ? calledArgs.command : "")) {
@@ -9995,7 +10247,7 @@ var init_toolExecution = __esm({
9995
10247
  denied = true;
9996
10248
  result = `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u5DF2\u6709\u540E\u53F0\u4EFB\u52A1\u6301\u6709\u9501 [${lockKey}]\uFF0C[${calledName}] \u8C03\u7528\u88AB\u8DF3\u8FC7\u3002`;
9997
10249
  }
9998
- if (needApproval && !denied) {
10250
+ if (needApproval && !denied && !scriptRunnerBlocked) {
9999
10251
  const auto = await runAutoCheck(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx, permissionMode === "auto");
10000
10252
  if (auto === "allow") {
10001
10253
  needApproval = false;
@@ -10030,7 +10282,7 @@ var init_toolExecution = __esm({
10030
10282
  if (!denied && !preHooksAdvanced) {
10031
10283
  let veto;
10032
10284
  try {
10033
- veto = await runPreHooks(calledName, calledArgs, toolCtx);
10285
+ veto = await runPreHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx);
10034
10286
  } catch (e) {
10035
10287
  veto = { deny: true, reason: `\u274C [Pre-hook \u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002` };
10036
10288
  }
@@ -10072,7 +10324,7 @@ ${result}`;
10072
10324
  events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: result } });
10073
10325
  }
10074
10326
  try {
10075
- const post = await runPostHooks(calledName, calledArgs, result, toolCtx);
10327
+ const post = await runPostHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, result, toolCtx);
10076
10328
  postHookOverride = post?.resultOverride;
10077
10329
  } catch (e) {
10078
10330
  console.warn(`\u26A0\uFE0F post-hook [${calledName}] \u5F02\u5E38\uFF08\u5DF2\u5FFD\u7565\uFF09:`, e?.message ?? e);
@@ -10232,7 +10484,8 @@ var init_toolScheduling = __esm({
10232
10484
  });
10233
10485
 
10234
10486
  // ../core/src/agent/streamInference.ts
10235
- var streamInference;
10487
+ import { createHash as createHash2 } from "node:crypto";
10488
+ var sha1short, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
10236
10489
  var init_streamInference = __esm({
10237
10490
  "../core/src/agent/streamInference.ts"() {
10238
10491
  "use strict";
@@ -10241,6 +10494,32 @@ var init_streamInference = __esm({
10241
10494
  init_transcript();
10242
10495
  init_contextCore();
10243
10496
  init_contentParts();
10497
+ sha1short = (s) => createHash2("sha1").update(s).digest("hex").slice(0, 10);
10498
+ RETRY_BACKOFF_CAP_MS = 3e4;
10499
+ extractRetryAfterMs = (e) => {
10500
+ try {
10501
+ const h = e?.headers;
10502
+ if (!h) return null;
10503
+ const get = (k) => typeof h.get === "function" ? h.get(k) ?? void 0 : h[k] ?? h[k.toLowerCase()];
10504
+ const ms = get("retry-after-ms");
10505
+ if (ms != null) {
10506
+ const n = Number(ms);
10507
+ if (Number.isFinite(n) && n >= 0) return n;
10508
+ }
10509
+ const sec = get("retry-after");
10510
+ if (sec != null) {
10511
+ const n = Number(sec);
10512
+ if (Number.isFinite(n) && n >= 0) return n * 1e3;
10513
+ }
10514
+ } catch {
10515
+ }
10516
+ return null;
10517
+ };
10518
+ withNudgeTail = (base, nudge) => {
10519
+ if (!nudge) return base;
10520
+ const lastRole = base[base.length - 1]?.role;
10521
+ return [...base, lastRole === "assistant" ? { ...nudge, role: "user" } : nudge];
10522
+ };
10244
10523
  streamInference = async function* (ctx) {
10245
10524
  const {
10246
10525
  message,
@@ -10253,21 +10532,29 @@ var init_streamInference = __esm({
10253
10532
  llmDecisionSource,
10254
10533
  signal,
10255
10534
  cleanedToolSchemas,
10256
- model: model2,
10535
+ model,
10257
10536
  thinkingLevel,
10258
10537
  events,
10259
10538
  keepRecentUnits,
10260
10539
  compactRatio,
10261
- modelWindow
10540
+ modelWindow,
10541
+ toolsTokens = 0
10262
10542
  } = ctx;
10263
- let inferenceMessages = nudgeMsg ? [...message, nudgeMsg] : message;
10543
+ let inferenceMessages = withNudgeTail(message, nudgeMsg);
10264
10544
  let assistantMessage = { role: "assistant", content: null };
10265
10545
  try {
10546
+ const prefixFingerprint = {
10547
+ toolsHash: sha1short(JSON.stringify(cleanedToolSchemas)),
10548
+ sysHash: sha1short(String(message[0]?.content ?? "")),
10549
+ sumHash: sha1short(String(message[1]?.content ?? "")),
10550
+ msgCount: message.length
10551
+ };
10266
10552
  events({
10267
10553
  sessionId,
10268
10554
  eventType: "llm.request",
10269
- metadata: { depth, decisionSource: userDecisionSource, ok: true, durationMs: performance.now() - startTime, round },
10270
- usage: { prompt_tokens: estimateTokens(inferenceMessages) },
10555
+ metadata: { depth, decisionSource: userDecisionSource, ok: true, durationMs: performance.now() - startTime, round, model, ...prefixFingerprint },
10556
+ // ★ P2 口径:估算加上工具 schema 常数项(与 API 真实 prompt_tokens 同口径,trace 里 est/real 才可比)
10557
+ usage: { prompt_tokens: estimateTokens(inferenceMessages) + toolsTokens },
10271
10558
  payload: { input: msgText(message[message.length - 1].content) }
10272
10559
  // ★ 多模态:trace 只记文本视图
10273
10560
  });
@@ -10277,12 +10564,12 @@ var init_streamInference = __esm({
10277
10564
  const toolCallsBuf = /* @__PURE__ */ new Map();
10278
10565
  let lastUsage;
10279
10566
  const MAX_STREAM_RETRIES = 2;
10280
- const MAX_API_RETRIES = 2;
10567
+ const MAX_API_RETRIES = 3;
10281
10568
  let apiRetries = 0;
10282
10569
  let compactedThisRound = false;
10283
10570
  for (let streamAttempt = 0; ; streamAttempt++) {
10284
10571
  try {
10285
- for await (const chunk of activeProvider.streamChat(inferenceMessages, cleanedToolSchemas, { signal, model: model2, thinkingLevel })) {
10572
+ for await (const chunk of activeProvider.streamChat(inferenceMessages, cleanedToolSchemas, { signal, model, thinkingLevel })) {
10286
10573
  if (signal?.aborted) break;
10287
10574
  switch (chunk.kind) {
10288
10575
  case "text":
@@ -10337,10 +10624,15 @@ var init_streamInference = __esm({
10337
10624
  }
10338
10625
  if (activeProvider.isTransientError(streamErr) && apiRetries < MAX_API_RETRIES && !signal?.aborted) {
10339
10626
  apiRetries++;
10340
- const backoffMs = 1e3 * Math.pow(2, apiRetries - 1);
10627
+ const retryAfterMs = extractRetryAfterMs(streamErr);
10628
+ const backoffMs = Math.min(
10629
+ Math.max(1e3 * Math.pow(2, apiRetries - 1), retryAfterMs ?? 0),
10630
+ RETRY_BACKOFF_CAP_MS
10631
+ );
10341
10632
  if (!noOutputYet) yield { type: "text.reset" };
10342
10633
  const statusHint = streamErr?.status ? `${streamErr.status} ` : "";
10343
- console.warn(`\u26A0\uFE0F API \u77AC\u65F6\u9519\u8BEF\uFF08${statusHint}${streamErr instanceof Error ? streamErr.message : String(streamErr)}\uFF09\uFF0C${backoffMs}ms \u540E\u7B2C ${apiRetries}/${MAX_API_RETRIES} \u6B21\u91CD\u8BD5...`);
10634
+ const raHint = retryAfterMs != null ? `\uFF0C\u9075 Retry-After=${retryAfterMs}ms` : "";
10635
+ console.warn(`\u26A0\uFE0F API \u77AC\u65F6\u9519\u8BEF\uFF08${statusHint}${streamErr instanceof Error ? streamErr.message : String(streamErr)}\uFF09\uFF0C${backoffMs}ms \u540E\u7B2C ${apiRetries}/${MAX_API_RETRIES} \u6B21\u91CD\u8BD5${raHint}...`);
10344
10636
  await new Promise((resolve, reject) => {
10345
10637
  const t = setTimeout(resolve, backoffMs);
10346
10638
  signal?.addEventListener("abort", () => {
@@ -10364,9 +10656,10 @@ var init_streamInference = __esm({
10364
10656
  modelWindow,
10365
10657
  events,
10366
10658
  depth,
10367
- signal
10659
+ signal,
10660
+ toolsTokens
10368
10661
  });
10369
- inferenceMessages = nudgeMsg ? [...message, nudgeMsg] : message;
10662
+ inferenceMessages = withNudgeTail(message, nudgeMsg);
10370
10663
  compactedThisRound = true;
10371
10664
  contentBuf = "";
10372
10665
  reasoningBuf = "";
@@ -10393,7 +10686,7 @@ var init_streamInference = __esm({
10393
10686
  events({
10394
10687
  sessionId,
10395
10688
  eventType: "llm.response",
10396
- metadata: { depth, decisionSource: llmDecisionSource, ok: true, durationMs: performance.now() - startTime, round },
10689
+ metadata: { depth, decisionSource: llmDecisionSource, ok: true, durationMs: performance.now() - startTime, round, model },
10397
10690
  usage: {
10398
10691
  prompt_tokens: lastUsage?.prompt_tokens,
10399
10692
  completion_tokens: lastUsage?.completion_tokens,
@@ -10447,12 +10740,12 @@ var init_agentNudges = __esm({
10447
10740
  GREP_REPEAT_THRESHOLD = 2;
10448
10741
  REPEAT_NUDGE_MAX = 3;
10449
10742
  PHANTOM_TEXT = "\u4F60\u7684\u4E0A\u4E00\u6761\u56DE\u590D\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u3001\u4E5F\u6CA1\u6709\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\uFF0C\u4F46\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\u3002\u8BF7\u7EE7\u7EED\u63A8\u8FDB\uFF08\u8C03\u7528\u5DE5\u5177\u6216\u7ED9\u51FA\u5B9E\u8D28\u56DE\u7B54\uFF09\uFF1B\u82E5\u786E\u5B9E\u53D7\u963B\u3001\u9700\u8981\u7528\u6237\u51B3\u7B56\uFF0C\u7528 ask_question \u8BF4\u660E\u5177\u4F53\u963B\u585E\u70B9\u3002\u4E0D\u8981\u8FD4\u56DE\u7A7A\u56DE\u590D\u3002";
10450
- EARLY_FINAL_TEXT = (round) => `\u4F60\u4EC5\u8FDB\u884C\u4E86 ${round} \u8F6E\u5DE5\u5177\u8C03\u7528\u5C31\u51C6\u5907\u6536\u5C3E\uFF0C\u4E14\u56DE\u7B54\u4E2D\u6CA1\u6709\u660E\u786E\u7684\u5B8C\u6210\u58F0\u660E\u3002\u8BF7\u4E25\u683C\u81EA\u68C0\uFF1A\u7528\u6237\u7684\u6BCF\u4E00\u4E2A\u5B50\u76EE\u6807\u662F\u5426\u90FD\u5DF2\u771F\u6B63\u843D\u5730\uFF08\u6240\u9700\u4FE1\u606F\u5DF2\u83B7\u53D6 / \u8BE5\u6539\u7684\u6587\u4EF6\u5DF2\u6539\u5B8C / \u5DF2\u9A8C\u8BC1\u901A\u8FC7\uFF09\uFF1F\u82E5\u786E\u5B9E\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D"\u5DF2\u5B8C\u6210"\u5E76\u7B80\u8FF0\u6210\u679C\uFF1B\u82E5\u8FD8\u6709\u4EFB\u4F55\u672A\u843D\u5730\u7684\u5B50\u76EE\u6807\uFF0C\u7ACB\u5373\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u63A8\u8FDB\uFF0C\u4E0D\u8981\u7528\u81EA\u7136\u8BED\u8A00\u8349\u7387\u603B\u7ED3\u6536\u5C3E\u3002`;
10743
+ EARLY_FINAL_TEXT = (round) => `\u4F60\u4EC5 ${round} \u8F6E\u5C31\u51C6\u5907\u6536\u5C3E\uFF0C\u4E14\u56DE\u7B54\u4E2D\u6CA1\u6709\u660E\u786E\u7684\u5B8C\u6210\u58F0\u660E\u3002\u8BF7\u81EA\u68C0\uFF1A\u82E5\u7528\u6237\u8F93\u5165\u672C\u5C31\u65E0\u9700\u5DE5\u5177\uFF08\u5BD2\u6684/\u95F2\u804A/\u7EAF\u95EE\u7B54\uFF09\u3001\u6216\u4F60\u5728\u7B49\u7528\u6237\u6F84\u6E05\u51B3\u7B56\uFF0C\u76F4\u63A5\u6B63\u5E38\u56DE\u5E94\u5373\u53EF\uFF0C\u5FFD\u7565\u672C\u81EA\u68C0\u3001\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u81EA\u68C0\u8BF4\u660E\uFF1B\u82E5\u786E\u6709\u4EFB\u52A1\u672A\u843D\u5730\uFF08\u6240\u9700\u4FE1\u606F\u672A\u83B7\u53D6\u9F50 / \u8BE5\u6539\u7684\u6587\u4EF6\u672A\u6539\u5B8C / \u672A\u9A8C\u8BC1\uFF09\uFF0C\u7ACB\u5373\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u63A8\u8FDB\uFF0C\u4E0D\u8981\u8349\u7387\u603B\u7ED3\u6536\u5C3E\uFF1B\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7B80\u8FF0\u6210\u679C\u3002`;
10451
10744
  TOOL_DIGEST_TEXT = "\u4F60\u521A\u6267\u884C\u5B8C\u5DE5\u5177\u62FF\u5230\u7ED3\u679C\uFF0C\u5374\u672A\u57FA\u4E8E\u8BE5\u7ED3\u679C\u7ED9\u51FA\u5B9E\u8D28\u56DE\u5E94\u5C31\u51C6\u5907\u6536\u5C3E\u3002\u8BF7\u7ED3\u5408\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\u7EE7\u7EED\u63A8\u8FDB\uFF1B\u82E5\u7ED3\u679C\u8868\u660E\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\uFF08\u5982\u4ECD\u5728\u7F16\u8BD1/\u8FD0\u884C\u3001\u9700\u7EE7\u7EED\u8F6E\u8BE2\uFF09\uFF0C\u7ACB\u5373\u91C7\u53D6\u4E0B\u4E00\u6B65\u884C\u52A8\uFF0C\u4E0D\u8981\u7A7A\u624B\u6536\u5C3E\u3002\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7B80\u8FF0\u6210\u679C\u3002";
10452
10745
  NUDGE_TEXT = (round) => `\u4F60\u5DF2\u6267\u884C\u7EA6 ${round} \u8F6E\u5DE5\u5177\u8C03\u7528\u3002\u8BF7\u81EA\u8BC4\uFF1A\u82E5\u4EFB\u52A1\u5DF2\u53EF\u5B8C\u6210\uFF0C\u7ACB\u5373\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\u3001\u4E0D\u518D\u8C03\u7528\u5DE5\u5177\uFF1B\u82E5\u786E\u9700\u66F4\u591A\u6B65\u9AA4\uFF0C\u7EE7\u7EED\uFF0C\u4F46\u786E\u4FDD\u6BCF\u6B65\u90FD\u5728\u5B9E\u8D28\u63A8\u8FDB\u4EFB\u52A1\u3001\u4E0D\u91CD\u590D\u68C0\u7D22\u3002`;
10453
- COMPLEX_VERBS = /实现|新增|添加|重构|改造|迁移|重写|拆分|升级|开发|编写|构建|集成|支持|完善/;
10454
- COMPLEX_OBJECTS = /功能|模块|系统|架构|流程|机制|组件|服务|页面|接口|能力|特性|面板/;
10455
- COMPLEX_MARKERS = /多个文件|多文件|整体|全套|端到端|从零|重新设计|一整套|跨[^,。\s]{1,6}/;
10746
+ COMPLEX_VERBS = /实现|新增|添加|重构|改造|迁移|重写|拆分|升级|开发|编写|构建|集成|支持|完善|implement|refactor|migrate|rewrite|rebuild|restructure|split|upgrade|integrate|develop/i;
10747
+ COMPLEX_OBJECTS = /功能|模块|系统|架构|流程|机制|组件|服务|页面|接口|能力|特性|面板|feature|module|system|architecture|pipeline|component|service|page|api\b|interface|panel|workflow|endpoint/i;
10748
+ COMPLEX_MARKERS = /多个文件|多文件|整体|全套|端到端|从零|重新设计|一整套|跨[^,。\s]{1,6}|multiple files|multi-file|end-to-end|from scratch|across\s+\S+/i;
10456
10749
  QUERY_LEAD = /^(请)?\s*(解释|说明|查(一下|询)?|搜索|搜一下|怎么看|如何(用|使用|配置|启动)|怎么用|为什么|是什么|帮我看看|分析一下|检查|review|对比|评价)/i;
10457
10750
  looksComplex = (text) => {
10458
10751
  const t = (text || "").trim();
@@ -10480,7 +10773,7 @@ var init_agentNudges = __esm({
10480
10773
  return s;
10481
10774
  };
10482
10775
  normQuery = (q) => String(q ?? "").trim().toLowerCase().replace(/\s+/g, " ");
10483
- REPEAT_READ_TEXT = (p, n) => `\u4F60\u5DF2\u7B2C ${n} \u6B21\u8BFB\u53D6\u300C${p}\u300D\u2014\u2014\u8BE5\u6587\u4EF6\u5185\u5BB9\u4F60\u65E9\u5DF2\u62E5\u6709\uFF0C\u65E0\u9700\u6574\u6587\u4EF6\u91CD\u8BFB\u3002\u5982\u9700\u67D0\u5904\u7EC6\u8282\u8BF7\u56DE\u770B\u4E4B\u524D\u7684\u5DE5\u5177\u7ED3\u679C\uFF1B\u82E5\u8BE5\u6587\u4EF6\u521A\u88AB\u6539\u52A8\u3001\u786E\u9700\u786E\u8BA4\uFF0C\u53EA\u8BFB\u6539\u52A8\u9644\u8FD1\u51E0\u884C\u5373\u53EF\uFF0C\u4E0D\u8981\u6574\u6587\u4EF6\u91CD\u8BFB\u3002`;
10776
+ REPEAT_READ_TEXT = (p, n) => `\u4F60\u5DF2\u7B2C ${n} \u6B21\u8BFB\u53D6\u300C${p}\u300D\u2014\u2014\u8BE5\u6587\u4EF6\u5185\u5BB9\u4F60\u65E9\u5DF2\u62E5\u6709\uFF0C\u65E0\u9700\u6574\u6587\u4EF6\u91CD\u8BFB\u3002\u5982\u9700\u67D0\u5904\u7EC6\u8282\u8BF7\u56DE\u770B\u4E4B\u524D\u7684\u5DE5\u5177\u7ED3\u679C\uFF08\u65E9\u524D\u8BFB\u53D6\u53EF\u80FD\u5DF2\u88AB\u5F52\u6863\uFF0C\u53EF\u7528 recall \u5DE5\u5177\u68C0\u7D22\u53D6\u56DE\u539F\u6587\uFF09\uFF1B\u82E5\u8BE5\u6587\u4EF6\u521A\u88AB\u6539\u52A8\u3001\u786E\u9700\u786E\u8BA4\uFF0C\u53EA\u8BFB\u6539\u52A8\u9644\u8FD1\u51E0\u884C\u5373\u53EF\uFF0C\u4E0D\u8981\u6574\u6587\u4EF6\u91CD\u8BFB\u3002`;
10484
10777
  REPEAT_GREP_TEXT = (q, n) => `\u4F60\u5DF2\u7B2C ${n} \u6B21\u68C0\u7D22\u300C${q}\u300D\u2014\u2014\u4E4B\u524D\u7684\u7ED3\u679C\u4F60\u5DF2\u62E5\u6709\u3002\u8BF7\u6539\u7528 read_file \u8BFB\u5B8C\u6574\u76EE\u6807\u6587\u4EF6\u7406\u89E3\u4E0A\u4E0B\u6587\uFF0C\u6216\u7F29\u5C0F/\u8C03\u6574\u68C0\u7D22\u8303\u56F4\uFF0C\u4E0D\u8981\u91CD\u590D\u540C\u4E00\u68C0\u7D22\u3002`;
10485
10778
  createNudgeScheduler = (opts = {}) => {
10486
10779
  const firstPrompt = opts.firstPrompt ?? "";
@@ -10548,7 +10841,7 @@ ${TOOL_DIGEST_TEXT}` };
10548
10841
  ${PHANTOM_TEXT}` };
10549
10842
  return true;
10550
10843
  }
10551
- if (!noEarlyFinal && text !== "" && round <= EARLY_FINAL_TURN_THRESHOLD && earlyFinalNudges < EARLY_FINAL_MAX && !looksComplete(finalText)) {
10844
+ if (!noEarlyFinal && (lastToolCallRound > 0 || looksComplex(firstPrompt)) && text !== "" && round <= EARLY_FINAL_TURN_THRESHOLD && earlyFinalNudges < EARLY_FINAL_MAX && !looksComplete(finalText)) {
10552
10845
  earlyFinalNudges++;
10553
10846
  earlyFinalPending = { role: "system", content: `${EARLY_FINAL_FENCE}
10554
10847
  ${EARLY_FINAL_TEXT(round)}` };
@@ -10608,10 +10901,11 @@ ${REPEAT_GREP_TEXT(q, n)}` };
10608
10901
  // ../core/src/agent/runAgent.ts
10609
10902
  async function* runAgent(message, options) {
10610
10903
  const sessionId = options.sessionId;
10904
+ const runId = createUUID();
10611
10905
  const rawEvents = options.events;
10612
10906
  const events = async (base) => {
10613
10907
  try {
10614
- await rawEvents(base);
10908
+ await rawEvents({ ...base, metadata: { ...base.metadata, runId } });
10615
10909
  } catch (e) {
10616
10910
  console.warn("\u26A0\uFE0F \u57CB\u70B9\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u63A8\u7406\uFF09:", e instanceof Error ? e.message : e);
10617
10911
  }
@@ -10623,7 +10917,7 @@ async function* runAgent(message, options) {
10623
10917
  const keepRecentUnits = options.keepRecentUnits;
10624
10918
  const compactRatio = options.compactRatio;
10625
10919
  const parentSystemPrompt = options.parentSystemPrompt;
10626
- const { rawTools, cleanedToolSchemas } = await prepareToolsAndInjections(message, options, events);
10920
+ const { rawTools, cleanedToolSchemas, toolsTokens } = await prepareToolsAndInjections(message, options, events);
10627
10921
  let round = 0;
10628
10922
  let lastContent = "";
10629
10923
  let stopReason = "normal";
@@ -10642,7 +10936,6 @@ async function* runAgent(message, options) {
10642
10936
  let lastRealPromptTokens = persistedCalib.lastRealPromptTokens;
10643
10937
  let lastCachedTokens = persistedCalib.lastCachedTokens;
10644
10938
  const breaker = createRepeatBreaker({ sessionId, depth, llmDecisionSource, startTime, events });
10645
- const runId = createUUID();
10646
10939
  let usageSum;
10647
10940
  const addUsage = (u) => {
10648
10941
  if (!u) return;
@@ -10703,7 +10996,8 @@ async function* runAgent(message, options) {
10703
10996
  signal,
10704
10997
  correctionRatio: calibRatio,
10705
10998
  lastRealPromptTokens,
10706
- lastCachedTokens
10999
+ lastCachedTokens,
11000
+ toolsTokens
10707
11001
  }
10708
11002
  );
10709
11003
  } catch (e) {
@@ -10735,7 +11029,8 @@ async function* runAgent(message, options) {
10735
11029
  events,
10736
11030
  keepRecentUnits,
10737
11031
  compactRatio,
10738
- modelWindow
11032
+ modelWindow,
11033
+ toolsTokens
10739
11034
  });
10740
11035
  if (infResult.kind === "aborted") {
10741
11036
  yield { type: "final", text: infResult.partialText || lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
@@ -10747,7 +11042,7 @@ async function* runAgent(message, options) {
10747
11042
  return;
10748
11043
  }
10749
11044
  if (infResult.usage?.prompt_tokens) {
10750
- const estAtInfer = estimateTokens(message);
11045
+ const estAtInfer = estimateTokens(message) + toolsTokens;
10751
11046
  if (estAtInfer > 1e3) {
10752
11047
  const observed = infResult.usage.prompt_tokens / estAtInfer;
10753
11048
  calibRatio = calibRatio * 0.6 + observed * 0.4;
@@ -10837,11 +11132,13 @@ async function* runAgent(message, options) {
10837
11132
  try {
10838
11133
  const reason = signal?.aborted ? "aborted" : stopReason;
10839
11134
  await dispatch("Stop", { sessionId, cwd, lastText: lastContent || "", reason });
10840
- } catch {
11135
+ } catch (e) {
11136
+ console.warn("\u26A0\uFE0F Stop hook \u5916\u5C42\u515C\u5E95\uFF08dispatch \u5185\u90E8\u5BB9\u9519\u5931\u6548\uFF0C\u5C5E\u5F02\u5E38\u4FE1\u53F7\uFF09:", e instanceof Error ? e.message : e);
10841
11137
  }
10842
11138
  try {
10843
11139
  await updateCalibration(sessionId, { calibRatio, lastRealPromptTokens, lastCachedTokens });
10844
- } catch {
11140
+ } catch (e) {
11141
+ console.warn("\u26A0\uFE0F \u538B\u7F29\u6821\u51C6\u72B6\u6001\u843D\u76D8\u5931\u8D25\uFF08\u4E0B run \u6821\u51C6\u91CD\u96F6\uFF0C\u7F13\u5B58\u611F\u77E5\u538B\u7F29\u9000\u5316\u56DE\u7F3A\u7701 1.4\uFF09:", e instanceof Error ? e.message : e);
10845
11142
  }
10846
11143
  }
10847
11144
  }
@@ -10984,9 +11281,9 @@ var init_content = __esm({
10984
11281
  return replaceImageParts(mm, "[\u5386\u53F2\u56FE\u7247\u5DF2\u6298\u53E0\uFF1A\u539F\u56FE\u4ECD\u5728\u4F1A\u8BDD\u5F52\u6863\u4E2D\uFF0C\u5982\u9700\u518D\u6B21\u67E5\u770B\u8BF7\u91CD\u65B0\u63D0\u4F9B\u8BE5\u56FE\u7247]");
10985
11282
  });
10986
11283
  };
10987
- NO_VISION_IMAGE_NOTE = "[\u56FE\u7247\u672A\u9001\u8FBE\uFF1A\u5F53\u524D\u672A\u5F00\u542F\u89C6\u89C9\u80FD\u529B\uFF08DEEP_SEEK_VISION\uFF09\uFF0C\u6A21\u578B\u770B\u4E0D\u5230\u8BE5\u56FE\uFF1B\u539F\u56FE\u4FDD\u7559\u5728\u4F1A\u8BDD\u5F52\u6863\u4E2D\uFF0C\u5982\u9700\u5206\u6790\u8BF7\u8BA9\u7528\u6237\u91CD\u65B0\u63D0\u4F9B]";
10988
- enforceVisionGate = (msgs) => isVisionEnabled() ? msgs : msgs.map((m) => collapseToText(m, NO_VISION_IMAGE_NOTE));
10989
- buildContextMessages = async (sessionId, currentUserMsg, systemPrompt) => {
11284
+ NO_VISION_IMAGE_NOTE = "[\u56FE\u7247\u672A\u9001\u8FBE\uFF1A\u5F53\u524D\u6A21\u578B\u65E0\u89C6\u89C9\u80FD\u529B\uFF0C\u770B\u4E0D\u5230\u8BE5\u56FE\uFF1B\u539F\u56FE\u4FDD\u7559\u5728\u4F1A\u8BDD\u5F52\u6863\u4E2D\uFF0C\u5982\u9700\u5206\u6790\u8BF7\u8BA9\u7528\u6237\u91CD\u65B0\u63D0\u4F9B\u6216\u5207\u6362 vision \u6A21\u578B]";
11285
+ enforceVisionGate = (msgs, modelId) => isVisionEnabled(modelId) ? msgs : msgs.map((m) => collapseToText(m, NO_VISION_IMAGE_NOTE));
11286
+ buildContextMessages = async (sessionId, currentUserMsg, systemPrompt, modelId) => {
10990
11287
  const lines = await readTranscriptLines(sessionId);
10991
11288
  const store = await getRollingState(sessionId);
10992
11289
  const eventAware = appConfig.transcriptEvents || lines.some(isEventLine);
@@ -11005,12 +11302,12 @@ var init_content = __esm({
11005
11302
  const messageAll = all.slice(archivedMessageCount);
11006
11303
  if (messageAll.length === 0) {
11007
11304
  result.push(currentUserMsg);
11008
- return enforceVisionGate(result);
11305
+ return enforceVisionGate(result, modelId);
11009
11306
  }
11010
11307
  const repair = repairOrphanToolCalls(messageAll, recovery.interruption.kind === "crashed");
11011
11308
  result.push(...decayOldToolResults(repair.msgs));
11012
11309
  result.push(currentUserMsg);
11013
- return enforceVisionGate(result);
11310
+ return enforceVisionGate(result, modelId);
11014
11311
  };
11015
11312
  }
11016
11313
  });
@@ -11171,7 +11468,7 @@ var init_subagent = __esm({
11171
11468
  console.log(`${resuming ? "\u{1F501} \u7EED\u8DD1" : "\u{1F423} \u6D3E\u751F"}\u5B50 Agent [\u6DF1\u5EA6: ${ctx.depth + 1}/${MAX_AGENT_DEPTH}][${subSessionId}]${manifest ? ` \u58F0\u660E\u5F0F=${manifest.name}` : ""} \u4EFB\u52A1: "${task.slice(0, 50)}..."`);
11172
11469
  activeSubagents.add(subSessionId);
11173
11470
  try {
11174
- const subMessages = await buildContextMessages(subSessionId, { role: "user", content: task }, subSystem);
11471
+ const subMessages = await buildContextMessages(subSessionId, { role: "user", content: task }, subSystem, manifest?.model);
11175
11472
  await appendMessage({ sessionId: subSessionId, role: "user", content: task });
11176
11473
  let subResult = "";
11177
11474
  let hasFinalResult = false;
@@ -11348,7 +11645,7 @@ var init_loader4 = __esm({
11348
11645
  console.warn(`\u26A0\uFE0F [hooks] ${where} \u975E\u5DE5\u5177\u4E8B\u4EF6\u4E0D\u652F\u6301 matcher\uFF0C\u5DF2\u5FFD\u7565`);
11349
11646
  }
11350
11647
  const matcher = TOOL_EVENTS.has(event) && typeof raw.matcher === "string" ? raw.matcher : void 0;
11351
- const timeoutMs = typeof raw.timeoutMs === "number" && raw.timeoutMs > 0 ? raw.timeoutMs : void 0;
11648
+ const timeoutMs2 = typeof raw.timeoutMs === "number" && raw.timeoutMs > 0 ? raw.timeoutMs : void 0;
11352
11649
  let denyOnNonZero = typeof raw.denyOnNonZero === "boolean" ? raw.denyOnNonZero : void 0;
11353
11650
  if (denyOnNonZero === true && !INTERCEPTABLE_EVENTS.has(event)) {
11354
11651
  console.warn(`\u26A0\uFE0F [hooks] ${where} \u975E\u62E6\u622A\u4E8B\u4EF6\uFF0CdenyOnNonZero \u4E0D\u751F\u6548\uFF08\u4EC5 PreToolUse/UserPromptSubmit \u53EF\u62E6\u622A\uFF09\uFF0C\u5DF2\u5FFD\u7565`);
@@ -11376,7 +11673,7 @@ var init_loader4 = __esm({
11376
11673
  text: type === "prompt" && typeof raw.text === "string" ? raw.text : void 0,
11377
11674
  task: type === "agent" && typeof raw.task === "string" ? raw.task : void 0,
11378
11675
  matcher,
11379
- timeoutMs,
11676
+ timeoutMs: timeoutMs2,
11380
11677
  denyOnNonZero,
11381
11678
  onError,
11382
11679
  requireApproval: raw.requireApproval === true ? true : void 0
@@ -11419,7 +11716,7 @@ var init_loader4 = __esm({
11419
11716
  };
11420
11717
  compileRule2 = (event, raw) => {
11421
11718
  const denyOnNonZero = raw.denyOnNonZero ?? event === "PreToolUse";
11422
- const timeoutMs = raw.timeoutMs ?? DEFAULT_TIMEOUT_BY_EVENT[event];
11719
+ const timeoutMs2 = raw.timeoutMs ?? DEFAULT_TIMEOUT_BY_EVENT[event];
11423
11720
  const base = { event, matcher: raw.matcher, source: "config", onError: raw.onError };
11424
11721
  if (raw.type === "prompt") {
11425
11722
  const text = raw.text;
@@ -11464,7 +11761,7 @@ var init_loader4 = __esm({
11464
11761
  headers: raw.headers,
11465
11762
  body: ctx,
11466
11763
  // 整个 hook 上下文 JSON 化发出(含 sessionId/toolName/args/prompt 等)
11467
- timeoutMs
11764
+ timeoutMs: timeoutMs2
11468
11765
  });
11469
11766
  if (!res.ok) {
11470
11767
  return denyOnNonZero ? { deny: true, reason: `hook http \u8BF7\u6C42\u5931\u8D25\uFF1A${res.error}` } : { deny: false };
@@ -11498,11 +11795,11 @@ var init_loader4 = __esm({
11498
11795
  command,
11499
11796
  cwd: ctx?.cwd,
11500
11797
  env: ctx?.env,
11501
- timeoutMs,
11798
+ timeoutMs: timeoutMs2,
11502
11799
  stdinPayload: ctx
11503
11800
  });
11504
11801
  if (res.timedOut) {
11505
- return denyOnNonZero ? { deny: true, reason: `hook \u6267\u884C\u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09` } : { deny: false };
11802
+ return denyOnNonZero ? { deny: true, reason: `hook \u6267\u884C\u8D85\u65F6\uFF08>${timeoutMs2}ms\uFF09` } : { deny: false };
11506
11803
  }
11507
11804
  if (denyOnNonZero && res.exitCode !== 0) {
11508
11805
  const detail = res.stderr ? `\uFF1A${res.stderr.slice(0, 200)}` : "";
@@ -11524,9 +11821,7 @@ var init_loader4 = __esm({
11524
11821
  if (!list || list.length === 0) continue;
11525
11822
  for (const raw of list) compiled.push(compileRule2(event, raw));
11526
11823
  }
11527
- if (compiled.length > 0) {
11528
- registerHooks(compiled);
11529
- }
11824
+ replaceDeclarativeHooks(compiled);
11530
11825
  return compiled.length;
11531
11826
  };
11532
11827
  initHooks = async (includeProject) => {
@@ -11715,7 +12010,7 @@ var init_loader6 = __esm({
11715
12010
  console.warn(`\u26A0\uFE0F [agents] frontmatter \u683C\u5F0F\u65E0\u6548\uFF08${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
11716
12011
  return null;
11717
12012
  }
11718
- const { name, description, role, tools, model: model2 } = parsed.frontmatter;
12013
+ const { name, description, role, tools, model } = parsed.frontmatter;
11719
12014
  if (!name || !description) {
11720
12015
  console.warn(`\u26A0\uFE0F [agents] \u7F3A\u5C11 name \u6216 description\uFF08${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
11721
12016
  return null;
@@ -11730,7 +12025,7 @@ var init_loader6 = __esm({
11730
12025
  description,
11731
12026
  role: role || void 0,
11732
12027
  tools: toolList,
11733
- model: model2 || void 0,
12028
+ model: model || void 0,
11734
12029
  body: parsed.body,
11735
12030
  source,
11736
12031
  dir
@@ -12103,14 +12398,6 @@ var init_runner = __esm({
12103
12398
  }
12104
12399
  });
12105
12400
 
12106
- // ../core/src/llm/createModel.ts
12107
- var init_createModel = __esm({
12108
- "../core/src/llm/createModel.ts"() {
12109
- "use strict";
12110
- init_client();
12111
- }
12112
- });
12113
-
12114
12401
  // ../core/src/observability/store.ts
12115
12402
  import path39 from "path";
12116
12403
  import fs37 from "fs/promises";
@@ -12427,11 +12714,12 @@ var init_systemPrompt = __esm({
12427
12714
  - \u88AB\u95EE"\u4F60\u662F\u8C01"\u65F6\u636E\u5B9E\u56DE\u7B54\uFF1A\u4F60\u662F ${p.displayName}\u3002\u5207\u52FF\u81EA\u79F0 ChatGPT\u3001GPT \u6216\u4EFB\u4F55\u5176\u4ED6\u516C\u53F8\u7684\u5927\u6A21\u578B/AI \u4EA7\u54C1\u3002
12428
12715
 
12429
12716
  \u3010\u56DE\u7B54\u51C6\u5219 / \u5B8C\u6210\u5224\u5B9A\u3011
12430
- - \u4EFB\u52A1\u5B8C\u6210\u540E\u76F4\u63A5\u7528\u81EA\u7136\u8BED\u8A00\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\uFF0C\u4E0D\u8981\u518D\u8C03\u7528\u5DE5\u5177\u3002
12431
- - **"\u8C03\u7528\u4E86\u4E00\u4E2A\u5DE5\u5177"\u4E0D\u7B49\u4E8E\u4EFB\u52A1\u5B8C\u6210**\uFF1A\u6BCF\u5B8C\u6210\u4E00\u6B65\u90FD\u8981\u5BF9\u7167\u7528\u6237\u539F\u59CB\u8BF7\u6C42\u4E0E todo \u6E05\u5355\uFF08\u82E5\u6709\uFF09\u9010\u9879\u6838\u5BF9\uFF0C\u786E\u8BA4\u5168\u90E8\u5B50\u76EE\u6807\u90FD\u5DF2\u843D\u5730\u3001\u4E14\u672A\u5F15\u5165\u65B0\u95EE\u9898\u540E\uFF0C\u624D\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\u3002\u4E0D\u8981\u5728\u53EA\u5B8C\u6210\u4E00\u90E8\u5206\u65F6\u5C31\u6536\u5C3E\u3002
12432
- - \u5DE5\u5177**\u6267\u884C\u5931\u8D25\u6216\u88AB\u7528\u6237\u62D2\u7EDD**\u65F6\uFF0C\u7EDD\u4E0D\u80FD\u9053\u6B49\u4E00\u4E24\u53E5\u5C31\u505C\u4E0B\u3002\u5148\u8BFB\u8FD4\u56DE\u7ED3\u679C\u5B9A\u4F4D\u5931\u8D25\u539F\u56E0\uFF0C\u4FEE\u6B63\u53C2\u6570\u91CD\u8BD5\uFF0C\u6216\u6362\u7B49\u6548\u5DE5\u5177/\u65B9\u6848\u7EE7\u7EED\u63A8\u8FDB\uFF1B\u786E\u5B9E\u53D7\u963B\u3001\u9700\u8981\u7528\u6237\u51B3\u7B56\u65F6\uFF0C\u7528 ask_question \u8BF4\u660E**\u5177\u4F53**\u7684\u963B\u585E\u70B9\u8BF7\u7528\u6237\u62CD\u677F\uFF0C\u800C\u4E0D\u662F\u7A7A\u6CDB\u5730\u8BF4"\u65E0\u6CD5\u5B8C\u6210"\u7136\u540E\u7ED3\u675F\u3002
12433
- - **\u4E25\u7981**\u5728\u672A\u771F\u6B63\u6267\u884C\u6216\u672A\u9A8C\u8BC1\u7684\u60C5\u51B5\u4E0B\u58F0\u79F0"\u5DF2\u5B8C\u6210/\u5DF2\u4FEE\u6539/\u5DF2\u4FEE\u590D"\u3002\u6539\u52A8\u540E\u82E5\u53EF\u9A8C\u8BC1\uFF08\u91CD\u65B0\u8BFB\u53D6\u786E\u8BA4\u3001\u8DD1\u6D4B\u8BD5\u3001\u7C7B\u578B\u68C0\u67E5\u7B49\uFF09\uFF0C\u5E94\u4E3B\u52A8\u9A8C\u8BC1\u518D\u6536\u5C3E\u3002
12434
- - **\u5DE5\u5177\u8FD4\u56DE\u540E\u5148\u81EA\u68C0\u518D\u51B3\u5B9A\u6536\u5C3E**\uFF1A\u6BCF\u6B21\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\uFF0C\u5148\u5224\u65AD\u7528\u6237\u7684\u6BCF\u4E00\u4E2A\u5B50\u76EE\u6807\u662F\u5426\u90FD\u5DF2\u6709\u843D\u5730\u4EA7\u7269\uFF08\u5DF2\u8BFB\u5230\u6240\u9700\u4FE1\u606F / \u5DF2\u6539\u5B8C\u8BE5\u6539\u7684\u6587\u4EF6 / \u5DF2\u9A8C\u8BC1\u901A\u8FC7\uFF09\u3002\u53EA\u8981\u8FD8\u6709\u672A\u843D\u5730\u7684\u5B50\u76EE\u6807\uFF0C\u5C31\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u63A8\u8FDB\u2014\u2014**\u4E0D\u8981\u5728\u62FF\u5230\u90E8\u5206\u68C0\u7D22\u6216\u8BFB\u53D6\u7ED3\u679C\u540E\u5C31\u7528\u81EA\u7136\u8BED\u8A00\u603B\u7ED3\u6536\u5C3E**\u3002\u590D\u6742\u4EFB\u52A1\u7528 todo_write \u62C6\u89E3\u5E76\u9010\u9879\u52FE\u9009\uFF0C\u6BCF\u8F6E\u5BF9\u7167\u6E05\u5355\u6838\u5BF9\u8FDB\u5EA6\u3002
12717
+ - \u4EFB\u52A1\u5B8C\u6210\u540E\u76F4\u63A5\u7528\u81EA\u7136\u8BED\u8A00\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\uFF0C\u4E0D\u8981\u518D\u8C03\u7528\u5DE5\u5177\u3002**\u5B8C\u6210\u5224\u636E = \u7528\u6237\u7684\u6BCF\u4E2A\u5B50\u76EE\u6807\u90FD\u6709\u843D\u5730\u4EA7\u7269**\uFF08\u6240\u9700\u4FE1\u606F\u5DF2\u5230\u624B / \u8BE5\u6539\u7684\u6587\u4EF6\u5DF2\u6539\u5B8C / \u5DF2\u9A8C\u8BC1\u901A\u8FC7\uFF09\uFF0C\u800C\u975E"\u8C03\u7528\u8FC7\u5DE5\u5177"\u3002\u6BCF\u8F6E\u5DE5\u5177\u8FD4\u56DE\u540E\u5BF9\u7167\u539F\u59CB\u8BF7\u6C42\u4E0E todo \u6E05\u5355\uFF08\u82E5\u6709\uFF09\u9010\u9879\u6838\u5BF9\uFF1A\u8FD8\u6709\u672A\u843D\u5730\u9879\u5C31\u7EE7\u7EED\u63A8\u8FDB\uFF0C\u4E0D\u8981\u62FF\u90E8\u5206\u68C0\u7D22/\u8BFB\u53D6\u7ED3\u679C\u603B\u7ED3\u6536\u5C3E\u3002\u590D\u6742\u4EFB\u52A1\u7528 todo_write \u62C6\u89E3\u5E76\u9010\u9879\u52FE\u9009\uFF0C\u6BCF\u8F6E\u5BF9\u7167\u6E05\u5355\u6838\u5BF9\u8FDB\u5EA6\u3002
12718
+ - \u5DE5\u5177**\u6267\u884C\u5931\u8D25\u6216\u88AB\u7528\u6237\u62D2\u7EDD**\u65F6\uFF0C\u8BFB\u8FD4\u56DE\u7ED3\u679C\u5B9A\u4F4D\u539F\u56E0\uFF0C\u4FEE\u6B63\u53C2\u6570\u6216\u6362\u7B49\u6548\u65B9\u6848\u7EE7\u7EED\uFF0C\u4E0D\u8981\u9053\u6B49\u4E00\u4E24\u53E5\u5C31\u505C\u4E0B\uFF1B\u786E\u5B9E\u53D7\u963B\u9700\u8981\u7528\u6237\u51B3\u7B56\u65F6\uFF0C\u7528 ask_question \u8BF4\u660E**\u5177\u4F53**\u963B\u585E\u70B9\u3002**\u4E25\u7981**\u5728\u672A\u771F\u6B63\u6267\u884C\u6216\u672A\u9A8C\u8BC1\u7684\u60C5\u51B5\u4E0B\u58F0\u79F0"\u5DF2\u5B8C\u6210/\u5DF2\u4FEE\u6539/\u5DF2\u4FEE\u590D"\u2014\u2014\u6539\u52A8\u82E5\u53EF\u9A8C\u8BC1\uFF08\u91CD\u8BFB\u786E\u8BA4\u3001\u8DD1\u6D4B\u8BD5\u3001\u7C7B\u578B\u68C0\u67E5\uFF09\uFF0C\u5148\u9A8C\u8BC1\u518D\u6536\u5C3E\u3002
12719
+
12720
+ \u3010\u56DE\u7B54\u5F62\u5F0F\u3011
12721
+ - **\u5148\u7ED3\u8BBA\u540E\u7EC6\u8282**\uFF1A\u5148\u76F4\u63A5\u56DE\u7B54\u6216\u6C47\u62A5\u7ED3\u679C\uFF0C\u518D\u5C55\u5F00\u5FC5\u8981\u7684\u89E3\u91CA\uFF1B\u6536\u5C3E\u603B\u7ED3\u7B80\u77ED\uFF08\u51E0\u884C\u4EE5\u5185\uFF09\uFF0C\u4E0D\u9010\u6587\u4EF6\u6D41\u6C34\u8D26\u3002
12722
+ - **\u4E0D\u56DE\u8D34\u5927\u6BB5\u4EE3\u7801**\uFF1A\u6539\u52A8\u5185\u5BB9\u7528\u6237\u5728\u7F16\u8F91\u5668/diff \u91CC\u770B\u5F97\u5230\uFF0C\u6B63\u6587\u53EA\u9700\u4E00\u53E5\u8BDD\u8BF4\u660E\u6539\u4E86\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\uFF0C\u6216\u5F15\u7528\u5173\u952E\u51E0\u884C\u3002
12435
12723
 
12436
12724
  \u3010\u5DE5\u5177\u4F7F\u7528\u51C6\u5219\u3011
12437
12725
  - \u4FEE\u6539\u6587\u4EF6**\u53EA\u80FD**\u7528 edit_file / create_file / write_file\u3002**\u4E25\u7981**\u7528 run_command \u901A\u8FC7 python/node/sed/awk/perl \u7B49\u811A\u672C\u8BFB\u5199\u6216\u4FEE\u6539\u6587\u4EF6\u2014\u2014\u811A\u672C\u8F6C\u4E49\u6781\u6613\u51FA\u9519\u3001\u6BCF\u6761\u547D\u4EE4\u90FD\u8981\u7528\u6237\u5BA1\u6279\u3001\u6548\u7387\u6781\u4F4E\uFF0C\u662F\u88AB\u660E\u786E\u7981\u6B62\u7684\u9000\u8DEF\u3002
@@ -12532,18 +12820,29 @@ ${lines.join("\n")}
12532
12820
  \u2605 \u8DEF\u5F84\u951A\u5B9A\u94C1\u5F8B\uFF1A\u5DE5\u5177\u7F3A\u7701\u53EA\u4F5C\u7528\u4E8E\u9ED8\u8BA4\u6839\uFF08${path41.basename(activeReal) || activeReal} = IDE \u5934\u90E8\u6240\u793A\u6839\uFF09\u3002\u5F53\u7528\u6237\u5728\u5BF9\u8BDD\u4E2D\u6307\u660E\u4E86\u67D0\u4E2A\u9879\u76EE\uFF08\u5982\u300C\u5728 A \u9879\u76EE\u300D\u300C\u6539\u524D\u7AEF\u7684 xxx\u300D\u300C\u641C\u540E\u7AEF\u300D\uFF09\uFF0C\u8BE5\u8F6E\u6240\u6709\u5E26 path \u53C2\u6570\u7684\u5DE5\u5177\u8C03\u7528\uFF08read_file / edit_file / search_grep / glob / list_dir / read_xlsx / read_docx \u7B49\uFF09\u5FC5\u987B\u628A\u5BF9\u5E94\u9879\u76EE\u6839\u4F5C\u4E3A path \u4F20\u5165\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u3001../<\u5144\u5F1F\u76EE\u5F55>\u3001\u6216\u9879\u76EE\u76EE\u5F55\u540D\uFF09\uFF0C\u5426\u5219\u53EA\u4F1A\u8BFB\u5199/\u641C\u7D22\u9ED8\u8BA4\u6839\uFF0C\u4E0E\u7528\u6237\u610F\u56FE\u4E0D\u7B26\u3002
12533
12821
  \u6CE8\u610F\uFF1A\u9879\u76EE\u7EA7\u914D\u7F6E\uFF08.deepseeker-code/\uFF09\u4EC5\u5728\u9ED8\u8BA4\u6839 ${path41.basename(activeReal) || activeReal} \u751F\u6548\u3002`;
12534
12822
  }
12535
- let replyText = "";
12536
- let userContent = inbound.content;
12537
- if (Array.isArray(inbound.attachments) && inbound.attachments.length > 0) {
12538
- userContent = isVisionEnabled() ? toWireUserContent(inbound.content, inbound.attachments) : `${inbound.content}
12823
+ const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
12824
+ const now = /* @__PURE__ */ new Date();
12825
+ const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
12826
+ const shellNote = process.platform === "win32" ? "\u7ECF shell \u6267\u884C\uFF08\u4F18\u5148 Git Bash\uFF0CPOSIX \u8BED\u4E49\uFF1B\u7F3A\u7701\u56DE\u9000 cmd.exe\uFF09" : "\u7ECF POSIX shell\uFF08sh\uFF09\u6267\u884C";
12827
+ const envLines = [
12828
+ ...roots.length > 1 ? [] : [`- \u5DE5\u4F5C\u533A\u6839\uFF08\u5DE5\u5177\u76F8\u5BF9\u8DEF\u5F84\u7684\u57FA\u51C6\uFF09: ${getActiveWorkspaceRoot()}`],
12829
+ `- \u64CD\u4F5C\u7CFB\u7EDF: ${osName}\uFF08${process.platform}\uFF09`,
12830
+ `- \u547D\u4EE4\u6267\u884C: ${shellNote}`,
12831
+ `- \u4ECA\u5929: ${today}`
12832
+ ];
12833
+ sysPrompt = sysPrompt + `
12539
12834
 
12540
- \u{1F5BC} \u6536\u5230\u56FE\u7247\u9644\u4EF6\uFF08${inbound.attachments.map((a) => a?.name || "image").join("\u3001")}\uFF09\uFF1A\u5F53\u524D\u6A21\u578B\u672A\u5F00\u542F\u89C6\u89C9\u80FD\u529B\uFF08DEEP_SEEK_VISION\uFF09\uFF0C\u5DF2\u5FFD\u7565\u56FE\u7247\u5185\u5BB9\u3002`;
12541
- }
12835
+ \u3010\u73AF\u5883\u3011
12836
+ ${envLines.join("\n")}`;
12837
+ let replyText = "";
12838
+ const { wire: userContent, archive: archiveContent } = toIngestContents(inbound.content, inbound.attachments, opts?.model);
12542
12839
  try {
12543
12840
  const fullMessages = await buildContextMessages(
12544
12841
  sessionId,
12545
12842
  { role: "user", content: userContent },
12546
- sysPrompt
12843
+ sysPrompt,
12844
+ opts?.model
12845
+ // ★ vision 自动判定与实际请求模型(opts.model ?? MODEL_NAME)同源
12547
12846
  );
12548
12847
  await emitTrace({
12549
12848
  sessionId,
@@ -12555,7 +12854,7 @@ ${lines.join("\n")}
12555
12854
  });
12556
12855
  void sweepStaleAtomicTmp().catch(() => {
12557
12856
  });
12558
- await appendMessage({ sessionId, role: "user", content: userContent });
12857
+ await appendMessage({ sessionId, role: "user", content: archiveContent });
12559
12858
  let streamedAnyText = false;
12560
12859
  const onUIEvent = opts?.onUIEvent ?? ((evt) => sseWrite?.(evt));
12561
12860
  const requestApproval2 = opts?.requestApproval ?? createWebRequestApproval((evt) => sseWrite?.(evt), abortSignal);
@@ -12744,6 +13043,75 @@ var init_fork = __esm({
12744
13043
  }
12745
13044
  });
12746
13045
 
13046
+ // ../core/src/commands/bashDirect.ts
13047
+ import { exec } from "node:child_process";
13048
+ var OUTPUT_MAX_CHARS, timeoutMs, truncateOutput, decodeBuffer, runBashDirect, formatBashEntry, recordBashEntry;
13049
+ var init_bashDirect = __esm({
13050
+ "../core/src/commands/bashDirect.ts"() {
13051
+ "use strict";
13052
+ init_transcript();
13053
+ init_guard();
13054
+ OUTPUT_MAX_CHARS = 4e3;
13055
+ timeoutMs = () => {
13056
+ const n = Number(process.env.DEEP_SEEK_BANG_TIMEOUT_MS);
13057
+ return Number.isFinite(n) && n > 0 ? n : 6e4;
13058
+ };
13059
+ truncateOutput = (s) => {
13060
+ if (s.length <= OUTPUT_MAX_CHARS) return s;
13061
+ const half = Math.floor(OUTPUT_MAX_CHARS / 2);
13062
+ return `${s.slice(0, half)}
13063
+ \u2026\uFF08\u4E2D\u6BB5\u7701\u7565 ${s.length - OUTPUT_MAX_CHARS} \u5B57\u7B26\uFF09\u2026
13064
+ ${s.slice(-half)}`;
13065
+ };
13066
+ decodeBuffer = (buf) => {
13067
+ const utf8 = buf.toString("utf8");
13068
+ if (!utf8.includes("\uFFFD")) return utf8;
13069
+ try {
13070
+ return new TextDecoder("gbk").decode(buf);
13071
+ } catch {
13072
+ return utf8;
13073
+ }
13074
+ };
13075
+ runBashDirect = (cmd, cwd, signal) => new Promise((resolve) => {
13076
+ let settled = false;
13077
+ const finish = (r) => {
13078
+ if (!settled) {
13079
+ settled = true;
13080
+ resolve(r);
13081
+ }
13082
+ };
13083
+ const start = Date.now();
13084
+ const child = exec(cmd, { cwd, timeout: timeoutMs(), maxBuffer: 4 * 1024 * 1024, windowsHide: true, env: scrubCommandEnv(), encoding: "buffer" }, (err, stdout, stderr) => {
13085
+ const durationMs = Date.now() - start;
13086
+ const out = stdout;
13087
+ const errBuf = stderr;
13088
+ const timedOut = err != null && err.killed === true && signal?.aborted !== true;
13089
+ const combined = errBuf.length ? (out.length ? `${decodeBuffer(out)}
13090
+ ` : "") + decodeBuffer(errBuf) : decodeBuffer(out);
13091
+ const exitCode = err == null ? 0 : typeof err.code === "number" ? err.code : null;
13092
+ finish({ ok: err == null, output: truncateOutput(combined ?? ""), exitCode, timedOut, durationMs });
13093
+ });
13094
+ if (signal) {
13095
+ const onAbort = () => {
13096
+ try {
13097
+ child.kill();
13098
+ } catch {
13099
+ }
13100
+ };
13101
+ if (signal.aborted) onAbort();
13102
+ else signal.addEventListener("abort", onAbort, { once: true });
13103
+ }
13104
+ });
13105
+ formatBashEntry = (cmd, r) => `!bash $ ${cmd}
13106
+ ` + (r.timedOut ? `[\u8D85\u65F6\u4E2D\u6B62\uFF08>${Math.round(r.durationMs / 1e3)}s\uFF09]
13107
+ ` : "") + (r.exitCode != null && r.exitCode !== 0 ? `[\u9000\u51FA\u7801 ${r.exitCode}]
13108
+ ` : "") + (r.output.trim() || "(\u65E0\u8F93\u51FA)");
13109
+ recordBashEntry = async (sessionId, text) => {
13110
+ await appendMessage({ sessionId, role: "user", content: text });
13111
+ };
13112
+ }
13113
+ });
13114
+
12747
13115
  // src/cliHost.ts
12748
13116
  var createCliRequestApproval, createCliRequestQuestion;
12749
13117
  var init_cliHost = __esm({
@@ -12938,9 +13306,12 @@ var init_useChatState = __esm({
12938
13306
  init_fork();
12939
13307
  init_inbox();
12940
13308
  init_contextCore();
13309
+ init_bashDirect();
13310
+ init_guard();
12941
13311
  init_createModel();
12942
13312
  init_tool();
12943
13313
  init_cliHost();
13314
+ init_prefs();
12944
13315
  init_strings();
12945
13316
  init_util();
12946
13317
  init_replay2();
@@ -12962,6 +13333,7 @@ var init_useChatState = __esm({
12962
13333
  const [pendingPlan, setPendingPlan] = useState(null);
12963
13334
  const [pendingSessions, setPendingSessions] = useState(null);
12964
13335
  const [pendingFork, setPendingFork] = useState(null);
13336
+ const [pendingModel, setPendingModel] = useState(null);
12965
13337
  const nextId = useRef(1);
12966
13338
  const assistantStreamingId = useRef(null);
12967
13339
  const thinkingStreamingId = useRef(null);
@@ -13333,6 +13705,22 @@ ${finalPlan}`, false, res.autoExecute);
13333
13705
  if (turnGenRef.current === myGen) setBusy(false);
13334
13706
  }
13335
13707
  }, [busy, initialSessionId, pushUser, pushInfo, runOnce, runPlanStage, takeEnterPlanRequest, takeProposedPlan, approveAndImplement]);
13708
+ const runBangCommand = useCallback(async (raw) => {
13709
+ const cmd = raw.replace(/^!/, "").trim();
13710
+ if (!cmd) {
13711
+ pushInfo("\u7528\u6CD5\uFF1A!<shell \u547D\u4EE4>\uFF08\u5982 !git status\uFF09\uFF1B\u8F93\u51FA\u4F1A\u5E26\u5165\u4E0B\u8F6E\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u3002");
13712
+ return;
13713
+ }
13714
+ pushUser(raw);
13715
+ if (sessionIdRef.current == null) {
13716
+ sessionIdRef.current = await getOrCreateSessionId(initialSessionId ?? void 0);
13717
+ }
13718
+ const r = await runBashDirect(cmd, getActiveWorkspaceRoot());
13719
+ const head = r.timedOut ? `\u23F1 \u8D85\u65F6\u4E2D\u6B62` : r.ok ? `\u2705 \u9000\u51FA\u7801 0` : r.exitCode != null ? `\u274C \u9000\u51FA\u7801 ${r.exitCode}` : `\u274C \u5DF2\u4E2D\u6B62`;
13720
+ pushInfo(`${head} \xB7 ${Math.round(r.durationMs / 100) / 10}s
13721
+ ${r.output.trim() || "(\u65E0\u8F93\u51FA)"}`);
13722
+ await recordBashEntry(sessionIdRef.current, formatBashEntry(cmd, r));
13723
+ }, [initialSessionId, pushUser, pushInfo]);
13336
13724
  const abortCurrent = useCallback(() => {
13337
13725
  const ac = currentAcRef.current;
13338
13726
  if (!ac) return;
@@ -13416,7 +13804,22 @@ ${finalPlan}`, false, res.autoExecute);
13416
13804
  }, [pushInfo, loadSession]);
13417
13805
  const setModelOverride = useCallback((m) => {
13418
13806
  modelRef.current = m;
13807
+ void writePrefs({ model: m });
13419
13808
  }, []);
13809
+ const resolveModel = useCallback((model) => {
13810
+ setPendingModel((prev) => {
13811
+ prev?.resolve(model);
13812
+ return null;
13813
+ });
13814
+ }, []);
13815
+ const openModelPicker = useCallback(async () => {
13816
+ const picked = await new Promise((resolve) => setPendingModel({ models: SELECTABLE_MODELS, resolve }));
13817
+ if (picked) {
13818
+ setModelOverride(picked);
13819
+ pushInfo(S.modelSwitched(picked));
13820
+ }
13821
+ return picked;
13822
+ }, [pushInfo, setModelOverride]);
13420
13823
  const setPlanMode = useCallback((on) => {
13421
13824
  planModeRef.current = on;
13422
13825
  }, []);
@@ -13444,6 +13847,7 @@ ${finalPlan}`, false, res.autoExecute);
13444
13847
  pendingPlan,
13445
13848
  pendingSessions,
13446
13849
  pendingFork,
13850
+ pendingModel,
13447
13851
  sessionIdRef,
13448
13852
  // 动作
13449
13853
  submit,
@@ -13452,6 +13856,7 @@ ${finalPlan}`, false, res.autoExecute);
13452
13856
  pushUser,
13453
13857
  pushInfo,
13454
13858
  pushEvent,
13859
+ runBangCommand,
13455
13860
  askApproval,
13456
13861
  resolveApproval,
13457
13862
  resolveQuestion,
@@ -13472,7 +13877,9 @@ ${finalPlan}`, false, res.autoExecute);
13472
13877
  resolveSession,
13473
13878
  loadSession,
13474
13879
  openForkPicker,
13475
- resolveFork
13880
+ resolveFork,
13881
+ openModelPicker,
13882
+ resolveModel
13476
13883
  };
13477
13884
  };
13478
13885
  }
@@ -13561,7 +13968,7 @@ var init_StreamingCursor = __esm({
13561
13968
  // src/components/MessageBlock.tsx
13562
13969
  import { Box, Text as Text3 } from "ink";
13563
13970
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
13564
- var UsageFooter, listPrefix, AssistantLine, MessageBlock;
13971
+ var UsageFooter, listPrefix, AssistantLine, isFenceMark, isMdRow, isMdSep, mdCells, parseBlocks, fitCell, tableWidths, buildVLines, AssistantVLine, MessageBlock;
13565
13972
  var init_MessageBlock = __esm({
13566
13973
  "src/components/MessageBlock.tsx"() {
13567
13974
  "use strict";
@@ -13600,6 +14007,94 @@ var init_MessageBlock = __esm({
13600
14007
  }
13601
14008
  return /* @__PURE__ */ jsx3(RichText, { text: line });
13602
14009
  };
14010
+ isFenceMark = (l) => /^\s*```/.test(l);
14011
+ isMdRow = (l) => l.includes("|");
14012
+ isMdSep = (l) => /^[ \t|:-]+$/.test(l) && l.includes("-") && l.includes("|");
14013
+ mdCells = (l) => l.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
14014
+ parseBlocks = (text) => {
14015
+ const src = String(text || "").split("\n");
14016
+ const blocks = [];
14017
+ let i = 0;
14018
+ while (i < src.length) {
14019
+ if (isFenceMark(src[i])) {
14020
+ const body = [];
14021
+ i++;
14022
+ while (i < src.length && !isFenceMark(src[i])) body.push(src[i++]);
14023
+ if (i < src.length) i++;
14024
+ blocks.push({ kind: "fence", lines: body });
14025
+ continue;
14026
+ }
14027
+ if (isMdRow(src[i]) && i + 1 < src.length && isMdSep(src[i + 1])) {
14028
+ const header = mdCells(src[i]);
14029
+ const rows = [];
14030
+ i += 2;
14031
+ while (i < src.length && isMdRow(src[i])) rows.push(mdCells(src[i++]));
14032
+ blocks.push({ kind: "table", header, rows });
14033
+ continue;
14034
+ }
14035
+ blocks.push({ kind: "line", text: src[i++] });
14036
+ }
14037
+ return blocks;
14038
+ };
14039
+ fitCell = (s, w) => {
14040
+ if (strWidth(s) <= w) return s + " ".repeat(w - strWidth(s));
14041
+ let out = "";
14042
+ let width = 0;
14043
+ const limit = Math.max(1, w - 1);
14044
+ for (const ch of s) {
14045
+ const cw = strWidth(ch);
14046
+ if (width + cw > limit) break;
14047
+ out += ch;
14048
+ width += cw;
14049
+ }
14050
+ return out + "\u2026";
14051
+ };
14052
+ tableWidths = (header, rows, contentW) => {
14053
+ const n = Math.max(1, header.length);
14054
+ const w = Array.from({ length: n }, (_, c) => {
14055
+ let m = strWidth(header[c] ?? "");
14056
+ for (const r of rows) m = Math.max(m, strWidth(r[c] ?? ""));
14057
+ return Math.min(m, 30);
14058
+ });
14059
+ const total = () => w.reduce((a, b) => a + b, 0) + 2 * (n - 1);
14060
+ while (total() > contentW && Math.max(...w) > 1) {
14061
+ const maxW = Math.max(...w);
14062
+ w[w.indexOf(maxW)] = maxW - 1;
14063
+ }
14064
+ return w;
14065
+ };
14066
+ buildVLines = (text, contentW) => {
14067
+ const out = [];
14068
+ for (const b of parseBlocks(text)) {
14069
+ if (b.kind === "line") {
14070
+ for (const l of wrapText(b.text, contentW)) out.push({ k: "text", s: l });
14071
+ } else if (b.kind === "fence") {
14072
+ for (const l of b.lines) out.push({ k: "code", s: l });
14073
+ } else {
14074
+ const w = tableWidths(b.header, b.rows, contentW);
14075
+ out.push({ k: "thead", cells: b.header, w }, { k: "tsep", w });
14076
+ for (const r of b.rows) out.push({ k: "trow", cells: r, w });
14077
+ }
14078
+ }
14079
+ return out.length ? out : [{ k: "text", s: "" }];
14080
+ };
14081
+ AssistantVLine = ({ v }) => {
14082
+ switch (v.k) {
14083
+ case "text":
14084
+ return /* @__PURE__ */ jsx3(AssistantLine, { line: v.s });
14085
+ case "code":
14086
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
14087
+ /* @__PURE__ */ jsx3(Text3, { color: THEME.divider, children: "\u2502 " }),
14088
+ /* @__PURE__ */ jsx3(Text3, { color: THEME.gray, children: v.s.length ? v.s : " " })
14089
+ ] });
14090
+ case "thead":
14091
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "row", children: v.w.map((cw, i) => /* @__PURE__ */ jsx3(Box, { width: cw + 2, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: "#ffffff", children: fitCell(v.cells[i] ?? "", cw) }) }, i)) });
14092
+ case "tsep":
14093
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "row", children: v.w.map((cw, i) => /* @__PURE__ */ jsx3(Box, { width: cw + 2, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text3, { color: THEME.divider, children: "\u2500".repeat(cw) }) }, i)) });
14094
+ case "trow":
14095
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "row", children: v.w.map((cw, i) => /* @__PURE__ */ jsx3(Box, { width: cw + 2, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text3, { color: THEME.body, children: fitCell(v.cells[i] ?? "", cw) }) }, i)) });
14096
+ }
14097
+ };
13603
14098
  MessageBlock = ({ row, wrapW, streamTail }) => {
13604
14099
  if (row.kind === "meta") {
13605
14100
  if (/^第 \d+ 轮$/.test(row.text)) {
@@ -13616,15 +14111,15 @@ var init_MessageBlock = __esm({
13616
14111
  ] }) });
13617
14112
  }
13618
14113
  if (row.kind === "info") {
13619
- const lines2 = wrapText(row.text, Math.max(8, wrapW));
13620
- return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: lines2.map((line, j) => /* @__PURE__ */ jsxs(Text3, { color: THEME.coralMuted, children: [
14114
+ const lines = wrapText(row.text, Math.max(8, wrapW));
14115
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: lines.map((line, j) => /* @__PURE__ */ jsxs(Text3, { color: THEME.coralMuted, children: [
13621
14116
  j === 0 ? "\u203A " : " ",
13622
14117
  line
13623
14118
  ] }, j)) });
13624
14119
  }
13625
14120
  if (row.kind === "user") {
13626
- const lines2 = wrapText(row.text, Math.max(8, wrapW - 2));
13627
- return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: lines2.map((line, j) => /* @__PURE__ */ jsx3(Text3, { children: j === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
14121
+ const lines = wrapText(row.text, Math.max(8, wrapW - 2));
14122
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: lines.map((line, j) => /* @__PURE__ */ jsx3(Text3, { children: j === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
13628
14123
  /* @__PURE__ */ jsx3(Text3, { color: THEME.coralBright, bold: true, children: "> " }),
13629
14124
  /* @__PURE__ */ jsx3(Text3, { color: THEME.white, children: line })
13630
14125
  ] }) : /* @__PURE__ */ jsxs(Text3, { color: THEME.white, children: [
@@ -13633,19 +14128,19 @@ var init_MessageBlock = __esm({
13633
14128
  ] }) }, j)) });
13634
14129
  }
13635
14130
  if (row.kind === "system") {
13636
- const lines2 = wrapText(row.text, Math.max(8, wrapW - 2));
13637
- return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: lines2.map((line, j) => /* @__PURE__ */ jsxs(Text3, { color: THEME.warn, children: [
14131
+ const lines = wrapText(row.text, Math.max(8, wrapW - 2));
14132
+ return /* @__PURE__ */ jsx3(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: lines.map((line, j) => /* @__PURE__ */ jsxs(Text3, { color: THEME.warn, children: [
13638
14133
  j === 0 ? "! " : " ",
13639
14134
  line
13640
14135
  ] }, j)) });
13641
14136
  }
13642
14137
  const contentW = Math.max(8, wrapW - 2);
13643
- const allLines = wrapText(row.text, contentW);
13644
- let lines = allLines;
14138
+ const allLines = buildVLines(row.text, contentW);
14139
+ let vlines = allLines;
13645
14140
  let folded = 0;
13646
14141
  if (row.streaming && streamTail && allLines.length > streamTail) {
13647
14142
  folded = allLines.length - streamTail;
13648
- lines = allLines.slice(folded);
14143
+ vlines = allLines.slice(folded);
13649
14144
  }
13650
14145
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0.5, marginBottom: 0.5, children: [
13651
14146
  folded > 0 ? /* @__PURE__ */ jsxs(Text3, { color: THEME.grayDim, children: [
@@ -13653,11 +14148,11 @@ var init_MessageBlock = __esm({
13653
14148
  folded,
13654
14149
  " \u884C\u2026"
13655
14150
  ] }) : null,
13656
- lines.map((line, j) => {
13657
- const isLast = j === lines.length - 1;
14151
+ vlines.map((v, j) => {
14152
+ const isLast = j === vlines.length - 1;
13658
14153
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
13659
14154
  /* @__PURE__ */ jsx3(Text3, { bold: true, color: THEME.coralBright, children: j === 0 ? "\u25CF " : " " }),
13660
- /* @__PURE__ */ jsx3(AssistantLine, { line }),
14155
+ /* @__PURE__ */ jsx3(AssistantVLine, { v }),
13661
14156
  isLast && row.streaming ? /* @__PURE__ */ jsx3(StreamingCursor, {}) : null
13662
14157
  ] }, j);
13663
14158
  }),
@@ -14281,9 +14776,44 @@ var init_ForkPicker = __esm({
14281
14776
  }
14282
14777
  });
14283
14778
 
14284
- // src/components/SlashMenu.tsx
14779
+ // src/components/ModelPicker.tsx
14285
14780
  import { Box as Box12, Text as Text14 } from "ink";
14286
14781
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
14782
+ var PAGE_SIZE3, ModelPicker;
14783
+ var init_ModelPicker = __esm({
14784
+ "src/components/ModelPicker.tsx"() {
14785
+ "use strict";
14786
+ init_theme();
14787
+ init_strings();
14788
+ init_util();
14789
+ PAGE_SIZE3 = 8;
14790
+ ModelPicker = ({ models, currentModel, selectedIndex, wrapW }) => {
14791
+ const start = Math.floor(Math.max(0, selectedIndex) / PAGE_SIZE3) * PAGE_SIZE3;
14792
+ const visible = models.slice(start, start + PAGE_SIZE3);
14793
+ const contentW = Math.max(24, wrapW);
14794
+ const metaSample = " \xB7 \uFF08\u5F53\u524D\uFF09";
14795
+ const modelW = Math.max(12, contentW - strWidth(metaSample) - 4);
14796
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: THEME.blue, paddingX: 1, paddingY: 1, marginTop: 1, children: [
14797
+ /* @__PURE__ */ jsx14(Text14, { color: THEME.blue, bold: true, children: S.modelPickerTitle(currentModel) }),
14798
+ /* @__PURE__ */ jsx14(Text14, { color: THEME.grayDim, children: S.modelPickerPrompt }),
14799
+ /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", marginTop: 0.5, children: visible.map((m, i) => {
14800
+ const idx = start + i;
14801
+ const active = idx === selectedIndex;
14802
+ const current = m === currentModel;
14803
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
14804
+ /* @__PURE__ */ jsx14(Text14, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u203A " : " " }),
14805
+ /* @__PURE__ */ jsx14(Text14, { color: active ? THEME.white : THEME.gray, bold: active, children: `${current ? "\u25CF " : " "}${truncateMiddle(m, modelW)}` }),
14806
+ current ? /* @__PURE__ */ jsx14(Text14, { color: THEME.grayDim, children: ` \xB7 ${S.modelCurrentTag}` }) : null
14807
+ ] }, m);
14808
+ }) })
14809
+ ] });
14810
+ };
14811
+ }
14812
+ });
14813
+
14814
+ // src/components/SlashMenu.tsx
14815
+ import { Box as Box13, Text as Text15 } from "ink";
14816
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
14287
14817
  var MAX_ROWS, SlashMenu;
14288
14818
  var init_SlashMenu = __esm({
14289
14819
  "src/components/SlashMenu.tsx"() {
@@ -14295,22 +14825,22 @@ var init_SlashMenu = __esm({
14295
14825
  const visible = entries.slice(start, start + MAX_ROWS);
14296
14826
  const cmdW = Math.min(22, Math.max(8, ...visible.map((e) => e.name.length)) + 1);
14297
14827
  const descMax = Math.max(12, cols - cmdW - 6);
14298
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: THEME.coral, paddingX: 1, paddingY: 0, marginTop: 1, children: [
14828
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: THEME.coral, paddingX: 1, paddingY: 0, marginTop: 1, children: [
14299
14829
  visible.map((e, i) => {
14300
14830
  const active = i + start === selectedIndex;
14301
14831
  const desc = e.description.length > descMax ? `${e.description.slice(0, descMax - 1)}\u2026` : e.description;
14302
14832
  const gap = Math.max(1, cmdW - e.name.length);
14303
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
14304
- /* @__PURE__ */ jsx14(Text14, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u276F " : " " }),
14305
- /* @__PURE__ */ jsxs12(Text14, { color: active ? THEME.white : THEME.gray, bold: active, children: [
14833
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", children: [
14834
+ /* @__PURE__ */ jsx15(Text15, { color: active ? THEME.coralBright : void 0, bold: true, children: active ? "\u276F " : " " }),
14835
+ /* @__PURE__ */ jsxs13(Text15, { color: active ? THEME.white : THEME.gray, bold: active, children: [
14306
14836
  "/",
14307
14837
  e.name
14308
14838
  ] }),
14309
- /* @__PURE__ */ jsx14(Text14, { color: THEME.grayDim, children: " ".repeat(gap) }),
14310
- /* @__PURE__ */ jsx14(Text14, { color: active ? THEME.gray : THEME.grayDim, children: desc })
14839
+ /* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: " ".repeat(gap) }),
14840
+ /* @__PURE__ */ jsx15(Text15, { color: active ? THEME.gray : THEME.grayDim, children: desc })
14311
14841
  ] }, e.name);
14312
14842
  }),
14313
- entries.length > MAX_ROWS ? /* @__PURE__ */ jsxs12(Text14, { color: THEME.coralMuted, children: [
14843
+ entries.length > MAX_ROWS ? /* @__PURE__ */ jsxs13(Text15, { color: THEME.coralMuted, children: [
14314
14844
  "\u2191\u2193 \u6EDA\u52A8 \xB7 ",
14315
14845
  selectedIndex + 1,
14316
14846
  "/",
@@ -14323,36 +14853,36 @@ var init_SlashMenu = __esm({
14323
14853
  });
14324
14854
 
14325
14855
  // src/components/StatusStrip.tsx
14326
- import { Box as Box13, Text as Text15 } from "ink";
14327
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
14856
+ import { Box as Box14, Text as Text16 } from "ink";
14857
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
14328
14858
  var StatusStrip;
14329
14859
  var init_StatusStrip = __esm({
14330
14860
  "src/components/StatusStrip.tsx"() {
14331
14861
  "use strict";
14332
14862
  init_theme();
14333
14863
  init_strings();
14334
- StatusStrip = ({ model: model2, busy, aborting, planMode, autoMode, sessionShort, customLine }) => {
14864
+ StatusStrip = ({ model, busy, aborting, planMode, autoMode, sessionShort, customLine }) => {
14335
14865
  const stateText = aborting ? S.statusAborting : busy ? S.statusStreaming : S.statusIdle;
14336
14866
  const stateColor = busy ? THEME.coralBright : THEME.gray;
14337
14867
  const bits = [
14338
- model2 ? `model: ${model2}` : "",
14868
+ model ? `model: ${model}` : "",
14339
14869
  planMode ? S.statusPlan : "",
14340
14870
  autoMode ? S.statusAuto : "",
14341
14871
  `state: ${stateText}`,
14342
14872
  sessionShort ? `session: ${sessionShort}` : ""
14343
14873
  ].filter(Boolean);
14344
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexShrink: 0, children: [
14345
- /* @__PURE__ */ jsx15(Text15, { color: THEME.coralMuted, children: customLine && customLine.trim() ? customLine : bits.join(" \xB7 ") }),
14346
- /* @__PURE__ */ jsx15(Text15, { color: stateColor, children: "" }),
14347
- /* @__PURE__ */ jsx15(Text15, { color: THEME.grayDim, children: S.cmdHint })
14874
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", flexShrink: 0, children: [
14875
+ /* @__PURE__ */ jsx16(Text16, { color: THEME.coralMuted, children: customLine && customLine.trim() ? customLine : bits.join(" \xB7 ") }),
14876
+ /* @__PURE__ */ jsx16(Text16, { color: stateColor, children: "" }),
14877
+ /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: S.cmdHint })
14348
14878
  ] });
14349
14879
  };
14350
14880
  }
14351
14881
  });
14352
14882
 
14353
14883
  // src/components/TopPanel.tsx
14354
- import { Box as Box14, Text as Text16 } from "ink";
14355
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
14884
+ import { Box as Box15, Text as Text17 } from "ink";
14885
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
14356
14886
  var ROBOT, INNER_H, TopPanel;
14357
14887
  var init_TopPanel = __esm({
14358
14888
  "src/components/TopPanel.tsx"() {
@@ -14372,36 +14902,36 @@ var init_TopPanel = __esm({
14372
14902
  const leftW = Math.floor(innerW * 0.33);
14373
14903
  const rightW = innerW - leftW - 1;
14374
14904
  const ruleLen = Math.max(8, rightW - 2);
14375
- const VDivider = /* @__PURE__ */ jsx16(Box14, { flexDirection: "column", width: 1, flexShrink: 0, children: Array.from({ length: INNER_H }).map((_, i) => /* @__PURE__ */ jsx16(Text16, { color: THEME.coral, children: "\u2502" }, i)) });
14376
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: panelW, marginBottom: 1, children: [
14377
- /* @__PURE__ */ jsxs14(Text16, { children: [
14378
- /* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, bold: true, children: "* Welcome to " }),
14379
- /* @__PURE__ */ jsx16(Text16, { bold: true, color: THEME.white, children: "deep" }),
14380
- /* @__PURE__ */ jsx16(Text16, { bold: true, color: THEME.coralBright, children: "Seek" }),
14381
- /* @__PURE__ */ jsx16(Text16, { bold: true, color: THEME.white, children: "Code" }),
14382
- /* @__PURE__ */ jsxs14(Text16, { color: THEME.coralMuted, children: [
14905
+ const VDivider = /* @__PURE__ */ jsx17(Box15, { flexDirection: "column", width: 1, flexShrink: 0, children: Array.from({ length: INNER_H }).map((_, i) => /* @__PURE__ */ jsx17(Text17, { color: THEME.coral, children: "\u2502" }, i)) });
14906
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: panelW, marginBottom: 1, children: [
14907
+ /* @__PURE__ */ jsxs15(Text17, { children: [
14908
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.coralBright, bold: true, children: "* Welcome to " }),
14909
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: THEME.white, children: "deep" }),
14910
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: THEME.coralBright, children: "Seek" }),
14911
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: THEME.white, children: "Code" }),
14912
+ /* @__PURE__ */ jsxs15(Text17, { color: THEME.coralMuted, children: [
14383
14913
  " ",
14384
14914
  VERSION
14385
14915
  ] })
14386
14916
  ] }),
14387
- /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: cwd }),
14388
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "row", borderStyle: "round", borderColor: THEME.coral, marginTop: 1, paddingX: 0, paddingY: 0, children: [
14389
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: leftW, height: INNER_H, alignItems: "center", justifyContent: "center", children: [
14390
- /* @__PURE__ */ jsx16(Text16, { color: THEME.white, bold: true, children: "Welcome back!" }),
14391
- ROBOT.map((l, i) => /* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, children: l }, i)),
14392
- /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62" }),
14393
- /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: "\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4" })
14917
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: cwd }),
14918
+ /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", borderStyle: "round", borderColor: THEME.coral, marginTop: 1, paddingX: 0, paddingY: 0, children: [
14919
+ /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: leftW, height: INNER_H, alignItems: "center", justifyContent: "center", children: [
14920
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.white, bold: true, children: "Welcome back!" }),
14921
+ ROBOT.map((l, i) => /* @__PURE__ */ jsx17(Text17, { color: THEME.coralBright, children: l }, i)),
14922
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62" }),
14923
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: "\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4" })
14394
14924
  ] }),
14395
14925
  VDivider,
14396
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: rightW, height: INNER_H, justifyContent: "space-between", paddingX: 1, children: [
14397
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
14398
- /* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, bold: true, children: "Tips for getting started" }),
14399
- /* @__PURE__ */ jsx16(Text16, { color: THEME.white, children: "\u8F93\u5165\u9700\u6C42\u5F00\u59CB\uFF0C\u8F93\u5165 / \u5524\u51FA\u547D\u4EE4\u3002" })
14926
+ /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: rightW, height: INNER_H, justifyContent: "space-between", paddingX: 1, children: [
14927
+ /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
14928
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.coralBright, bold: true, children: "Tips for getting started" }),
14929
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.white, children: "\u8F93\u5165\u9700\u6C42\u5F00\u59CB\uFF0C\u8F93\u5165 / \u5524\u51FA\u547D\u4EE4\u3002" })
14400
14930
  ] }),
14401
- /* @__PURE__ */ jsx16(Text16, { color: THEME.grayDim, children: "\u2500".repeat(ruleLen) }),
14402
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
14403
- /* @__PURE__ */ jsx16(Text16, { color: THEME.coralBright, bold: true, children: "What's new" }),
14404
- /* @__PURE__ */ jsx16(Text16, { color: THEME.white, children: "\u5DF2\u5BF9\u63A5\u672C\u5730 Tool Call \u667A\u80FD\u51FD\u6570\u8C03\u7528\u3002" })
14931
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: "\u2500".repeat(ruleLen) }),
14932
+ /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
14933
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.coralBright, bold: true, children: "What's new" }),
14934
+ /* @__PURE__ */ jsx17(Text17, { color: THEME.white, children: "\u5DF2\u5BF9\u63A5\u672C\u5730 Tool Call \u667A\u80FD\u51FD\u6570\u8C03\u7528\u3002" })
14405
14935
  ] })
14406
14936
  ] })
14407
14937
  ] })
@@ -14552,9 +15082,9 @@ __export(App_exports, {
14552
15082
  App: () => App
14553
15083
  });
14554
15084
  import { useCallback as useCallback2, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState4 } from "react";
14555
- import { Box as Box15, Static, Text as Text17, useApp, useInput as useInput2, useStdout } from "ink";
14556
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
14557
- var KNOWN_MODELS, CWD2, RowView, isDynamicRow, App;
15085
+ import { Box as Box16, Static, Text as Text18, useApp, useInput as useInput2, useStdout } from "ink";
15086
+ import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
15087
+ var CWD2, RowView, isDynamicRow, SPINNER_FRAMES, App;
14558
15088
  var init_App = __esm({
14559
15089
  "src/App.tsx"() {
14560
15090
  "use strict";
@@ -14579,26 +15109,27 @@ var init_App = __esm({
14579
15109
  init_PlanEditor();
14580
15110
  init_SessionPicker();
14581
15111
  init_ForkPicker();
15112
+ init_ModelPicker();
14582
15113
  init_SlashMenu();
14583
15114
  init_MultilineInput();
14584
15115
  init_StatusStrip();
14585
15116
  init_TopPanel();
14586
15117
  init_inspect();
14587
- KNOWN_MODELS = ["deepseek-v4", "deepseek-v4-flash"];
14588
15118
  CWD2 = process.cwd();
14589
15119
  RowView = ({ row, wrapW, streamTail, showThinking }) => {
14590
- if (row.kind === "todos") return /* @__PURE__ */ jsx17(TodosPanel, { todos: row.todos, wrapW });
14591
- if (row.kind === "tool") return /* @__PURE__ */ jsx17(ToolCard, { toolName: row.toolName, args: row.args, result: row.result, ok: row.ok, status: row.status, progress: row.progress, wrapW });
14592
- if (row.kind === "thinking") return /* @__PURE__ */ jsx17(ThinkingBlock, { streaming: row.streaming ?? false, startedAt: row.startedAt, durationMs: row.durationMs, tokens: row.tokens, text: row.text, expanded: showThinking, wrapW });
15120
+ if (row.kind === "todos") return /* @__PURE__ */ jsx18(TodosPanel, { todos: row.todos, wrapW });
15121
+ if (row.kind === "tool") return /* @__PURE__ */ jsx18(ToolCard, { toolName: row.toolName, args: row.args, result: row.result, ok: row.ok, status: row.status, progress: row.progress, wrapW });
15122
+ if (row.kind === "thinking") return /* @__PURE__ */ jsx18(ThinkingBlock, { streaming: row.streaming ?? false, startedAt: row.startedAt, durationMs: row.durationMs, tokens: row.tokens, text: row.text, expanded: showThinking, wrapW });
14593
15123
  if (row.kind === "user") {
14594
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginTop: 1, children: [
14595
- /* @__PURE__ */ jsx17(Text17, { color: THEME.grayDim, children: "\u2500".repeat(Math.max(8, wrapW)) }),
14596
- /* @__PURE__ */ jsx17(MessageBlock, { row, wrapW, streamTail })
15124
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, children: [
15125
+ /* @__PURE__ */ jsx18(Text18, { color: THEME.grayDim, children: "\u2500".repeat(Math.max(8, wrapW)) }),
15126
+ /* @__PURE__ */ jsx18(MessageBlock, { row, wrapW, streamTail })
14597
15127
  ] });
14598
15128
  }
14599
- return /* @__PURE__ */ jsx17(MessageBlock, { row, wrapW, streamTail });
15129
+ return /* @__PURE__ */ jsx18(MessageBlock, { row, wrapW, streamTail });
14600
15130
  };
14601
15131
  isDynamicRow = (r) => r.kind === "assistant" && !!r.streaming || r.kind === "thinking" && !!r.streaming || r.kind === "tool" && r.status === "running" || r.kind === "todos" && !!r.active;
15132
+ SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
14602
15133
  App = ({ resumeSessionId, initialPlanMode, initialAutoMode, initialIncludeProject, engineReady }) => {
14603
15134
  const state = useChatState(resumeSessionId, initialPlanMode, initialAutoMode);
14604
15135
  const { exit } = useApp();
@@ -14672,6 +15203,15 @@ var init_App = __esm({
14672
15203
  await refreshStatusLineRef.current();
14673
15204
  })();
14674
15205
  }, []);
15206
+ useEffect4(() => {
15207
+ void (async () => {
15208
+ const saved = await readModelPref();
15209
+ if (saved) {
15210
+ state.setModelOverride(saved);
15211
+ setModelDisplay(saved);
15212
+ }
15213
+ })();
15214
+ }, []);
14675
15215
  useEffect4(() => {
14676
15216
  void refreshStatusLineRef.current();
14677
15217
  }, [state.busy, state.aborting, modelDisplay]);
@@ -14698,6 +15238,8 @@ var init_App = __esm({
14698
15238
  return S.cmdAuto;
14699
15239
  case "model":
14700
15240
  return S.cmdModel;
15241
+ case "switch":
15242
+ return S.cmdSwitch;
14701
15243
  case "thinking":
14702
15244
  return S.cmdThinking;
14703
15245
  case "lang":
@@ -14740,17 +15282,41 @@ var init_App = __esm({
14740
15282
  const q = input.slice(1).toLowerCase();
14741
15283
  return menuEntries.filter((c) => c.name.toLowerCase().startsWith(q));
14742
15284
  }, [input, menuEntries]);
14743
- const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null;
15285
+ const menuActive = state.pendingApproval != null || state.pendingQuestion != null || state.pendingPlan != null || state.pendingSessions != null || state.pendingFork != null || state.pendingModel != null;
15286
+ const [busyClock, setBusyClock] = useState4({ frame: 0, sec: 0 });
15287
+ const busyStartRef = useRef2(0);
15288
+ useEffect4(() => {
15289
+ if (!state.busy) {
15290
+ busyStartRef.current = 0;
15291
+ setBusyClock((c) => c.frame === 0 && c.sec === 0 ? c : { frame: 0, sec: 0 });
15292
+ return;
15293
+ }
15294
+ if (busyStartRef.current === 0) busyStartRef.current = Date.now();
15295
+ if (menuActive) return;
15296
+ const drive = () => setBusyClock({
15297
+ frame: Math.floor(Date.now() / 120) % SPINNER_FRAMES.length,
15298
+ sec: Math.floor((Date.now() - busyStartRef.current) / 1e3)
15299
+ });
15300
+ drive();
15301
+ const t = setInterval(drive, 120);
15302
+ return () => clearInterval(t);
15303
+ }, [state.busy, menuActive]);
14744
15304
  const slashVisible = !menuActive && input.startsWith("/") && filteredCommands.length > 0;
14745
15305
  const inputActive = !menuActive;
14746
15306
  useEffect4(() => {
14747
15307
  setSelectIdx(0);
14748
15308
  setPlanEditing(false);
14749
- }, [state.pendingApproval, state.pendingQuestion, state.pendingPlan, state.pendingSessions, state.pendingFork, slashVisible, filteredCommands.length]);
15309
+ }, [state.pendingApproval, state.pendingQuestion, state.pendingPlan, state.pendingSessions, state.pendingFork, state.pendingModel, slashVisible, filteredCommands.length]);
14750
15310
  useEffect4(() => {
14751
15311
  setQCursor(0);
14752
15312
  setQChecked(/* @__PURE__ */ new Set());
14753
15313
  }, [state.pendingQuestion]);
15314
+ useEffect4(() => {
15315
+ if (state.pendingModel) {
15316
+ const idx = state.pendingModel.models.indexOf(modelDisplay);
15317
+ setSelectIdx(idx >= 0 ? idx : 0);
15318
+ }
15319
+ }, [state.pendingModel]);
14754
15320
  const runLocalSlash = async (text) => {
14755
15321
  const [cmd, ...rest] = text.trim().split(/\s+/);
14756
15322
  const arg = rest.join(" ");
@@ -14827,13 +15393,17 @@ ${list}
14827
15393
  }
14828
15394
  case "/model":
14829
15395
  if (!arg) {
14830
- state.pushInfo(`\u5F53\u524D\u6A21\u578B\uFF1A${modelDisplay}
14831
- \u53EF\u9009\uFF1A${KNOWN_MODELS.join("\u3001")} \u6216\u4EFB\u610F\u6A21\u578B id\u3002`);
15396
+ state.pushInfo(S.modelUsage(modelDisplay));
14832
15397
  return true;
14833
15398
  }
14834
15399
  state.setModelOverride(arg);
14835
15400
  setModelDisplay(arg);
14836
- state.pushInfo(`\u6A21\u578B\u5DF2\u5207\u6362\uFF1A${arg}`);
15401
+ state.pushInfo(S.modelSwitched(arg));
15402
+ return true;
15403
+ case "/switch":
15404
+ void state.openModelPicker().then((picked) => {
15405
+ if (picked) setModelDisplay(picked);
15406
+ });
14837
15407
  return true;
14838
15408
  case "/thinking": {
14839
15409
  const lvl = arg.toLowerCase();
@@ -14922,7 +15492,7 @@ ${listText}`);
14922
15492
  const v = inputRef.current.trim();
14923
15493
  if (!v) return;
14924
15494
  if (busyRef.current) {
14925
- if (!v.startsWith("/") && state.queueInput(v)) {
15495
+ if (!v.startsWith("/") && !v.startsWith("!") && state.queueInput(v)) {
14926
15496
  setInput("");
14927
15497
  setCursor(0);
14928
15498
  } else {
@@ -14932,6 +15502,10 @@ ${listText}`);
14932
15502
  }
14933
15503
  setInput("");
14934
15504
  setCursor(0);
15505
+ if (v.startsWith("!")) {
15506
+ await state.runBangCommand(v);
15507
+ return;
15508
+ }
14935
15509
  if (v.startsWith("/")) {
14936
15510
  await executeSlash(v);
14937
15511
  return;
@@ -15020,6 +15594,16 @@ ${listText}`);
15020
15594
  } else if (key.escape || key.ctrl && ch === "g") state.resolveFork(null);
15021
15595
  return;
15022
15596
  }
15597
+ if (state.pendingModel) {
15598
+ const list = state.pendingModel.models;
15599
+ if (key.upArrow) setSelectIdx((i) => (i - 1 + list.length) % list.length);
15600
+ else if (key.downArrow) setSelectIdx((i) => (i + 1) % list.length);
15601
+ else if (key.return) {
15602
+ const sel = list[Math.min(selectIdxRef.current, list.length - 1)];
15603
+ state.resolveModel(sel ?? null);
15604
+ } else if (key.escape || key.ctrl && ch === "g") state.resolveModel(null);
15605
+ return;
15606
+ }
15023
15607
  if (key.ctrl && ch === "t") {
15024
15608
  state.toggleShowThinking();
15025
15609
  return;
@@ -15060,19 +15644,16 @@ ${listText}`);
15060
15644
  }
15061
15645
  });
15062
15646
  const sessionShort = state.sessionIdRef.current ? truncateMiddle(state.sessionIdRef.current, 12) : "";
15063
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: cols, children: [
15064
- /* @__PURE__ */ jsx17(Static, { items: staticItems, children: (item) => item.kind === "__header" ? /* @__PURE__ */ jsx17(TopPanel, { cwd: CWD2, cols }, "header") : /* @__PURE__ */ jsx17(RowView, { row: item, wrapW, streamTail, showThinking: state.showThinkingText }, item.id) }),
15065
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", width: cols, children: [
15066
- menuActive ? null : /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, children: [
15067
- dynamicRows.map((row) => /* @__PURE__ */ jsx17(RowView, { row, wrapW, streamTail, showThinking: state.showThinkingText }, row.id)),
15068
- state.busy ? /* @__PURE__ */ jsx17(Box15, { marginTop: 0.5, children: /* @__PURE__ */ jsxs15(Text17, { color: THEME.coralBright, children: [
15069
- "\u25CF ",
15070
- S.generating
15071
- ] }) }) : null
15647
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", width: cols, children: [
15648
+ /* @__PURE__ */ jsx18(Static, { items: staticItems, children: (item) => item.kind === "__header" ? /* @__PURE__ */ jsx18(TopPanel, { cwd: CWD2, cols }, "header") : /* @__PURE__ */ jsx18(RowView, { row: item, wrapW, streamTail, showThinking: state.showThinkingText }, item.id) }),
15649
+ /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", width: cols, children: [
15650
+ menuActive ? null : /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", paddingX: 1, children: [
15651
+ dynamicRows.map((row) => /* @__PURE__ */ jsx18(RowView, { row, wrapW, streamTail, showThinking: state.showThinkingText }, row.id)),
15652
+ state.busy ? /* @__PURE__ */ jsx18(Box16, { marginTop: 0.5, children: /* @__PURE__ */ jsx18(Text18, { color: THEME.coralBright, children: `${SPINNER_FRAMES[busyClock.frame]} ${S.generating} (${busyClock.sec}s \xB7 ${S.escInterrupt})` }) }) : null
15072
15653
  ] }),
15073
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, flexShrink: 0, children: [
15074
- state.pendingApproval ? /* @__PURE__ */ jsx17(ApprovalModal, { toolName: state.pendingApproval.toolName, detail: state.pendingApproval.detail, selectedIndex: selectIdx, wrapW }) : null,
15075
- state.pendingQuestion ? /* @__PURE__ */ jsx17(
15654
+ /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", paddingX: 1, flexShrink: 0, children: [
15655
+ state.pendingApproval ? /* @__PURE__ */ jsx18(ApprovalModal, { toolName: state.pendingApproval.toolName, detail: state.pendingApproval.detail, selectedIndex: selectIdx, wrapW }) : null,
15656
+ state.pendingQuestion ? /* @__PURE__ */ jsx18(
15076
15657
  QuestionModal,
15077
15658
  {
15078
15659
  question: state.pendingQuestion.req.question,
@@ -15083,7 +15664,7 @@ ${listText}`);
15083
15664
  wrapW
15084
15665
  }
15085
15666
  ) : null,
15086
- state.pendingPlan ? planEditing ? /* @__PURE__ */ jsx17(
15667
+ state.pendingPlan ? planEditing ? /* @__PURE__ */ jsx18(
15087
15668
  PlanEditor,
15088
15669
  {
15089
15670
  value: planDraft,
@@ -15094,15 +15675,16 @@ ${listText}`);
15094
15675
  },
15095
15676
  onSubmit: confirmPlanEdit
15096
15677
  }
15097
- ) : /* @__PURE__ */ jsx17(PlanModal, { selectedIndex: selectIdx }) : null,
15098
- state.pendingSessions ? /* @__PURE__ */ jsx17(SessionPicker, { sessions: state.pendingSessions.sessions, selectedIndex: selectIdx, wrapW }) : null,
15099
- state.pendingFork ? /* @__PURE__ */ jsx17(ForkPicker, { anchors: state.pendingFork.anchors, selectedIndex: selectIdx, wrapW }) : null,
15100
- slashVisible ? /* @__PURE__ */ jsx17(SlashMenu, { entries: filteredCommands, selectedIndex: selectIdx, cols }) : null
15678
+ ) : /* @__PURE__ */ jsx18(PlanModal, { selectedIndex: selectIdx }) : null,
15679
+ state.pendingSessions ? /* @__PURE__ */ jsx18(SessionPicker, { sessions: state.pendingSessions.sessions, selectedIndex: selectIdx, wrapW }) : null,
15680
+ state.pendingFork ? /* @__PURE__ */ jsx18(ForkPicker, { anchors: state.pendingFork.anchors, selectedIndex: selectIdx, wrapW }) : null,
15681
+ state.pendingModel ? /* @__PURE__ */ jsx18(ModelPicker, { models: state.pendingModel.models, currentModel: modelDisplay, selectedIndex: selectIdx, wrapW }) : null,
15682
+ slashVisible ? /* @__PURE__ */ jsx18(SlashMenu, { entries: filteredCommands, selectedIndex: selectIdx, cols }) : null
15101
15683
  ] }),
15102
- /* @__PURE__ */ jsx17(Box15, { flexDirection: "column", paddingX: 1, marginTop: 1, flexShrink: 0, children: (() => {
15684
+ /* @__PURE__ */ jsx18(Box16, { flexDirection: "column", paddingX: 1, marginTop: 1, flexShrink: 0, children: (() => {
15103
15685
  const planOn = state.getPlanMode();
15104
15686
  const autoOn = !planOn && state.getAutoMode();
15105
- const inputEl = /* @__PURE__ */ jsx17(
15687
+ const inputEl = /* @__PURE__ */ jsx18(
15106
15688
  MultilineInput,
15107
15689
  {
15108
15690
  value: input,
@@ -15120,17 +15702,17 @@ ${listText}`);
15120
15702
  }
15121
15703
  );
15122
15704
  if (planOn || autoOn) {
15123
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", borderStyle: "round", borderColor: planOn ? THEME.thinking : THEME.warn, paddingX: 1, children: [
15124
- /* @__PURE__ */ jsx17(Text17, { color: planOn ? THEME.thinking : THEME.warn, children: planOn ? S.inputBadgePlan : S.inputBadgeAuto }),
15705
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", borderStyle: "round", borderColor: planOn ? THEME.thinking : THEME.warn, paddingX: 1, children: [
15706
+ /* @__PURE__ */ jsx18(Text18, { color: planOn ? THEME.thinking : THEME.warn, children: planOn ? S.inputBadgePlan : S.inputBadgeAuto }),
15125
15707
  inputEl
15126
15708
  ] });
15127
15709
  }
15128
- return /* @__PURE__ */ jsxs15(Fragment3, { children: [
15129
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, color: THEME.gray, children: dimRule(cols) }),
15710
+ return /* @__PURE__ */ jsxs16(Fragment3, { children: [
15711
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, color: THEME.gray, children: dimRule(cols) }),
15130
15712
  inputEl
15131
15713
  ] });
15132
15714
  })() }),
15133
- /* @__PURE__ */ jsx17(Box15, { paddingX: 1, marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx17(StatusStrip, { model: modelDisplay, busy: state.busy, aborting: state.aborting, planMode: state.getPlanMode(), autoMode: state.getAutoMode(), sessionShort, cols, customLine: statusLineText }) })
15715
+ /* @__PURE__ */ jsx18(Box16, { paddingX: 1, marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx18(StatusStrip, { model: modelDisplay, busy: state.busy, aborting: state.aborting, planMode: state.getPlanMode(), autoMode: state.getAutoMode(), sessionShort, cols, customLine: statusLineText }) })
15134
15716
  ] })
15135
15717
  ] });
15136
15718
  };
@@ -15190,7 +15772,7 @@ import { render } from "ink";
15190
15772
  import os5 from "os";
15191
15773
  import path42 from "path";
15192
15774
  import { emitKeypressEvents } from "node:readline";
15193
- import { jsx as jsx18 } from "react/jsx-runtime";
15775
+ import { jsx as jsx19 } from "react/jsx-runtime";
15194
15776
  var parseArgs = (argv) => {
15195
15777
  const out = {};
15196
15778
  for (let i = 0; i < argv.length; i++) {
@@ -15340,7 +15922,7 @@ var main = async () => {
15340
15922
  resumeId = await getMostRecentSessionId2() ?? void 0;
15341
15923
  }
15342
15924
  const { waitUntilExit } = render(
15343
- /* @__PURE__ */ jsx18(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject, engineReady }),
15925
+ /* @__PURE__ */ jsx19(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject, engineReady }),
15344
15926
  // exitOnCtrlC:false:Ctrl+C 交由 App useInput 处理(统一退出/中止语义);
15345
15927
  // patchConsole:false:避免 console 劫持与全屏重绘叠加闪屏。
15346
15928
  { exitOnCtrlC: false, patchConsole: false }