deepseeker-code 1.0.47 → 1.0.49

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 +3 -0
  2. package/dist/cli.mjs +336 -105
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -139,6 +139,8 @@ DeepSeeker-Code 支持三种配置来源,优先级 **环境变量 > config.jso
139
139
  | `DEEP_SEEK_REASONING_EFFORT` | 推理强度,仅 `high` / `max` | `high` |
140
140
  | `DEEP_SEEK_THINKING` | 深度思考开关,设 `0` 关闭 | 开 |
141
141
  | `DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS` | 流式 idle 超时(ms) | `120000` |
142
+ | `DEEP_SEEK_VISION` | 视觉多模态开关,设 `1`/`true` 开启(开启后聊天支持贴图附件) | 关 |
143
+ | `DEEP_SEEK_IMAGE_TOKENS` | 单张图片折算 token 数(下限 100) | `1500` |
142
144
 
143
145
  #### 产品行为
144
146
 
@@ -148,6 +150,7 @@ DeepSeeker-Code 支持三种配置来源,优先级 **环境变量 > config.jso
148
150
  | `DEEP_SEEK_PARALLEL_SAFE_TOOLS` | 设 `0` 关闭同轮只读工具并发(默认开) | 开(并发) |
149
151
  | `DEEP_SEEK_WORKFLOW_CONCURRENCY` | run_workflow 子 agent 并发上限 | `4` |
150
152
  | `DEEP_SEEK_WORKFLOW_MAX_STEPS` | run_workflow 单次步数上限 | `8` |
153
+ | `RUN_COMMAND_AUTO_BG_MS` | run_command 前台超时自动转后台阈值(ms);到期进程收编进后台注册表返回 task_id,不杀进程 | `120000` |
151
154
  | `SEARCH_PROVIDER` | 搜索后端 `tavily` / `bing` / `ddg` | 自动(有 Tavily key 用 Tavily,否则 Bing) |
152
155
  | `TAVILY_API_KEY` | Tavily 搜索密钥 | — |
153
156
  | `WEB_FETCH_ALLOW_PRIVATE` | 设 `1` 放行 web_fetch 访问内网/回环(云元数据端点仍硬拦) | 关(SSRF 安全) |
