@shgroup/dsh-serenity-hooks 1.34.1 → 1.35.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/lib/index.js CHANGED
@@ -5,12 +5,9 @@ import { i as cccRootForExec, n as agentCwdFor, o as listCccs, r as cccRootForCw
5
5
  import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, g as runLocalStore, h as readStore, i as matchWeixinRoute, l as weixinSessionIdFor, m as readGitTrack, n as extractWeixinText, o as readWeixinSettings, p as localstorePath, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-B2ajFypI.js";
6
6
  import { a as readSkiffRoles, c as roleMsmWhitelist, d as systemPromptSource, f as trajectorySubset, i as isSkiffSessionId, l as roleToolWhitelist, n as buildSkiffBasePrompt, o as resolveRoleSystemPrompt, r as createRolePromptReader, s as resolveSkiffKind } from "./skiff-role-CEHL5cek.js";
7
7
  import { a as unregisterSkiffSession$1, i as skiffSessionSnapshot$1, n as skiffRoleFor$1, r as skiffSessionInfo$1, t as registerSkiffSession$1 } from "./skiff-registry-FTjoWQTQ.js";
8
- import { _ as summarize, a as extractSessionMdPathFromText, d as readActiveSessionMd, f as resolveSessionByTitle, g as showSession, h as setActiveSessionInfo, i as createSession, l as listSessions, m as sessionsRoot, n as TRAJECTORY_ACTIONS, o as findSession, p as sessionEvents$1, r as clearActiveSessionInfo, s as getActiveSessionInfo, t as DEFAULT_SESSION_SCOPE, u as parseSessionContextFromEvents, v as useSession } from "./trajectory-ops-mrXK0H0I.js";
9
- import { i as resolveSessionTrajectoryLabel, n as listBoundSessionIds, r as readLastBound, t as appendBound } from "./trajectory-bound-Cax1p9ut.js";
10
- import { a as splitDueWakes, i as loadWakeRegistry, n as addWake, o as updateWake, r as listWakes, t as WAKE_CATCH_UP_MS } from "./wake-registry-CYxIb484.js";
11
- import { c as biasProviderAbsPath, d as isAutopilotSession, f as judgeWake, g as resolveTargetMd, h as readUtf8, l as fetchBiasContent, p as readAutopilotSettings, r as DEFAULT_BIAS_PROVIDER, s as beijingHour, t as AUTO_DIR_SUFFIX } from "./autopilot-core-DCRkiWEe.js";
12
- import { c as registerAutopilot, i as autopilotWakeInFlight, l as registerDisposer, n as autopilotGloballyEnabled, o as diagLive, r as autopilotRuntimeFacts, s as getAutopilotStatus, t as autopilotClockState, u as createClock } from "./autopilot-trajectory-D9Znxo75.js";
13
- import { a as FACE_PORTS, c as MECHANISM_PORTS, i as ACP_HTTP_PORT, l as SKIFF_DEBUG_PORT, n as registerSettingsSection, o as GATEWAY_PORT, s as MAIN_WEB_PORT, t as readSimpleSettings, u as WEIXIN_SEND_PORT } from "./settings-section-DTYZjwUs.js";
8
+ import { _ as sessionsRoot, a as DEFAULT_SESSION_SCOPE, b as summarize, c as createSession, d as getActiveSessionInfo, f as listSessions, g as sessionEvents$1, h as resolveSessionByTitle, i as resolveSessionTrajectoryLabel, l as extractSessionMdPathFromText, m as readActiveSessionMd, n as listBoundSessionIds, o as TRAJECTORY_ACTIONS, p as parseSessionContextFromEvents, r as readLastBound, s as clearActiveSessionInfo, t as appendBound, u as findSession, v as setActiveSessionInfo, x as useSession, y as showSession } from "./trajectory-bound-DETSEBpL.js";
9
+ import { a as splitDueWakes, i as loadWakeRegistry, n as addWake, o as updateWake, r as listWakes, t as WAKE_CATCH_UP_MS } from "./wake-registry-Cj2aSHME.js";
10
+ import { a as FACE_PORTS, c as MECHANISM_PORTS, i as ACP_HTTP_PORT, l as SKIFF_DEBUG_PORT, n as registerSettingsSection, o as GATEWAY_PORT, s as MAIN_WEB_PORT, t as readSimpleSettings, u as WEIXIN_SEND_PORT } from "./settings-section-DMDUoQum.js";
14
11
  import { a as markdownToPlainText, c as sendTyping, i as getUpdates, l as sniffImageExt, n as downloadMedia, o as sendFileMessage, r as getConfig, s as sendTextMessage, t as TypingStatus } from "./weixin-api-CtFYm53S.js";
15
12
  import z from "@deepseek-ai/schemastery";
16
13
  import { defineTool } from "@deepseek-ai/dsh-tools";
@@ -961,7 +958,6 @@ Directory of ACC capabilities — each area lists where to go for details (guide
961
958
  ① 轨迹本体 → container_trajectory(list/show/create/use + rebuild 超限重建 + wake-later 未来一次性唤醒)
962
959
  原 logbook 已并入(v1.33 用户裁决:废除 logbook 词);close/archive 删(归档走 container_fs mv),
963
960
  health/qa/summary 分别并入 use / list,hook-develop-guide 并进 container_admin msm guide
964
- ①b 周期自唤醒 → container_admin autopilot(status/init/generate-bias——D59 单例;status 含就绪与下一步)
965
961
  ② 认知质量框架 → praxis(eap/neat/cce 三合一——可实践理论注入,section 渐进披露,无参即目录)
966
962
  ③ 工具与执行 → container_fs(容器文件系统 15 子命令)/ container_git(git)/ dashboard(health 含注册表检查/time/wait)
967
963
  acc_msm 已并入 msm(执行+发现)与 container_admin(管理)——msm("<name>", ["<args>"]);inspect=true 查用法
@@ -1110,15 +1106,16 @@ validate 校验 / apply 生效 / list 查看:container_admin role <guide|valid
1110
1106
  "systemPromptFile": ".opencode/skiff/qa.md"
1111
1107
  } } } }
1112
1108
 
1113
- ── 7. container_trajectory(轨迹:autopilot 自主巡航 + 唤醒注册表,D58)──
1114
- CCC 定义的一条自主 trajectory——时钟到点自动唤起(前台注入,用户可见可介入)。
1115
- **autopilot trajectory 的子集**:周期自唤醒的特例;trajectory 本身是一等概念(一个 CCC 内可多条并行)。
1116
- 未配置或 enabled=false 完全不启动(零资源占用)。多 CCC 独立:每 CCC 自己的配置。
1117
- 唤起消息四段式:轨迹焦点 topPrompt(最先注入,稳定锚)→ 身份锚定 先验偏见
1118
- (CCC 根脚本 biasProvider 输出)→ 任务。目标会话 session 必填(目录须带 --auto 后缀)。
1119
- 诊断/状态/立即唤起:container_admin autopilot(status/init/generate-bias);ACC 负责人专用运行态诊断见开发面。
1109
+ ── 7. container_trajectory(轨迹 + 唤醒注册表,D58)──
1110
+ CCC 的一条 trajectory = 持久身体(SESSION.md)+ 时间轴(AGENT_SESSIONS/ 目录 + 唤醒注册表)。
1111
+ 一个 CCC 内可多条并行;条目全部由人类/agent 显式登记。
1112
+ ⚠️ ACC 不再提供**周期自唤醒**(原 autopilot,2026-09-15 整段退场:机制/时钟/container_admin autopilot
1113
+ 域/面板区块一并删除)。自主巡航改由 CCC 自己驱动(home-serenity msm autopilot-round 每轮自排下一轮,
1114
+ 真相源 = S151 SESSION.md §1/§1b/§1c)。**本轮的 trajectory.autopilot 配置段保留**(见下表)——
1115
+ 它现由 **CCC 侧脚本**读取,ACC 不再消费该段。
1120
1116
  唤醒注册表(container_trajectory wake-later):唤醒 = 未来时刻 + 一条 message,可唤醒自己或别的 trajectory;
1121
1117
  落点 AGENT_SESSIONS/wake-registry.json,中心调度器到点投递(fire-and-forget;无回执、无回收)。
1118
+ 运行态诊断(ACC 负责人专用):acc-diag(① live 运行态 / ①b 唤醒时钟 / ③ 唤醒注册表)。
1122
1119
 
1123
1120
  Config:
