dsh-remote-bridge 2.2.3 → 2.2.4

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/lib/index.js CHANGED
@@ -389,7 +389,7 @@ var DshChatAdapter = class {
389
389
  payload
390
390
  }));
391
391
  }
392
- async prompt(principal, sessionId, parts, mode, clientTimeZone) {
392
+ async prompt(principal, sessionId, parts, mode, clientTimeZone, locale) {
393
393
  await this.requireSession(principal, sessionId);
394
394
  const hasText = parts.some((part) => part.type === "text" && part.text.trim() !== "");
395
395
  const hasAttachment = parts.some((part) => part.type !== "text");
@@ -400,7 +400,8 @@ var DshChatAdapter = class {
400
400
  sessionId,
401
401
  mode,
402
402
  content: parts,
403
- ...clientTimeZone === void 0 ? {} : { clientTimeZone }
403
+ ...clientTimeZone === void 0 ? {} : { clientTimeZone },
404
+ ...locale === void 0 ? {} : { locale }
404
405
  }
405
406
  }));
406
407
  }
@@ -1956,18 +1957,29 @@ async function modelIsConfirmedTextOnly(host, sessionId) {
1956
1957
  if (!Array.isArray(modalities)) return false;
1957
1958
  return !modalities.includes("image");
1958
1959
  }
1960
+ /** 从 `Accept-Language` 里挑一个我们支持的语种;认不出来就是中文。 */
1961
+ function handoffLocaleOf(acceptLanguage) {
1962
+ if (acceptLanguage === void 0) return "zh";
1963
+ for (const tag of acceptLanguage.split(",")) {
1964
+ const language = (tag.split(";")[0] ?? "").trim().toLowerCase();
1965
+ if (language.startsWith("zh")) return "zh";
1966
+ if (language.startsWith("en")) return "en";
1967
+ }
1968
+ return "zh";
1969
+ }
1959
1970
  /**
1960
- * 需要交接时,把每个 `image` 部件换成**两行文本**;否则原样返回。
1961
- *
1962
- * 同一行路径要同时伺候两类读者:
1963
- * - **给人看**(第一行):图片序号、原始文件名、工作目录里的短路径;
1964
- * - **给机器看**(第二行 `[Image: source: <绝对路径>]`):这是读图技能明确
1965
- * 识别的占位形状之一(带图片扩展名的绝对路径同样触发),任何按路径读图的
1966
- * 工具都能接手,不出现任何具体插件名。
1971
+ * 交接说明的第一行(给人看的那行)。
1967
1972
  *
1968
- * 任何一步抛错都退回原内容:这个功能的失败**不该**把「发消息」本身弄坏。
1973
+ * 写的是**人话**,不是挂在用户眼前的祈使句 —— 早期版本写「请先用读图工具打开下面那行的
1974
+ * 绝对路径」,那是写给机器看的措辞,用户看了的反馈是看不懂。现在一句话说清两件事:
1975
+ * 模型看不到图、图存在哪;要看图该怎么办。语言跟随界面(见 [handoffLocaleOf])。
1976
+ * 第二行的 `[Image: source: …]` 是机器契约,跨语言不变。
1969
1977
  */