package/dist/cli.mjs CHANGED
@@ -423,6 +423,12 @@ var init_config = __esm({
423
423
  /** 后台工具(isSync:false)兜底超时(ms):超时强制收尾释放互斥锁,防 generator 卡死导致锁永久泄漏。
424
424
  * abort 仍是主取消通道,此值仅作最后防线;默认 30min 远超合理后台任务时长,正常任务不受影响。 */
425
425
  MAX_BACKGROUND_TOOL_MS: 30 * 60 * 1e3,
426
+ /** ★ run_command 运行时看门狗(自动转后台阈值 ms):前台流式超过此时长仍未退出 → 进程自动收编进
427
+ * 后台任务注册表并返回 task_id(get_background_output / stop_background_task 接管),不再阻塞主循环、
428
+ * 也不杀进程。与内部空闲看门狗(RUN_COMMAND_IDLE_TIMEOUT_MS,默认 60s)共用降级路径。
429
+ * env RUN_COMMAND_AUTO_BG_MS 覆盖。勿把 IDLE 调到高于 DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS
430
+ * (空闲分支到期会产出降级消息喂流,不会饿死 collectToolResult 的 idle 熔断)。 */
431
+ runCommandAutoBgMs: Number(process.env.RUN_COMMAND_AUTO_BG_MS) || 12e4,
426
432
  userWorkspaceDir: (() => {
427
433
  let cwd = process.cwd().replace(/\\/g, "/");
428
434
  if (/^[a-z]:/i.test(cwd)) {
@@ -506,6 +512,7 @@ var init_config = __esm({
506
512
  parallelSafeTools: "DEEP_SEEK_PARALLEL_SAFE_TOOLS",
507
513
  workflowConcurrency: "DEEP_SEEK_WORKFLOW_CONCURRENCY",
508
514
  workflowMaxSteps: "DEEP_SEEK_WORKFLOW_MAX_STEPS",
515
+ runCommandAutoBgMs: "RUN_COMMAND_AUTO_BG_MS",
509
516
  planEnforcement: "DEEP_SEEK_PLAN_ENFORCEMENT",
510
517
  mcpExposeMode: "DEEP_SEEK_MCP_EXPOSE_MODE",
511
518
  transcriptEvents: "DEEP_SEEK_TRANSCRIPT_EVENTS",
@@ -659,7 +666,7 @@ ${body}`;
659
666
  const oldBody = next?.index !== void 0 ? after.slice(0, next.index) : after;
660
667
  if (oldBody === `
661
668
  ${body}`) return;
662
- console.warn(`\u26A0\uFE0F ${fence} \u5DF2\u9501\u5B9A\uFF08\u4F1A\u8BDD\u5185\u4E0D\u53D8\u4EE5\u4FDD\u524D\u7F00\u7F13\u5B58\uFF09\uFF0C\u5185\u5BB9\u53D8\u5316\u5C06\u4E8E\u4E0B\u4E2A\u65B0\u4F1A\u8BDD\u751F\u6548\u3002`);
669
+ console.warn(`\u26A0\uFE0F ${fence} \u5DF2\u9501\u5B9A\uFF08run \u5185\u4E0D\u53D8\u4EE5\u4FDD\u524D\u7F00\u7F13\u5B58\uFF09\uFF0C\u5185\u5BB9\u53D8\u5316\u5C06\u4E8E\u4E0B\u8F6E\u91CD\u5EFA message[0] \u65F6\u751F\u6548\u3002`);
663
670
  };
664
671
  decodeProcessOutput = (buf) => {
665
672
  const utf8 = buf.toString("utf8");
@@ -973,11 +980,96 @@ var init_deepseek = __esm({
973
980
  }
974
981
  });
975
982
 
983
+ // ../core/src/session/contentParts.ts
984
+ var MAX_IMAGE_BYTES, msgText, imageUrlsOf, hasImagePart, isVisionEnabled, estimateImageTokens, toWireUserContent, replaceImageParts, collapseToText, degradeImagesForAux;
985
+ var init_contentParts = __esm({
986
+ "../core/src/session/contentParts.ts"() {
987
+ "use strict";
988
+ MAX_IMAGE_BYTES = 8 * 1024 * 1024;
989
+ msgText = (content) => {
990
+ if (typeof content === "string") return content;
991
+ if (Array.isArray(content)) {
992
+ return content.filter((p) => typeof p?.text === "string").map((p) => p.text).join(" ");
993
+ }
994
+ return "";
995
+ };
996
+ imageUrlsOf = (content) => {
997
+ if (!Array.isArray(content)) return [];
998
+ return content.filter((p) => p?.type === "image_url" && typeof p?.image_url?.url === "string").map((p) => p.image_url.url);
999
+ };
1000
+ hasImagePart = (content) => imageUrlsOf(content).length > 0;
1001
+ isVisionEnabled = () => {
1002
+ const v = process.env.DEEP_SEEK_VISION;
1003
+ return v === "1" || v === "true";
1004
+ };
1005
+ estimateImageTokens = () => {
1006
+ const n = Number(process.env.DEEP_SEEK_IMAGE_TOKENS);
1007
+ return Number.isFinite(n) && n >= 100 ? Math.floor(n) : 1500;
1008
+ };
1009
+ toWireUserContent = (text, attachments) => {
1010
+ if (!Array.isArray(attachments) || attachments.length === 0) return text;
1011
+ const accepted = [];
1012
+ const skipped = [];
1013
+ for (const a of attachments) {
1014
+ const name = typeof a?.name === "string" && a.name.trim() ? a.name : "image";
1015
+ const b64 = typeof a?.base64 === "string" ? a.base64 : "";
1016
+ if (!b64) {
1017
+ skipped.push(`\u{1F5BC} \u56FE\u7247 ${name} \u5DF2\u5FFD\u7565\uFF1A\u7A7A\u6570\u636E`);
1018
+ continue;
1019
+ }
1020
+ if (!/^image\//.test(a?.mime || "")) {
1021
+ skipped.push(`\u{1F5BC} \u56FE\u7247 ${name} \u5DF2\u5FFD\u7565\uFF1A\u975E\u56FE\u7247\u7C7B\u578B`);
1022
+ continue;
1023
+ }
1024
+ if (b64.length * 3 / 4 > MAX_IMAGE_BYTES) {
1025
+ skipped.push(`\u{1F5BC} \u56FE\u7247 ${name} \u5DF2\u5FFD\u7565\uFF1A\u8D85\u8FC7 ${Math.round(MAX_IMAGE_BYTES / 1024 / 1024)}MB \u4E0A\u9650`);
1026
+ continue;
1027
+ }
1028
+ accepted.push({ name, url: `data:${a.mime};base64,${b64}` });
1029
+ }
1030
+ const labels = accepted.map((p) => `\u{1F5BC} [\u56FE\u7247: ${p.name}]`);
1031
+ const footer = [...skipped, ...labels];
1032
+ const finalText = footer.length ? `${text}
1033
+
1034
+ ${footer.join("\n")}` : text;
1035
+ if (accepted.length === 0) return finalText;
1036
+ const parts = [
1037
+ { type: "text", text: finalText },
1038
+ ...accepted.map((p) => ({ type: "image_url", image_url: { url: p.url } }))
1039
+ ];
1040
+ return parts;
1041
+ };
1042
+ replaceImageParts = (m, note) => {
1043
+ if (!m || !Array.isArray(m.content)) return m;
1044
+ let touched = false;
1045
+ const parts = m.content.map((p) => {
1046
+ if (p?.type === "image_url") {
1047
+ touched = true;
1048
+ return { type: "text", text: note };
1049
+ }
1050
+ return p;
1051
+ });
1052
+ if (!touched) return m;
1053
+ return { ...m, content: parts };
1054
+ };
1055
+ collapseToText = (m, imageNote) => {
1056
+ if (!m || !Array.isArray(m.content)) return m;
1057
+ const text = msgText(m.content);
1058
+ if (imageNote && hasImagePart(m.content)) {
1059
+ return { ...m, content: text ? `${text} ${imageNote}` : imageNote };
1060
+ }
1061
+ return { ...m, content: text };
1062
+ };
1063
+ degradeImagesForAux = (m) => collapseToText(m, "[\u56FE\u7247\u5DF2\u5F52\u6863]");
1064
+ }
1065
+ });
1066
+
976
1067
  // ../core/src/llm/providers/replay/index.ts
977
1068
  var createReplayProvider;
978
1069
  var init_replay = __esm({
979
1070
  "../core/src/llm/providers/replay/index.ts"() {
980
1071
  "use strict";
1072
+ init_contentParts();
981
1073
  createReplayProvider = (script) => {
982
1074
  const turns = Array.isArray(script.turns) ? script.turns : [];
983
1075
  let cursor = 0;
@@ -1095,6 +1187,7 @@ var isCjkCodePoint, estimateTextTokens, estimateTokens, cleanMsg, groupUnits, sp
1095
1187
  var init_contextCore = __esm({
1096
1188
  "../core/src/session/contextCore.ts"() {
1097
1189
  "use strict";
1190
+ init_contentParts();
1098
1191
  isCjkCodePoint = (c) => c >= 19968 && c <= 40959 || // CJK 统一表意文字(基本区)
1099
1192
  c >= 13312 && c <= 19903 || // CJK 扩展 A
1100
1193
  c >= 12352 && c <= 12543 || // 日文假名(平假名 + 片假名)
@@ -1117,8 +1210,14 @@ var init_contextCore = __esm({
1117
1210
  estimateTokens = (messagesArr) => {
1118
1211
  return messagesArr.reduce((total, m) => {
1119
1212
  let pureText = "";
1213
+ let imageCount = 0;
1120
1214
  if (m.content) {
1121
- pureText += typeof m.content === "string" ? m.content : JSON.stringify(m.content);
1215
+ if (typeof m.content === "string") {
1216
+ pureText += m.content;
1217
+ } else if (Array.isArray(m.content)) {
1218
+ pureText += msgText(m.content);
1219
+ imageCount += imageUrlsOf(m.content).length;
1220
+ }
1122
1221
  }
1123
1222
  if (m.tool_calls && Array.isArray(m.tool_calls)) {
1124
1223
  for (const call of m.tool_calls) {
@@ -1134,7 +1233,7 @@ var init_contextCore = __esm({
1134
1233
  }
1135
1234
  }
1136
1235
  const isStructured = m.role === "tool" || Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
1137
- const tokens = estimateTextTokens(pureText, isStructured ? 4 : 4.8) + 4;
1236
+ const tokens = estimateTextTokens(pureText, isStructured ? 4 : 4.8) + imageCount * estimateImageTokens() + 4;
1138
1237
  return total + (isNaN(tokens) ? 0 : tokens);
1139
1238
  }, 0);
1140
1239
  };
@@ -1242,6 +1341,7 @@ var init_store = __esm({
1242
1341
  "use strict";
1243
1342
  init_config();
1244
1343
  init_common();
1344
+ init_contentParts();
1245
1345
  getSessionsDirPath = (mainSessionId) => {
1246
1346
  assertSafeSessionId(mainSessionId);
1247
1347
  const fileName = getFileName(mainSessionId);
@@ -1406,8 +1506,8 @@ var init_store = __esm({
1406
1506
  }
1407
1507
  if (typeof msg?.dscEvent === "string") continue;
1408
1508
  messageCount++;
1409
- if (!preview && msg?.role === "user" && typeof msg.content === "string") {
1410
- const t = msg.content.replace(/\s+/g, " ").trim();
1509
+ if (!preview && msg?.role === "user" && msg.content != null) {
1510
+ const t = (msgText(msg.content) || "\uFF08\u8BE5\u6761\u6D88\u606F\u5E26\u56FE\u7247\u9644\u4EF6\uFF09").replace(/\s+/g, " ").trim();
1411
1511
  if (t.includes("\uFFFD")) {
1412
1512
  const cleaned = t.replace(/\uFFFD+/g, "").trim();
1413
1513
  preview = cleaned ? `\uFF08\u542B\u4E71\u7801\u7247\u6BB5\uFF09${cleaned}` : "\uFF08\u8BE5\u6761\u6D88\u606F\u5185\u5BB9\u4E3A\u4E71\u7801\uFF0C\u65E0\u6CD5\u6B63\u5E38\u663E\u793A\uFF09";
@@ -1838,6 +1938,7 @@ var init_truncate = __esm({
1838
1938
  init_config();
1839
1939
  init_model();
1840
1940
  init_contextCore();
1941
+ init_contentParts();
1841
1942
  init_store();
1842
1943
  init_transcript();
1843
1944
  init_hooks();
@@ -1904,7 +2005,8 @@ ${tail}`;
1904
2005
  };
1905
2006
  compactBatch = async (batch, signal) => {
1906
2007
  const resp = await chatWithModelWithSummary(
1907
- [...batch, { 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' }],
2008
+ // ★ 辅助模型是 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' }],
1908
2010
  [],
1909
2011
  { signal }
1910
2012
  );
@@ -1915,7 +2017,7 @@ ${tail}`;
1915
2017
  extractArchiveEntities = (batch) => {
1916
2018
  let text = "";
1917
2019
  for (const m of batch) {
1918
- if (m.content) text += ` ${typeof m.content === "string" ? m.content : JSON.stringify(m.content)}`;
2020
+ if (m.content) text += ` ${msgText(m.content)}`;
1919
2021
  const calls = m.tool_calls;
1920
2022
  if (Array.isArray(calls)) for (const c of calls) text += ` ${c?.function?.name ?? ""} ${c?.function?.arguments ?? ""}`;
1921
2023
  }
@@ -2702,7 +2804,7 @@ async function killBackgroundTasksUnder(dirPath) {
2702
2804
  }
2703
2805
  return killed;
2704
2806
  }
2705
- var _winShellCache, resolveWinShell, MAX_BUFFER_CHARS, registry, backgroundTools, killAllBackgroundTasks;
2807
+ var _winShellCache, resolveWinShell, MAX_BUFFER_CHARS, registry, createBgTask, attachTaskLifecycle, adoptRunningProcess, backgroundTools, killAllBackgroundTasks;
2706
2808
  var init_background = __esm({
2707
2809
  "../core/src/tool/registry/background.ts"() {
2708
2810
  "use strict";
@@ -2743,12 +2845,110 @@ var init_background = __esm({
2743
2845
  };
2744
2846
  MAX_BUFFER_CHARS = 1e5;
2745
2847
  registry = /* @__PURE__ */ new Map();
2848
+ createBgTask = (proc, command, cwd, sessionId) => {
2849
+ const task = {
2850
+ taskId: createUUID(),
2851
+ command,
2852
+ cwd,
2853
+ sessionId: sessionId ?? "",
2854
+ proc,
2855
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2856
+ status: "running",
2857
+ exitCode: null,
2858
+ outputBuffer: ""
2859
+ };
2860
+ registry.set(task.taskId, task);
2861
+ return task;
2862
+ };
2863
+ attachTaskLifecycle = (task, proc, opts = {}) => {
2864
+ return new Promise((resolve) => {
2865
+ let finished = false;
2866
+ const finish = (apply) => {
2867
+ if (finished) return;
2868
+ finished = true;
2869
+ apply();
2870
+ setTimeout(() => registry.delete(task.taskId), 6e4);
2871
+ opts.signal?.removeEventListener("abort", onAbort);
2872
+ try {
2873
+ proc?.stdout?.removeAllListeners?.();
2874
+ } catch {
2875
+ }
2876
+ try {
2877
+ proc?.stderr?.removeAllListeners?.();
2878
+ } catch {
2879
+ }
2880
+ try {
2881
+ proc?.removeAllListeners?.();
2882
+ } catch {
2883
+ }
2884
+ resolve();
2885
+ };
2886
+ const onAbort = () => {
2887
+ killTree(proc).finally(() => {
2888
+ finish(() => {
2889
+ if (task.status === "running") task.status = "killed";
2890
+ task.exitCode = task.exitCode ?? -1;
2891
+ appendOutput(task, `
2892
+ [\u7528\u6237\u4E2D\u6B62\uFF0C\u8FDB\u7A0B\u6811\u5DF2\u7EC8\u6B62]`);
2893
+ });
2894
+ });
2895
+ };
2896
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
2897
+ proc.on("close", (code) => {
2898
+ finish(() => {
2899
+ if (opts.codec) {
2900
+ try {
2901
+ appendOutput(task, opts.codec.flush());
2902
+ } catch {
2903
+ }
2904
+ }
2905
+ task.status = task.status === "killed" ? "killed" : "exited";
2906
+ task.exitCode ??= code ?? 0;
2907
+ appendOutput(task, `
2908
+ [\u8FDB\u7A0B\u7ED3\u675F exit=${code}]`);
2909
+ });
2910
+ });
2911
+ proc.on("error", (e) => {
2912
+ finish(() => {
2913
+ if (task.status === "running") task.status = "exited";
2914
+ task.exitCode = task.exitCode ?? -1;
2915
+ appendOutput(task, `
2916
+ [spawn error: ${e.message}]`);
2917
+ });
2918
+ });
2919
+ });
2920
+ };
2921
+ adoptRunningProcess = (proc, meta, seedOutput, codec) => {
2922
+ const task = createBgTask(proc, meta.command, meta.cwd, meta.sessionId);
2923
+ if (seedOutput) appendOutput(task, seedOutput);
2924
+ try {
2925
+ proc?.stdout?.removeAllListeners?.("data");
2926
+ } catch {
2927
+ }
2928
+ try {
2929
+ proc?.stderr?.removeAllListeners?.("data");
2930
+ } catch {
2931
+ }
2932
+ try {
2933
+ proc?.removeAllListeners?.("close");
2934
+ } catch {
2935
+ }
2936
+ try {
2937
+ proc?.removeAllListeners?.("error");
2938
+ } catch {
2939
+ }
2940
+ const onData = (d) => appendOutput(task, codec.decode(d));
2941
+ proc.stdout?.on("data", onData);
2942
+ proc.stderr?.on("data", onData);
2943
+ void attachTaskLifecycle(task, proc, { signal: meta.signal, codec });
2944
+ return task.taskId;
2945
+ };
2746
2946
  backgroundTools = [
2747
2947
  {
2748
2948
  type: "function",
2749
2949
  function: {
2750
2950
  name: "run_in_background",
2751
- description: "\u5728\u540E\u53F0\u542F\u52A8\u4E00\u4E2A\u957F\u8FDE\u63A5/\u5E38\u9A7B shell \u547D\u4EE4\uFF08\u5982 dev server\u3001watch\u3001tail -f\uFF09\uFF0C\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u963B\u585E\u540E\u7EED\u63A8\u7406\u3002\u5E38\u7528\u4E8E\u5148\u8D77\u670D\u52A1\u518D\u7EE7\u7EED\u5E72\u522B\u7684\u6D3B\u3002\u7528 get_background_output \u67E5\u65E5\u5FD7\u4E0E\u72B6\u6001\uFF0Cstop_background_task \u7EC8\u6B62\u3002",
2951
+ description: "\u5728\u540E\u53F0\u542F\u52A8\u4E00\u4E2A\u957F\u65F6 shell \u547D\u4EE4\u5E76\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u963B\u585E\u540E\u7EED\u63A8\u7406\u3002\u9002\u7528\u4E24\u7C7B\u573A\u666F\uFF1A\u2460 \u957F\u8FDE\u63A5/\u5E38\u9A7B\u8FDB\u7A0B\uFF08dev server\u3001watch\u3001tail -f\uFF09\u2014\u2014\u5148\u8D77\u670D\u52A1\u518D\u7EE7\u7EED\u5E72\u522B\u7684\u6D3B\uFF1B\u2461 \u9884\u8BA1\u8FD0\u884C\u6570\u5206\u949F\u4EE5\u4E0A\u3001\u65E0\u9700\u5B9E\u65F6\u76EF\u770B\u7684\u6709\u9650\u957F\u4EFB\u52A1\uFF08\u5168\u91CF\u6D4B\u8BD5 / \u5927\u6784\u5EFA / \u5927\u4F53\u79EF\u5B89\u88C5\u2014\u2014\u8DD1\u5B8C\u7528 get_background_output \u5E26 wait_seconds \u7B49\u7ED3\u679C\uFF0C\u4E0D\u8981\u7528\u524D\u53F0 run_command \u5E72\u7B49\uFF09\u3002\u7528 get_background_output \u67E5\u65E5\u5FD7\u4E0E\u72B6\u6001\uFF0Cstop_background_task \u7EC8\u6B62\u3002",
2752
2952
  parameters: {
2753
2953
  type: "object",
2754
2954
  properties: {
@@ -2798,75 +2998,25 @@ var init_background = __esm({
2798
2998
  yield `\u274C [\u540E\u53F0\u542F\u52A8\u5931\u8D25]\uFF1A${e.message}`;
2799
2999
  return;
2800
3000
  }
2801
- const taskId = createUUID();
2802
- const task = {
2803
- taskId,
2804
- command: args.command,
2805
- cwd,
2806
- sessionId: ctx?.sessionId ?? "",
2807
- proc,
2808
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2809
- status: "running",
2810
- exitCode: null,
2811
- outputBuffer: ""
3001
+ const codec = {
3002
+ decode: decodeChunk,
3003
+ // flush:close 时收尾两个 stream 解码器的尾字节(GBK/UTF-8 残留,不 flush 丢最后一个字符)
3004
+ flush: () => {
3005
+ let s = "";
3006
+ if (decoder) s += decoder.decode();
3007
+ s += pendingUtf8.decode();
3008
+ return s;
3009
+ }
2812
3010
  };
2813
- registry.set(taskId, task);
2814
- proc.stdout?.on("data", (d) => appendOutput(task, decodeChunk(d)));
2815
- proc.stderr?.on("data", (d) => appendOutput(task, decodeChunk(d)));
3011
+ const task = createBgTask(proc, args.command, cwd, ctx?.sessionId);
3012
+ const taskId = task.taskId;
3013
+ proc.stdout?.on("data", (d) => appendOutput(task, codec.decode(d)));
3014
+ proc.stderr?.on("data", (d) => appendOutput(task, codec.decode(d)));
2816
3015
  yield `\u2705 [\u540E\u53F0\u4EFB\u52A1\u5DF2\u542F\u52A8]
2817
3016
  task_id: ${taskId}
2818
3017
  \u547D\u4EE4: ${args.command}
2819
3018
  \u7528 get_background_output(task_id="${taskId}") \u67E5\u65E5\u5FD7\uFF0Cstop_background_task(task_id="${taskId}") \u7EC8\u6B62\u3002`;
2820
- await new Promise((resolve) => {
2821
- let finished = false;
2822
- const finish = (apply) => {
2823
- if (finished) return;
2824
- finished = true;
2825
- apply();
2826
- setTimeout(() => registry.delete(taskId), 6e4);
2827
- ctx?.abortSignal?.removeEventListener("abort", onAbort);
2828
- try {
2829
- proc?.stdout?.removeAllListeners?.();
2830
- } catch {
2831
- }
2832
- try {
2833
- proc?.stderr?.removeAllListeners?.();
2834
- } catch {
2835
- }
2836
- try {
2837
- proc?.removeAllListeners?.();
2838
- } catch {
2839
- }
2840
- resolve();
2841
- };
2842
- const onAbort = () => {
2843
- killTree(proc).finally(() => {
2844
- finish(() => {
2845
- if (task.status === "running") task.status = "killed";
2846
- task.exitCode = task.exitCode ?? -1;
2847
- appendOutput(task, `
2848
- [\u7528\u6237\u4E2D\u6B62\uFF0C\u8FDB\u7A0B\u6811\u5DF2\u7EC8\u6B62]`);
2849
- });
2850
- });
2851
- };
2852
- ctx?.abortSignal?.addEventListener("abort", onAbort, { once: true });
2853
- proc.on("close", (code) => {
2854
- finish(() => {
2855
- task.status = task.status === "killed" ? "killed" : "exited";
2856
- task.exitCode ??= code ?? 0;
2857
- appendOutput(task, `
2858
- [\u8FDB\u7A0B\u7ED3\u675F exit=${code}]`);
2859
- });
2860
- });
2861
- proc.on("error", (e) => {
2862
- finish(() => {
2863
- if (task.status === "running") task.status = "exited";
2864
- task.exitCode = task.exitCode ?? -1;
2865
- appendOutput(task, `
2866
- [spawn error: ${e.message}]`);
2867
- });
2868
- });
2869
- });
3019
+ await attachTaskLifecycle(task, proc, { signal: ctx?.abortSignal, codec });
2870
3020
  }
2871
3021
  }
2872
3022
  },
@@ -2878,7 +3028,7 @@ task_id: ${taskId}
2878
3028
  parameters: {
2879
3029
  type: "object",
2880
3030
  properties: {
2881
- task_id: { type: "string", description: "run_in_background \u8FD4\u56DE\u7684 task_id" },
3031
+ task_id: { type: "string", description: "\u540E\u53F0\u4EFB\u52A1\u7684 task_id\uFF08run_in_background \u8FD4\u56DE\uFF0C\u6216 run_command \u8D85\u65F6\u81EA\u52A8\u8F6C\u540E\u53F0\u8FD4\u56DE\uFF09" },
2882
3032
  tail_lines: { type: "number", description: "\u53EA\u8FD4\u56DE\u6700\u540E N \u884C\u65E5\u5FD7\uFF08\u9ED8\u8BA4 50\uFF0C\u907F\u514D\u4E00\u6B21\u704C\u5165\u8FC7\u591A\uFF09" },
2883
3033
  wait_seconds: { type: "number", description: "\u963B\u585E\u7B49\u5F85\u4EFB\u52A1\u9000\u51FA\uFF08\u6216\u5230\u8FBE\u6B64\u65F6\u957F\uFF09\u540E\u518D\u8FD4\u56DE\uFF0C\u4E0A\u9650 120 \u79D2\uFF1B\u4EFB\u52A1\u5DF2\u5728\u8DD1\u4E14\u9700\u8981\u7B49\u7ED3\u679C\u65F6\u4F7F\u7528\uFF0C\u4E0D\u9700\u8981\u7B49\u5C31\u7701\u7565" }
2884
3034
  },
@@ -2919,7 +3069,7 @@ task_id: ${taskId}
2919
3069
  parameters: {
2920
3070
  type: "object",
2921
3071
  properties: {
2922
- task_id: { type: "string", description: "run_in_background \u8FD4\u56DE\u7684 task_id" }
3072
+ task_id: { type: "string", description: "\u540E\u53F0\u4EFB\u52A1\u7684 task_id\uFF08run_in_background \u8FD4\u56DE\uFF0C\u6216 run_command \u8D85\u65F6\u81EA\u52A8\u8F6C\u540E\u53F0\u8FD4\u56DE\uFF09" }
2923
3073
  },
2924
3074
  required: ["task_id"]
2925
3075
  },
@@ -4918,7 +5068,7 @@ var init_workflow = __esm({
4918
5068
  // ../core/src/tool/tsHost.ts
4919
5069
  import fsSync3 from "fs";
4920
5070
  import path15 from "path";
4921
- var _ts, _tsTried, getTs, buildDefaultOptions, findTsConfigBounded, resolveTsConfig, createProjectHost, lsCache, getLanguageService, realpathNative, posToLineCol, lineColToPos;
5071
+ var _ts, _tsTried, getTs, buildDefaultOptions, findTsConfigBounded, resolveTsConfig, createProjectHost, lsCache, getLanguageService, realpathNative, posToLineCol, SUPPORTED_SOURCE_EXTS, checkSupportedSourceExt, lineColToPos;
4922
5072
  var init_tsHost = __esm({
4923
5073
  "../core/src/tool/tsHost.ts"() {
4924
5074
  "use strict";
@@ -5064,6 +5214,12 @@ var init_tsHost = __esm({
5064
5214
  const { line, character } = ts.getLineAndCharacterOfPosition(sf, pos);
5065
5215
  return { line: line + 1, column: character + 1 };
5066
5216
  };
5217
+ SUPPORTED_SOURCE_EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
5218
+ checkSupportedSourceExt = (displayPath) => {
5219
+ const ext = path15.extname(displayPath).toLowerCase();
5220
+ if (SUPPORTED_SOURCE_EXTS.has(ext)) return null;
5221
+ return `\u274C [\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301]\uFF1A[${displayPath}] \u7684\u6269\u5C55\u540D ${ext || "(\u65E0)"} \u4E0D\u5728 TS \u5F15\u64CE\u652F\u6301\u8303\u56F4\uFF08\u4EC5 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF09\u3002\u8BF7\u6539\u7528 read_file/grep \u67E5\u770B\u5185\u5BB9\uFF1B\u7F16\u8BD1/\u7C7B\u578B\u7C7B\u9A8C\u8BC1\u7528 run_command \u8DD1\u5BF9\u5E94\u8BED\u8A00\u5DE5\u5177\u94FE\uFF08\u5982 mvn compile / tsc --noEmit\uFF09\u3002`;
5222
+ };
5067
5223
  lineColToPos = (ts, sf, line1, col1) => ts.getPositionOfLineAndCharacter(sf, Math.max(0, line1 - 1), Math.max(0, col1 - 1));
5068
5224
  }
5069
5225
  });
@@ -5666,7 +5822,7 @@ ${summaries.join("\n")}`;
5666
5822
  type: "function",
5667
5823
  function: {
5668
5824
  name: "view_symbol_outline",
5669
- description: "\u901A\u8FC7\u62BD\u8C61\u8BED\u6CD5\u6811(AST)\u5FEB\u901F\u63D0\u53D6\u6307\u5B9A\u6587\u4EF6\u4E2D\u7684\u7B26\u53F7\u5927\u7EB2\uFF08\u7C7B\u3001\u63A5\u53E3\u3001\u51FD\u6570\u540D\u3001\u5BFC\u51FA\u9879\u3001\u5165\u53C2\u7B7E\u540D\u7B49\uFF09\u3002\u9002\u5408\u5728\u4E0D\u8BFB\u53D6\u51E0\u5343\u884C\u5177\u4F53\u4EE3\u7801\u7684\u524D\u63D0\u4E0B\uFF0C\u5B8F\u89C2\u4E86\u89E3\u6587\u4EF6\u67B6\u6784\u3002",
5825
+ description: "\u901A\u8FC7\u62BD\u8C61\u8BED\u6CD5\u6811(AST)\u5FEB\u901F\u63D0\u53D6\u6307\u5B9A TS/JS \u6E90\u6587\u4EF6\u4E2D\u7684\u7B26\u53F7\u5927\u7EB2\uFF08\u7C7B\u3001\u63A5\u53E3\u3001\u51FD\u6570\u540D\u3001\u5BFC\u51FA\u9879\u3001\u5165\u53C2\u7B7E\u540D\u7B49\uFF09\u3002\u9002\u5408\u5728\u4E0D\u8BFB\u53D6\u51E0\u5343\u884C\u5177\u4F53\u4EE3\u7801\u7684\u524D\u63D0\u4E0B\uFF0C\u5B8F\u89C2\u4E86\u89E3\u6587\u4EF6\u67B6\u6784\u3002\u4EC5\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B\u5176\u4ED6\u7C7B\u578B\u6587\u4EF6\uFF08.java/.vue/.py \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\uFF0C\u8BF7\u6539\u7528 read_file\u3002",
5670
5826
  parameters: {
5671
5827
  type: "object",
5672
5828
  properties: {
@@ -5682,6 +5838,8 @@ ${summaries.join("\n")}`;
5682
5838
  const absPath = resolveReadablePath(args.path);
5683
5839
  const readBlock = await assertReadable(absPath, args.path);
5684
5840
  if (readBlock) return readBlock;
5841
+ const extBlock = checkSupportedSourceExt(args.path);
5842
+ if (extBlock) return extBlock;
5685
5843
  const statForSize = await fs12.stat(absPath);
5686
5844
  if (statForSize.size > 1024 * 1024) {
5687
5845
  return `\u26A0\uFE0F [\u6587\u4EF6\u8FC7\u5927]\uFF1A[${args.path}] \u7EA6 ${Math.round(statForSize.size / 1024)}KB\uFF0C\u8D85\u8FC7\u7B26\u53F7\u5927\u7EB2\u5206\u6790\u7684 1MB \u4E0A\u9650\uFF08\u9632\u5168\u91CF readFile + AST \u9A7B\u7559\u5403\u5185\u5B58\uFF09\u3002\u8BF7\u6539\u7528 read_file \u5206\u7247\u67E5\u770B\uFF0C\u6216\u7F29\u5C0F\u76EE\u6807\u6587\u4EF6\u3002`;
@@ -5970,6 +6128,7 @@ var init_command = __esm({
5970
6128
  init_type();
5971
6129
  init_guard();
5972
6130
  init_background();
6131
+ init_config();
5973
6132
  EXIT_SENTINEL = (code) => `
5974
6133
  \u27E6DSC_EXIT:${code}\u27E7`;
5975
6134
  EXIT_SENTINEL_RE = /⟦DSC_EXIT:(-?\d+)⟧/g;
@@ -5979,7 +6138,7 @@ var init_command = __esm({
5979
6138
  type: "function",
5980
6139
  function: {
5981
6140
  name: "run_command",
5982
- description: "\u5728\u5DE5\u4F5C\u533A\u6267\u884C shell \u547D\u4EE4\uFF08\u8DD1\u6D4B\u8BD5 / \u6784\u5EFA / git \u7B49\uFF09\uFF0C\u6D41\u5F0F\u8FD4\u56DE stdout/stderr \u5E76\u9644\u5E26\u9000\u51FA\u7801\u3002\u5C5E\u4E8E\u9AD8\u5371\u64CD\u4F5C\uFF0C\u6BCF\u6B21\u6267\u884C\u90FD\u9700\u8981\u7528\u6237\u5BA1\u6279\u3002",
6141
+ description: "\u5728\u5DE5\u4F5C\u533A\u6267\u884C shell \u547D\u4EE4\uFF08\u77ED\u5E73\u5FEB\u7684\u6D4B\u8BD5 / \u6784\u5EFA / git \u7B49\uFF09\uFF0C\u6D41\u5F0F\u8FD4\u56DE stdout/stderr \u5E76\u9644\u5E26\u9000\u51FA\u7801\u3002\u5C5E\u4E8E\u9AD8\u5371\u64CD\u4F5C\uFF0C\u6BCF\u6B21\u6267\u884C\u90FD\u9700\u8981\u7528\u6237\u5BA1\u6279\u3002\u9884\u8BA1\u8FD0\u884C\u6570\u5206\u949F\u4EE5\u4E0A\u7684\u957F\u4EFB\u52A1\uFF08\u5168\u91CF\u6D4B\u8BD5 / \u5927\u6784\u5EFA / \u5927\u4F53\u79EF\u5B89\u88C5\uFF09\u8BF7\u6539\u7528 run_in_background \u540E\u53F0\u6267\u884C\uFF1B\u82E5\u672C\u5DE5\u5177\u6267\u884C\u8FC7\u4E45\u4ECD\u672A\u7ED3\u675F\uFF0C\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u8FDB\u7A0B\u8F6C\u5165\u540E\u53F0\u5E76\u8FD4\u56DE task_id\uFF08\u7528 get_background_output \u67E5\u7ED3\u679C\u3001stop_background_task \u7EC8\u6B62\uFF09\u3002",
5983
6142
  parameters: {
5984
6143
  type: "object",
5985
6144
  properties: {
@@ -6024,6 +6183,10 @@ ${tail}`,
6024
6183
  const cwd = args.cwd ? resolveSafePath(args.cwd) : getActiveWorkspaceRoot();
6025
6184
  const maxChars = RUN_COMMAND_MAX_CHARS;
6026
6185
  let totalYieldedChars = 0;
6186
+ let fullOutput = "";
6187
+ let degraded = false;
6188
+ let degradeDue = false;
6189
+ let degradeReason = null;
6027
6190
  const isWin = process.platform === "win32";
6028
6191
  if (ctx?.abortSignal?.aborted) {
6029
6192
  yield `\u274C [\u5DF2\u4E2D\u6B62]\uFF1A\u547D\u4EE4 [${args.command}] \u672A\u6267\u884C\uFF08\u7528\u6237\u5DF2\u4E2D\u65AD\uFF09\u3002`;
@@ -6078,6 +6241,15 @@ ${tail}`,
6078
6241
  }
6079
6242
  return (decoder ?? pendingUtf8).decode(buf, { stream: true });
6080
6243
  };
6244
+ const codec = {
6245
+ decode: decodeChunk,
6246
+ flush: () => {
6247
+ let s = "";
6248
+ if (decoder) s += decoder.decode();
6249
+ s += pendingUtf8.decode();
6250
+ return s;
6251
+ }
6252
+ };
6081
6253
  proc.stdout?.on("data", (d) => {
6082
6254
  queue.push(decodeChunk(d));
6083
6255
  notifyNewData();
@@ -6104,11 +6276,22 @@ ${tail}`,
6104
6276
  void killTree(proc);
6105
6277
  };
6106
6278
  ctx?.abortSignal?.addEventListener("abort", onAbort);
6279
+ const IDLE_TIMEOUT_MS = Number(process.env.RUN_COMMAND_IDLE_TIMEOUT_MS) || 6e4;
6280
+ const autoBgMs = Math.max(1, appConfig.runCommandAutoBgMs);
6281
+ const runtimeTimer = setTimeout(() => {
6282
+ degradeDue = true;
6283
+ notifyNewData();
6284
+ }, autoBgMs);
6285
+ runtimeTimer.unref?.();
6107
6286
  let overLimit = false;
6108
6287
  try {
6109
- while ((!settled || queue.length > 0) && !overLimit) {
6288
+ while ((!settled || queue.length > 0) && !overLimit && !degraded) {
6289
+ if (degradeDue && !settled) {
6290
+ degraded = true;
6291
+ degradeReason = "runtime";
6292
+ break;
6293
+ }
6110
6294
  if (queue.length === 0) {
6111
- const IDLE_TIMEOUT_MS = 6e4;
6112
6295
  let timedOut = false;
6113
6296
  await new Promise((r) => {
6114
6297
  resolveWaiter = r;
@@ -6117,20 +6300,26 @@ ${tail}`,
6117
6300
  r();
6118
6301
  }, IDLE_TIMEOUT_MS);
6119
6302
  });
6303
+ if (degradeDue && !settled) {
6304
+ degraded = true;
6305
+ degradeReason = "runtime";
6306
+ break;
6307
+ }
6120
6308
  if (timedOut && !settled && queue.length === 0) {
6121
- yield `
6122
-
6123
- \u23F1\uFE0F [\u770B\u95E8\u72D7]\uFF1A\u547D\u4EE4\u8FDE\u7EED ${IDLE_TIMEOUT_MS / 1e3}s \u65E0\u8F93\u51FA\u4E14\u672A\u9000\u51FA\uFF0C\u5224\u5B9A\u4E3A\u5E38\u9A7B\u8FDB\u7A0B\uFF08\u5982 dev server / tail -f\uFF09\u3002\u5DF2\u4E3B\u52A8\u7EC8\u6B62\u4EE5\u91CA\u653E\u4E3B\u5FAA\u73AF\u2014\u2014\u5E38\u9A7B\u547D\u4EE4\u8BF7\u6539\u7528 run_in_background\u3002
6124
- `;
6125
- onAbort();
6126
- settled = true;
6127
- overLimit = true;
6309
+ degraded = true;
6310
+ degradeReason = "idle";
6128
6311
  break;
6129
6312
  }
6130
6313
  }
6131
6314
  while (queue.length > 0) {
6315
+ if (degradeDue && !settled) {
6316
+ degraded = true;
6317
+ degradeReason = "runtime";
6318
+ break;
6319
+ }
6132
6320
  const chunk = queue.shift();
6133
6321
  totalYieldedChars += chunk.length;
6322
+ fullOutput += chunk;
6134
6323
  if (totalYieldedChars > maxChars) {
6135
6324
  yield `
6136
6325
 
@@ -6144,14 +6333,30 @@ ${tail}`,
6144
6333
  yield chunk;
6145
6334
  }
6146
6335
  }
6147
- if (overLimit) {
6336
+ if (degraded) {
6337
+ const taskId = adoptRunningProcess(
6338
+ proc,
6339
+ { command: args.command, cwd, sessionId: ctx?.sessionId, signal: ctx?.abortSignal },
6340
+ fullOutput + queue.join(""),
6341
+ codec
6342
+ );
6343
+ const why = degradeReason === "idle" ? `\u8FDE\u7EED ${Math.round(IDLE_TIMEOUT_MS / 1e3)}s \u65E0\u8F93\u51FA\u4E14\u672A\u9000\u51FA` : `\u5DF2\u6301\u7EED\u8FD0\u884C\u8D85\u8FC7 ${Math.round(autoBgMs / 1e3)}s`;
6344
+ yield `
6345
+
6346
+ \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
+ task_id: ${taskId}
6348
+ \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
6349
+ `;
6350
+ } else if (overLimit) {
6148
6351
  yield EXIT_SENTINEL(-1);
6149
6352
  } else if (exitCode !== null) {
6150
6353
  yield EXIT_SENTINEL(exitCode);
6151
6354
  }
6152
6355
  } finally {
6153
6356
  if (idleTimer) clearTimeout(idleTimer);
6357
+ clearTimeout(runtimeTimer);
6154
6358
  ctx?.abortSignal?.removeEventListener("abort", onAbort);
6359
+ if (!settled && !degraded) void killTree(proc);
6155
6360
  }
6156
6361
  }
6157
6362
  }
@@ -7177,6 +7382,8 @@ var init_typescript = __esm({
7177
7382
  precheck = async (displayPath, absPath) => {
7178
7383
  const readBlock = await assertReadable(absPath, displayPath);
7179
7384
  if (readBlock) return readBlock;
7385
+ const extBlock = checkSupportedSourceExt(displayPath);
7386
+ if (extBlock) return extBlock;
7180
7387
  const stat = await fs16.stat(absPath);
7181
7388
  if (stat.size > MAX_FILE_BYTES) {
7182
7389
  return `\u26A0\uFE0F [\u6587\u4EF6\u8FC7\u5927]\uFF1A[${displayPath}] \u7EA6 ${Math.round(stat.size / 1024)}KB\uFF0C\u8D85\u8FC7 ${MAX_FILE_BYTES / 1024}KB \u4E0A\u9650\uFF08\u9632\u5168\u91CF type-check \u5403\u5185\u5B58\uFF09\u3002`;
@@ -7239,7 +7446,7 @@ ${body}${tail}` };
7239
7446
  type: "function",
7240
7447
  function: {
7241
7448
  name: "get_diagnostics",
7242
- 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\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",
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",
7243
7450
  parameters: {
7244
7451
  type: "object",
7245
7452
  properties: {
@@ -7294,7 +7501,7 @@ ${sections.join("\n\n")}`;
7294
7501
  type: "function",
7295
7502
  function: {
7296
7503
  name: "goto_definition",
7297
- 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\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",
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",
7298
7505
  parameters: {
7299
7506
  type: "object",
7300
7507
  properties: {
@@ -10033,6 +10240,7 @@ var init_streamInference = __esm({
10033
10240
  init_truncate();
10034
10241
  init_transcript();
10035
10242
  init_contextCore();
10243
+ init_contentParts();
10036
10244
  streamInference = async function* (ctx) {
10037
10245
  const {
10038
10246
  message,
@@ -10060,7 +10268,8 @@ var init_streamInference = __esm({
10060
10268
  eventType: "llm.request",
10061
10269
  metadata: { depth, decisionSource: userDecisionSource, ok: true, durationMs: performance.now() - startTime, round },
10062
10270
  usage: { prompt_tokens: estimateTokens(inferenceMessages) },
10063
- payload: { input: message[message.length - 1].content }
10271
+ payload: { input: msgText(message[message.length - 1].content) }
10272
+ // ★ 多模态:trace 只记文本视图
10064
10273
  });
10065
10274
  console.log(`\u{1F504} \u4EE3\u7406\u63A8\u7406\u7B2C ${round} \u8F6E\uFF08\u6D41\u5F0F\uFF09...`);
10066
10275
  let contentBuf = "";
@@ -10192,7 +10401,8 @@ var init_streamInference = __esm({
10192
10401
  prompt_cache_hit_tokens: lastUsage?.cached_tokens,
10193
10402
  prompt_cache_miss_tokens: (lastUsage?.prompt_tokens || 0) - (lastUsage?.cached_tokens || 0)
10194
10403
  },
10195
- payload: { input: message[message.length - 1].content }
10404
+ payload: { input: msgText(message[message.length - 1].content) }
10405
+ // ★ 多模态:trace 只记文本视图
10196
10406
  });
10197
10407
  console.log(`[DSC-DIAG] streamInference round ${round} \u5B8C\u6210: content=${contentBuf.length}chars reasoning=${reasoningBuf.length}chars toolCalls=${toolCallsBuf.size}`);
10198
10408
  return { kind: "completed", assistantMessage, usage: lastUsage };
@@ -10205,7 +10415,8 @@ var init_streamInference = __esm({
10205
10415
  sessionId,
10206
10416
  eventType: "llm.error",
10207
10417
  metadata: { depth, decisionSource: llmDecisionSource, ok: false, durationMs: performance.now() - startTime, attempt: round },
10208
- payload: { input: message[message.length - 1].content, output: err.message }
10418
+ payload: { input: msgText(message[message.length - 1].content), output: err.message }
10419
+ // ★ 多模态:文本视图
10209
10420
  });
10210
10421
  console.log(`[DSC-DIAG] streamInference round ${round} \u2550\u2550\u2550 ERROR: ${err.message}`);
10211
10422
  return { kind: "error", error: err };
@@ -10420,7 +10631,7 @@ async function* runAgent(message, options) {
10420
10631
  const firstPrompt = (() => {
10421
10632
  if (depth !== 0) return "";
10422
10633
  const m = message.find((x) => x?.role === "user");
10423
- return typeof m?.content === "string" ? m.content : "";
10634
+ return msgText(m?.content);
10424
10635
  })();
10425
10636
  const nudges = createNudgeScheduler({ firstPrompt, planMode: !!options.planMode, noEarlyFinal: !!options.noEarlyFinal });
10426
10637
  const userDecisionSource = depth > 0 ? "spawn_agent" : "user";
@@ -10548,7 +10759,7 @@ async function* runAgent(message, options) {
10548
10759
  const assistantMessage = infResult.assistantMessage;
10549
10760
  message.push(assistantMessage);
10550
10761
  await appendMessage({ ...assistantMessage, sessionId });
10551
- if (assistantMessage.content) {
10762
+ if (typeof assistantMessage.content === "string" && assistantMessage.content) {
10552
10763
  lastContent = assistantMessage.content;
10553
10764
  }
10554
10765
  if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
@@ -10641,6 +10852,7 @@ var init_runAgent = __esm({
10641
10852
  init_inbox();
10642
10853
  init_common();
10643
10854
  init_contextCore();
10855
+ init_contentParts();
10644
10856
  init_store();
10645
10857
  init_truncate();
10646
10858
  init_hooks();
@@ -10733,11 +10945,12 @@ var init_recovery = __esm({
10733
10945
  });
10734
10946
 
10735
10947
  // ../core/src/session/content.ts
10736
- var decayOldToolResults, buildContextMessages;
10948
+ var decayOldToolResults, NO_VISION_IMAGE_NOTE, enforceVisionGate, buildContextMessages;
10737
10949
  var init_content = __esm({
10738
10950
  "../core/src/session/content.ts"() {
10739
10951
  "use strict";
10740
10952
  init_contextCore();
10953
+ init_contentParts();
10741
10954
  init_transcript();
10742
10955
  init_store();
10743
10956
  init_config();
@@ -10747,15 +10960,20 @@ var init_content = __esm({
10747
10960
  const cutoffUnitIdx = Math.max(0, units.length - appConfig.KEEP_RECENT_UNITS);
10748
10961
  if (cutoffUnitIdx === 0) return msgs;
10749
10962
  const decayIds = /* @__PURE__ */ new Set();
10963
+ let hasOldImage = false;
10750
10964
  for (let u = 0; u < cutoffUnitIdx; u++) {
10751
10965
  for (const m of units[u]) {
10752
10966
  const mm = m;
10753
10967
  if (mm.role === "tool" && typeof mm.tool_call_id === "string") decayIds.add(mm.tool_call_id);
10968
+ if (hasImagePart(m.content)) hasOldImage = true;
10754
10969
  }
10755
10970
  }
10756
- if (decayIds.size === 0) return msgs;
10971
+ if (decayIds.size === 0 && !hasOldImage) return msgs;
10757
10972
  const keep = appConfig.BOUNDARY_TOOL_KEEP_CHARS;
10973
+ const oldRefs = /* @__PURE__ */ new Set();
10974
+ for (let u = 0; u < cutoffUnitIdx; u++) for (const m of units[u]) oldRefs.add(m);
10758
10975
  return msgs.map((m) => {
10976
+ if (!oldRefs.has(m)) return m;
10759
10977
  const mm = m;
10760
10978
  if (mm.role === "tool" && decayIds.has(mm.tool_call_id) && typeof mm.content === "string" && mm.content.length > keep) {
10761
10979
  const head = mm.content.slice(0, keep);
@@ -10763,9 +10981,11 @@ var init_content = __esm({
10763
10981
 
10764
10982
  [\u2026 \u8BE5\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\u5DF2\u6298\u53E0\uFF08\u5171 ${mm.content.length} \u5B57\u7B26\uFF09\uFF0C\u5982\u9700\u7EC6\u8282\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177 \u2026]` };
10765
10983
  }
10766
- return m;
10984
+ 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]");
10767
10985
  });
10768
10986
  };
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));
10769
10989
  buildContextMessages = async (sessionId, currentUserMsg, systemPrompt) => {
10770
10990
  const lines = await readTranscriptLines(sessionId);
10771
10991
  const store = await getRollingState(sessionId);
@@ -10785,12 +11005,12 @@ var init_content = __esm({
10785
11005
  const messageAll = all.slice(archivedMessageCount);
10786
11006
  if (messageAll.length === 0) {
10787
11007
  result.push(currentUserMsg);
10788
- return result;
11008
+ return enforceVisionGate(result);
10789
11009
  }
10790
11010
  const repair = repairOrphanToolCalls(messageAll, recovery.interruption.kind === "crashed");
10791
11011
  result.push(...decayOldToolResults(repair.msgs));
10792
11012
  result.push(currentUserMsg);
10793
- return result;
11013
+ return enforceVisionGate(result);
10794
11014
  };
10795
11015
  }
10796
11016
  });
@@ -11164,9 +11384,12 @@ var init_loader4 = __esm({
11164
11384
  };
11165
11385
  parseStdoutDecision = (stdout) => {
11166
11386
  const s = (stdout ?? "").trim();
11167
- if (!s || !s.startsWith("{")) return void 0;
11387
+ if (!s) return void 0;
11388
+ const match = s.match(/\{[\s\S]*\}$/);
11389
+ if (!match) return void 0;
11390
+ const jsonStr = match[0];
11168
11391
  try {
11169
- const parsed = JSON.parse(s);
11392
+ const parsed = JSON.parse(jsonStr);
11170
11393
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
11171
11394
  const out = {};
11172
11395
  if (parsed.deny === true) {
@@ -12254,6 +12477,7 @@ var init_chatProcessing = __esm({
12254
12477
  init_store();
12255
12478
  init_transcript();
12256
12479
  init_content();
12480
+ init_contentParts();
12257
12481
  init_trace();
12258
12482
  init_webHost();
12259
12483
  init_registry();
@@ -12309,10 +12533,16 @@ ${lines.join("\n")}
12309
12533
  \u6CE8\u610F\uFF1A\u9879\u76EE\u7EA7\u914D\u7F6E\uFF08.deepseeker-code/\uFF09\u4EC5\u5728\u9ED8\u8BA4\u6839 ${path41.basename(activeReal) || activeReal} \u751F\u6548\u3002`;
12310
12534
  }
12311
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}
12539
+
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
+ }
12312
12542
  try {
12313
12543
  const fullMessages = await buildContextMessages(
12314
12544
  sessionId,
12315
- { role: "user", content: inbound.content },
12545
+ { role: "user", content: userContent },
12316
12546
  sysPrompt
12317
12547
  );
12318
12548
  await emitTrace({
@@ -12325,7 +12555,7 @@ ${lines.join("\n")}
12325
12555
  });
12326
12556
  void sweepStaleAtomicTmp().catch(() => {
12327
12557
  });
12328
- await appendMessage({ sessionId, role: "user", content: inbound.content });
12558
+ await appendMessage({ sessionId, role: "user", content: userContent });
12329
12559
  let streamedAnyText = false;
12330
12560
  const onUIEvent = opts?.onUIEvent ?? ((evt) => sseWrite?.(evt));
12331
12561
  const requestApproval2 = opts?.requestApproval ?? createWebRequestApproval((evt) => sseWrite?.(evt), abortSignal);
@@ -12399,6 +12629,7 @@ var init_fork = __esm({
12399
12629
  "../core/src/session/fork.ts"() {
12400
12630
  "use strict";
12401
12631
  init_common();
12632
+ init_contentParts();
12402
12633
  init_transcript();
12403
12634
  init_store();
12404
12635
  listForkAnchors = (lines) => {
@@ -12408,7 +12639,7 @@ var init_fork = __esm({
12408
12639
  if (isEventLine(l)) continue;
12409
12640
  const row = l;
12410
12641
  if (row?.role === "user") {
12411
- lastUser = typeof row.content === "string" ? row.content : "";
12642
+ lastUser = msgText(row.content) || (hasImagePart(row.content) ? "\u{1F5BC} [\u56FE\u7247\u6D88\u606F]" : "");
12412
12643
  } else if (row?.role === "assistant") {
12413
12644
  const content = typeof row.content === "string" ? row.content : "";
12414
12645
  const names = Array.isArray(row.tool_calls) ? row.tool_calls.map((tc) => tc?.function?.name ?? "").filter(Boolean).join(", ") : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepseeker-code",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "DeepSeeker-Code 终端客户端(React Ink,in-process 直驱 runAgent)",
5
5
  "homepage": "https://github.com/xknk/deepSeekCode#readme",
6
6
  "repository": {