@shgroup/dsh-serenity-hooks 1.20.4 → 1.20.6

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/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.20.4",
3
+ "version": "1.20.6",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/loop + 拦截缝机械约束(safe-mode/路径守卫)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。私有(dsh-external 组织)。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -8,11 +8,11 @@ window.__ModuleLoader__.load({
8
8
  let react = require("react");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SafeModePanel
11
- const css$1 = "/* SafeModePanel — 会话头部 Serenity 状态徽章(点击展开详情卡)。\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 尺寸与官方 header 标签一致;前缀 sp- 防冲突。 */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 4px 0 4px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 24px;\n white-space: nowrap;\n}\n\n.sp-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 22px;\n padding: 0 6px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-badge:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-badge:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 18px;\n padding: 0 8px;\n border: none;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggle:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-toggle:disabled {\n opacity: 0.55;\n cursor: default;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.sp-toggleOn:hover:not(:disabled) {\n background: var(--dsw-alias-state-error-secondary);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleOff:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleLg {\n height: 26px;\n padding: 0 14px;\n font-size: 13px;\n line-height: 26px;\n border-radius: 6px;\n}\n\n/* 详情卡:相对徽章绝对定位(右对齐),卡片 token */\n.sp-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 20;\n width: 300px;\n box-sizing: border-box;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-specific-tip);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-popRow {\n display: flex;\n align-items: baseline;\n gap: 10px;\n min-width: 0;\n}\n\n.sp-popLabel {\n flex: none;\n width: 34px;\n font-size: 12px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-popValue {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-popActions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n/* loop 运行状态区块(WebUI 等待界面;并行任务各自一行) */\n.sp-popSection {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 2px 0 2px 44px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-loopLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-state-info-primary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-loopResp {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sp-toggle,\n .sp-badge {\n transition: none;\n }\n}\n";
11
+ const css = "/* SafeModePanel — 会话头部 Serenity 状态徽章(点击展开详情卡)。\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 尺寸与官方 header 标签一致;前缀 sp- 防冲突。 */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 4px 0 4px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 24px;\n white-space: nowrap;\n}\n\n.sp-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 22px;\n padding: 0 6px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-badge:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-badge:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 18px;\n padding: 0 8px;\n border: none;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggle:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-toggle:disabled {\n opacity: 0.55;\n cursor: default;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.sp-toggleOn:hover:not(:disabled) {\n background: var(--dsw-alias-state-error-secondary);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleOff:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleLg {\n height: 26px;\n padding: 0 14px;\n font-size: 13px;\n line-height: 26px;\n border-radius: 6px;\n}\n\n/* 详情卡:相对徽章绝对定位(右对齐),卡片 token */\n.sp-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 20;\n width: 300px;\n box-sizing: border-box;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-specific-tip);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-popRow {\n display: flex;\n align-items: baseline;\n gap: 10px;\n min-width: 0;\n}\n\n.sp-popLabel {\n flex: none;\n width: 34px;\n font-size: 12px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-popValue {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-popActions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n/* loop 运行状态区块(WebUI 等待界面;并行任务各自一行) */\n.sp-popSection {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 2px 0 2px 44px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-loopLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-state-info-primary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-loopResp {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sp-toggle,\n .sp-badge {\n transition: none;\n }\n}\n";
12
12
  if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
13
13
  const tag = document.createElement("style");
14
14
  tag.setAttribute("data-sp-css", "1");
15
- tag.textContent = css$1;
15
+ tag.textContent = css;
16
16
  document.head.appendChild(tag);
17
17
  }
18
18
  //#endregion
@@ -240,95 +240,19 @@ window.__ModuleLoader__.load({
240
240
  });
241
241
  }
242
242
  //#endregion
