deepseeker-code 1.0.47 → 1.0.48

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 +316 -98
  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",
@@ -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
  },
@@ -5970,6 +6120,7 @@ var init_command = __esm({
5970
6120
  init_type();
5971
6121
  init_guard();
5972
6122
  init_background();
6123
+ init_config();
5973
6124
  EXIT_SENTINEL = (code) => `
5974
6125
  \u27E6DSC_EXIT:${code}\u27E7`;
5975
6126
  EXIT_SENTINEL_RE = /⟦DSC_EXIT:(-?\d+)⟧/g;
@@ -5979,7 +6130,7 @@ var init_command = __esm({
5979
6130
  type: "function",
5980
6131
  function: {
5981
6132
  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",
6133
+ 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
6134
  parameters: {
5984
6135
  type: "object",
5985
6136
  properties: {
@@ -6024,6 +6175,10 @@ ${tail}`,
6024
6175
  const cwd = args.cwd ? resolveSafePath(args.cwd) : getActiveWorkspaceRoot();
6025
6176
  const maxChars = RUN_COMMAND_MAX_CHARS;
6026
6177
  let totalYieldedChars = 0;
6178
+ let fullOutput = "";
6179
+ let degraded = false;
6180
+ let degradeDue = false;
6181
+ let degradeReason = null;
6027
6182
  const isWin = process.platform === "win32";
6028
6183
  if (ctx?.abortSignal?.aborted) {
6029
6184
  yield `\u274C [\u5DF2\u4E2D\u6B62]\uFF1A\u547D\u4EE4 [${args.command}] \u672A\u6267\u884C\uFF08\u7528\u6237\u5DF2\u4E2D\u65AD\uFF09\u3002`;
@@ -6078,6 +6233,15 @@ ${tail}`,
6078
6233
  }
6079
6234
  return (decoder ?? pendingUtf8).decode(buf, { stream: true });
6080
6235
  };
6236
+ const codec = {
6237
+ decode: decodeChunk,
6238
+ flush: () => {
6239
+ let s = "";
6240
+ if (decoder) s += decoder.decode();
6241
+ s += pendingUtf8.decode();
6242
+ return s;
6243
+ }
6244
+ };
6081
6245
  proc.stdout?.on("data", (d) => {
6082
6246
  queue.push(decodeChunk(d));
6083
6247
  notifyNewData();
@@ -6104,11 +6268,22 @@ ${tail}`,
6104
6268
  void killTree(proc);
6105
6269
  };
6106
6270
  ctx?.abortSignal?.addEventListener("abort", onAbort);
6271
+ const IDLE_TIMEOUT_MS = Number(process.env.RUN_COMMAND_IDLE_TIMEOUT_MS) || 6e4;
6272
+ const autoBgMs = Math.max(1, appConfig.runCommandAutoBgMs);
6273
+ const runtimeTimer = setTimeout(() => {
6274
+ degradeDue = true;
6275
+ notifyNewData();
6276
+ }, autoBgMs);
6277
+ runtimeTimer.unref?.();
6107
6278
  let overLimit = false;
6108
6279
  try {
6109
- while ((!settled || queue.length > 0) && !overLimit) {
6280
+ while ((!settled || queue.length > 0) && !overLimit && !degraded) {
6281
+ if (degradeDue && !settled) {
6282
+ degraded = true;
6283
+ degradeReason = "runtime";
6284
+ break;
6285
+ }
6110
6286
  if (queue.length === 0) {
6111
- const IDLE_TIMEOUT_MS = 6e4;
6112
6287
  let timedOut = false;
6113
6288
  await new Promise((r) => {
6114
6289
  resolveWaiter = r;
@@ -6117,20 +6292,26 @@ ${tail}`,
6117
6292
  r();
6118
6293
  }, IDLE_TIMEOUT_MS);
6119
6294
  });
6295
+ if (degradeDue && !settled) {
6296
+ degraded = true;
6297
+ degradeReason = "runtime";
6298
+ break;
6299
+ }
6120
6300
  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;
6301
+ degraded = true;
6302
+ degradeReason = "idle";
6128
6303
  break;
6129
6304
  }
6130
6305
  }
6131
6306
  while (queue.length > 0) {
6307
+ if (degradeDue && !settled) {
6308
+ degraded = true;
6309
+ degradeReason = "runtime";
6310
+ break;
6311
+ }
6132
6312
  const chunk = queue.shift();
6133
6313
  totalYieldedChars += chunk.length;
6314
+ fullOutput += chunk;
6134
6315
  if (totalYieldedChars > maxChars) {
6135
6316
  yield `
6136
6317
 
@@ -6144,14 +6325,30 @@ ${tail}`,
6144
6325
  yield chunk;
6145
6326
  }
6146
6327
  }
6147
- if (overLimit) {
6328
+ if (degraded) {
6329
+ const taskId = adoptRunningProcess(
6330
+ proc,
6331
+ { command: args.command, cwd, sessionId: ctx?.sessionId, signal: ctx?.abortSignal },
6332
+ fullOutput + queue.join(""),
6333
+ codec
6334
+ );
6335
+ 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`;
6336
+ yield `
6337
+
6338
+ \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
6339
+ task_id: ${taskId}
6340
+ \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
6341
+ `;
6342
+ } else if (overLimit) {
6148
6343
  yield EXIT_SENTINEL(-1);
6149
6344
  } else if (exitCode !== null) {
6150
6345
  yield EXIT_SENTINEL(exitCode);
6151
6346
  }
6152
6347
  } finally {
6153
6348
  if (idleTimer) clearTimeout(idleTimer);
6349
+ clearTimeout(runtimeTimer);
6154
6350
  ctx?.abortSignal?.removeEventListener("abort", onAbort);
6351
+ if (!settled && !degraded) void killTree(proc);
6155
6352
  }
6156
6353
  }
6157
6354
  }
@@ -10033,6 +10230,7 @@ var init_streamInference = __esm({
10033
10230
  init_truncate();
10034
10231
  init_transcript();
10035
10232
  init_contextCore();
10233
+ init_contentParts();
10036
10234
  streamInference = async function* (ctx) {
10037
10235
  const {
10038
10236
  message,
@@ -10060,7 +10258,8 @@ var init_streamInference = __esm({
10060
10258
  eventType: "llm.request",
10061
10259
  metadata: { depth, decisionSource: userDecisionSource, ok: true, durationMs: performance.now() - startTime, round },
10062
10260
  usage: { prompt_tokens: estimateTokens(inferenceMessages) },
10063
- payload: { input: message[message.length - 1].content }
10261
+ payload: { input: msgText(message[message.length - 1].content) }
10262
+ // ★ 多模态:trace 只记文本视图
10064
10263
  });
10065
10264
  console.log(`\u{1F504} \u4EE3\u7406\u63A8\u7406\u7B2C ${round} \u8F6E\uFF08\u6D41\u5F0F\uFF09...`);
10066
10265
  let contentBuf = "";
@@ -10192,7 +10391,8 @@ var init_streamInference = __esm({
10192
10391
  prompt_cache_hit_tokens: lastUsage?.cached_tokens,
10193
10392
  prompt_cache_miss_tokens: (lastUsage?.prompt_tokens || 0) - (lastUsage?.cached_tokens || 0)
10194
10393
  },
10195
- payload: { input: message[message.length - 1].content }
10394
+ payload: { input: msgText(message[message.length - 1].content) }
10395
+ // ★ 多模态:trace 只记文本视图
10196
10396
  });
10197
10397
  console.log(`[DSC-DIAG] streamInference round ${round} \u5B8C\u6210: content=${contentBuf.length}chars reasoning=${reasoningBuf.length}chars toolCalls=${toolCallsBuf.size}`);
10198
10398
  return { kind: "completed", assistantMessage, usage: lastUsage };
@@ -10205,7 +10405,8 @@ var init_streamInference = __esm({
10205
10405
  sessionId,
10206
10406
  eventType: "llm.error",
10207
10407
  metadata: { depth, decisionSource: llmDecisionSource, ok: false, durationMs: performance.now() - startTime, attempt: round },
10208
- payload: { input: message[message.length - 1].content, output: err.message }
10408
+ payload: { input: msgText(message[message.length - 1].content), output: err.message }
10409
+ // ★ 多模态:文本视图
10209
10410
  });
10210
10411
  console.log(`[DSC-DIAG] streamInference round ${round} \u2550\u2550\u2550 ERROR: ${err.message}`);
10211
10412
  return { kind: "error", error: err };
@@ -10420,7 +10621,7 @@ async function* runAgent(message, options) {
10420
10621
  const firstPrompt = (() => {
10421
10622
  if (depth !== 0) return "";
10422
10623
  const m = message.find((x) => x?.role === "user");
10423
- return typeof m?.content === "string" ? m.content : "";
10624
+ return msgText(m?.content);
10424
10625
  })();
10425
10626
  const nudges = createNudgeScheduler({ firstPrompt, planMode: !!options.planMode, noEarlyFinal: !!options.noEarlyFinal });
10426
10627
  const userDecisionSource = depth > 0 ? "spawn_agent" : "user";
@@ -10548,7 +10749,7 @@ async function* runAgent(message, options) {
10548
10749
  const assistantMessage = infResult.assistantMessage;
10549
10750
  message.push(assistantMessage);
10550
10751
  await appendMessage({ ...assistantMessage, sessionId });
10551
- if (assistantMessage.content) {
10752
+ if (typeof assistantMessage.content === "string" && assistantMessage.content) {
10552
10753
  lastContent = assistantMessage.content;
10553
10754
  }
10554
10755
  if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
@@ -10641,6 +10842,7 @@ var init_runAgent = __esm({
10641
10842
  init_inbox();
10642
10843
  init_common();
10643
10844
  init_contextCore();
10845
+ init_contentParts();
10644
10846
  init_store();
10645
10847
  init_truncate();
10646
10848
  init_hooks();
@@ -10733,11 +10935,12 @@ var init_recovery = __esm({
10733
10935
  });
10734
10936
 
10735
10937
  // ../core/src/session/content.ts
10736
- var decayOldToolResults, buildContextMessages;
10938
+ var decayOldToolResults, NO_VISION_IMAGE_NOTE, enforceVisionGate, buildContextMessages;
10737
10939
  var init_content = __esm({
10738
10940
  "../core/src/session/content.ts"() {
10739
10941
  "use strict";
10740
10942
  init_contextCore();
10943
+ init_contentParts();
10741
10944
  init_transcript();
10742
10945
  init_store();
10743
10946
  init_config();
@@ -10747,15 +10950,20 @@ var init_content = __esm({
10747
10950
  const cutoffUnitIdx = Math.max(0, units.length - appConfig.KEEP_RECENT_UNITS);
10748
10951
  if (cutoffUnitIdx === 0) return msgs;
10749
10952
  const decayIds = /* @__PURE__ */ new Set();
10953
+ let hasOldImage = false;
10750
10954
  for (let u = 0; u < cutoffUnitIdx; u++) {
10751
10955
  for (const m of units[u]) {
10752
10956
  const mm = m;
10753
10957
  if (mm.role === "tool" && typeof mm.tool_call_id === "string") decayIds.add(mm.tool_call_id);
10958
+ if (hasImagePart(m.content)) hasOldImage = true;
10754
10959
  }
10755
10960
  }
10756
- if (decayIds.size === 0) return msgs;
10961
+ if (decayIds.size === 0 && !hasOldImage) return msgs;
10757
10962
  const keep = appConfig.BOUNDARY_TOOL_KEEP_CHARS;
10963
+ const oldRefs = /* @__PURE__ */ new Set();
10964
+ for (let u = 0; u < cutoffUnitIdx; u++) for (const m of units[u]) oldRefs.add(m);
10758
10965
  return msgs.map((m) => {
10966
+ if (!oldRefs.has(m)) return m;
10759
10967
  const mm = m;
10760
10968
  if (mm.role === "tool" && decayIds.has(mm.tool_call_id) && typeof mm.content === "string" && mm.content.length > keep) {
10761
10969
  const head = mm.content.slice(0, keep);
@@ -10763,9 +10971,11 @@ var init_content = __esm({
10763
10971
 
10764
10972
  [\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
10973
  }
10766
- return m;
10974
+ 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
10975
  });
10768
10976
  };
10977
+ 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]";
10978
+ enforceVisionGate = (msgs) => isVisionEnabled() ? msgs : msgs.map((m) => collapseToText(m, NO_VISION_IMAGE_NOTE));
10769
10979
  buildContextMessages = async (sessionId, currentUserMsg, systemPrompt) => {
10770
10980
  const lines = await readTranscriptLines(sessionId);
10771
10981
  const store = await getRollingState(sessionId);
@@ -10785,12 +10995,12 @@ var init_content = __esm({
10785
10995
  const messageAll = all.slice(archivedMessageCount);
10786
10996
  if (messageAll.length === 0) {
10787
10997
  result.push(currentUserMsg);
10788
- return result;
10998
+ return enforceVisionGate(result);
10789
10999
  }
10790
11000
  const repair = repairOrphanToolCalls(messageAll, recovery.interruption.kind === "crashed");
10791
11001
  result.push(...decayOldToolResults(repair.msgs));
10792
11002
  result.push(currentUserMsg);
10793
- return result;
11003
+ return enforceVisionGate(result);
10794
11004
  };
10795
11005
  }
10796
11006
  });
@@ -12254,6 +12464,7 @@ var init_chatProcessing = __esm({
12254
12464
  init_store();
12255
12465
  init_transcript();
12256
12466
  init_content();
12467
+ init_contentParts();
12257
12468
  init_trace();
12258
12469
  init_webHost();
12259
12470
  init_registry();
@@ -12309,10 +12520,16 @@ ${lines.join("\n")}
12309
12520
  \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
12521
  }
12311
12522
  let replyText = "";
12523
+ let userContent = inbound.content;
12524
+ if (Array.isArray(inbound.attachments) && inbound.attachments.length > 0) {
12525
+ userContent = isVisionEnabled() ? toWireUserContent(inbound.content, inbound.attachments) : `${inbound.content}
12526
+
12527
+ \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`;
12528
+ }
12312
12529
  try {
12313
12530
  const fullMessages = await buildContextMessages(
12314
12531
  sessionId,
12315
- { role: "user", content: inbound.content },
12532
+ { role: "user", content: userContent },
12316
12533
  sysPrompt
12317
12534
  );
12318
12535
  await emitTrace({
@@ -12325,7 +12542,7 @@ ${lines.join("\n")}
12325
12542
  });
12326
12543
  void sweepStaleAtomicTmp().catch(() => {
12327
12544
  });
12328
- await appendMessage({ sessionId, role: "user", content: inbound.content });
12545
+ await appendMessage({ sessionId, role: "user", content: userContent });
12329
12546
  let streamedAnyText = false;
12330
12547
  const onUIEvent = opts?.onUIEvent ?? ((evt) => sseWrite?.(evt));
12331
12548
  const requestApproval2 = opts?.requestApproval ?? createWebRequestApproval((evt) => sseWrite?.(evt), abortSignal);
@@ -12399,6 +12616,7 @@ var init_fork = __esm({
12399
12616
  "../core/src/session/fork.ts"() {
12400
12617
  "use strict";
12401
12618
  init_common();
12619
+ init_contentParts();
12402
12620
  init_transcript();
12403
12621
  init_store();
12404
12622
  listForkAnchors = (lines) => {
@@ -12408,7 +12626,7 @@ var init_fork = __esm({
12408
12626
  if (isEventLine(l)) continue;
12409
12627
  const row = l;
12410
12628
  if (row?.role === "user") {
12411
- lastUser = typeof row.content === "string" ? row.content : "";
12629
+ lastUser = msgText(row.content) || (hasImagePart(row.content) ? "\u{1F5BC} [\u56FE\u7247\u6D88\u606F]" : "");
12412
12630
  } else if (row?.role === "assistant") {
12413
12631
  const content = typeof row.content === "string" ? row.content : "";
12414
12632
  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.48",
4
4
  "description": "DeepSeeker-Code 终端客户端(React Ink,in-process 直驱 runAgent)",
5
5
  "homepage": "https://github.com/xknk/deepSeekCode#readme",
6
6
  "repository": {