1124
1121
  { "trajectory": {
@@ -3375,375 +3372,6 @@ const localstoreTool = defineTool({
3375
3372
  }
3376
3373
  });
3377
3374
  //#endregion
3378
- //#region src/autopilot-chain.ts
3379
- /**
3380
- * autopilot-chain.ts — **唤起条件链的唯一实现**("为什么这轮没被唤起")
3381
- *
3382
- * 为什么单独成模块(R↓,⑥ C6a 的硬约束):
3383
- * C6 裁决把 autopilot 拆两半——**入口**分两处(CCC 工具面 / 开发面),但**判据只能有一份**。
3384
- * 若开发面另写"文件读取版",刚消掉的分歧会在开发面复活。故:
3385
- * · `acc-diag` ④ 段(进程内,带运行态事实)—— `./diag-ops.ts` 调 {@link buildWakeChain}
3386
- * · 开发面 `dsh-develop diag [--ccc <path>]`(离线 bun 进程,无运行态事实)—— **import 同一实现**
3387
- * 两者的差别**只是传入的 facts**(进程内三项已知 / 离线三项不可知),判据本身不复制。
3388
- *
3389
- * 本模块**零 DSH 依赖**(只 node:fs/path + `ccc.js` + `autopilot-core.js`)——离线 bun 进程
3390
- * 才 import 得动。判据一律取自 {@link judgeWake}(判据唯一真相源),本模块只做**分解与分类**。
3391
- *
3392
- * 判决语义(§12.7 修复,**不得回退**):
3393
- * `✗` = 配置/环境问题(需人改,如 enabled=false / session 未命中 / agent 未加载)
3394
- * `⏸` = 等待中(到点/出窗口/上一轮跑完自会满足——**非**配置问题)
3395
- * `?` = 不可知(离线通道看不到的运行态;**不**当成满足)
3396
- * **只有 `✗` 与 `⏸` 都为空、且无 `?` 时才印 ✅**——旧实现只统计 `✗` ⇒ 每天北京 8~18 点
3397
- * (10 小时)都印「✅ 唤起条件全部满足」而插件实际不唤起(假阳性,已实测复现)。
3398
- */
3399
- /** 离线通道事实(三项运行态全不可知) */
3400
- const NO_RUNTIME_FACTS = {
3401
- globalGate: null,
3402
- running: null,
3403
- live: null
3404
- };
3405
- /**
3406
- * 组装条件链(**唯一实现**)。
3407
- *
3408
- * @param root CCC 根
3409
- * @param facts 运行态事实(离线通道传 {@link NO_RUNTIME_FACTS})
3410
- * @param now 当前时刻(测试注入)
3411
- * @param biasProbe 偏见执行(默认 {@link fetchBiasContent}——**不另写 spawn**)
3412
- * @returns 结构化报告(渲染见 {@link renderWakeChain})
3413
- */
3414
- async function buildWakeChain(root, facts = NO_RUNTIME_FACTS, now = Date.now(), biasProbe = fetchBiasContent) {
3415
- const lines = [`═══ 自主轨迹唤起诊断:${basename(root)}(${root})═══`];
3416
- const blockers = [];
3417
- const waiting = [];
3418
- const unknowns = [];
3419
- const suggestions = [];
3420
- const mark = (kind, text) => {
3421
- const line = `${kind === "ok" ? "✓" : kind === "bad" ? "✗" : kind === "wait" ? "⏸" : kind === "unknown" ? "?" : "·"} ${text}`;
3422
- lines.push(` ${line}`);
3423
- if (kind === "bad") blockers.push(line);
3424
- if (kind === "wait") waiting.push(line);
3425
- if (kind === "unknown") unknowns.push(line);
3426
- };
3427
- if (facts.globalGate === null) mark("unknown", "周期自唤醒全局闸:不可知(离线通道——只有插件进程能读 DSH 设置)");
3428
- else if (!facts.globalGate) {
3429
- mark("bad", "周期自唤醒全局闸关闭(autopilotWakeEnabled=false)——时钟不武装,绝不唤起");
3430
- suggestions.push("在 DSH 设置面板打开「周期自唤醒」(autopilotWakeEnabled)——关着则本 CCC 永不唤起");
3431
- } else mark("ok", "周期自唤醒全局闸开启(autopilotWakeEnabled=true)");
3432
- const cfg = readAutopilotSettings(root);
3433
- if (!cfg) {
3434
- mark("bad", "trajectory.autopilot 未配置(.opencode/serenity.json 缺段)");
3435
- suggestions.push("配置:container_admin autopilot init 一键写入 trajectory.autopilot 段");
3436
- } else if (!cfg.enabled) {
3437
- mark("bad", "enabled = false(该 CCC 的周期自唤醒关闭)");
3438
- suggestions.push("置 trajectory.autopilot.enabled = true");
3439
- } else mark("ok", "enabled = true");
3440
- if (cfg) {
3441
- lines.push(` 配置: intervalHours=${cfg.intervalHours ?? 12} | biasProvider=${cfg.biasProvider?.trim() || "autopilot-bias.ts"}${cfg.session ? ` | session=${cfg.session}` : " | session=未配置"}`);
3442
- const avoid = cfg.avoidWakeHours;
3443
- lines.push(` 窗口避开北京 ${avoid?.start ?? 8}~${avoid?.end ?? 18} 点`);
3444
- }
3445
- const md = resolveTargetMd(root, cfg ?? {});
3446
- if (!cfg?.session) {
3447
- mark("bad", "session 未配置(必填——自动唤起不默认任何会话)");
3448
- suggestions.push("配置 trajectory.autopilot.session: \"S###\"(目标会话 S###/目录名)");
3449
- } else if (!md) {
3450
- mark("bad", `session=${cfg.session} 未命中(AGENT_SESSIONS 无匹配目录;匹配规则 = findSession:补零/去大小写)`);
3451
- suggestions.push("确认目标会话存在(目录名含该关键字;S### 可省略补零)");
3452
- } else mark("ok", `session=${cfg.session} 命中(${basename(dirname(md))})`);
3453
- if (md) {
3454
- if (isAutopilotSession(md)) mark("ok", "会话目录带 --auto 标志");
3455
- else {
3456
- mark("bad", `会话目录无 --auto 标志(${basename(dirname(md))})`);
3457
- suggestions.push(`目录名加 ${AUTO_DIR_SUFFIX} 后缀:AGENT_SESSIONS/<date>--<desc>${AUTO_DIR_SUFFIX}/`);
3458
- }
3459
- }
3460
- const j = judgeWake(cfg, md, now);
3461
- if (md && j.idleHours !== null) {
3462
- if (j.intervalOk) mark("ok", `距上次轨迹活动 ${j.idleHours.toFixed(1)}h ≥ 阈值 ${j.interval}h`);
3463
- else mark("wait", `距上次轨迹活动 ${j.idleHours.toFixed(1)}h < 阈值 ${j.interval}h(等待中——不满足不唤起)`);
3464
- }
3465
- const h = beijingHour(now);
3466
- if (j.windowOk) mark("ok", `当前北京 ${h} 点——在唤起窗口内`);
3467
- else mark("wait", `当前北京 ${h} 点——高峰避开中(缺省 8~18,用量峰谷省钱)`);
3468
- if (cfg?.enabled) {
3469
- const bias = await biasProbe(root, cfg.biasProvider?.trim() || "autopilot-bias.ts");
3470
- if (bias.error) {
3471
- mark("bad", `偏见脚本: ${bias.error}`);
3472
- suggestions.push("实现偏见脚本(container_admin autopilot init 生成模板后编辑)");
3473
- } else mark("ok", `偏见脚本可运行(输出: ${bias.text ?? "(空)"})`);
3474
- }
3475
- if (facts.live === null) mark("unknown", "目标会话 live 运行态:不可知(离线通道——live 会话/agent 只有插件进程能看到)");
3476
- else if (facts.live.agentResolved === null) lines.push(` · live 会话: 本 CCC ${facts.live.sessionCount} 个(agent 可解析性不适用——目标会话未命中)`);
3477
- else if (facts.live.agentResolved) mark("ok", `目标会话 agent 已定位(本 CCC live 会话 ${facts.live.sessionCount} 个)`);
3478
- else {
3479
- mark("bad", `目标会话 agent 未定位(本 CCC live 会话 ${facts.live.sessionCount} 个)`);
3480
- if (facts.live.diagnosis) lines.push(` ${facts.live.diagnosis}`);
3481
- suggestions.push("在 WebUI 打开目标会话(唤起需要 live agent 接收注入)");
3482
- }
3483
- if (facts.running === null) mark("unknown", "重入守卫:不可知(离线通道——进行中的唤起轮只有插件进程知道)");
3484
- else if (facts.running) mark("wait", "该 CCC 已有一轮唤起在进行中(本轮跳过——跑完自然恢复)");
3485
- else mark("ok", "无进行中的唤起轮");
3486
- lines.push("");
3487
- let verdict;
3488
- let judgement;
3489
- if (blockers.length === 0 && waiting.length === 0 && unknowns.length === 0) {
3490
- verdict = "ready";
3491
- judgement = `✅ 唤起条件全部满足——等待下一个 5min tick 自动唤起(前台可见)`;
3492
- } else if (blockers.length === 0 && waiting.length === 0) {
3493
- verdict = "unknown";
3494
- judgement = `? 已知条件全部满足——但有 ${unknowns.length} 项不可知(${unknowns.length > 0 ? "离线通道看不到运行态" : ""});是否真会唤起需插件进程判定(acc-diag)`;
3495
- } else if (blockers.length === 0) {
3496
- verdict = "waiting";
3497
- judgement = `⏸ 尚未满足 ${waiting.length} 项(等待中——到点/出窗口后自动满足,**非**配置问题;见上方 ⏸ 行)`;
3498
- } else {
3499
- verdict = "blocked";
3500
- judgement = `⚠️ 阻断点 ${blockers.length} 项${waiting.length > 0 ? `(另 ${waiting.length} 项等待中)` : ""}(全部满足才唤起):`;
3501
- }
3502
- lines.push(judgement);
3503
- if (verdict === "blocked") for (const s of suggestions) lines.push(` → ${s}`);
3504
- if (verdict !== "blocked" && unknowns.length > 0) lines.push(` · 不可知项:${unknowns.length} 项(见上方 ? 行——离线通道看不到运行态,**不**当作满足)`);
3505
- return {
3506
- root,
3507
- lines,
3508
- blockers,
3509
- waiting,
3510
- unknowns,
3511
- suggestions,
3512
- verdict,
3513
- judgement
3514
- };
3515
- }
3516
- /** 条件链渲染(④ 段文本;`renderAccDiag` 与开发面 `diag` 共用) */
3517
- function renderWakeChain(r) {
3518
- return r.lines.join("\n");
3519
- }
3520
- //#endregion
3521
- //#region src/autopilot-ops.ts
3522
- /**
3523
- * CCC 配置路径回退链(与 `ccc.ts` 的 `DEFAULT_SERENITY_CONFIG_PATHS` 同序):
3524
- * `.opencode/serenity.json` 优先,不存在则 `.dsh/serenity.json`(**沿用旧脚本语义**)。
3525
- */
3526
- const CONFIG_PATHS = [".opencode/serenity.json", ".dsh/serenity.json"];
3527
- /** 偏见内容提供者脚本脚手架模板(init 生成;CCC 按自己的反馈信息来源改写) */
3528
- const BIAS_TEMPLATE = `#!/usr/bin/env bun
3529
- /**
3530
- * autopilot-bias.ts — 自主轨迹偏见内容提供者(CCC 实现)
3531
- *
3532
- * stdout 输出一行/一段文本 = 本轮唤起注入的偏见内容(反事实问题 / 探索方向 /
3533
- * 任何让轨迹偏离既有路径的输入)。偏见内容归 CCC——用本 CCC 自己的反馈信息来源
3534
- * 保证"足够随机"(历史会话 / 技能 / 文档 / 外部信息 / 真随机源……)。
3535
- *
3536
- * 本文件为脚手架——请按本 CCC 的信息来源改写。
3537
- */
3538
- const sources = [
3539
- 'AGENT_SESSIONS 中的历史会话',
3540
- '.opencode/skills 技能目录',
3541
- 'docs/ 设计文档',
3542
- '…(CCC 自己的信息来源)',
3543
- ]
3544
- const pick = sources[Math.floor(Math.random() * sources.length)]
3545
- console.log(\`反事实:如果「\${pick}」换个做法会怎样?\`)
3546
- `;
3547
- /** 背景摘要(一站式报告的第一节——CCC agent 快速理解"为什么/验证什么") */
3548
- const BACKGROUND = [
3549
- "═══ 自主轨迹实验(Self-Sustaining Trajectory)═══",
3550
- "背景:Trajectory 是主体(Agent 可替换、Session 载体可重建)。瓶颈 = 人类 waiting——",
3551
- " 轨迹以\"等待人类\"为推进条件时,事件序列时间被人类响应间隔锚定,运转被拖慢。",
3552
- "猜想:无人等待的 trajectory——时钟自动唤起 + 先验偏见(自生动机 + 偏见内容)→ 运转加速。",
3553
- "验证:P4 可自动唤起并携带偏见 / P5 速度提升→更快产生满意效果;反证=熵增失控/自我确认/噪音化。",
3554
- "理论:specs §0.7 记录器→校准器(预测加工)。完整定义:experiments/autopilot-trajectory/SKILL.md。",
3555
- ""
3556
- ].join("\n");
3557
- /** 步骤指引(`status` 末节;与 SKILL.md §3「参与方式」同源) */
3558
- const GUIDE = [
3559
- "[autopilot-trajectory] 实验步骤指引",
3560
- "",
3561
- "① 初始化(一键):container_admin autopilot init",
3562
- " ——写配置(.opencode/serenity.json 的 trajectory.autopilot 段)+ 生成偏见提供者脚本模板(CCC 根 autopilot-bias.ts)",
3563
- "② **定义轨迹焦点(topPrompt)**:编辑配置中的 topPrompt——**CCC 自己填写**本轨迹的核心",
3564
- " 目标/纪律/质量要求(示例:\"持续深化某领域认知,产出可重建的结论与决策记录\")。",
3565
- " 它会在每次唤起时最先注入([轨迹焦点] 段),作为稳定焦点锚定 trajectory 防漂移——",
3566
- " **实验观察:无焦点锚定时多轮唤起轨迹腐化严重(焦点丢失)**。勿留空。",
3567
- "③ 实现偏见内容提供者:编辑 autopilot-bias.ts,stdout 输出偏见内容(反事实方向/探索动机,",
3568
- " 用本 CCC 自己的信息来源保证\"足够随机\");container_admin autopilot generate-bias 验证",
3569
- "④ 标记目标会话:目录名加 --auto 后缀 AGENT_SESSIONS/<date>--<desc>--auto/",
3570
- " (可选)该 SESSION.md 写「下一轮动机」段作自生偏见",
3571
- "⑤ 验证就绪:container_admin autopilot status(一站式报告应为 ✅;topPrompt 未定义会 ⚠ 提示)",
3572
- "⑥ **确认全局闸已开**:DSH 设置面板「周期自唤醒」(autopilotWakeEnabled)——关着则本 CCC 永不唤起",
3573
- "⑦ 观察:无人类活动满 intervalHours 且北京非高峰 → 前台会话自动出现 [自主轨迹唤起]",
3574
- " 产出落 SESSION.md「自主探索日志」+ 预写「下一轮动机」",
3575
- "",
3576
- "分工:焦点(topPrompt)= 稳定锚,每轮不变;偏见(biasProvider)= 随机探索方向,每轮不同——两者都由 CCC 定义。",
3577
- "定义全文见 experiments/autopilot-trajectory/SKILL.md(仓库内文档,不再有可执行脚本 entry)",
3578
- "运行态诊断(live 会话 / agent 定位 / 唤醒注册表 / 本条件链):专属工具 acc-diag(ACC 负责人)"
3579
- ].join("\n");
3580
- /**
3581
- * 条件链事实:全局闸与重入守卫**恒为真值**(二者不需要 ctx);live 运行态在无 ctx 时标"不可知"。
3582
- * 即"闸关着"在任何进程中路径都**不会**被降级成 `?`(假报告的正是这一类)。
3583
- */
3584
- function factsFor(root, ctx) {
3585
- return autopilotRuntimeFacts(ctx ?? null, root);
3586
- }
3587
- /** 就绪检查(= 旧脚本 `check`):条件链的逐条件报告 + 判决(判据单一真相源 = `buildWakeChain`) */
3588
- async function autopilotCheck(root, ctx) {
3589
- return buildWakeChain(root, factsFor(root, ctx));
3590
- }
3591
- /**
3592
- * 进程态区块(**进程内新增的增益**——旧脚本永远看不到这一行):
3593
- * 全局闸 / 时钟是否武装 / 已 tick 次数 / 上次 tick / 上次跳过原因 / 是否有唤起在进行中。
3594
- * "为什么没唤起"的第一手判据:闸关着、时钟没武装时,条件再全绿也不会唤起。
3595
- */
3596
- function runtimeBlock(root) {
3597
- const s = autopilotClockState();
3598
- const ts = (ms) => ms === null ? "(从未)" : new Date(ms).toISOString();
3599
- const parts = [
3600
- `全局闸=${s.enabled ? "开" : "关"}`,
3601
- `时钟已武装=${s.armed}`,
3602
- `tick 次数=${s.ticks}`,
3603
- `上次 tick=${ts(s.lastTickAt)}`
3604
- ];
3605
- if (s.lastSkipReason) parts.push(`上次跳过: ${s.lastSkipReason}`);
3606
- parts.push(`本轮唤起进行中=${autopilotWakeInFlight(root)}`);
3607
- return `进程态(本插件进程):${parts.join(" | ")}`;
3608
- }
3609
- /** 状态快照区块(配置 / 目标 / 空闲时长 / 窗口——数值取自 `autopilot-core` 的同一判据) */
3610
- function stateBlock(root) {
3611
- const cfg = readAutopilotSettings(root);
3612
- const now = Date.now();
3613
- const hour = Math.floor((now + 288e5) % 864e5 / 36e5);
3614
- const head = `[autopilot-trajectory] 实验状态(CCC: ${basename(root)},北京 ${hour} 点)`;
3615
- if (!cfg?.enabled) return [
3616
- head,
3617
- "",
3618
- "trajectory.autopilot 未启用(enabled=false 或未配置)——零资源占用"
3619
- ].join("\n");
3620
- const lines = [
3621
- head,
3622
- "",
3623
- `配置: intervalHours=${cfg.intervalHours ?? 12} | biasProvider=${cfg.biasProvider?.trim() || "autopilot-bias.ts"}${cfg.session ? ` | session=${cfg.session}` : ""}${cfg.topPrompt?.trim() ? " | topPrompt ✓" : ""}`
3624
- ];
3625
- const md = resolveTargetMd(root, cfg);
3626
- if (!md) {
3627
- lines.push("目标会话:未找到");
3628
- return lines.join("\n");
3629
- }
3630
- const j = judgeWake(cfg, md, now);
3631
- lines.push(`目标会话:${basename(dirname(md))}${isAutopilotSession(md) ? "(--auto ✓)" : "(无 --auto 标志)"}`);
3632
- lines.push(`上次轨迹活动:${j.idleHours === null ? "(不可读)" : `${j.idleHours.toFixed(1)} 小时前`}(阈值 ${j.interval}h)`);
3633
- lines.push(`唤起窗口:${j.windowOk ? "✅ 允许唤起" : "⏸ 高峰避开中(北京 8~18)"}`);
3634
- lines.push(`是否可唤起:${j.wakeable ? `✅ 时间条件满足(仍须全局闸开 + agent 已定位;见上/下)` : !j.intervalOk ? `否——距上次活动不足 ${j.interval}h` : "否——高峰避开中"}`);
3635
- return lines.join("\n");
3636
- }
3637
- /** 下一步判据(与旧脚本 `all` 的「下一步」同语义,但改读**条件链判决**——不再自己判一遍) */
3638
- function nextSteps(chain) {
3639
- const lines = ["═══ 下一步 ═══"];
3640
- const noCfg = chain.blockers.some((b) => b.includes("trajectory.autopilot 未配置"));
3641
- const disabled = chain.blockers.some((b) => b.includes("enabled = false"));
3642
- if (noCfg) lines.push("→ 本 CCC 尚未配置 trajectory.autopilot。开始实验:container_admin autopilot init 一键初始化。");
3643
- else if (disabled) lines.push("→ trajectory.autopilot.enabled 为 false——置 true 开启实验(或 init 重新初始化)。");
3644
- else if (chain.verdict === "blocked") lines.push("→ 存在未就绪项(见上)。补齐后重跑本命令确认。");
3645
- else if (chain.verdict === "waiting") lines.push("→ 条件尚未到点(见上 ⏸ 行)——等待中,非配置问题;到点/出窗口后自动唤起。");
3646
- else {
3647
- lines.push("→ 已就绪。保持会话空闲满 intervalHours 且北京非高峰 → 自动唤起(前台可见)。");
3648
- lines.push("→ 之后可随时 container_admin autopilot status 查看距下次唤起的进度。");
3649
- }
3650
- return lines.join("\n");
3651
- }
3652
- /**
3653
- * `status` —— 一站式全报告(背景 + 就绪检查 + 进程态 + 状态 + 下一步 + 指引)。
3654
- * 由进程内数据组装(旧实现是 `bun` 子进程跑脚本,故看不到任何运行态)。
3655
- */
3656
- async function autopilotStatus(root, ctx) {
3657
- const chain = await autopilotCheck(root, ctx);
3658
- return {
3659
- action: "status",
3660
- output: [
3661
- BACKGROUND,
3662
- renderWakeChain(chain),
3663
- "",
3664
- runtimeBlock(root),
3665
- "",
3666
- stateBlock(root),
3667
- "",
3668
- nextSteps(chain),
3669
- "",
3670
- GUIDE
3671
- ].join("\n")
3672
- };
3673
- }
3674
- /**
3675
- * `init` —— 写/迁移 CCC 配置 + 生成偏见脚本模板。
3676
- *
3677
- * 迁移语义(沿用旧脚本,理由写在码内):写入**新键** `trajectory.autopilot`(D58);
3678
- * 旧键 `autopilotTrajectory` / `autotrajectory` 存在则**迁移并删除**——保留两处会形成
3679
- * "改了不生效"的双真相源(读侧新键优先,旧键是死数据)。
3680
- */
3681
- function autopilotInit(root) {
3682
- const out = ["[autopilot-trajectory] 初始化实验", ""];
3683
- let cfgPath = join(root, CONFIG_PATHS[0]);
3684
- if (!existsSync(cfgPath)) {
3685
- const alt = join(root, CONFIG_PATHS[1]);
3686
- cfgPath = existsSync(alt) ? alt : cfgPath;
3687
- }
3688
- mkdirSync(dirname(cfgPath), { recursive: true });
3689
- let merged = {};
3690
- if (existsSync(cfgPath)) try {
3691
- merged = JSON.parse(readUtf8(cfgPath));
3692
- } catch {}
3693
- const traj = merged.trajectory ?? {};
3694
- const legacy = merged.autopilotTrajectory ?? merged.autotrajectory;
3695
- const at = traj.autopilot ?? legacy ?? {};
3696
- merged.trajectory = {
3697
- ...traj,
3698
- autopilot: {
3699
- enabled: true,
3700
- intervalHours: 12,
3701
- biasProvider: DEFAULT_BIAS_PROVIDER,
3702
- topPrompt: "本轨迹的核心焦点:<CCC 填写——例如:持续深化某领域认知,产出可重建的结论与决策记录>",
3703
- ...at
3704
- }
3705
- };
3706
- const migrated = Boolean(legacy);
3707
- delete merged.autopilotTrajectory;
3708
- delete merged.autotrajectory;
3709
- writeFileSync(cfgPath, `${JSON.stringify(merged, null, 2)}\n`);
3710
- out.push(`✓ 配置写入: ${cfgPath}`);
3711
- out.push(` trajectory.autopilot = ${JSON.stringify(merged.trajectory.autopilot)}`);
3712
- if (migrated) out.push(" ↑ 已从旧键 autopilotTrajectory/autotrajectory 迁移到 trajectory.autopilot(旧键已删除,避免双真相源)");
3713
- out.push(" ⚠ 请编辑 topPrompt 为**本 CCC 的轨迹焦点**(现为占位——每次唤起最先注入,防轨迹漂移,勿留空)");
3714
- const scriptAbs = biasProviderAbsPath(root, DEFAULT_BIAS_PROVIDER);
3715
- if (existsSync(scriptAbs)) out.push(`· 偏见提供者脚本已存在(保留): ${DEFAULT_BIAS_PROVIDER}`);
3716
- else {
3717
- writeFileSync(scriptAbs, BIAS_TEMPLATE);
3718
- out.push(`✓ 已生成偏见提供者脚本模板: ${DEFAULT_BIAS_PROVIDER}(编辑它,stdout 输出偏见内容)`);
3719
- }
3720
- const gate = autopilotGloballyEnabled();
3721
- out.push(gate ? "✓ 周期自唤醒全局闸已开(autopilotWakeEnabled=true)" : "✗ 周期自唤醒全局闸**关着**(autopilotWakeEnabled=false)——配置写完也不会唤起,请在 DSH 设置面板打开「周期自唤醒」");
3722
- out.push("", "下一步:② 编辑配置 topPrompt(轨迹焦点——CCC 自己填写本轨迹核心目标,勿留空)→ ③ 标记目标会话 --auto 后缀 → ⑤ container_admin autopilot status 验证就绪");
3723
- return {
3724
- action: "init",
3725
- output: out.join("\n")
3726
- };
3727
- }
3728
- /**
3729
- * `generate-bias` —— 运行偏见提供者脚本并打印本轮内容(验证用)。
3730
- * **复用 tick 的 {@link fetchBiasContent}**(同一份执行代码:60s 超时 / 8KB 截断 /
3731
- * 旧默认名回退)——不另写 spawn(§12.13)。
3732
- */
3733
- async function autopilotGenerateBias(root) {
3734
- const provider = readAutopilotSettings(root)?.biasProvider?.trim() || "autopilot-bias.ts";
3735
- const bias = await fetchBiasContent(root, provider);
3736
- if (bias.error) return {
3737
- action: "generate-bias",
3738
- output: "",
3739
- error: bias.error
3740
- };
3741
- return {
3742
- action: "generate-bias",
3743
- output: `[autopilot-trajectory] 当前偏见内容(${provider}):\n${bias.text ?? "(空输出)"}`
3744
- };
3745
- }
3746
- //#endregion
3747
3375
  //#region src/tools/skiff-admin.ts
3748
3376
  /** Skiff 定义教程(核心):概念 / schema / 认知 MSM 写法 / 双白名单 / 轨迹纪律 / 示例角色 */
3749
3377
  const SKIFF_GUIDE = `═══ Skiff Definition Guide (F4, experimental) ═══
@@ -3895,9 +3523,9 @@ function applySkiffConfig(root) {
3895
3523
  * 参数极简(LLM 直觉):domain(管理域)+ action(该域子命令)+ 少量业务参数。
3896
3524
  *
3897
3525
  * v1.34.1(⑥ C6a):由 `export const containerAdminTool` 改为**工厂**
3898
- * `createContainerAdminTool(ctx)`——autopilot 域搬进进程内后,`status` 必须读只有进程内
3899
- * 能看到的事实(全局闸 / live 会话 / agent 可解析性,见 `../autopilot-ops.js`)。
3900
- * 这是本轮唯一为"取 ctx"而做的形态改动(其余域不依赖 ctx,行为不变)。
3526
+ * `createContainerAdminTool(ctx)`——原为 autopilot 域传进程内运行态事实(全局闸 / live 会话 /
3527
+ * agent 可解析性)。该域已于 2026-09-15 ACC autopilot 整段退场删除(所有者裁决 (a)):
3528
+ * `ctx` 现**无消费者**,工厂形态保留只是为了让装配点签名不再变动(见 index.ts 注册处)。
3901
3529
  */
3902
3530
  function agentSessionId(exec) {
3903
3531
  return exec.agent?.session?.id ?? "";
@@ -3908,8 +3536,8 @@ function renderText$1(value) {
3908
3536
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
3909
3537
  }];
3910
3538
  }
3911
- /** 管理面动作映射:domain 可执行 action(转发到底层 runMsm / skiff 逻辑 / autopilot 进程内实现) */
3912
- const ADMIN_ACTIONS = {
3539
+ /** 合法 domain 名(拒绝文案与 schema enum 同源——不得各写一份) */
3540
+ const DOMAIN_NAMES = Object.keys({
3913
3541
  role: [
3914
3542
  "guide",
3915
3543
  "validate",
@@ -3924,44 +3552,22 @@ const ADMIN_ACTIONS = {
3924
3552
  "catalog",
3925
3553
  "ccc-config"
3926
3554
  ],
3927
- config: ["view"],
3928
- autopilot: [
3929
- "status",
3930
- "init",
3931
- "generate-bias"
3932
- ]
3933
- };
3934
- /**
3935
- * autopilot 域动作(`container_admin` 的对外实名)。
3936
- * 三动作的实现在**插件进程内**(`../autopilot-ops.js`)——S142 §12.8 裁决「拆两半、退掉独立脚本」:
3937
- * 旧实现 spawn 包内脚本(独立进程 ⇒ 看不到全局闸 / live 会话 / agent 可解析性 ⇒ 8 项结构性分歧),
3938
- * 现直调进程内实现,与 tick 读**同一份判据**。`diag`/`doc`/`check`/`guide` 仍不在 CCC 工具面
3939
- * (条件链归 `acc-diag` ④ 段 + 开发面 `dsh-develop diag`)。
3940
- */
3941
- const AUTOPILOT_ACTIONS = [
3942
- "status",
3943
- "init",
3944
- "generate-bias"
3945
- ];
3555
+ config: ["view"]
3556
+ });
3946
3557
  function createContainerAdminTool(ctx) {
3947
3558
  return defineTool({
3948
3559
  name: "container_admin",
3949
- description: "Container administration (the ship's maintenance bay — one entry for managing the CCC): role — Skiff cognitive-subset roles (guide/validate/apply/list); msm — MSM registry management (register/deregister/check quality DC-M1~M4) + manuals (guide dev manual / catalog ACC capability directory / ccc-config CCC configuration reference); config — view CCC configuration; autopilot — periodic self-wake (D59): status (one-shot full report: background + readiness + state + next steps) / init (write config + bias-provider script template) / generate-bias (run the CCC bias script — the randomness source belongs to the CCC). Execution of registered MSMs belongs to the msm tool; this tool manages the container.",
3560
+ description: "Container administration (the ship's maintenance bay — one entry for managing the CCC): role — Skiff cognitive-subset roles (guide/validate/apply/list); msm — MSM registry management (register/deregister/check quality DC-M1~M4) + manuals (guide dev manual / catalog ACC capability directory / ccc-config CCC configuration reference); config — view CCC configuration. Execution of registered MSMs belongs to the msm tool; this tool manages the container.",
3950
3561
  parameters: {
3951
3562
  domain: {
3952
3563
  type: "string",
3953
- enum: [
3954
- "role",
3955
- "msm",
3956
- "config",
3957
- "autopilot"
3958
- ],
3564
+ enum: DOMAIN_NAMES,
3959
3565
  required: true,
3960
- description: "Management domain: role (Skiff roles) / msm (MSM registry + manuals) / config (CCC config view) / autopilot (periodic self-wake)"
3566
+ description: "Management domain: role (Skiff roles) / msm (MSM registry + manuals) / config (CCC config view)"
3961
3567
  },
3962
3568
  action: {
3963
3569
  type: "string",
3964
- description: "Domain subcommand (see domain descriptions; e.g. role: validate/apply/list/guide; msm: register/deregister/check/guide/catalog/ccc-config; autopilot: status/init/generate-bias)"
3570
+ description: "Domain subcommand (see domain descriptions; e.g. role: validate/apply/list/guide; msm: register/deregister/check/guide/catalog/ccc-config)"
3965
3571
  },
3966
3572
  name: {
3967
3573
  type: "string",
@@ -4034,20 +3640,8 @@ function createContainerAdminTool(ctx) {
4034
3640
  usage: args.usage
4035
3641
  });
4036
3642
  }
4037
- if (domain === "autopilot") {
4038
- if (!AUTOPILOT_ACTIONS.includes(action)) throw new Error(`container_admin autopilot requires action: ${ADMIN_ACTIONS.autopilot.join(" | ")}`);
4039
- const r = action === "init" ? autopilotInit(root) : action === "generate-bias" ? await autopilotGenerateBias(root) : await autopilotStatus(root, ctx);
4040
- if (r.error) return {
4041
- error: r.error,
4042
- action: r.action
4043
- };
4044
- return {
4045
- action: r.action,
4046
- output: r.output
4047
- };
4048
- }
4049
3643
  if (domain === "config") return { config: runMsm(root, { action: "ccc-config" }) };
4050
- throw new Error(`container_admin requires domain: role | msm | config | autopilot`);
3644
+ throw new Error(`container_admin requires domain: ${DOMAIN_NAMES.join(" | ")}`);
4051
3645
  }
4052
3646
  });
4053
3647
  }
@@ -5983,7 +5577,7 @@ function toolsBlock() {
5983
5577
  " praxis — actionable theory injection: praxis (index) / praxis eap / praxis neat / praxis cce",
5984
5578
  " handyman — delegate work to a worker agent on a CCC-whitelisted model; mode=\"foreground\" (default) = one serial child returns its final text; mode=\"background\" = loop-validated worker (completion code + round cap + restart + progress file), jobs=[] orchestrates parallel work",
5985
5579
  " localstore — ACC local credential/config storage (CCC-root localstore.json; git policy localstore.gitTrack default deny)",
5986
- " container_admin — container administration (the maintenance bay): role (Skiff roles: guide/validate/apply/list) / msm (register/deregister/check/guide/catalog/ccc-config) / config (view) / autopilot (status/init/generate-bias — periodic self-wake)",
5580
+ " container_admin — container administration (the maintenance bay): role (Skiff roles: guide/validate/apply/list) / msm (register/deregister/check/guide/catalog/ccc-config) / config (view)",
5987
5581
  "",
5988
5582
  "MSM call (registered CCC MSMs, deterministic Mech & Semi-Mech):",
5989
5583
  " Execute: msm(\"<name>\", [\"<arg1>\", \"<arg2>\"]) — run a registered MSM directly (partial name returns matching candidates)",
@@ -6781,7 +6375,6 @@ const FILE_UPLOAD_PATH = "/serenity/file-upload";
6781
6375
  const CONFIG_PATH = "/serenity/config";
6782
6376
  const CCCS_PATH = "/serenity/cccs";
6783
6377
  const PUBLIC_ASK_PATH = "/serenity/public-ask";
6784
- const TRAJECTORY_PATH = "/serenity/trajectory";
6785
6378
  const WEIXIN_PATH = "/serenity/weixin";
6786
6379
  /** 图片落盘目录(CCC 根相对;S142 图片自动识别基础设施——粘贴图片落盘供 agent 经 CCC vlm MSM 自主处理) */
6787
6380
  const IMAGE_UPLOAD_DIR = "_tmp/images_from_user";
@@ -7134,7 +6727,7 @@ function registerStatusApi(ctx, opts = {}) {
7134
6727
  return;
7135
6728
  }
7136
6729
  const settings = readAdvancedSettings();
7137
- const { readSimpleSettings } = await import("./settings-section-DTYZjwUs.js").then((n) => n.r);
6730
+ const { readSimpleSettings } = await import("./settings-section-DMDUoQum.js").then((n) => n.r);
7138
6731
  const simple = readSimpleSettings();
7139
6732
  const allowed = settings.publicAsk.allowed;
7140
6733
  const port = simple.acpHttpPort ?? ACP_HTTP_PORT;
@@ -7156,66 +6749,6 @@ function registerStatusApi(ctx, opts = {}) {
7156
6749
  }
7157
6750
  }
7158
6751
  });
7159
- ctx.webServer.register({
7160
- kind: "exact",
7161
- path: TRAJECTORY_PATH,
7162
- handler: async (req, res) => {
7163
- try {
7164
- if (req.method === "POST") {
7165
- if (!requireWebUi(req, res, "立即唤起仅限 WebUI(agent 不可自行唤起)")) return;
7166
- const raw = await readBody$4(req, 16384);
7167
- const body = JSON.parse(raw);
7168
- if (body.action !== "wake") {
7169
- sendJson$3(res, 400, { error: "unsupported action (expected \"wake\")" });
7170
- return;
7171
- }
7172
- const workspace = resolveWorkspace(ctx, {
7173
- sessionId: body.sessionId,
7174
- workspace: body.workspace
7175
- });
7176
- const root = cccRootForCwd(workspace);
7177
- const effectiveRoot = body.ccc?.trim() || !body.workspace && !body.sessionId ? body.ccc?.trim() ?? (await import("./autopilot-trajectory-D9Znxo75.js").then((n) => n.a)).collectAutopilotCccs(ctx)[0] ?? root : root;
7178
- if (!effectiveRoot) {
7179
- sendJson$3(res, 404, { error: `no CCC found from workspace: ${workspace}` });
7180
- return;
7181
- }
7182
- const { performAutopilotWake } = await import("./autopilot-trajectory-D9Znxo75.js").then((n) => n.a);
7183
- const { readAutopilotSettings } = await import("./autopilot-core-DCRkiWEe.js").then((n) => n.o);
7184
- const settings = readAutopilotSettings(effectiveRoot);
7185
- if (!settings) {
7186
- sendJson$3(res, 400, { error: "Autopilot Trajectory 未配置(.opencode/serenity.json 缺段)" });
7187
- return;
7188
- }
7189
- const result = await performAutopilotWake(ctx, effectiveRoot, settings, { force: true });
7190
- sendJson$3(res, result.ok ? 200 : 400, result);
7191
- return;
7192
- }
7193
- if (req.method !== "GET") {
7194
- sendJson$3(res, 405, { error: "method not allowed" });
7195
- return;
7196
- }
7197
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
7198
- const workspace = resolveWorkspace(ctx, {
7199
- sessionId: url.searchParams.get("sessionId") ?? void 0,
7200
- workspace: url.searchParams.get("workspace") ?? void 0
7201
- });
7202
- const root = cccRootForCwd(workspace);
7203
- const { collectAutopilotCccs } = await import("./autopilot-trajectory-D9Znxo75.js").then((n) => n.a);
7204
- const effectiveRoot = url.searchParams.get("ccc")?.trim() ?? (!url.searchParams.get("workspace") && !url.searchParams.get("sessionId") ? collectAutopilotCccs(ctx)[0] : null) ?? root;
7205
- if (!effectiveRoot) {
7206
- sendJson$3(res, 200, { status: null });
7207
- return;
7208
- }
7209
- const { containerAutopilot, containerWakes } = await Promise.resolve().then(() => container_status_exports);
7210
- sendJson$3(res, 200, {
7211
- status: containerAutopilot(effectiveRoot),
7212
- wakes: containerWakes(effectiveRoot).entries
7213
- });
7214
- } catch (err) {
7215
- sendJson$3(res, 400, { error: err.message ?? String(err) });
7216
- }
7217
- }
7218
- });
7219
6752
  ctx.webServer.register({
7220
6753
  kind: "exact",
7221
6754
  path: WEIXIN_PATH,
@@ -7966,38 +7499,29 @@ function probeHostContract(ctx, hostVersion = null) {
7966
7499
  };
7967
7500
  }
7968
7501
  /**
7969
- * 宿主契约报告的**进程内快照**。
7502
+ * 取宿主契约报告——**每次调用现探**(`ctx` 决定"问谁",**不缓存"答案"**)。
7970
7503
  *
7971
- * 为什么需要(R↓,C5 唯一实证的重复取数):`probeHostContract` 此前被算了**两次**——
7972
- * `index.ts` 的启动探针(apply 时一次,用于启动告警)与 `kit-ops.ts` 的 `dashboard health`
7973
- * (每次请求一次)。两者对**同一个已装载的宿主**问同一个问题,第二次计算的答案不可能不同,
7974
- * 成本却是一次全服务/全成员遍历。
7504
+ * 为什么**不缓存**(R↓:一个被实测推翻的设计,留档以免重犯):
7505
+ * v1.34.1 曾把 `apply()` 时的探针结果存为模块级快照,理由是"两者对**同一个已装载的宿主**
7506
+ * 问同一个问题,第二次计算的答案**不可能不同**,成本却是一次全服务遍历"。
7507
+ * **该前提被实测证伪**(本机 2026-09-15,v1.34.1 部署后):
7508
+ * - `apply()` 跑在装载早期,表中 `access:'lazy'` 的服务**尚未实例化**(`ctx.get` 返回 undefined)
7509
+ * ⇒ 启动探针把它们记为 `not available (lazy)`;
7510
+ * - 到 `dashboard health` 被调用时它们早已可用 ⇒ **现探给的是相反结论**。
7511
+ * - 实测差异:快照 = `checked 36 / issues 3`(含 `workspaceRegistry not available` + 后果
7512
+ * "工作区白名单下拉为空"),而**同一时刻** `GET /serenity/cccs` 正常返回 **4 个工作区**
7513
+ * (含 3 个无 live 会话的冷 CCC)⇒ 快照渲染出的是**假阴性**。
7514
+ * ⇒ 「两次探针」**不是重复计算,问的是两个时刻**;缓存答案 = 把装载瞬间的观测冒充运行态事实。
7975
7515
  *
7976
- * 收敛策略与时序(**这是本模块唯一的可变状态,语义见下**):
7977
- * 1. `apply()` 调 `hostContractReport(ctx, readDshVersion())` —— **唯一计算点**,探针在此跑一次;
7978
- * 启动告警仍由返回值渲染(`summarizeHostContract`),语义逐字保留。
7979
- * 2. 其后任何消费者(`dashboard health` / `container-status`)调同一函数 → 命中快照,
7980
- * **不再**触发探针。
7516
+ * 保留统一入口的收益(这才是 C5 的正确部分):取数口唯一——`container-status` / `kit-ops`
7517
+ * 只问这一个函数;将来若要"筛掉装载期噪声",只需改这一处。
7981
7518
  *
7982
- * 为什么这不是"会导致状态陈旧的缓存":报告描述的是**这次装载的宿主面**(HOST_SERVICES 列出的
7983
- * 都是宿主核心服务/成员,由宿主在插件装载前注册)+装载时读到的宿主版本——它是**一次性事实**,
7984
- * 不是随运行变化的量。反之,时钟/注册表这类**会变**的量一律不在此缓存(各自由其唯一来源现读)。
7985
- * 代价(诚实记录):若宿主在 apply 之后才补挂某个 `HOST_SERVICES` 里的服务,`health` 不会察觉。
7986
- * 需要"当场真探"的场合仍可直接调纯函数 {@link probeHostContract}(本函数不删它)。
7987
- */
7988
- let snapshot = null;
7989
- /**
7990
- * 取宿主契约报告——**进程内唯一来源**。
7991
- *
7992
- * @param ctx 宿主上下文;**仅当快照尚未捕获时**用于跑探针(首见即捕获一次)
7519
+ * @param ctx 宿主上下文;**未给** ⇒ 返回 `null`(不猜、不暗跑探针)
7993
7520
  * @param hostVersion 运行中宿主版本(`readDshVersion()`;null = 未知)
7994
- * @returns 契约报告;快照未捕获且未给 `ctx` → `null`(不猜、不重算)
7995
7521
  */
7996
7522
  function hostContractReport(ctx, hostVersion = null) {
7997
- if (snapshot !== null) return snapshot;
7998
7523
  if (ctx === void 0) return null;
7999
- snapshot = probeHostContract(ctx, hostVersion);
8000
- return snapshot;
7524
+ return probeHostContract(ctx, hostVersion);
8001
7525
  }
8002
7526
  /** 单行摘要(启动告警用) */
8003
7527
  function summarizeHostContract(report) {
@@ -12512,6 +12036,244 @@ qInput.focus()
12512
12036
  </html>`;
12513
12037
  }
