@shgroup/dsh-serenity-hooks 1.108.0 → 1.109.0

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.108.0",
3
+ "version": "1.109.0",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——12 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/localstore/container_admin;另两个 im-bridge/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -4862,6 +4862,32 @@ Instruction-following style:
4862
4862
  style: { margin: "12px 0 4px" },
4863
4863
  children: "投递(实际:发生了没有)"
4864
4864
  }),
4865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4866
+ style: {
4867
+ opacity: .75,
4868
+ fontSize: 12,
4869
+ marginBottom: 4
4870
+ },
4871
+ children: [
4872
+ "机制计数:收到 agent/status ",
4873
+ data.eval.statusEvents,
4874
+ " 次 | running ",
4875
+ data.eval.runningSeen,
4876
+ " / idle",
4877
+ " ",
4878
+ data.eval.idleSeen,
4879
+ " | 判成一轮结束 ",
4880
+ data.eval.turnFinished,
4881
+ Object.keys(data.eval.skips).length > 0 && ` | 跳过 ${Object.entries(data.eval.skips).map(([k, v]) => `${k}=${v}`).join("、")}`
4882
+ ]
4883
+ }),
4884
+ data.eval.statusEvents === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4885
+ style: {
4886
+ opacity: .7,
4887
+ fontSize: 12
4888
+ },
4889
+ children: "⚠ 一次 agent/status 都没收到 —— 可能此刻没人跑轮次,也可能订阅未装配;两者在读数上同形。"
4890
+ }),
4865
4891
  data.deliveries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4866
4892
  style: { opacity: .7 },
4867
4893
  children: "(本进程内还没有任何投递发生 —— 配了但没出现 = 还没触发过,不等于在工作)"
package/lib/index.js CHANGED
@@ -13,7 +13,7 @@ import { i as sessionsRootDir } from "./cleanup-CPl1SSaK.js";
13
13
  import { n as readSimpleSettings, r as registerSettingsSection } from "./settings-DY4_DYYY.js";
14
14
  import { a as readFeishuCredential, c as readWeixinSettings, d as weixinInboundDir, f as weixinSessionIdForTrajectory, i as matchWeixinRoute, n as extractWeixinText, r as hasVoiceItem, s as readWeixinCredential, t as extractWeixinMedia, u as sanitizeFileName$1 } from "./route-reyv8qcB.js";
15
15
  import { i as runImBridge, n as isImWiredTrajectory, r as registerImChannel, t as imChannelIds } from "./channels-Dty_NIl_.js";
16
- import { n as registerTrajectorySubscriptions, r as subscriptionStats, t as defaultSubscriptionDeps } from "./subscriptions-CyrfNpcG.js";
16
+ import { i as subscriptionStats, n as registerTrajectorySubscriptions, r as subscriptionEval, t as defaultSubscriptionDeps } from "./subscriptions-DBR99OVm.js";
17
17
  import { c as sendTextMessage, i as getUpdates, l as sendTyping, n as downloadMedia, o as markdownToPlainText, r as getConfig, s as sendFileMessage, t as TypingStatus, u as sniffImageExt } from "./ilink-CsZ-f64q.js";
18
18
  import { a as registerImAccountImpl, i as normalizeAccountChannel, n as getImAccountImpl, r as imAccountChannels } from "./accounts-BHQbakUh.js";
19
19
  import { createRequire } from "node:module";
@@ -569,7 +569,7 @@ async function runKit(root, args, hostCtx) {
569
569
  }
570
570
  case "diag": {
571
571
  if (hostCtx === void 0) throw new Error("diag requires the plugin context (live session data unavailable without it)");
572
- const { runDiag, renderDiag } = await import("./service-xvjwsX3p.js");
572
+ const { runDiag, renderDiag } = await import("./service-CCzsVeN_.js");
573
573
  return renderDiag(await runDiag(hostCtx, root ?? ""));
574
574
  }
575
575
  default: throw new Error(`Unknown action: ${args.action}`);
@@ -9547,7 +9547,8 @@ function registerStatusApi(ctx, opts = {}) {
9547
9547
  note: s.note ?? null
9548
9548
  })),
