@shgroup/dsh-serenity-hooks 1.107.0 → 1.108.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.107.0",
3
+ "version": "1.108.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
@@ -4754,10 +4754,135 @@ Instruction-following style:
4754
4754
  title: "会话清理",
4755
4755
  desc: "删除 N 天前无活动的 DSH 会话(手动,不可恢复)",
4756
4756
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionCleanupBlock, {})
4757
+ }),
4758
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapse, {
4759
+ title: "轨迹订阅",
4760
+ desc: "观察者盯一组成员,任一轮结束即通知(只读;配置在 serenity.json)",
4761
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubscriptionsBlock, {})
4757
4762
  })
4758
4763
  ]
4759
4764
  });
4760
4765
  }
4766
+ /**
4767
+ * 「轨迹订阅」块(v1.108.0,owner 2026-11-01 P3)。
4768
+ *
4769
+ * 🔴 **只读**,且刻意分两栏显示「意图」与「实际」:
4770
+ * · **意图栏** = 配置里写了谁盯谁;**实际栏** = 真的发生过投递的键。
4771
+ * · 两者**必须分开看**:一个配了却不出现在实际栏的订阅 = **还没触发过**(不是"在工作");
4772
+ * 出现在实际栏且 `failed>0` = **触发了但送不到**(那才是要修的)。
4773
+ * 把这两件事合成一句话,人就分不出来了(这正是 diag ⑤ 段同一个理由)。
4774
+ *
4775
+ * ⚠️ 计数是**进程内**的(重启清零)—— UI 上必须写明,否则"0"会被读成"从来没配过"。
4776
+ */
4777
+ function SubscriptionsBlock() {
4778
+ const [data, setData] = (0, react.useState)(null);
4779
+ const [busy, setBusy] = (0, react.useState)(false);
4780
+ const [error, setError] = (0, react.useState)(null);
4781
+ const load = async () => {
4782
+ setBusy(true);
4783
+ setError(null);
4784
+ try {
4785
+ const res = await fetch("/serenity/subscriptions", { headers: {
4786
+ accept: "application/json",
4787
+ "x-serenity-ui": "1"
4788
+ } });
4789
+ if (!res.ok) {
4790
+ const body = await res.json().catch(() => null);
4791
+ setError(body?.error ?? `HTTP ${res.status}`);
4792
+ return;
4793
+ }
4794
+ setData(await res.json());
4795
+ } catch (err) {
4796
+ setError(err instanceof Error ? err.message : String(err));
4797
+ } finally {
4798
+ setBusy(false);
4799
+ }
4800
+ };
4801
+ (0, react.useEffect)(() => {
4802
+ load();
4803
+ }, []);
4804
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4805
+ className: "ss-field",
4806
+ children: [
4807
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4808
+ className: "ss-row",
4809
+ style: {
4810
+ display: "flex",
4811
+ alignItems: "center",
4812
+ gap: 8
4813
+ },
4814
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4815
+ type: "button",
4816
+ className: "ss-btn",
4817
+ disabled: busy,
4818
+ onClick: () => void load(),
4819
+ children: busy ? "读取中…" : "刷新读数"
4820
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4821
+ style: {
4822
+ opacity: .7,
4823
+ fontSize: 12
4824
+ },
4825
+ children: "投递计数只在宿主进程内有效(重启清零);配置全表在 .opencode/serenity.json"
4826
+ })]
4827
+ }),
4828
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4829
+ className: "ss-error",
4830
+ children: ["⚠ ", error]
4831
+ }),
4832
+ data !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4833
+ data.problems.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4834
+ className: "ss-error",
4835
+ children: [
4836
+ "⚠ 配置坏项 ",
4837
+ data.problems.length,
4838
+ " 条(配了但不生效):",
4839
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
4840
+ style: { margin: "4px 0 0 16px" },
4841
+ children: data.problems.map((p, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: p }, i))
4842
+ })
4843
+ ]
4844
+ }),
4845
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
4846
+ style: { margin: "12px 0 4px" },
4847
+ children: "配置(意图:谁盯谁)"
4848
+ }),
4849
+ data.subscriptions.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4850
+ style: { opacity: .7 },
4851
+ children: "(未配置任何订阅 —— 在 .opencode/serenity.json 里写 trajectorySubscriptions)"
4852
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
4853
+ style: { margin: "0 0 0 16px" },
4854
+ children: data.subscriptions.map((s, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4855
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: s.watcher }),
4856
+ " 盯 ",
4857
+ s.members.join("、"),
4858
+ s.note !== null ? `(${s.note})` : ""
4859
+ ] }, i))
4860
+ }),
4861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
4862
+ style: { margin: "12px 0 4px" },
4863
+ children: "投递(实际:发生了没有)"
4864
+ }),
4865
+ data.deliveries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4866
+ style: { opacity: .7 },
4867
+ children: "(本进程内还没有任何投递发生 —— 配了但没出现 = 还没触发过,不等于在工作)"
4868
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
4869
+ style: { margin: "0 0 0 16px" },
4870
+ children: data.deliveries.map((d, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4871
+ d.key,
4872
+ ":成功 ",
4873
+ d.ok,
4874
+ " / 失败 ",
4875
+ d.failed,
4876
+ d.failed > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4877
+ className: "ss-error",
4878
+ children: [" ⚠ ", d.lastError ?? "(无说明)"]
4879
+ })
4880
+ ] }, i))
4881
+ })
4882
+ ] })
4883
+ ]
4884
+ });
4885
+ }
4761
4886
  /** 「会话清理」块(v1.29 需求③,S142 用户拍板:lastActive + 手动按钮 + 直接物理删):
4762
4887
  * 天数输入 → GET 预览(dryRun 列表)→ 确认 → POST 执行。live 会话由服务端保护跳过。 */