12514
12038
  //#endregion
12039
+ //#region src/host/effect.ts
12040
+ /** 已告警的标签(进程级去重:重复装配/多测试替身不刷屏,但绝不静默) */
12041
+ const warnedLabels = /* @__PURE__ */ new Set();
12042
+ /**
12043
+ * 登记一个卸载时执行的清理。
12044
+ * @param ctx 宿主插件上下文
12045
+ * @param label 日志标签(定位是哪个资源未装配)
12046
+ * @param cleanup 卸载时执行;抛错被吞掉(不阻断其它清理)
12047
+ * @returns 是否成功装配(false = 宿主无 `ctx.effect`,已告警)
12048
+ */
12049
+ function registerDisposer(ctx, label, cleanup) {
12050
+ const effect = ctx?.effect;
12051
+ if (typeof effect !== "function") {
12052
+ if (!warnedLabels.has(label)) {
12053
+ warnedLabels.add(label);
12054
+ console.warn(`[serenity-hooks] ✗ 资源拆卸未装配(${label}):ctx.effect 不可用(宿主契约缺失)`);
12055
+ }
12056
+ return false;
12057
+ }
12058
+ try {
12059
+ effect.call(ctx, () => () => {
12060
+ try {
12061
+ cleanup();
12062
+ } catch (err) {
12063
+ console.warn(`[serenity-hooks] ✗ 资源清理失败(${label}): ${String(err?.message ?? err)}`);
12064
+ }
12065
+ }, `dsh-serenity-hooks: ${label}`);
12066
+ return true;
12067
+ } catch (err) {
12068
+ console.warn(`[serenity-hooks] ✗ 资源拆卸注册失败(${label}): ${String(err?.message ?? err)}`);
12069
+ return false;
12070
+ }
12071
+ }
12072
+ //#endregion
12073
+ //#region src/clock-runtime.ts
12074
+ /**
12075
+ * clock-runtime.ts — **时钟运行时工厂**(S142 §38 复审候选 C6b,路径 **P2**)
12076
+ *
12077
+ * 本仓有**两条各自独立运行**的 5min 时钟:
12078
+ * · 唤醒调度器(`wake-scheduler.ts`,一次性唤醒,D58)
12079
+ * · autopilot 时钟(`autopilot-trajectory.ts`,周期自唤醒,D59)
12080
+ * 二者的**业务判据完全不同**(谁来扫 / 扫什么 / 投递给谁 / 按什么周期),但**引擎骨架逐字同形**
12081
+ * ——进程态 + 快照 + 复位 + `startTimer` + tick 外壳 + 事件接线 + disposer + `timer.unref()`。
12082
+ * 本条把**引擎**抽到本工厂,**各钟仍各持自己的定时器与自己的串行链**。
12083
+ *
12084
+ * ───────────────────────────────────────────────────────────────────────────
12085
+ * 🔴 硬约束一:**两条时钟绝不可共用"排队执行"的链**(这是 P2 与 P1 的分界判据)
12086
+ *
12087
+ * autopilot 每轮要跑该 CCC 的**偏见脚本**(`fetchBiasContent`,超时 60s),可能阻塞数十秒;
12088
+ * 若与唤醒调度器共用一条串行链,会把另一条一起**卡住**(一次性唤醒被无关的偏见脚本拖住)。
12089
+ * ⇒ 本工厂把 `chain` 作为**每实例私有状态**:`createClock()` 调用一次 = 一条独立的串行域。
12090
+ * 实例之间**零共享**(无模块级单例、无共享 Promise、无共享 map)。
12091
+ * ⚠️ 若日后有人把 `chain` 提到模块级"省一次分配",**两条时钟的隔离即刻失效**——不许。
12092
+ *
12093
+ * 🔴 硬约束二:**武装门只判全局闸**(`gate()`),**不判"本次有无目标"**
12094
+ *
12095
+ * 2026-09-14 F 段实测缺陷:旧实现把"有 live CCC"当**武装前置条件**,而宿主刚重启时 live 会话
12096
+ * 必为空,且**"恢复旧会话"不触发 `session/created`**(只有新建会)⇒ 启动瞬间判定落空 =
12097
+ * **时钟永久不武装**(实测 6.6h 零 tick)。⇒ 现在「**全局闸开即武装**」,"有无目标"降级为
12098
+ * tick 内的廉价判定。**武装与"本次有无目标"是两件事,本工厂不得把二者耦合。**
12099
+ *
12100
+ * 🔴 硬约束三:**两条闸必须继续分开传**(v1.34 S-1 解耦)
12101
+ *
12102
+ * 唤醒调度器 `wakeSchedulerEnabled`(**缺省开**)|autopilot `autopilotWakeEnabled`
12103
+ * (**缺省关**)——取值方向相反。工厂取 `gate` **回调**(每次现读),**不做**任何合并或缓存。
12104
+ *
12105
+ * ───────────────────────────────────────────────────────────────────────────
12106
+ * 两条时钟**逐项语义差**(`ClockOptions` 的每个可配项都对应下表一行,不是随意的灵活性):
12107
+ *
12108
+ * | 项 | 唤醒调度器 | autopilot | 工厂表达 |
12109
+ * |----|-----------|-----------|---------|
12110
+ * | `chain` | 有(全局串行) | 有(全局串行) | **总是私有**(硬约束一) |
12111
+ * | `lastTickLog` | 有(逐行摘要) | **无** | `logFrom` 不给 = 不记 |
12112
+ * | `ticks` 记账 | 工厂在 body **完成后**记 | **本钟**在同步段记 | `bodyCountsTick` + `begin` |
12113
+ * | body 返回类型 | `string[]`(人读摘要) | `void` | 泛型 `T` |
12114
+ * | 启动日志 | 固定串 | 含启用 CCC 计数 | `startLog: () => string` |
12115
+ * | body 异常兜底 | 留痕 + `console.warn` | 同 | 工厂统一(**吞异常不改语义**) |
12116
+ *
12117
+ * ───────────────────────────────────────────────────────────────────────────
12118
+ * **本工厂是"加法式"重构**(P2 的性质):不新增任何判定逻辑,只把同形骨架收成一份代码;
12119
+ * 两个模块仍各自导出 `wakeSchedulerState()` / `autopilotClockState()` 与各自的
12120
+ * `__reset…ForTest()`,**对外可观测面(字段名与语义)零变化**——`acc-diag` ①b 段、
12121
+ * `container-status.ts:containerClocks()` 与面板都在读它们(见该文件 :225–250 的设计红线)。
12122
+ *
12123
+ * 为什么**不**取 P1(真合并成一个 `setInterval` + 两条策略)——2026-09-15 所有者裁决:
12124
+ * P1 只多省 ~20 行,却要把 `armed` 拆成"宿主 + 每策略"两层、并强行分开串行域,
12125
+ * 即**动两条正在跑的时钟的骨架**;**不值当**(裁决原文见 SESSION §12.8②)。
12126
+ */
12127
+ /**
12128
+ * tick 周期(**5 分钟**)。
12129
+ *
12130
+ * 2026-09-15 S142「ACC 侧 autopilot 退场」:本值原从 `autopilot-core.ts` 导入(`TICK_MS`),
12131
+ * 但该模块随 autopilot 退场,而**本模块的唯一消费者是唤醒调度器**(`wake-scheduler`)——
12132
+ * 值本身与 autopilot 无关 ⇒ 随消费者归位到本模块(不再依赖 autopilot 判据层)。
12133
+ *
12134
+ * ⚠️ 语义不得变:该值同时是**报告文案的来源**("等下个 Nmin tick"),历史上曾因文案写死
12135
+ * 10min 与实现 5min 不符而误导排查(§12.7 文案纠错)。
12136
+ */
12137
+ const TICK_MS = 3e5;
12138
+ /**
12139
+ * 挂事件 → 触发 `start()`(**每个事件一次 try/catch 吞异常**:事件通道缺失不阻断时钟)。
12140
+ *
12141
+ * 为什么吞异常:宿主(headless profile 等)可能没有事件通道;**启动时已 `start()` 过一次**,
12142
+ * 事件只是"跟上面板开关 / 新会话"的热启动补丁,缺它不该让整个装配抛错。
12143
+ * @param on 宿主的 `on`(可能不存在)
12144
+ * @param ctx `on` 的 `this`(宿主 API 常要求绑定)
12145
+ * @param clock 目标时钟
12146
+ * @param events 事件名列表(回调恒为 `() => clock.start()`)
12147
+ */
12148
+ function attachClockEvents(ctx, clock, events) {
12149
+ const on = ctx?.on;
12150
+ if (typeof on !== "function") return;
12151
+ for (const name of events) try {
12152
+ on.call(ctx, name, () => clock.start());
12153
+ } catch {}
12154
+ }
12155
+ /**
12156
+ * 建一条时钟(**每钟各调一次**——本工厂**不是**单例,调用次数 = 时钟条数)。
12157
+ *
12158
+ * 顺序忠实于既有实现:`start()` 先武装并立即跑一次 tick → 再挂事件。
12159
+ * 拆卸由调用方经 `registerDisposer(ctx, label, clock.dispose)` 落到宿主的 `ctx.effect`。
12160
+ * @param opts 见 {@link ClockOptions}(每项对应一条既有的语义差)
12161
+ * @returns 该钟的 `{start, dispose, snapshot, reset}`
12162
+ */
12163
+ function createClock(opts) {
12164
+ const runtime = {
12165
+ armed: false,
12166
+ armedAt: null,
12167
+ lastTickAt: null,
12168
+ ticks: 0,
12169
+ lastSkipReason: null
12170
+ };
12171
+ if (opts.logFrom) runtime.lastTickLog = [];
12172
+ const st = {
12173
+ timer: null,
12174
+ chain: Promise.resolve(),
12175
+ runtime,
12176
+ wired: false,
12177
+ opts
12178
+ };
12179
+ /** 记一次 tick(工厂内部与 body 共用同一实现——只有一处会计逻辑) */
12180
+ const countTick = () => {
12181
+ runtime.lastTickAt = Date.now();
12182
+ runtime.ticks += 1;
12183
+ };
12184
+ /** 逐行打印(`logFrom` 缺省 ⇒ 本钟无 tick 日志,整段为死代码路径) */
12185
+ const emit = (lines) => {
12186
+ if (!opts.logFrom) return;
12187
+ for (const line of lines) console.log(`${opts.logFrom.prefix}${line}`);
12188
+ };
12189
+ const tick = () => {
12190
+ if (!opts.gate()) {
12191
+ runtime.lastSkipReason = opts.gateOffReason;
12192
+ return;
12193
+ }
12194
+ if (opts.begin) try {
12195
+ opts.begin();
12196
+ } catch (err) {
12197
+ console.warn(`[serenity-hooks] ✗ tick 前置异常: ${String(err?.message ?? err)}(${opts.label})`);
12198
+ return;
12199
+ }
12200
+ st.chain = st.chain.then(async () => {
12201
+ try {
12202
+ const value = await opts.body();
12203
+ if (!opts.bodyCountsTick) countTick();
12204
+ if (opts.logFrom) {
12205
+ runtime.lastTickLog = opts.logFrom.lines(value);
12206
+ emit(runtime.lastTickLog);
12207
+ }
12208
+ } catch (err) {
12209
+ const msg = `✗ tick 异常: ${String(err?.message ?? err)}`;
12210
+ if (opts.logFrom) runtime.lastTickLog = [msg];
12211
+ console.warn(`[serenity-hooks] ${msg}(${opts.label})`);
12212
+ }
12213
+ });
12214
+ };
12215
+ /**
12216
+ * 幂等启动 + 武装门:**已武装 → 原样返回**(防重复起定时器);
12217
+ * **闸关 → 不武装**(零资源占用);闸开则**无条件**武装(硬约束二:不判"有无目标")。
12218
+ */
12219
+ const start = () => {
12220
+ if (st.timer) return;
12221
+ if (opts.events && !st.wired) {
12222
+ st.wired = true;
12223
+ attachClockEvents(opts.ctx, api, opts.events);
12224
+ }
12225
+ if (!opts.gate()) return;
12226
+ st.timer = setInterval(tick, TICK_MS);
12227
+ st.timer.unref();
12228
+ runtime.armed = true;
12229
+ runtime.armedAt = Date.now();
12230
+ console.log(opts.startLog());
12231
+ if (opts.immediate !== false) tick();
12232
+ };
12233
+ const dispose = () => {
12234
+ if (st.timer) {
12235
+ clearInterval(st.timer);
12236
+ st.timer = null;
12237
+ }
12238
+ runtime.armed = false;
12239
+ runtime.armedAt = null;
12240
+ opts.onDispose?.();
12241
+ };
12242
+ const api = {
12243
+ start,
12244
+ dispose,
12245
+ snapshot: () => ({
12246
+ enabled: opts.gate(),
12247
+ ...runtime,
12248
+ ...runtime.lastTickLog ? { lastTickLog: [...runtime.lastTickLog] } : {}
12249
+ }),
12250
+ noteSkipReason: (reason) => {
12251
+ runtime.lastSkipReason = reason;
12252
+ },
12253
+ countTick,
12254
+ enqueue: (work) => {
12255
+ st.chain = st.chain.then(work, () => void 0).then(() => void 0, (err) => {
12256
+ console.warn(`[serenity-hooks] ✗ 串行任务异常(${opts.label}): ${String(err?.message ?? err)}`);
12257
+ });
12258
+ },
12259
+ reset: () => {
12260
+ if (st.timer) {
12261
+ clearInterval(st.timer);
12262
+ st.timer = null;
12263
+ }
12264
+ runtime.armed = false;
12265
+ runtime.armedAt = null;
12266
+ runtime.lastTickAt = null;
12267
+ runtime.ticks = 0;
12268
+ runtime.lastSkipReason = null;
12269
+ if (opts.logFrom) runtime.lastTickLog = [];
12270
+ st.wired = false;
12271
+ opts.onReset?.();
12272
+ }
12273
+ };
12274
+ return api;
12275
+ }
12276
+ //#endregion
12515
12277
  //#region src/wake-scheduler.ts