243
- //#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/ImageFallbackDock
244
- const css = "/* ImageFallbackDock — 图片自动落盘兜底状态条(S142) */\n\n.serenity-image-fallback {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n line-height: 1.5;\n padding: 2px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-surface-muted, rgba(127, 127, 127, 0.1));\n color: var(--dsw-alias-text-secondary, currentColor);\n}\n\n.serenity-image-fallback[data-state='busy'] {\n color: var(--dsw-alias-text-secondary, currentColor);\n}\n\n.serenity-image-fallback[data-state='done'] {\n color: var(--dsw-alias-text-success, currentColor);\n}\n\n.serenity-image-fallback[data-state='error'] {\n color: var(--dsw-alias-text-danger, currentColor);\n}\n\n.serenity-image-fallback code {\n font-family: var(--dsw-alias-font-mono, monospace);\n font-size: 11px;\n background: var(--dsw-alias-surface-muted, rgba(127, 127, 127, 0.12));\n border-radius: 4px;\n padding: 0 4px;\n}\n";
245
- if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
246
- const tag = document.createElement("style");
247
- tag.setAttribute("data-sp-css", "1");
248
- tag.textContent = css;
249
- document.head.appendChild(tag);
250
- }
251
- //#endregion
252
- //#region src/client/ImageFallbackDock.tsx
253
- /** input.dock 条目:图片发送失败自动落盘 + 文本重发(补救后自动清空输入框 rail 图片) */
254
- function ImageFallbackDock(props) {
255
- const zone = props;
256
- const session = zone.session;
257
- const input = zone.input;
258
- const sessionId = props.sessionId;
259
- const { uploadImage, getDraftFiles, resendText } = props;
260
- const inputActions = props.inputActions;
261
- const [state, setState] = (0, react.useState)("idle");
262
- const [paths, setPaths] = (0, react.useState)([]);
263
- const [errorText, setErrorText] = (0, react.useState)(null);
264
- const handlingRef = (0, react.useRef)(false);
265
- const promptError = session?.promptError?.error;
266
- const imageIds = input?.imageIds ?? [];
267
- (0, react.useEffect)(() => {
268
- if (promptError?.code !== "attachment-error") return;
269
- if (promptError.details?.reason !== "MODEL_DOES_NOT_SUPPORT_IMAGES") return;
270
- if (handlingRef.current || imageIds.length === 0) return;
271
- handlingRef.current = true;
272
- setState("uploading");
273
- (async () => {
274
- try {
275
- const files = await getDraftFiles(String(sessionId), imageIds);
276
- if (files.length === 0) throw new Error("no draft image files");
277
- const saved = [];
278
- for (const file of files) saved.push(await uploadImage(file, String(sessionId)));
279
- const note = saved.map((p) => `用户提供了图片在 ${p}`).join("\n");
280
- const draft = input?.draft ?? "";
281
- const text = draft === "" ? note : `${draft}\n${note}`;
282
- if (inputActions?.removeImage !== void 0 && inputActions.setDraft !== void 0 && inputActions.submit !== void 0) {
283
- for (const id of imageIds) inputActions.removeImage(String(id));
284
- inputActions.setDraft(text);
285
- inputActions.submit();
286
- } else await resendText(String(sessionId), text);
287
- setPaths(saved);
288
- setState("done");
289
- } catch (err) {
290
- const message = String(err?.message ?? err);
291
- console.warn(`[serenity] image fallback failed: ${message}`);
292
- setErrorText(message);
293
- setState("error");
294
- } finally {
295
- handlingRef.current = false;
296
- }
297
- })();
298
- }, [
299
- promptError,
300
- imageIds,
301
- sessionId,
302
- uploadImage,
303
- getDraftFiles,
304
- resendText,
305
- input?.draft,
306
- inputActions
307
- ]);
308
- if (state === "idle") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
309
- className: "serenity-image-fallback",
310
- "data-state": "idle"
311
- });
312
- if (state === "uploading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
313
- className: "serenity-image-fallback",
314
- "data-state": "busy",
315
- children: "图片已保存中…"
316
- });
317
- if (state === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
318
- className: "serenity-image-fallback",
319
- "data-state": "done",
320
- children: paths.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: p }, p))
321
- });
322
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
323
- className: "serenity-image-fallback",
324
- "data-state": "error",
325
- children: ["图片保存失败:", errorText ?? "未知错误"]
326
- });
327
- }
328
- //#endregion
329
243
  //#region src/client/image-fallback-api.ts
