@shgroup/dsh-serenity-hooks 1.89.0 → 1.90.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 +1 -1
- package/lib/index.js +154 -98
- package/lib/wake/scheduler.d.ts +11 -0
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.90.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/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/index.js
CHANGED
|
@@ -10341,13 +10341,39 @@ async function runWakeTick(ctx) {
|
|
|
10341
10341
|
* - ⚠️ **本函数任何失败都不得连累既有链路**(设计 §5 铁律):整体 try/catch 吞掉并留一行日志。
|
|
10342
10342
|
*/
|
|
10343
10343
|
async function runInterruptedSafetyNet(ctx, root, log) {
|
|
10344
|
+
const s0 = wakeSchedulerState();
|
|
10345
|
+
const tickNow = s0.ticks + 1;
|
|
10346
|
+
const phase = tickNow <= 1 ? "启动首拍" : tickNow <= 3 ? "刚启动窗口" : "常规 tick";
|
|
10347
|
+
const tickTag = `tick=${s0.ticks}(当前第${tickNow}拍·${phase})`;
|
|
10344
10348
|
try {
|
|
10345
10349
|
const agents = hostAgents(ctx);
|
|
10346
10350
|
const sc = hostService(ctx, "sessionController");
|
|
10347
|
-
|
|
10348
|
-
|
|
10351
|
+
/**
|
|
10352
|
+
* 🔴 **A105:live 排除的粒度 =「此刻有没有在跑」,不是「宿主认不认识它」。**
|
|
10353
|
+
*
|
|
10354
|
+
* 修前:`collectLiveSessionIdsForSafetyNet` 取的是 `hostSessions(ctx).list()`
|
|
10355
|
+
* ⇒ 那是**载入状态**(会话对象在本进程里),**不是**轮次状态 ⇒
|
|
10356
|
+
* 一条「已载入、但上一轮被重启打断」的会话会被**误排除**,
|
|
10357
|
+
* 而那恰恰是**最该被接走**的一类(2026-10-23 实测:这正是那次 13 分钟盲窗的形状)。
|
|
10358
|
+
*
|
|
10359
|
+
* ⇒ 现在与权威 `interrupted` 动作**用同一个取数口**(`listRunningSessionIds`),
|
|
10360
|
+
* 两处对"在跑"的认知**不可能再不一致**(A105 的全部目的就是这一条)。
|
|
10361
|
+
*/
|
|
10362
|
+
const liveIds = new Set(listRunningSessionIds(Date.now()));
|
|
10349
10363
|
const scan = scanInterruptedSessions(`${sessionsRootDir()}/${projectDirNameForCwd(root)}`, { liveIds });
|
|
10350
|
-
|
|
10364
|
+
/**
|
|
10365
|
+
* 🔴 **A106:无命中也要出声**(`ok`/`fail`/`not-ready`/`no-wake` 之后缺的第五格)。
|
|
10366
|
+
*
|
|
10367
|
+
* 修前这里 `return`,**一行都不写** ⇒ 宿主日志里没有任何「中断兜底」字样 ⇒
|
|
10368
|
+
* **「我扫了、没东西」与「我没被跑」在读数上完全一样**(D216 同族第四次)。
|
|
10369
|
+
* ⇒ 判据同 A102:**可以不出声地判"没事",但"判过"本身要留痕**。
|
|
10370
|
+
* ⚠️ 密度很低(每 tick 一行,5min 一次),且**必须带拍号与扫描读数**,
|
|
10371
|
+
* 否则又是一堆分不清是哪一拍的日志行(guard 明确要求)。
|
|
10372
|
+
*/
|
|
10373
|
+
if (scan.sessions.length === 0) {
|
|
10374
|
+
log.push(`· ${root}: 中断兜底 ${tickTag} → 扫 ${scan.scanned} 个 · 命中 0 · 排除在跑 ${liveIds.size} 个 · 跳过 ${scan.skipped} ⇒ 无需注入`);
|
|
10375
|
+
return;
|
|
10376
|
+
}
|
|
10351
10377
|
const resumeOne = createResumeOne({
|
|
10352
10378
|
agents,
|
|
10353
10379
|
sessionController: sc,
|
|
@@ -10359,6 +10385,7 @@ async function runInterruptedSafetyNet(ctx, root, log) {
|
|
|
10359
10385
|
source: PLUGIN_SOURCE
|
|
10360
10386
|
})
|
|
10361
10387
|
});
|
|
10388
|
+
log.push(`· ${root}: 中断兜底 ${tickTag} → 扫 ${scan.scanned} 个 · 命中 ${scan.sessions.length} · 排除在跑 ${liveIds.size} 个`);
|
|
10362
10389
|
for (const hit of scan.sessions) try {
|
|
10363
10390
|
const out = await resumeOne(hit);
|
|
10364
10391
|
log.push(`· ${root}: 中断兜底 ${hit.sessionId}(turn ${hit.turn ?? "?"})→ ` + (out.ok ? `已注入(${out.how ?? "?"})` : `未注入:${out.error ?? "未知"}`));
|
|
@@ -10366,18 +10393,20 @@ async function runInterruptedSafetyNet(ctx, root, log) {
|
|
|
10366
10393
|
log.push(`· ${root}: 中断兜底 ${hit.sessionId} 注入抛错(已忽略):${String(err?.message ?? err)}`);
|
|
10367
10394
|
}
|
|
10368
10395
|
} catch (err) {
|
|
10369
|
-
log.push(`· ${root}:
|
|
10396
|
+
log.push(`· ${root}: 中断兜底 ${tickTag} 整体失败(已忽略,不影响既有链路):${String(err?.message ?? err)}`);
|
|
10370
10397
|
}
|
|
10371
10398
|
}
|
|
10372
|
-
/**
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10399
|
+
/**
|
|
10400
|
+
* ⚠️ **2026-10-23 A105 已删除 `collectLiveSessionIdsForSafetyNet`。**
|
|
10401
|
+
*
|
|
10402
|
+
* 它取的是 `hostSessions(ctx).list()` =「宿主认不认识」,而 A105 的结论是
|
|
10403
|
+
* 判据必须是「**此刻有没有在跑**」。现在两处(本兜底与权威 `interrupted` 动作)
|
|
10404
|
+
* 都走 `listRunningSessionIds()` —— **同一张表、同一个口径**,不可能再打架。
|
|
10405
|
+
*
|
|
10406
|
+
* 🔴 后人若要"把 live 表也加回来做双保险":**不要** ——
|
|
10407
|
+
* `live ∧ ¬running` 恰是「已载入、但上一轮被打断」那一类,**它最该被接走**;
|
|
10408
|
+
* 加回来就是把最该救的那一格重新排除掉。
|
|
10409
|
+
*/
|
|
10381
10410
|
/**
|
|
10382
10411
|
* 装配调度器(index.ts apply 调用):5min tick + 生命周期 disposer +
|
|
10383
10412
|
* 会话出现/settings 变化时热启动(与 autopilot 同款触发面,互不干扰)。
|
|
@@ -10728,85 +10757,6 @@ function listResident(root) {
|
|
|
10728
10757
|
return lines.join("\n");
|
|
10729
10758
|
}
|
|
10730
10759
|
//#endregion
|
|
10731
|
-
//#region src/trajectory/live.ts
|
|
10732
|
-
/**
|
|
10733
|
-
* trajectory/live.ts — 进程内 live 会话取数 + 精简诊断
|
|
10734
|
-
*
|
|
10735
|
-
* ── 为什么单独成模块(R↓,2026-09-15 S142「ACC 侧 autopilot 退场」S1 搬迁)──
|
|
10736
|
-
* 这两个函数原先住在 `autopilot-trajectory.ts`,但它们的**主语不是 autopilot**:
|
|
10737
|
-
* `listLiveSessions` 服务 `acc-diag` ① 段的"**哪个会话属于哪个 CCC**"(会话维度,
|
|
10738
|
-
* 不是 CCC 维度),而"**独立进程看不到运行时**"正是 `acc-diag` 存在的理由。
|
|
10739
|
-
* autopilot 退场后它们必须活下去 ⇒ 独立成模块,不再挂着已死机制的名字。
|
|
10740
|
-
*
|
|
10741
|
-
* ── 迁移边界(不搬什么,以及为什么)──────────────────────────────────────
|
|
10742
|
-
* 原 `diagLive` 还含两段,**均不搬**(其主语是 autopilot,随机制退场):
|
|
10743
|
-
* · `autopilotCccs`(逐 CCC 目标命中 / agent 定位诊断)—— 依赖 `readAutopilotSettings` /
|
|
10744
|
-
* `getAutopilotStatus` / `resolveTargetAgent`
|
|
10745
|
-
* · `panelResolved`(无参面板请求落到哪个 CCC)—— 面板区块随机制退场
|
|
10746
|
-
* 留下的 = **进程 cwd/CCC + live 会话清单**(`acc-diag` ① 段仍需要)。
|
|
10747
|
-
*
|
|
10748
|
-
* 判据纪律:本模块**不得**反向依赖 autopilot 判据层(那会让退场半途而废)。
|
|
10749
|
-
*
|
|
10750
|
-
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
10751
|
-
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触
|
|
10752
|
-
* (直接 import 宿主 `cordis` 的类型)。
|
|
10753
|
-
*
|
|
10754
|
-
* **为什么这是"真解耦"而不是"把类型藏起来"(判据):** `ctx` 在本模块**从不被解引用** ——
|
|
10755
|
-
* 它只被**原样透传**给 `hostSessions(ctx)`,而后者(`host/access.ts`,**L0**)的参数
|
|
10756
|
-
* 本就声明为 `ctx: unknown` ⇒ `Context` 这个 import **在语义上是多余的**,
|
|
10757
|
-
* 它没有参与任何类型检查,只贡献了一处 L1→宿主 的接触点。
|
|
10758
|
-
* 🔵 **证据**:grep 本文件 `ctx` 全部 5 处命中 —— 3 处在参数/调用位、2 处在注释,
|
|
10759
|
-
* **零处**属性访问(`ctx.xxx`)。
|
|
10760
|
-
*/
|
|
10761
|
-
/**
|
|
10762
|
-
* 从 dsh 会话 log 读取标题(latest-wins `session/title` 事件)——
|
|
10763
|
-
* **标题不在 `sessions.list()` 条目上**(wire/对象均无 title 字段;F3 命名经
|
|
10764
|
-
* `sessionTitle.rename` 写进 session log),必须从 events 提取(rebuild 同款读取模式)。
|
|
10765
|
-
*/
|
|
10766
|
-
function readSessionTitle(session) {
|
|
10767
|
-
try {
|
|
10768
|
-
const events = sessionEvents(session);
|
|
10769
|
-
for (let i = events.length - 1; i >= 0; i--) {
|
|
10770
|
-
const e = events[i];
|
|
10771
|
-
if (e?.type === "session/title" && typeof e.data?.title === "string" && e.data.title.trim() !== "") return e.data.title.trim();
|
|
10772
|
-
}
|
|
10773
|
-
} catch {}
|
|
10774
|
-
return null;
|
|
10775
|
-
}
|
|
10776
|
-
/**
|
|
10777
|
-
* 逐条 live 会话(`cccRoot` = 该会话 cwd 的 CCC 归属,无 → null)。
|
|
10778
|
-
*
|
|
10779
|
-
* CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
|
|
10780
|
-
* 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
|
|
10781
|
-
*
|
|
10782
|
-
* @param ctx 插件上下文(**只透传**给 L0 取数口,本模块不解引用 ⇒ 类型为 `unknown`)
|
|
10783
|
-
* @returns 逐条 live 会话
|
|
10784
|
-
*/
|
|
10785
|
-
function listLiveSessions(ctx) {
|
|
10786
|
-
const out = [];
|
|
10787
|
-
try {
|
|
10788
|
-
const sessions = hostSessions(ctx);
|
|
10789
|
-
for (const s of sessions?.list?.() ?? []) {
|
|
10790
|
-
const cwd = s?.header?.cwd ?? null;
|
|
10791
|
-
out.push({
|
|
10792
|
-
id: s?.id ?? "",
|
|
10793
|
-
cwd,
|
|
10794
|
-
cccRoot: cwd ? cccRootForCwd(cwd) : null,
|
|
10795
|
-
title: readSessionTitle(s)
|
|
10796
|
-
});
|
|
10797
|
-
}
|
|
10798
|
-
} catch {}
|
|
10799
|
-
return out;
|
|
10800
|
-
}
|
|
10801
|
-
function diagLive(ctx) {
|
|
10802
|
-
const processCwd = process.cwd();
|
|
10803
|
-
return {
|
|
10804
|
-
processCwd,
|
|
10805
|
-
processCcc: cccRootForCwd(processCwd),
|
|
10806
|
-
liveSessions: listLiveSessions(ctx)
|
|
10807
|
-
};
|
|
10808
|
-
}
|
|
10809
|
-
//#endregion
|
|
10810
10760
|
//#region src/tools/cognitive-trajectory.ts
|
|
10811
10761
|
/**
|
|
10812
10762
|
* trajectory.ts — `cognitive_trajectory` 真实 DSH 工具定义(defineTool)
|
|
@@ -10848,17 +10798,44 @@ function diagLive(ctx) {
|
|
|
10848
10798
|
* 活跃会话跟踪:内存 Map(按 dsh 会话 id 隔离)+ 从 events 恢复(S134)。
|
|
10849
10799
|
*/
|
|
10850
10800
|
/**
|
|
10851
|
-
*
|
|
10801
|
+
* 当前**正在跑轮次**的 dsh 会话 id(用于把 live 会话排除出"待续"名单)。
|
|
10852
10802
|
*
|
|
10853
10803
|
* 🔴 **为什么必须排除**:一个正在跑的会话,其日志末条**不是** `session/end-seed`
|
|
10854
10804
|
* (它还在写),所以按判据本来也不会命中;但排除是**双保险** ——
|
|
10855
10805
|
* 万一某轮刚被中断、宿主还没来得及继续,把 live 会话当"待续"会导致**重复投递**。
|
|
10856
|
-
*
|
|
10806
|
+
*
|
|
10807
|
+
* ── 🔴🔴 2026-10-23 A105:**从「live 表」改为「在跑表」,且从 `ctx` 取数** ──────────
|
|
10808
|
+
*
|
|
10809
|
+
* **修前有两个缺陷,叠在一起让这段排除在生产上形同虚设:**
|
|
10810
|
+
*
|
|
10811
|
+
* ① **`ctx` 传的是 `undefined`** ⇒ `listLiveSessions(undefined)` ⇒ `hostSessions(undefined)`
|
|
10812
|
+
* ⇒ 取不到宿主 ⇒ **live 表恒空** ⇒ `scanInterruptedSessions` 的 `liveIds` 是空集。
|
|
10813
|
+
* ⚠️ 原注释写「ctx 在 execute 里拿不到(本工具只拿到 exec)」,**那句是错的**:
|
|
10814
|
+
* 本工具的工厂 `createCognitiveTrajectoryTool(ctx)` **闭包捕获了真 ctx**,
|
|
10815
|
+
* 且同文件的 `continue-all-interrupted` 分支早就靠它取 `hostAgents(ctx)` / `hostService(ctx,…)`
|
|
10816
|
+
* —— 见那一支的注释「**必须用工厂参数 `ctx`,不能用 `exec`**」。**同一份源码里,
|
|
10817
|
+
* 一处说"拿不到"、另一处说"必须用它"**,这正是两个读数打架的典型形态。
|
|
10818
|
+
*
|
|
10819
|
+
* ② **粒度错了:`live ≠ 在跑`。** `listLiveSessions` 答的是「宿主认不认识这个会话」
|
|
10820
|
+
* (会话对象已载入本进程),**不是**「它此刻有没有轮次在跑」。
|
|
10821
|
+
* ⇒ 一条**已载入但空闲**的会话会被误排除 ⇒ **它恰恰是"上一轮被重启打断、最该被接走"的那一类**。
|
|
10822
|
+
* 2026-10-23 实测:同一时刻 `acc-diag` 把一条会话列为 live,而本动作把它当命中扫出 ⇒
|
|
10823
|
+
* 两个口径两个答案;随后真机证明那条会话当时**确实该被接走**。
|
|
10824
|
+
*
|
|
10825
|
+
* ⇒ **现在答的是「此刻有没有在跑」**,取数口 = `listRunningSessionIds(nowMs)`(`cro/turns.ts`):
|
|
10826
|
+
* 由 `agent/status=running` 置位、`idle` 清位,`TURN_TTL_MS` 兜底。
|
|
10827
|
+
*
|
|
10828
|
+
* 🔴 **不要再改回「宿主认不认识它」** —— 这是本函数最容易被"改得更省事"而改回去的地方。
|
|
10829
|
+
* 「宿主认识」是**载入状态**,「在跑」是**轮次状态**,两者互不蕴含:
|
|
10830
|
+
* 载入而空闲(该接走)、在跑但刚被中断(该接走)、在跑且健康(才该排除)。
|
|
10831
|
+
* 只有第三格该排除 ⇒ 判据必须是轮次状态。
|
|
10832
|
+
*
|
|
10833
|
+
* 拿不到 running 表时退化为空集(不阻断扫描,只少一层保护)—— 与修前同口径。
|
|
10857
10834
|
*/
|
|
10858
|
-
function
|
|
10835
|
+
function collectRunningSessionIds(nowMs) {
|
|
10859
10836
|
const out = /* @__PURE__ */ new Set();
|
|
10860
10837
|
try {
|
|
10861
|
-
for (const
|
|
10838
|
+
for (const id of listRunningSessionIds(nowMs)) if (typeof id === "string" && id) out.add(id);
|
|
10862
10839
|
} catch {}
|
|
10863
10840
|
return out;
|
|
10864
10841
|
}
|
|
@@ -11380,7 +11357,7 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
11380
11357
|
lookbackHours,
|
|
11381
11358
|
scan: scanInterruptedSessions(projectDir, {
|
|
11382
11359
|
lookbackMs: lookbackHours * 36e5,
|
|
11383
|
-
liveIds:
|
|
11360
|
+
liveIds: collectRunningSessionIds(Date.now())
|
|
11384
11361
|
})
|
|
11385
11362
|
});
|
|
11386
11363
|
}
|
|
@@ -11404,7 +11381,7 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
11404
11381
|
const projectDir = join(sessionsRootPath, projectDirNameForCwd(cwdRead));
|
|
11405
11382
|
const scan = scanInterruptedSessions(projectDir, {
|
|
11406
11383
|
lookbackMs: lookbackHours * 36e5,
|
|
11407
|
-
liveIds:
|
|
11384
|
+
liveIds: collectRunningSessionIds(Date.now())
|
|
11408
11385
|
});
|
|
11409
11386
|
if (dryRun) return renderContinueResult({
|
|
11410
11387
|
projectDir,
|
|
@@ -23292,6 +23269,85 @@ function createImBridgeTool() {
|
|
|
23292
23269
|
});
|
|
23293
23270
|
}
|
|
23294
23271
|
//#endregion
|
|
23272
|
+
//#region src/trajectory/live.ts
|
|
23273
|
+
/**
|
|
23274
|
+
* trajectory/live.ts — 进程内 live 会话取数 + 精简诊断
|
|
23275
|
+
*
|
|
23276
|
+
* ── 为什么单独成模块(R↓,2026-09-15 S142「ACC 侧 autopilot 退场」S1 搬迁)──
|
|
23277
|
+
* 这两个函数原先住在 `autopilot-trajectory.ts`,但它们的**主语不是 autopilot**:
|
|
23278
|
+
* `listLiveSessions` 服务 `acc-diag` ① 段的"**哪个会话属于哪个 CCC**"(会话维度,
|
|
23279
|
+
* 不是 CCC 维度),而"**独立进程看不到运行时**"正是 `acc-diag` 存在的理由。
|
|
23280
|
+
* autopilot 退场后它们必须活下去 ⇒ 独立成模块,不再挂着已死机制的名字。
|
|
23281
|
+
*
|
|
23282
|
+
* ── 迁移边界(不搬什么,以及为什么)──────────────────────────────────────
|
|
23283
|
+
* 原 `diagLive` 还含两段,**均不搬**(其主语是 autopilot,随机制退场):
|
|
23284
|
+
* · `autopilotCccs`(逐 CCC 目标命中 / agent 定位诊断)—— 依赖 `readAutopilotSettings` /
|
|
23285
|
+
* `getAutopilotStatus` / `resolveTargetAgent`
|
|
23286
|
+
* · `panelResolved`(无参面板请求落到哪个 CCC)—— 面板区块随机制退场
|
|
23287
|
+
* 留下的 = **进程 cwd/CCC + live 会话清单**(`acc-diag` ① 段仍需要)。
|
|
23288
|
+
*
|
|
23289
|
+
* 判据纪律:本模块**不得**反向依赖 autopilot 判据层(那会让退场半途而废)。
|
|
23290
|
+
*
|
|
23291
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
23292
|
+
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触
|
|
23293
|
+
* (直接 import 宿主 `cordis` 的类型)。
|
|
23294
|
+
*
|
|
23295
|
+
* **为什么这是"真解耦"而不是"把类型藏起来"(判据):** `ctx` 在本模块**从不被解引用** ——
|
|
23296
|
+
* 它只被**原样透传**给 `hostSessions(ctx)`,而后者(`host/access.ts`,**L0**)的参数
|
|
23297
|
+
* 本就声明为 `ctx: unknown` ⇒ `Context` 这个 import **在语义上是多余的**,
|
|
23298
|
+
* 它没有参与任何类型检查,只贡献了一处 L1→宿主 的接触点。
|
|
23299
|
+
* 🔵 **证据**:grep 本文件 `ctx` 全部 5 处命中 —— 3 处在参数/调用位、2 处在注释,
|
|
23300
|
+
* **零处**属性访问(`ctx.xxx`)。
|
|
23301
|
+
*/
|
|
23302
|
+
/**
|
|
23303
|
+
* 从 dsh 会话 log 读取标题(latest-wins `session/title` 事件)——
|
|
23304
|
+
* **标题不在 `sessions.list()` 条目上**(wire/对象均无 title 字段;F3 命名经
|
|
23305
|
+
* `sessionTitle.rename` 写进 session log),必须从 events 提取(rebuild 同款读取模式)。
|
|
23306
|
+
*/
|
|
23307
|
+
function readSessionTitle(session) {
|
|
23308
|
+
try {
|
|
23309
|
+
const events = sessionEvents(session);
|
|
23310
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
23311
|
+
const e = events[i];
|
|
23312
|
+
if (e?.type === "session/title" && typeof e.data?.title === "string" && e.data.title.trim() !== "") return e.data.title.trim();
|
|
23313
|
+
}
|
|
23314
|
+
} catch {}
|
|
23315
|
+
return null;
|
|
23316
|
+
}
|
|
23317
|
+
/**
|
|
23318
|
+
* 逐条 live 会话(`cccRoot` = 该会话 cwd 的 CCC 归属,无 → null)。
|
|
23319
|
+
*
|
|
23320
|
+
* CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
|
|
23321
|
+
* 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
|
|
23322
|
+
*
|
|
23323
|
+
* @param ctx 插件上下文(**只透传**给 L0 取数口,本模块不解引用 ⇒ 类型为 `unknown`)
|
|
23324
|
+
* @returns 逐条 live 会话
|
|
23325
|
+
*/
|
|
23326
|
+
function listLiveSessions(ctx) {
|
|
23327
|
+
const out = [];
|
|
23328
|
+
try {
|
|
23329
|
+
const sessions = hostSessions(ctx);
|
|
23330
|
+
for (const s of sessions?.list?.() ?? []) {
|
|
23331
|
+
const cwd = s?.header?.cwd ?? null;
|
|
23332
|
+
out.push({
|
|
23333
|
+
id: s?.id ?? "",
|
|
23334
|
+
cwd,
|
|
23335
|
+
cccRoot: cwd ? cccRootForCwd(cwd) : null,
|
|
23336
|
+
title: readSessionTitle(s)
|
|
23337
|
+
});
|
|
23338
|
+
}
|
|
23339
|
+
} catch {}
|
|
23340
|
+
return out;
|
|
23341
|
+
}
|
|
23342
|
+
function diagLive(ctx) {
|
|
23343
|
+
const processCwd = process.cwd();
|
|
23344
|
+
return {
|
|
23345
|
+
processCwd,
|
|
23346
|
+
processCcc: cccRootForCwd(processCwd),
|
|
23347
|
+
liveSessions: listLiveSessions(ctx)
|
|
23348
|
+
};
|
|
23349
|
+
}
|
|
23350
|
+
//#endregion
|
|
23295
23351
|
//#region src/surface/observation.ts
|
|
23296
23352
|
/**
|
|
23297
23353
|
* surface/observation.ts — 容器状态模型的**取数层唯一出口**(C5「观察面归一」,S142 2026-09-15)
|
package/lib/wake/scheduler.d.ts
CHANGED
|
@@ -157,6 +157,17 @@ export declare function sendToCognitiveTrajectory(ctx: unknown, root: string, ta
|
|
|
157
157
|
* @returns 投递正文
|
|
158
158
|
*/
|
|
159
159
|
export declare function buildCroWakeText(dirName: string, mdPath: string, prompt: string, reason: string | null): string;
|
|
160
|
+
/**
|
|
161
|
+
* ⚠️ **2026-10-23 A105 已删除 `collectLiveSessionIdsForSafetyNet`。**
|
|
162
|
+
*
|
|
163
|
+
* 它取的是 `hostSessions(ctx).list()` =「宿主认不认识」,而 A105 的结论是
|
|
164
|
+
* 判据必须是「**此刻有没有在跑**」。现在两处(本兜底与权威 `interrupted` 动作)
|
|
165
|
+
* 都走 `listRunningSessionIds()` —— **同一张表、同一个口径**,不可能再打架。
|
|
166
|
+
*
|
|
167
|
+
* 🔴 后人若要"把 live 表也加回来做双保险":**不要** ——
|
|
168
|
+
* `live ∧ ¬running` 恰是「已载入、但上一轮被打断」那一类,**它最该被接走**;
|
|
169
|
+
* 加回来就是把最该救的那一格重新排除掉。
|
|
170
|
+
*/
|
|
160
171
|
/**
|
|
161
172
|
* 装配调度器(index.ts apply 调用):5min tick + 生命周期 disposer +
|
|
162
173
|
* 会话出现/settings 变化时热启动(与 autopilot 同款触发面,互不干扰)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.90.0",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/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": {
|