12516
12278
  const PLUGIN_SOURCE$1 = {
12517
12279
  kind: "plugin",
@@ -12674,8 +12436,8 @@ async function deliverWake(ctx, root, entry) {
12674
12436
  * (工作区注册表 ∪ 持久化会话 ∪ live 会话):**没有 live 会话的 CCC 也会被扫到**。
12675
12437
  *
12676
12438
  * ⚠️ 边界(本步**只**改"枚举哪些 CCC",**不改**"怎么找到要交付的 agent"):
12677
- * 把 message 交给某条 trajectory 仍必须靠 **live 会话**解析(见
12678
- * `autopilot-trajectory.resolveTargetAgent`);无 live 会话时该 CCC 只是**被扫到、
12439
+ * 把 message 交给某条 trajectory 仍必须靠 **live 会话**解析(见本文件
12440
+ * `acquireWakeAgent`);无 live 会话时该 CCC 只是**被扫到、
12679
12441
  * 无交付对象** ⇒ `deliverWake` 返回 `无绑定会话记录…`,条目回 `pending` 并留痕。
12680
12442
  * 因此本函数**不得**让 tick 抛错(异常一律由调用方 tick 兜住,见 `runWakeTick` 的 try)。
12681
12443
  * @param ctx 插件上下文
@@ -14575,6 +14337,56 @@ function createImBridgeTool() {
14575
14337
  });
14576
14338
  }