330
244
  /** 图片落盘接口路径(node half api.ts,client 专属 x-serenity-ui 头) */
331
245
  const UPLOAD_PATH = "/serenity/image-upload";
246
+ /**
247
+ * 图片提示消息模板(协议固有,S142 用户迭代):
248
+ * - v1.20.5 目录级提示(无文件名)→ 用户反馈 agent 还要猜
249
+ * - v1.20.6 恢复具体路径(对话里一定写名图片路径,agent 直接可用,无需猜)
250
+ * 单图:用户提供了一张图片(路径:...);多图:每张一行路径
251
+ */
252
+ function imageNoteTemplate(paths) {
253
+ if (paths.length === 1) return `用户提供了一张图片(路径:${paths[0]})`;
254
+ return `用户提供了 ${paths.length} 张图片:\n${paths.map((p) => `- ${p}`).join("\n")}`;
255
+ }
332
256
  /** 浏览器 File → base64(与 ui-conversation serializeImages 等价的最小实现) */
333
257
  function fileToBase64(file) {
334
258
  return file.arrayBuffer().then((buffer) => {
@@ -389,6 +313,56 @@ window.__ModuleLoader__.load({
389
313
  if (!result.ok) throw new Error(`serenity image fallback resend failed: ${result.error?.code}: ${result.error?.message}`);
390
314
  }
391
315
  //#endregion
316
+ //#region src/client/ImageFallbackDock.tsx
317
+ /** input.dock 条目(静默):图片发送失败自动落盘 + 文本重发,无 UI */
318
+ function ImageFallbackDock(props) {
319
+ const zone = props;
320
+ const session = zone.session;
321
+ const input = zone.input;
322
+ const sessionId = props.sessionId;
323
+ const { uploadImage, getDraftFiles, resendText } = props;
324
+ const inputActions = props.inputActions;
325
+ const handlingRef = (0, react.useRef)(false);
326
+ const promptError = session?.promptError?.error;
327
+ const imageIds = input?.imageIds ?? [];
328
+ (0, react.useEffect)(() => {
329
+ if (promptError?.code !== "attachment-error") return;
330
+ if (promptError.details?.reason !== "MODEL_DOES_NOT_SUPPORT_IMAGES") return;
331
+ if (handlingRef.current || imageIds.length === 0) return;
332
+ handlingRef.current = true;
333
+ (async () => {
334
+ try {
335
+ const files = await getDraftFiles(String(sessionId), imageIds);
336
+ if (files.length === 0) throw new Error("no draft image files");
337
+ const saved = [];
338
+ for (const file of files) saved.push(await uploadImage(file, String(sessionId)));
339
+ const note = imageNoteTemplate(saved);
340
+ const draft = input?.draft ?? "";
341
+ const text = draft === "" ? note : `${draft}\n${note}`;
342
+ if (inputActions?.removeImage !== void 0 && inputActions.setDraft !== void 0 && inputActions.submit !== void 0) {
343
+ for (const id of imageIds) inputActions.removeImage(String(id));
344
+ inputActions.setDraft(text);
345
+ inputActions.submit();
346
+ } else await resendText(String(sessionId), text);
347
+ } catch (err) {
348
+ console.warn(`[serenity] image fallback failed: ${String(err?.message ?? err)}`);
349
+ } finally {
350
+ handlingRef.current = false;
351
+ }
352
+ })();
353
+ }, [
354
+ promptError,
355
+ imageIds,
356
+ sessionId,
357
+ uploadImage,
358
+ getDraftFiles,
359
+ resendText,
360
+ input?.draft,
361
+ inputActions
362
+ ]);
363
+ return null;
364
+ }
365
+ //#endregion
392
366
  //#region src/client/index.ts
393
367
  const inject = [
394
368
  "slots",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.20.4",
3
+ "version": "1.20.6",
4
4
  "description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
5
5
  "license": "MIT",
6
6
  "repository": {