4763
4888
  function SessionCleanupBlock() {
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { i as __toESM, n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
2
2
  import { c as hostAgents, d as hostSessions, f as hostSettings, i as cccRootForExec, m as hostWebServer, n as agentCwdFor, o as listCccs, p as hostWeb, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-DPMwBywc.js";
3
- import { a as findSerenityRoot$1, d as readCccName, g as resolveInside, l as pathInside, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-BmpE3cQz.js";
3
+ import { a as findSerenityRoot$1, d as readCccName, g as resolveInside, l as pathInside, m as readTrajectorySubscriptions, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-BmpE3cQz.js";
4
4
  import { A as useSession, C as resolveSessionByTitle, D as showSession, E as setActiveSessionInfo, F as isoLocal, I as localFileStamp, M as TRAJECTORY_LEGACY_FILENAMES, O as summarize, S as readTrajectoryUid, T as sessionsRoot, _ as listSessions, b as parseTrajectoryCode, c as UID_FILENAME, d as createSession, f as extractSessionMdPathFromText, j as TRAJECTORY_FILENAME, l as archiveSession, m as getActiveSessionInfo, o as COGNITIVE_TRAJECTORY_ACTIONS, p as findSession, u as clearActiveSessionInfo, v as namingTitleFor, w as sessionEvents, y as parseSessionContextFromEvents } from "./bind-file-B0TuOP89.js";
5
5
  import { n as checkLocalstoreGitCompliance, o as readStore, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./service-Z3aSR1cn.js";
6
6
  import { d as findPackageRoot, i as constraintSystemPromptSource, l as resolveConstraintSystemPrompt, r as constraintCognitiveTrajectory, t as TRAJECTORY_CONSTRAINT_HINT, u as ACC_VERSION } from "./rules-BLkYTPUy.js";
@@ -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, t as defaultSubscriptionDeps } from "./subscriptions-CyrfNpcG.js";
16
+ import { n as registerTrajectorySubscriptions, r as subscriptionStats, t as defaultSubscriptionDeps } from "./subscriptions-CyrfNpcG.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";
@@ -9184,6 +9184,7 @@ const FILE_UPLOAD_PATH = "/serenity/file-upload";
9184
9184
  const CONFIG_PATH = "/serenity/config";
9185
9185
  const CCCS_PATH = "/serenity/cccs";
9186
9186
  const WEIXIN_PATH = "/serenity/weixin";
9187
+ const SUBSCRIPTIONS_PATH = "/serenity/subscriptions";
9187
9188
  /** 图片落盘目录(CCC 根相对;S142 图片自动识别基础设施——粘贴图片落盘供 agent 经 CCC vlm MSM 自主处理) */
9188
9189
  const IMAGE_UPLOAD_DIR = "_tmp/images_from_user";
9189
9190
  const IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Set([
@@ -9519,6 +9520,40 @@ function registerStatusApi(ctx, opts = {}) {
9519
9520
  }
9520
9521
  }
9521
9522
  });
9523
+ ctx.webServer.register({
9524
+ kind: "exact",
9525
+ path: SUBSCRIPTIONS_PATH,
9526
+ handler: async (req, res) => {
9527
+ try {
9528
+ if (req.method !== "GET") {
9529
+ sendJson$2(res, 405, { error: "method not allowed" });
9530
+ return;
9531
+ }
9532
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
9533
+ const workspace = resolveWorkspace(ctx, {
9534
+ sessionId: url.searchParams.get("sessionId") ?? void 0,
9535
+ workspace: url.searchParams.get("workspace") ?? void 0
9536
+ });
9537
+ const root = cccRootForCwd(workspace) ?? "";
9538
+ const { subs, problems } = root ? readTrajectorySubscriptions(root) : {
9539
+ subs: [],
9540
+ problems: []
9541
+ };
9542
+ sendJson$2(res, 200, {
9543
+ root,
9544
+ subscriptions: subs.map((s) => ({
9545
+ watcher: s.watcher,
9546
+ members: s.members,
9547
+ note: s.note ?? null
9548
+ })),
9549
+ problems,
9550
+ deliveries: subscriptionStats()
9551
+ });
9552
+ } catch (err) {
9553
+ sendJson$2(res, 400, { error: err.message ?? String(err) });
9554
+ }
9555
+ }
9556
+ });
9522
9557
  ctx.webServer.register({
9523
9558
  kind: "exact",
9524
9559
  path: WEIXIN_PATH,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.107.0",
3
+ "version": "1.108.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": {