@shgroup/dsh-serenity-hooks 1.31.7 → 1.31.9

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/README.md CHANGED
@@ -317,7 +317,7 @@ AI 一次能"记住"的内容有上限。满了不用你手动开新会话:
317
317
 
318
318
  ```bash
319
319
  pnpm typecheck # 类型检查(node + 浏览器端两套)
320
- pnpm test # 全量测试(当前 78 个文件 / 1169 个用例)
320
+ pnpm test # 全量测试(当前 80 个文件 / 1176 个用例)
321
321
  pnpm build # 打包(lib/index.js + client.js)
322
322
  ```
323
323
 
@@ -381,4 +381,4 @@ pnpm build # 打包(lib/index.js + client.js)
381
381
 
382
382
  MIT(见 [LICENSE](https://github.com/tellmewhattodo/dsh-serenity-plugin/blob/master/LICENSE))
383
383
 
384
- > **版本**:v1.31.7 | **前置**:DSH 0.1.5-rc.1+ / Node ≥ 20 或 bun | **测试**:78 个文件 / 1169 个用例
384
+ > **版本**:v1.31.9 | **前置**:DSH 0.1.5-rc.1+ / Node ≥ 20 或 bun | **测试**:80 个文件 / 1176 个用例
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.31.7",
3
+ "version": "1.31.9",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——11 个工具(container_fs/logbook/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/autopilot-trajectory/im-bridge)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/自主巡航。适配 DSH 0.1.5-rc.1(deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -461,22 +461,22 @@ window.__ModuleLoader__.load({
461
461
  const inputActions = props.inputActions;
462
462
  const handlingRef = (0, react.useRef)(false);
463
463
  const promptError = session?.promptError?.error;
464
- const imageIds = input?.imageIds ?? [];
464
+ const attachmentIds = input?.attachmentIds ?? [];
465
465
  (0, react.useEffect)(() => {
466
466
  if (handlingRef.current || !isImageFallbackTrigger(promptError?.code, promptError?.details?.reason)) return;
467
- if (imageIds.length === 0) return;
467
+ if (attachmentIds.length === 0) return;
468
468
  handlingRef.current = true;
469
469
  (async () => {
470
470
  try {
471
- const files = await getDraftFiles(String(sessionId), imageIds);
471
+ const files = await getDraftFiles(String(sessionId), attachmentIds);
472
472
  if (files.length === 0) throw new Error("no draft image files");
473
473
  const saved = [];
474
474
  for (const file of files) saved.push(await uploadImage(file, String(sessionId)));
475
475
  const note = imageNoteTemplate(saved);
476
476
  const draft = input?.draft ?? "";
477
477
  const text = draft === "" ? note : `${draft}\n${note}`;
478
- if (inputActions?.removeImage !== void 0 && inputActions.setDraft !== void 0 && inputActions.submit !== void 0) {
479
- for (const id of imageIds) inputActions.removeImage(String(id));
478
+ if (inputActions?.removeAttachment !== void 0 && inputActions.setDraft !== void 0 && inputActions.submit !== void 0) {
479
+ for (const id of attachmentIds) inputActions.removeAttachment(id);
480
480
  inputActions.setDraft(text);
481
481
  inputActions.submit();
482
482
  } else await resendText(String(sessionId), text);
@@ -488,7 +488,7 @@ window.__ModuleLoader__.load({
488
488
  })();
489
489
  }, [
490
490
  promptError,
491
- imageIds,
491
+ attachmentIds,
492
492
  sessionId,
493
493
  uploadImage,
494
494
  getDraftFiles,
@@ -55,7 +55,7 @@ export interface HostEventContract {
55
55
  * 在名字写错/宿主改名时**编译失败**(v1.30.7,review F-04/F-05)。
56
56
  * 该 import 为 type-only,编译期擦除,不产生运行时依赖。
57
57
  */
58
- export declare const HOST_EVENT_NAMES: readonly ["agent/session-start", "agent/pre-step", "agent/turn-stopping", "agent/status", "agent/inbox/inserted", "session/event", "session/created", "tools/pre-execute", "tools/post-execute", "system-prompt/assemble"];
58
+ export declare const HOST_EVENT_NAMES: readonly ["agent/session-start", "agent/pre-step", "agent/turn-stopping", "agent/status", "agent/inbox/inserted", "agent/disposed", "session/event", "session/created", "session/disposed", "settings/updated", "tools/pre-execute", "tools/post-execute", "system-prompt/assemble"];
59
59
  export type HostEventName = (typeof HOST_EVENT_NAMES)[number];
60
60
  /**
61
61
  * dsp 订阅的宿主事件清单(与 HOST_EVENT_NAMES 同源;impact/site 供 health 报告与文档)。
@@ -0,0 +1,44 @@
1
+ /**
2
+ * host/type-contract.ts — 宿主**类型**契约(编译期反证层,v1.31.8,S142 全量 review 产物)
3
+ *
4
+ * ## 为什么存在(R↓)
5
+ *
6
+ * dsp 用**结构化断言**隔离宿主(手写 interface + `as` / `as never`,零宿主运行时 import),
7
+ * 换来 peer-only 打包与"不随宿主版本耦合"的灵活度;代价是**类型检查对宿主漂移失明**:
8
+ * 宿主把成员改名、改返回形状,dsp 侧拿不到任何编译期信号,只能靠**人读宿主源码**发现。
9
+ *
10
+ * 历史已三次静默失效:
11
+ * - `Session.events` → `snapshotEvents()`(v1.28.0)
12
+ * - `conversation.draftImages()` → `resolveDraftAttachments()`(v1.31.6)
13
+ * - `sessionPersistence.list()` 返回形状 `SessionHeader[]`(`h.cwd`)→
14
+ * `SessionPersistenceSnapshot[]`(`h.header.cwd`)(v1.31.6)
15
+ * 后两条是升级 0.1.5-rc.1 时**逐文件人读**才挖出来的——不是机制发现的。
16
+ *
17
+ * ## 既有三道防线的盲区(2026-09-10 全量 review 结论)
18
+ *
19
+ * | 防线 | 覆盖 | 盲区 |
20
+ * |------|------|------|
21
+ * | `contract.ts` 的 `satisfies keyof Events` | 事件**名** | payload 形状 |
22
+ * | `probeHostContract`(运行期) | 服务**名** + 成员**存在性** | 成员**签名**、**返回形状** |
23
+ * | `tests/**` | —— | **完全不参与类型检查**(tsconfig `include: ["src"]`)→ 写在测试里的"真实宿主类型断言"从未生效 |
24
+ *
25
+ * ## 本模块补的那一格:用宿主真实类型反证 dsp 的形状假设
26
+ *
27
+ * 机制:
28
+ * ① `import type {} from '<宿主包>'` 把该包的 `declare module '@deepseek-ai/cordis'` 声明增强
29
+ * 拉进 program(**空类型 import,编译期擦除 → 零运行时依赖**,peer-only 打包不受影响;
30
+ * 与 `src/client/index.ts` 引 `dsh-client-ui-session` 同款手法)
31
+ * ② 每条 `Expect<Extends<宿主真相, dsp 假设>>` 是一道**编译期闸门**:宿主改名/改形状 →
32
+ * `dsh-develop typecheck` 当场红,升级当天报警,而不是等线上静默失效
33
+ * ③ 换宿主版本时 `dsh-develop typecheck-host <ver>` 会对**解包的新宿主**跑同一批断言
34
+ *
35
+ * ## 维护纪律(强制)
36
+ *
37
+ * **新增一处对宿主的形状依赖 → 在此加一条对应闸门**(断言旁注明 dsp 消费点与后果)。
38
+ * 断言只表达 dsp **真正依赖**的那部分(不求与宿主全等)→ 宿主新增字段/放宽类型不会误报,
39
+ * 只有"dsp 依赖的东西没了或变了"才红。
40
+ *
41
+ * 注:本文件**没有任何运行时语句**,编译产物只有 `export {}`,不影响包体行为。
42
+ */
43
+ /** 哨兵:确保本模块不是空文件被 tree-shake 掉(无运行时副作用,值为类型) */
44
+ export type HostTypeContractSatisfied = true;
package/lib/index.js CHANGED
@@ -8623,10 +8623,22 @@ async function queueRebuild(ctx, opts) {
8623
8623
  function performRebuild(session, pending, meter) {
8624
8624
  const nodes = [...session.surface.nodes];
8625
8625
  if (nodes.length === 0) return false;
8626
+ /**
8627
+ * v1.31.8 二次修正(0.1.5-rc.1 实证,diag 原文):
8628
+ * `surface replace: node 0 holds the system prompt and may be rewritten only by a
8629
+ * system/message over exactly that node`(宿主 `dsh-session/lib/types/surface.js`
8630
+ * `assertSystemHeadRewrite`)。
8631
+ *
8632
+ * → **surface node 0 是系统提示节点且被宿主保护**;而重建的语义恰恰是"保留系统提示、
8633
+ * 只清对话历史",故替换范围必须从 **node 1** 起。(旧实现整体替换 `nodes[0..last]`,
8634
+ * 在 0.1.5 上必被拒——这是 §14 之后的第二条 rebuild 阻断规则。)
8635
+ */
8636
+ const targets = nodes.slice(1);
8637
+ if (targets.length === 0) return false;
8626
8638
  if (meter) {
8627
8639
  let shadowedTokenCount = 0;
8628
8640
  const events = sessionEvents(session);
8629
- for (const seq of nodes) {
8641
+ for (const seq of targets) {
8630
8642
  const event = events[seq];
8631
8643
  if (!event) continue;
8632
8644
  const message = deriveEventMessage(event);
@@ -8634,10 +8646,10 @@ function performRebuild(session, pending, meter) {
8634
8646
  }
8635
8647
  session.append("compaction/prune", {
8636
8648
  shadowedRange: {
8637
- start: nodes[0],
8638
- end: nodes[nodes.length - 1]
8649
+ start: targets[0],
8650
+ end: targets[targets.length - 1]
8639
8651
  },
8640
- shadowedSeqs: nodes,
8652
+ shadowedSeqs: targets,
8641
8653
  shadowedTokenCount
8642
8654
  });
8643
8655
  }
@@ -8650,10 +8662,10 @@ function performRebuild(session, pending, meter) {
8650
8662
  }, {
8651
8663
  surfaceOp: {
8652
8664
  op: "replace",
8653
- start: nodes[0],
8654
- end: nodes[nodes.length - 1]
8665
+ startSeq: targets[0],
8666
+ endSeq: targets[targets.length - 1]
8655
8667
  },
8656
- sourceEventSeqs: nodes
8668
+ sourceEventSeqs: targets
8657
8669
  });
8658
8670
  return true;
8659
8671
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.31.7",
3
+ "version": "1.31.9",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:11 个工具(container_fs/logbook/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/autopilot-trajectory/im-bridge)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/自主巡航。适配 DSH 0.1.5-rc.1。",
5
5
  "license": "MIT",
6
6
  "repository": {