dsh-deepseek-web-login 0.6.33 → 0.6.34

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,45 @@
2
2
 
3
3
  本项目大致遵循语义化版本;日期为本地时间。
4
4
 
5
+ ## 0.6.34 — 2026-10-02
6
+
7
+ **修复:工具返回被当成「用户说的话」发给模型 —— 所以它回「收到,…」而不是回答问题。**
8
+
9
+ 用户现场(原话):「提示词还是把答案直接告诉模型了,难怪最后模型的回答有『收到,…』」
10
+ (附分享页 + 截图:问「你知道现在几点嘛」→ 答「**收到**,2026-10-02 星期五 18:54,傍晚快七点了。」)
11
+
12
+ 先排除掉已修的那一半:0.6.33 确实在跑(日志已有 `firstDiff`),而且**增量已经正常** ——
13
+ `18:51:37 chars=17` / `18:54:09 chars=11` / `18:54:26 chars=14` / `18:54:30 chars=31`,
14
+ 对比修之前的 **40193**。所以这次的「答案在提示词里」不是回声。
15
+
16
+ **证据链**(`dev/dump-turn-input.mjs` 读 DSH 自己的会话日志 + 分享页逐行核对):
17
+
18
+ ```
19
+ turn 3 step 1: user 「你知道现在几点嘛」
20
+ assistant 「pwsh」 ← 工具调用
21
+ step 2: assistant 「收到,2026-10-02 星期五 18:54,傍晚快七点了。」
22
+ ```
23
+
24
+ **中间那条工具返回,在 DSH 的消息列表里根本不存在**(不是 `tool-result` 块)。
25
+ 而分享页里渲染出来的是 `User: 2026-10-02 18:54:28 星期五` ——
26
+ **`User: ` 前缀只可能由我们的"纯文本分支"产生** ⇒ DSH 把 pwsh 的输出当成一条**普通 user 文本**交给我们。
27
+
28
+ 于是模型读到的是"用户告诉了我时间"。它自己的思考原话(分享页里看得到):
29
+
30
+ > 「用户告诉我时间。不需要工具。」
31
+ > 「不过我应该核对一下——**我没拿到工具结果,用户直接给了时间**。那就接受。」
32
+
33
+ ⇒ 回答从"回答几点"变成"收到,…"。**用户看到的"答案被喂进去"是真的,但它的根因不是重发,是错标。**
34
+
35
+ **修法**:`serializePromptParts` 认出这种形状 —— **assistant 发过工具调用 ⇒ 紧随其后的那条 user 文本
36
+ 按构造只能是工具返回**(agent 循环里必须先有工具返回才轮到下一条用户输入),于是标成
37
+ `[Tool Result for <id>]`,不再渲染成 `User: …`。
38
+ 一对多时逐个配对(一个调用一条返回);数量对不齐就合并成一条(宁可少一层对应关系,
39
+ 也不能让它看起来像用户说的话);**带图片的 user 消息不误标**(那是用户真的发的)。
40
+
41
+ **新增 5 条用例**(`check-context-feed` 65 项):必须标成工具返回 / 没有工具调用时不许误标 /
42
+ 多调用多返回逐个配对 / 数量不齐时合并 / 带图消息不误标。**一处变异确认变红**(关掉这条判据 → 3 红)。
43
+
5
44
  ## 0.6.33 — 2026-10-02
6
45
 
7
46
  **修复:断链重发时把模型自己刚说的话又喂了回去 —— 一句话走掉 4 万字符。**