14577
14339
  //#endregion
14340
+ //#region src/live-sessions.ts
14341
+ /**
14342
+ * 从 dsh 会话 log 读取标题(latest-wins `session/title` 事件)——
14343
+ * **标题不在 `sessions.list()` 条目上**(wire/对象均无 title 字段;F3 命名经
14344
+ * `sessionTitle.rename` 写进 session log),必须从 events 提取(rebuild 同款读取模式)。
14345
+ */
14346
+ function readSessionTitle(session) {
14347
+ try {
14348
+ const events = sessionEvents$1(session);
14349
+ for (let i = events.length - 1; i >= 0; i--) {
14350
+ const e = events[i];
14351
+ if (e?.type === "session/title" && typeof e.data?.title === "string" && e.data.title.trim() !== "") return e.data.title.trim();
14352
+ }
14353
+ } catch {}
14354
+ return null;
14355
+ }
14356
+ /**
14357
+ * 逐条 live 会话(`cccRoot` = 该会话 cwd 的 CCC 归属,无 → null)。
14358
+ *
14359
+ * CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
14360
+ * 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
14361
+ *
14362
+ * @param ctx 插件上下文
14363
+ * @returns 逐条 live 会话
14364
+ */
14365
+ function listLiveSessions(ctx) {
14366
+ const out = [];
14367
+ try {
14368
+ const sessions = hostSessions(ctx);
14369
+ for (const s of sessions?.list?.() ?? []) {
14370
+ const cwd = s?.header?.cwd ?? null;
14371
+ out.push({
14372
+ id: s?.id ?? "",
14373
+ cwd,
14374
+ cccRoot: cwd ? cccRootForCwd(cwd) : null,
14375
+ title: readSessionTitle(s)
14376
+ });
14377
+ }
14378
+ } catch {}
14379
+ return out;
14380
+ }
14381
+ function diagLive(ctx) {
14382
+ const processCwd = process.cwd();
14383
+ return {
14384
+ processCwd,
14385
+ processCcc: cccRootForCwd(processCwd),
14386
+ liveSessions: listLiveSessions(ctx)
14387
+ };
14388
+ }
14389
+ //#endregion
14578
14390
  //#region src/container-status.ts