1970
- async function handoffImagesForTextOnlyModel(host, roots, fallbackDir, sessionId, content) {
1978
+ function handoffLine(index, name, relative, locale) {
1979
+ if (locale === "en") return `🖼️ Image ${index} "${name}" (this model can't see images, saved to ${relative}; to view it, open the path below with an image-reading tool)`;
1980
+ return `🖼️ 图片 ${index}「${name}」(当前模型看不到图,已存到 ${relative};要看图,请用识图工具打开下面那行的路径)`;
1981
+ }
1982
+ async function handoffImagesForTextOnlyModel(host, roots, fallbackDir, locale, sessionId, content) {
1971
1983
  if (!content.some((part) => part.type === "image")) return content;
1972
1984
  try {
1973
1985
  if (!await modelIsConfirmedTextOnly(host, sessionId)) return content;
@@ -1978,10 +1990,10 @@ async function handoffImagesForTextOnlyModel(host, roots, fallbackDir, sessionId
1978
1990
  if (part.type !== "image") return part;
1979
1991
  index += 1;
1980
1992
  const { absolute, relative } = writeHandoffFile(dir, workspace, part.mediaType, part.data);
1981
- const name = typeof part.name === "string" && part.name.trim() !== "" ? part.name.trim() : `图片 ${index}`;
1993
+ const name = typeof part.name === "string" && part.name.trim() !== "" ? part.name.trim() : locale === "en" ? `image ${index}` : `图片 ${index}`;
1982
1994
  return {
1983
1995
  type: "text",
1984
- text: [`🖼️ 图片 ${index}「${name}」(当前模型不支持图片输入,已保存到工作目录 ${relative};请先用读图工具打开下面那行的绝对路径,看清内容再回答)`, `[Image: source: ${absolute}]`].join("\n")
1996
+ text: [handoffLine(index, name, relative, locale), `[Image: source: ${absolute}]`].join("\n")
1985
1997
  };
1986
1998
  });
1987
1999
  } catch {
@@ -2106,15 +2118,16 @@ function createDsh02ApiProxy(host, options = {}) {
2106
2118
  }),
2107
2119
  prompt: (request) => call(async () => {
2108
2120
  const requestId = randomUUID();
2121
+ const { locale, ...kernelPayload } = request.payload;
2109
2122
  const receiptIds = request.payload.content.filter((part) => part.type === "file").map((part) => part.receiptId);
2110
- const content = imageHandoff ? await handoffImagesForTextOnlyModel(host, handoffRoots, imageHandoffDir, request.payload.sessionId, request.payload.content) : request.payload.content;
2123
+ const content = imageHandoff ? await handoffImagesForTextOnlyModel(host, handoffRoots, imageHandoffDir, locale ?? "zh", request.payload.sessionId, request.payload.content) : request.payload.content;
2111
2124
  const binding = receiptIds.length === 0 ? void 0 : await (async () => {
2112
2125
  if (fileUploadService == null) throw new Error("FILE_UPLOADS_UNAVAILABLE: this DSH build provides no ctx.fileUploads service, so file attachments cannot be sent");
2113
2126
  return fileUploadService.bindPrompt(await agentFor(request.payload.sessionId), receiptIds, requestId);
2114
2127
  })();
2115
2128
  await host.sessionController.prompt({
2116
2129
  requestId,
2117
- ...request.payload,
2130
+ ...kernelPayload,
2118
2131
  content
2119
2132
  }, new AbortController().signal);
2120
2133
  binding?.commit();
@@ -3356,7 +3369,8 @@ var ApiRouter = class {
3356
3369
  }
3357
3370
  const parts = readPromptParts(body);
3358
3371
  const mode = body.mode === "steer" ? "steer" : "queue";
3359
- const response = { message: await this.options.chat.prompt(principal, sessionId, parts, mode, typeof body.clientTimeZone === "string" ? body.clientTimeZone : void 0) };
3372
+ const acceptLanguage = req.headers["accept-language"];
3373
+ const response = { message: await this.options.chat.prompt(principal, sessionId, parts, mode, typeof body.clientTimeZone === "string" ? body.clientTimeZone : void 0, handoffLocaleOf(Array.isArray(acceptLanguage) ? acceptLanguage[0] : acceptLanguage)) };
3360
3374
  this.saveIdempotent(principal, body.clientRequestId, 202, response);
3361
3375
  this.audit(principal, "chat.message.submit", void 0, void 0, requestId, {
3362
3376
  sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-bridge",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "description": "Versioned REST bridge for DeepSeek Harness: remote access for trusted clients (chat, files, devices) plus a Phone-settings panel inside the DSH WebUI.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.19.0",