package/lib/index.js CHANGED
@@ -1892,6 +1892,19 @@ function serializePromptParts(options) {
1892
1892
  const toolSection = buildToolSection(options.tools, toolBudget);
1893
1893
  const protocol = toolSection ? `\n\n${protocolText}${toolSection}` : "";
1894
1894
  const lines = [];
1895
+ /**
1896
+ * 上一条 assistant 消息里的工具调用 id —— 用来把**紧随其后的那条 user 文本**认成工具返回。
1897
+ *
1898
+ * 🔴 2026-10-02 现场(用户报「提示词把答案直接告诉模型了,所以它回『收到,…』」):
1899
+ * DSH 执行 `pwsh` 之后,把工具输出当成一条**普通的 user 文本消息**交给我们,于是我们照实
1900
+ * 渲染成 `User: 2026-10-02 18:54:28 星期五` —— 模型读到的是"用户告诉了我时间",
1901
+ * 它自己的思考原话就是:「**我没拿到工具结果,用户直接给了时间**。那就接受。」
1902
+ * 回答于是从"回答几点"变成"收到,…" —— 看着就像在复述一个已知答案。
1903
+ *
1904
+ * 判据为什么成立:在 agent 循环里,assistant 发出工具调用之后**必须先有工具返回**,
1905
+ * 才轮得到下一条用户输入。所以紧跟其后的那条 user 文本,按构造只能是工具返回。
1906
+ */
1907
+ let pendingToolCallIds = [];
1895
1908
  for (const message of options.messages ?? []) {
1896
1909
  if (!message || typeof message !== "object") continue;
1897
1910
  const blocks = Array.isArray(message.content) ? message.content : [];
@@ -1903,6 +1916,7 @@ function serializePromptParts(options) {
1903
1916
  if (message.role === "assistant") {
1904
1917
  const text = flattenText(blocks).join("");
1905
1918
  const renderedCalls = renderToolCalls(blocks);
1919
+ pendingToolCallIds = renderedCalls ? blocks.filter((block) => block?.type === "tool-call").map((block) => String(block.toolCallId ?? block.id ?? "")) : [];
1906
1920
  if (renderedCalls) lines.push(`Assistant: ${renderedCalls}`);
1907
1921
  else if (text.trim()) lines.push(`Assistant: ${text}`);
1908
1922
  continue;
@@ -1911,6 +1925,15 @@ function serializePromptParts(options) {
1911
1925
  const text = flattenText(blocks.filter((block) => block?.type !== "tool-result")).join("");
1912
1926
  const imageMarks = blockImageMarks(blocks, options.keptImageKeys);
1913
1927
  const images = imageMarks.length;
1928
+ if (toolResults.length === 0 && pendingToolCallIds.length > 0 && images === 0 && text.trim()) {
1929
+ const textBlocks = blocks.filter((block) => block?.type === "text" && typeof block.text === "string" && block.text.trim()).map((block) => block.text);
1930
+ const ids = pendingToolCallIds;
1931
+ pendingToolCallIds = [];
1932
+ if (textBlocks.length === ids.length && ids.length > 0) for (let i = 0; i < ids.length; i += 1) lines.push(`[Tool Result for ${ids[i]}]\n${textBlocks[i]}`);
1933
+ else lines.push(`[Tool Result for ${ids.filter(Boolean).join(", ")}]\n${text}`);
1934
+ continue;
1935
+ }
1936
+ pendingToolCallIds = [];
1914
1937
  if (text.trim() || toolResults.length === 0 && images === 0 || images > 0) {
1915
1938
  const imageNote = imageMarks.length > 0 ? `\n${imageMarks.join(" ")}` : "";
1916
1939
  lines.push(`User: ${text}${imageNote}`);
@@ -10419,7 +10442,7 @@ async function checkForUpdate(current, fetchImpl) {
10419
10442
  * 兜底常量与 package.json 的一致性由 `tests/check-smoke.mjs` 守着,不会漂。
10420
10443
  */
10421
10444
  /** 与 package.json 保持一致的兜底版本(由测试保证不会漂)。 */
10422
- const FALLBACK_VERSION = "0.6.33";
10445
+ const FALLBACK_VERSION = "0.6.34";
10423
10446
  let cached;
10424
10447
  /** 本插件版本(如 `0.1.26`)。 */
10425
10448
  function pluginVersion() {