14579
14391
  /**
14580
14392
  * container-status.ts — 容器状态模型的**取数层唯一出口**(C5「观察面归一」,S142 2026-09-15)
@@ -14593,20 +14405,19 @@ function createImBridgeTool() {
14593
14405
  * 其模块级运行时对象里,任何"重新推导"得到的都是与真实调度器不一致的假值。
14594
14406
  *
14595
14407
  * 依赖分层(为什么本模块"重"而 `kit-ops` / `status` 保持"轻"):
14596
- * {@link containerClocks} 必须读 `wake-scheduler` / `autopilot-trajectory` 的进程内快照,而这两个
14597
- * 模块带 **@deepseek-ai 值依赖**(`dsh-llm` 的 `createUserMessage`)。故本模块**只能**被本就重型或
14598
- * 异步的消费方使用:`diag-ops`(静态 import)、`api.ts` / `kit-ops`(`await import(...)`,与
14599
- * `api.ts` 既有的"保持静态链纯净"约定一致)。**轻链模块不得静态 import 本模块**。
14408
+ * {@link containerClocks} 必须读 `wake-scheduler` 的进程内快照,而该模块带 **@deepseek-ai 值依赖**
14409
+ * (`dsh-llm` 的 `createUserMessage`)。故本模块**只能**被本就重型或异步的消费方使用:
14410
+ * `diag-ops`(静态 import)、`api.ts` / `kit-ops`(`await import(...)`,与 `api.ts` 既有的
14411
+ * "保持静态链纯净"约定一致)。**轻链模块不得静态 import 本模块**。
14600
14412
  *
14601
14413
  * 已有的单一真相源继续指向原处(**不复制**):
14602
14414
  * L0 根解析 `ccc.ts findSerenityRoot`|L1/L2 根与枚举 `ccc-roots.ts`|宿主契约 `host/contract.ts`
14603
- * |CCC 配置 `ccc.ts loadSerenityConfig`|注册表 `msm-ops.ts`|时钟 `wake-scheduler.ts` /
14604
- * `autopilot-trajectory.ts`|唤醒注册表 `wake-registry.ts`。
14415
+ * |CCC 配置 `ccc.ts loadSerenityConfig`|注册表 `msm-ops.ts`|时钟 `wake-scheduler.ts`
14416
+ * |唤醒注册表 `wake-registry.ts`。
14605
14417
  */