9549
9549
  problems,
9550
- deliveries: subscriptionStats()
9550
+ deliveries: subscriptionStats(),
9551
+ eval: subscriptionEval()
9551
9552
  });
9552
9553
  } catch (err) {
9553
9554
  sendJson$2(res, 400, { error: err.message ?? String(err) });
@@ -3,7 +3,7 @@ import { m as readTrajectorySubscriptions } from "./ccc-BmpE3cQz.js";
3
3
  import { F as isoLocal, w as sessionEvents } from "./bind-file-B0TuOP89.js";
4
4
  import { m as WAKE_CATCH_UP_MS, p as listCroCognitiveTrajectories } from "./scheduler-B0pB9mxI.js";
5
5
  import { n as readSimpleSettings } from "./settings-DY4_DYYY.js";
6
- import { r as subscriptionStats } from "./subscriptions-CyrfNpcG.js";
6
+ import { i as subscriptionStats, r as subscriptionEval } from "./subscriptions-DBR99OVm.js";
7
7
  import { containerClocks, containerWakes } from "./observation-BWpkA865.js";
8
8
  //#region src/trajectory/live.ts
9
9
  /**
@@ -187,7 +187,8 @@ async function runDiag(ctx, root, nowMs = Date.now()) {
187
187
  },
188
188
  nowMs,
189
189
  subscriptions: subscriptionStats(),
190
- subscriptionProblems: readTrajectorySubscriptions(root).problems
190
+ subscriptionProblems: readTrajectorySubscriptions(root).problems,
191
+ subscriptionEval: subscriptionEval()
191
192
  };
192
193
  }
193
194
  /** 人读渲染一行唤醒条目 */
@@ -234,6 +235,15 @@ function renderDiag(r) {
234
235
  }
235
236
  lines.push("");
236
237
  lines.push(`── ⑤ 轨迹消息订阅(投递读数;只列发生过的投递,配置全表见 serenity.json)──`);
238
+ {
239
+ const e = r.subscriptionEval;
240
+ const skipText = Object.keys(e.skips).length === 0 ? "无" : Object.entries(e.skips).map(([k, v]) => `${k}=${v}`).join(" / ");
241
+ lines.push(`机制计数: 收到 agent/status ${e.statusEvents} 次|running ${e.runningSeen}|idle ${e.idleSeen}|判成一轮结束 ${e.turnFinished}|跳过: ${skipText}`);
242
+ if (e.statusEvents === 0) {
243
+ lines.push(" ⚠ 一次 agent/status 都没收到 ⇒ 要么此刻真的没人在跑轮次,要么订阅未装配。");
244
+ lines.push(" 两者在读数上同形 —— 判\"未装配\"要看宿主日志里有没有「轨迹消息订阅未装配」。");
245
+ }
246
+ }
237
247
  if (r.subscriptionProblems.length > 0) {
238
248
  lines.push(`⚠ 配置坏项 ${r.subscriptionProblems.length} 条(**配了但不生效**):`);
239
249
  for (const p of r.subscriptionProblems) lines.push(` · ${p}`);
@@ -57,6 +57,34 @@ import { d as registerDisposer } from "./scheduler-B0pB9mxI.js";
57
57
  * 它有上界(键 = 订阅数),且随插件卸载清空({@link registerTrajectorySubscriptions} 的拆卸)。
58
58
  */
59
59
  const stats = /* @__PURE__ */ new Map();
60
+ const evalStats = {
61
+ statusEvents: 0,
62
+ runningSeen: 0,
63
+ idleSeen: 0,
64
+ turnFinished: 0,
65
+ skips: {}
66
+ };
67
+ /** 评估计数快照(诊断/面板读) */
68
+ function subscriptionEval() {
69
+ return {
70
+ ...evalStats,
71
+ skips: { ...evalStats.skips }
72
+ };
73
+ }
74
+ function bump(key) {
75
+ evalStats[key] += 1;
76
+ }
77
+ function bumpSkip(reason) {
78
+ evalStats.skips[reason] = (evalStats.skips[reason] ?? 0) + 1;
79
+ }
80
+ /** 测试用:评估计数复位 */
81
+ function __resetSubscriptionEvalForTest() {
82
+ evalStats.statusEvents = 0;
83
+ evalStats.runningSeen = 0;
84
+ evalStats.idleSeen = 0;
85
+ evalStats.turnFinished = 0;
86
+ evalStats.skips = {};
87
+ }
60
88
  function statOf(key) {
61
89
  const cur = stats.get(key);
62
90
  if (cur) return cur;
@@ -120,30 +148,42 @@ function buildSubscriptionText(memberDir, sub, atMs, iso) {
120
148
  */
121
149
  async function onTurnFinished(deps, sessionId, nowMs) {
122
150
  const root = deps.rootForSession(sessionId);
123
- if (root === null) return {
124
- skipped: "no-ccc",
125
- delivered: 0,
126
- failed: 0
127
- };
151
+ if (root === null) {
152
+ bumpSkip("no-ccc");
153
+ return {
154
+ skipped: "no-ccc",
155
+ delivered: 0,
156
+ failed: 0
157
+ };
158
+ }
128
159
  const { subs, problems } = deps.readSubs(root);
129
160
  for (const p of problems) deps.warn(`[serenity-hooks] ✗ 轨迹订阅配置有坏项(${root}):${p}`);
130
- if (subs.length === 0) return {
131
- skipped: "no-subscriptions",
132
- delivered: 0,
133
- failed: 0
134
- };
161
+ if (subs.length === 0) {
162
+ bumpSkip("no-subscriptions");
163
+ return {
164
+ skipped: "no-subscriptions",
165
+ delivered: 0,
166
+ failed: 0
167
+ };
168
+ }
135
169
  const memberDir = deps.dirForSession(root, sessionId);
136
- if (memberDir === null) return {
137
- skipped: "unbound-member",
138
- delivered: 0,
139
- failed: 0
140
- };
170
+ if (memberDir === null) {
171
+ bumpSkip("unbound-member");
172
+ return {
173
+ skipped: "unbound-member",
174
+ delivered: 0,
175
+ failed: 0
176
+ };
177
+ }
141
178
  const hits = pickSubscriptionsFor(subs, memberDir);
142
- if (hits.length === 0) return {
143
- skipped: "unbound-member",
144
- delivered: 0,
145
- failed: 0
146
- };
179
+ if (hits.length === 0) {
180
+ bumpSkip("not-in-any-members");
181
+ return {
182
+ skipped: "unbound-member",
183
+ delivered: 0,
184
+ failed: 0
185
+ };
186
+ }
147
187
  let delivered = 0;
148
188
  let failed = 0;
149
189
  for (const sub of hits) {
@@ -192,11 +232,13 @@ function registerTrajectorySubscriptions(ctx, deps) {
192
232
  const lastStatus = /* @__PURE__ */ new Map();
193
233
  try {
194
234
  ctx.on("agent/status", (payload) => {
235
+ bump("statusEvents");
195
236
  const p = payload;
196
237
  const sessionId = (p?.agent)?.session?.id;
197
238
  if (typeof sessionId !== "string" || sessionId === "") return;
198
239
  const status = typeof p.status === "string" ? p.status : "";
199
240
  if (status === "running") {
241
+ bump("runningSeen");
200
242
  lastStatus.set(sessionId, "running");
201
243
  return;
202
244
  }
@@ -204,9 +246,14 @@ function registerTrajectorySubscriptions(ctx, deps) {
204
246
  lastStatus.delete(sessionId);
205
247
  return;
206
248
  }
249
+ bump("idleSeen");
207
250
  const prev = lastStatus.get(sessionId);
208
251
  lastStatus.delete(sessionId);
209
- if (prev !== "running") return;
252
+ if (prev !== "running") {
253
+ bumpSkip("idle-without-running");
254
+ return;
255
+ }
256
+ bump("turnFinished");
210
257
  onTurnFinished(deps, sessionId, Date.now()).catch((err) => {
211
258
  deps.warn(`[serenity-hooks] ✗ 轨迹订阅处理异常(会话 ${sessionId}):${String(err?.message ?? err)}`);
212
259
  });
@@ -217,6 +264,7 @@ function registerTrajectorySubscriptions(ctx, deps) {
217
264
  registerDisposer(ctx, "trajectory subscriptions", () => {
218
265
  lastStatus.clear();
219
266
  stats.clear();
267
+ __resetSubscriptionEvalForTest();
220
268
  });
221
269
  }
222
270
  /**
@@ -270,4 +318,4 @@ function defaultSubscriptionDeps(ctx) {
270
318
  };
271
319
  }
272
320
  //#endregion
273
- export { registerTrajectorySubscriptions as n, subscriptionStats as r, defaultSubscriptionDeps as t };
321
+ export { subscriptionStats as i, registerTrajectorySubscriptions as n, subscriptionEval as r, defaultSubscriptionDeps as t };
@@ -112,6 +112,17 @@ export interface DiagReport {
112
112
  }>;
113
113
  /** 订阅配置本身的**坏项**(配了但不生效的条目 —— 必须显形,否则用户以为它在工作) */
114
114
  subscriptionProblems: string[];
115
+ /**
116
+ * 订阅机制的**评估计数**(v1.108.0):收到多少 `agent/status`、认出多少次一轮结束、各因何跳过。
117
+ * 🔴 它回答的是「**机制在跑吗**」—— 与「送出过东西吗」是两个问题;缺了它两者同形(都是空)。
118
+ */
119
+ subscriptionEval: {
120
+ statusEvents: number;
121
+ runningSeen: number;
122
+ idleSeen: number;
123
+ turnFinished: number;
124
+ skips: Record<string, number>;
125
+ };
115
126
  }
116
127
  /**
117
128
  * 装配报告(一次调用即全报告)。
@@ -63,6 +63,33 @@ interface DeliveryStat {
63
63
  /** 最后一次投递尝试的时刻 */
64
64
  lastAt: number | null;
65
65
  }
66
+ /**
67
+ * 评估计数(**"这个机制到底有没有在跑"的唯一读数**,v1.108.0)。
68
+ *
69
+ * 🔴 **为什么必须有它**(我自己在第一版里漏掉了):只有 `stats`(投递计数)时,
70
+ * **「机制没装配」与「装配了但从未命中」在面板上完全同形** —— 两者都是空表。
71
+ * 而那正是本文件头批评过的失败形态("配了但不生效"与"没人干活"分不开)。
72
+ * ⇒ 本计数把**事件流**也记下来:收到多少 `agent/status`、认出多少次 running→idle、
73
+ * 每次因何跳过。有它才能回答「它在跑吗」,而不只是「它送出过东西吗」。
74
+ *
75
+ * ⚠️ 上界:固定字段(不是无界 Map)⇒ 结构上不可能"只增不清"。
76
+ */
77
+ export interface EvalStats {
78
+ /** 收到的 `agent/status` 事件数(含非 running/idle 的) */
79
+ statusEvents: number;
80
+ /** 其中被认成 running 的 */
81
+ runningSeen: number;
82
+ /** 其中被认成 idle 的 */
83
+ idleSeen: number;
84
+ /** 真被判成「一轮结束」(= 有前置 running 的 idle)的次数 */
85
+ turnFinished: number;
86
+ /** 跳过原因计数(键 = `no-ccc` / `no-subscriptions` / `unbound-member`) */
87
+ skips: Record<string, number>;
88
+ }
89
+ /** 评估计数快照(诊断/面板读) */
90
+ export declare function subscriptionEval(): EvalStats;
91
+ /** 测试用:评估计数复位 */
92
+ export declare function __resetSubscriptionEvalForTest(): void;
66
93
  /** 投递统计快照(`dashboard diag` 与测试读;空 = 从未有投递尝试) */
67
94
  export declare function subscriptionStats(): Array<{
68
95
  key: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.108.0",
3
+ "version": "1.109.0",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:12 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/localstore/container_admin;另两个 im-bridge/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档)。",
5
5
  "license": "MIT",
6
6
  "repository": {