14606
14418
  var container_status_exports = /* @__PURE__ */ __exportAll({
14607
14419
  checkRegistryHealth: () => checkRegistryHealth,
14608
14420
  checkRegistryQuality: () => checkRegistryQuality,
14609
- containerAutopilot: () => containerAutopilot,
14610
14421
  containerClocks: () => containerClocks,
14611
14422
  containerIdentity: () => containerIdentity,
14612
14423
  containerPrinciples: () => containerPrinciples,
@@ -14741,18 +14552,14 @@ function checkRegistryHealth(root) {
14741
14552
  };
14742
14553
  }
14743
14554
  /**
14744
- * 两条时钟的进程内状态("为什么这轮没被唤起"的第一手判据)。
14555
+ * 时钟的进程内状态("为什么这轮没被唤起"的第一手判据)。
14745
14556
  *
14746
14557
  * 🔴 **只读快照,不重算**(设计红线):`armed`/`ticks`/`lastTickAt`/`lastSkipReason` 只存在于
14747
- * 调度器自己的模块级运行时对象里(`wake-scheduler.ts schedulerRuntime` /
14748
- * `autopilot-trajectory.ts` 同规格对象)。任何"从配置或文件重新推导"得到的值都会与真实
14749
- * 调度器的状态不一致——那比没有可观测量更糟(会给出假的"一切正常")。
14558
+ * 调度器自己的模块级运行时对象里(`wake-scheduler.ts schedulerRuntime`)。任何"从配置或文件
14559
+ * 重新推导"得到的值都会与真实调度器的状态不一致——那比没有可观测量更糟(会给出假的"一切正常")。
14750
14560
  */
14751
14561
  function containerClocks() {
14752
- return {
14753
- wake: wakeSchedulerState(),
14754
- autopilot: autopilotClockState()
14755
- };
14562
+ return { wake: wakeSchedulerState() };
14756
14563
  }
14757
14564
  /** 唤醒注册表取数(文件 IO:每个渲染点自己决定是否要看它) */
14758
14565
  function containerWakes(root) {
@@ -14763,16 +14570,13 @@ function containerWakes(root) {
14763
14570
  pending: entries.filter((e) => e.state === "pending").length
14764
14571
  };
14765
14572
  }
14766
- /** @returns CCC 根缺失(null)→ null */
14767
- function containerAutopilot(root) {
14768
- return root ? getAutopilotStatus(root) : null;
14769
- }
14770
14573
  /**
14771
14574
  * 核心段组装(`dashboard health` 的取数:身份 + 三原则 + 注册表两判据 + 配置 + 宿主契约)。
14772
14575
  *
14773
- * 时间轴三段(时钟/唤醒注册表/autopilot 目标)**不在本组装内**:它们是各自独立消费方的需要
14774
- * (`acc-diag` / `/serenity/trajectory`),硬塞进来会让只想看三原则的调用方付无谓的 IO。
14775
- * 需要时按名字调 {@link containerClocks} / {@link containerWakes} / {@link containerAutopilot}
14576
+ * 时间轴两段(时钟/唤醒注册表)**不在本组装内**:它们是各自独立消费方的需要
14577
+ * (`acc-diag` / 开发面诊断),硬塞进来会让只想看三原则的调用方付无谓的 IO。
14578
+ * 需要时按名字调 {@link containerClocks} / {@link containerWakes}。
14579
+ * (原第三段「autopilot 目标」及其取数函数 `containerAutopilot` 已随 ACC 侧 autopilot 退场删除。)
14776
14580
  */
14777
14581
  function containerStatus(opts = {}) {
14778
14582
  const root = opts.root ?? null;
@@ -14797,7 +14601,7 @@ function containerStatus(opts = {}) {
14797
14601
  //#endregion
14798
14602
  //#region src/diag-ops.ts
14799
14603
  /**
14800
- * diag-ops.ts — `acc-diag` 报告装配(v1.33,S142 §32.11
14604
+ * diag-ops.ts — `acc-diag` 报告装配(v1.33,S142 §32.11;2026-09-15 段数收缩)
14801
14605
  *
14802
14606
  * 为什么单独成模块(R↓):工具面(`tools/acc-diag.ts`)只做"取 CCC 根 + 渲染",
14803
14607
  * 装配逻辑放这里 ⇒ 纯函数可单测、不依赖 DSH 工具注册。
@@ -14805,22 +14609,28 @@ function containerStatus(opts = {}) {
14805
14609
  * 为什么**无动作参数**:诊断是低频动作(§32.9 收敛取向),一次调用即出全报告,
14806
14610
  * 不需要子命令面;动作越多,模型选择成本越高。
14807
14611
  *
14808
- * 四段内容(数据来源各自单一真相源,本模块只聚合不复制语义):
14809
- * ① live 运行态 —— 插件进程内 `diagLive(ctx)`(live 会话清单 + autopilot CCC 的
14810
- * 目标命中 / agent 定位诊断)——**独立进程看不到运行时**,这正是本工具存在的理由
14811
- * 面板解析 —— `diagLive.panelResolved`(无参面板请求会落到哪个 CCC)
14812
- * ③ 唤醒注册表 —— `wake-registry.json` 全量条目(state / at / target / lastResult)+ 补跑窗口
14813
- * ④ 唤起条件链 —— **进程内** {@link buildWakeChain}(⑥ C6a):逐条件值 + 阻断点 + 修复建议
14814
- * + 只有进程内能看到的三项事实(全局闸 / live 会话 / agent 可解析性)
14815
- *
14816
- * C5「观察面归一」(2026-09-15):①/② 仍走 `diagLive`(那是**live 运行态**的独有取数,
14817
- * 没有第二个来源),但 ①b 时钟与 ③ 注册表改经 `container-status.ts` 取(唯一取数出口)——
14818
- * **投影仍在本文件**(诊断段不含 `message`,面板含;这是渲染差异,不是取数差异)。
14819
- *
14820
- * C6a(2026-09-15):④ 段由"spawn 包内脚本"改为**进程内计算**。旧实现是本报告里
14821
- * 唯一一条**跨进程**取数:脚本看不到全局闸/live/agent ⇒ 与本工具 ① 段自相矛盾
14822
- * (① 说"agent 未定位",④ 却印"条件全部满足")。现在 ④ 段与 ① 段读**同一份事实**
14823
- * (`autopilotRuntimeFacts`),且条件链**只有一份实现**(开发面 `dsh-develop diag` import 同一个)。
14612
+ * 三段内容(数据来源各自单一真相源,本模块只聚合不复制语义):
14613
+ * ① live 运行态 —— 插件进程内 `diagLive(ctx)`(进程 cwd/CCC + live 会话清单)
14614
+ * ——**独立进程看不到运行时**,这正是本工具存在的理由
14615
+ * ①b 唤醒时钟 —— `containerClocks().wake`(唤醒调度器进程内快照:武装态/计数/上次跳过原因)
14616
+ * ③ 唤醒注册表 —— `wake-registry.json` 全量条目(state / at / target / lastResult)+ 补跑窗口
14617
+ *
14618
+ * ── 2026-09-15:段数由四降为三(S142「ACC autopilot 退场」§1 二阶裁定)─────────────
14619
+ * ACC 侧 autopilot 整段退场后,报告里两段的**主语消失**,故删除(不是"暂时关掉"):
14620
+ * · 面板解析 —— `diagLive.panelResolved` 的来源是"哪个 CCC 配了 autopilot",
14621
+ * 面板区块同批删除 主语不存在。
14622
+ * · ④ 唤起条件链 —— 整段都是 ACC autopilot 的唤起条件(`buildWakeChain` +
14623
+ * `autopilotRuntimeFacts`),实现模块已删 ⇒ 不留第二份条件链、也不留半死的手写版。
14624
+ * · 段的 autopilot CCC 渲染(逐 CCC 目标命中 / agent 定位)同批删除。
14625
+ *
14626
+ * 🔴 **已知缺口(登记,不静默丢弃)**:④「为什么这轮没唤起」的诊断在 **CCC 自管理链**上归零。
14627
+ * 新链的失败形态是"S151 漏了自排下一轮"或"调度器闸关/未 tick",而删掉 后这两种病因都不可见。
14628
+ * 按所有者裁决本次**不补**(一句话可补,代价 = 一条新条件链:调度器 armed / 闸 / tick 在跑 /
14629
+ * 条目到期 / 补跑窗口 / 目标可解析 / CCC 闸)。**不得**以"顺手保留 ④"的方式把它偷偷带回。
14630
+ *
14631
+ * C5「观察面归一」(2026-09-15):① 走 `diagLive`(那是**live 运行态**的独有取数,没有第二个
14632
+ * 来源);①b 时钟与 ③ 注册表经 `container-status.ts` 取(唯一取数出口)——**投影仍在本文件**
14633
+ * (诊断段不含 `message`,面板含;这是渲染差异,不是取数差异)。
14824
14634
  */
14825
14635
  /** 人读渲染一行时钟状态("为何没有 tick" 的第一手判据) */
14826
14636
  function renderClock(label, s) {
@@ -14838,21 +14648,17 @@ function renderClock(label, s) {
14838
14648
  /**
14839
14649
  * 装配报告(一次调用即全报告)。
14840
14650
  * @param ctx 插件上下文(进程内读 sessions/agents)
14841
- * @param root 调用方 CCC 根(条件链诊断目标)
14651
+ * @param root 调用方 CCC 根(唤醒注册表取数目标)
14842
14652
  * @returns 结构化报告(渲染由 {@link renderAccDiag} 负责)
14843
14653
  */
14844
14654
  async function runAccDiag(ctx, root) {
14845
14655
  const live = diagLive(ctx);
14846
14656
  const clocks = containerClocks();
14847
14657
  const wakes = containerWakes(root);
14848
- const chain = await buildWakeChain(root, autopilotRuntimeFacts(ctx, root));
14849
14658
  return {
14850
14659
  ccc: root,
14851
14660
  live,
14852
- clocks: {
14853
- wake: clocks.wake,
14854
- autopilot: clocks.autopilot
14855
- },
14661
+ clocks: { wake: clocks.wake },
14856
14662
  wakes: {
14857
14663
  error: wakes.error,
14858
14664
  entries: wakes.entries.map((e) => ({
@@ -14865,12 +14671,10 @@ async function runAccDiag(ctx, root) {
14865
14671
  lastResult: e.lastResult ?? null
14866
14672
  })),
14867
14673
  pending: wakes.pending
14868
- },
14869
- autopilotChain: renderWakeChain(chain),
14870
- autopilotVerdict: chain.verdict
14674
+ }
14871
14675
  };
14872
14676
  }
14873
- /** 人读渲染(四段;空段也要显式说明"无",避免"沉默 = 一切正常"的误读) */
14677
+ /** 人读渲染(三段;空段也要显式说明"无",避免"沉默 = 一切正常"的误读) */
14874
14678
  function renderAccDiag(r) {
14875
14679
  const lines = [];
14876
14680
  lines.push(`═══ ACC 运行态诊断(acc-diag)═══`);
@@ -14881,20 +14685,8 @@ function renderAccDiag(r) {
14881
14685
  lines.push(`进程 CCC: ${r.live.processCcc ?? "(无——进程 cwd 不在任何 CCC 内)"}`);
14882
14686
  lines.push(`live 会话: ${r.live.liveSessions.length} 个`);
14883
14687
  for (const s of r.live.liveSessions) lines.push(` · ${s.id}${s.title ? `「${s.title}」` : ""} — ccc=${s.cccRoot ?? "(非 CCC)"} cwd=${s.cwd ?? "(无)"}`);
14884
- if (r.live.autopilotCccs.length === 0) lines.push("autopilot CCC: 无(没有 live 会话配置了 trajectory.autopilot)");
14885
- else {
14886
- lines.push(`autopilot CCC: ${r.live.autopilotCccs.length} 个`);
14887
- for (const c of r.live.autopilotCccs) {
14888
- lines.push(` · ${c.root} enabled=${c.enabled} session=${c.session ?? "(未配置)"} 目标=${c.target?.dirName ?? "(未命中)"} wakeable=${c.target?.wakeable ?? false} agent=${c.agentResolved ? "已定位" : "未定位"}`);
14889
- if (c.agentDiagnosis) lines.push(` 诊断: ${c.agentDiagnosis}`);
14890
- }
14891
- }
14892
14688
  lines.push("");
14893
14689
  lines.push(renderClock("唤醒调度器(wake-registry tick)", r.clocks.wake));
14894
- lines.push(renderClock("autopilot 时钟(周期自唤醒 tick)", r.clocks.autopilot));
14895
- lines.push("");
14896
- lines.push(`── ② 面板解析(无参 /serenity/trajectory 落到哪个 CCC)──`);
14897
- lines.push(r.live.panelResolved ?? "(解析不到——本进程无 live+enabled 的 autopilot CCC)");
14898
14690
  lines.push("");
14899
14691
  lines.push(`── ③ 唤醒注册表(${r.ccc}/AGENT_SESSIONS/wake-registry.json)──`);
14900
14692
  if (r.wakes.error) lines.push(`⚠ 注册表读取问题: ${r.wakes.error}`);
@@ -14904,9 +14696,6 @@ function renderAccDiag(r) {
14904
14696
  lines.push(` · ${w.id} [${w.state}] at=${w.at} → ${w.target} by=${w.createdBy} attempts=${w.attempts}`);
14905
14697
  if (w.lastResult) lines.push(` lastResult: ${w.lastResult}`);
14906
14698
  }
14907
- lines.push("");
14908
- lines.push(`── ④ 唤起条件链("为什么这轮没被唤起")──`);
14909
- lines.push(r.autopilotChain);
14910
14699
  return lines.join("\n");
14911
14700
  }
14912
14701
  //#endregion
@@ -14929,12 +14718,15 @@ function renderAccDiag(r) {
14929
14718
  */
14930
14719
  /**
14931
14720
  * 运行态诊断工具(`acc-diag`)。**无动作参数**:一次调用即全报告
14932
- * live 运行态 / 面板解析 / 唤醒注册表 / 唤起条件链)。
14721
+ * (① live 运行态 / ①b 唤醒时钟 / 唤醒注册表)。
14722
+ *
14723
+ * ⚠️ 2026-09-15(ACC 侧 autopilot 退场,S142):原四段降为三段——原 ②「面板解析」与
14724
+ * ④「唤起条件链」的主语随机制消失,已删(见 `../diag-ops.ts` 文件头;④ 的缺口已登记)。
14933
14725
  */
14934
14726
  function createAccDiagTool(ctx) {
14935
14727
  return defineTool({
14936
14728
  name: "acc-diag",
14937
- description: "ACC runtime diagnosis (ACC maintainers only — hidden unless this container declares \"acc-diag\" in .opencode/serenity.json exclusiveTools). One call returns the full report: (1) live runtime — live sessions with cwd/CCC + each autopilot CCC target resolution and agent-location diagnosis; (2) panel resolution which CCC a parameterless panel request lands on; (3) wake registry — entries with state/at/target/lastResult plus the catch-up window; (4) wake condition chain — per-condition values, blocking points and fix suggestions (\"why did this round not wake\"). Read-only: it inspects process state and files, it changes nothing.",
14729
+ description: "ACC runtime diagnosis (ACC maintainers only — hidden unless this container declares \"acc-diag\" in .opencode/serenity.json exclusiveTools). One call returns the full report (3 sections): (1) live runtime — live sessions with cwd/CCC; (1b) wake clock the wake scheduler process snapshot (armed / gate / ticks / last tick / last skip reason); (3) wake registry — entries with state/at/target/lastResult plus the catch-up window. Read-only: it inspects process state and files, it changes nothing.",
14938
14730
  parameters: {},
14939
14731
  output: {
14940
14732
  schema: { type: "json" },
@@ -15051,7 +14843,6 @@ function apply(ctx, config) {
15051
14843
  registerBootstrap(ctx);
15052
14844
  registerSkiff(ctx);
15053
14845
  registerAcp(ctx);
15054
- registerAutopilot(ctx);
15055
14846
  registerWakeScheduler(ctx);
15056
14847
  registerWeixinBridge(ctx);
15057
14848
  registerWeixinSendApi(ctx);