@shgroup/dsh-serenity-hooks 1.34.2 → 1.36.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
  }
@@ -5032,8 +4626,8 @@ var KeeperTracker = class {
5032
4626
  return this.score;
5033
4627
  }
5034
4628
  };
5035
- function reminderText(code, score) {
5036
- return `${eventToken("checkpoint")} Score threshold reached (${score}). Please acknowledge with [${ACK_PREFIX}-${code}] once progress is synced to the working session (acc-session show). No need to interrupt your work — just acknowledge inline and keep going.`;
4629
+ function reminderText(code) {
4630
+ return `${eventToken("checkpoint")} Score threshold reached. Please acknowledge with [${ACK_PREFIX}-${code}] once progress is synced to the working session (acc-session show). No need to interrupt your work — just acknowledge inline and keep going.`;
5037
4631
  }
5038
4632
  /**
5039
4633
  * F2 rebuild 提示(v1.21;v1.22.1 对齐"轨迹跟踪器"概念;v1.23.0 英化 + 载体关系;
@@ -5048,18 +4642,24 @@ function reminderText(code, score) {
5048
4642
  * escalated=true(v1.23.3):连续多轮超阈值仍未 rebuild → 升级强制语气
5049
4643
  * (STOP and rebuild now,持续注入直到调用 container_trajectory rebuild)。
5050
4644
  *
5051
- * 需求①(S142 用户拍板):百分比比例 → K 数值——tokensK = 实际占用(千 token),
5052
- * thresholdK = 配置阈值(千 token);文案 `Context usage at NNNK (threshold NNNK)`。
4645
+ * 需求①(S142 用户拍板,v1.28.0):百分比比例 → K 数值——**判定**改为绝对
4646
+ * `projectedTokens thresholdK*1000`(触发语义,未变)。
4647
+ * 🔴 2026-09-16(S142 所有者指令,D64):**判定用 K,但注入文本里不再给任何数值**——
4648
+ * 原文案 `Context usage at NNNK (threshold NNNK)` 属于"向 LLM 透露上下文消耗与限制",
4649
+ * 是非必要信息(LLM 不据此决策,只占注意力)。⇒ 参数退化为仅 `escalated`。
4650
+ * ⚠️ 触发判定与提醒行为**一字未改**(仍读 projectedTokens / thresholdK 比对;仍升级催);
4651
+ * 变的只是**注入文本不再携带 tokensK / thresholdK**。
4652
+ * 使用者若要查看当前占用/阈值:看面板(`rebuildThresholdK` 滑块 + 状态显示)——那是给人看的。
5053
4653
  *
5054
4654
  * v1.31.1 交接协议(S142 用户需求"要求 LLM 将当前手头事项写在 SESSION.md 尾部,并要求
5055
4655
  * rebuild 后去读并处理"):**写侧** = 这里(两条文案都要求把 in-flight 事项写在
5056
4656
  * SESSION.md 末尾的 `IN_FLIGHT_HEADING` 之下);**读侧** = rebuild 锚点(rebuild.ts)。
5057
4657
  * 两侧共用 trajectory-assistant 的标题常量(单一真相源)。
5058
4658
  */
5059
- function rebuildReminderText(tokensK, thresholdK, escalated = false) {
4659
+ function rebuildReminderText(escalated = false) {
5060
4660
  const handover = ` Before rebuilding, write your current in-flight items (the exact step you are in the middle of, what is not finished yet, and what the next action is) at the very end of SESSION.md under the heading "${IN_FLIGHT_HEADING}" — the rebuilt conversation reads that section first and continues from it.`;
5061
- if (escalated) return `${eventToken("limitMandatory")} Context usage at ${Math.round(tokensK)}K (threshold ${Math.round(thresholdK)}K) — you have been reminded repeatedly and have NOT called the container_trajectory rebuild action. This is now mandatory: STOP at the current task step, preserve valuable cognition into the CCC skills (or write a new-skill proposal into SESSION.md), then call container_trajectory rebuild immediately, passing --summary "<content summary ≤20 chars>" (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild).${handover} The conversation will be cleared and rebuilt in place; SESSION.md is the persistent trajectory and stays in place — identity continues from it. Do not continue working without rebuilding; this reminder persists until you call container_trajectory rebuild.`;
5062
- return `${eventToken("limit")} Context usage at ${Math.round(tokensK)}K (threshold ${Math.round(thresholdK)}K). This session is the rebuildable carrier of the trajectory: SESSION.md is the persistent body, this conversation is only a temporary work copy. Before rebuilding: if this conversation produced valuable cognition, revise the relevant existing skill of this CCC (structure it with eap); if a new skill is warranted, write a short proposal into SESSION.md for the user to review — do not create it yourself.${handover} ACT NOW: at the next natural pause (end of the current task step), call the container_trajectory rebuild action — passing --summary "<content summary ≤20 chars>" describing the next work phase (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild) — to clear and rebuild this conversation: the current copy is discarded, identity continues from SESSION.md. If you are in the middle of an unbreakable step, continue it, then rebuild at its end. Do not ignore this; rebuild is the expected action, not an option.`;
4661
+ if (escalated) return `${eventToken("limitMandatory")} Context usage has reached the configured rebuild threshold — you have been reminded repeatedly and have NOT called the container_trajectory rebuild action. This is now mandatory: STOP at the current task step, preserve valuable cognition into the CCC skills (or write a new-skill proposal into SESSION.md), then call container_trajectory rebuild immediately, passing --summary "<content summary ≤20 chars>" (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild).${handover} The conversation will be cleared and rebuilt in place; SESSION.md is the persistent trajectory and stays in place — identity continues from it. Do not continue working without rebuilding; this reminder persists until you call container_trajectory rebuild.`;
4662
+ return `${eventToken("limit")} Context usage has reached the configured rebuild threshold. This session is the rebuildable carrier of the trajectory: SESSION.md is the persistent body, this conversation is only a temporary work copy. Before rebuilding: if this conversation produced valuable cognition, revise the relevant existing skill of this CCC (structure it with eap); if a new skill is warranted, write a short proposal into SESSION.md for the user to review — do not create it yourself.${handover} ACT NOW: at the next natural pause (end of the current task step), call the container_trajectory rebuild action — passing --summary "<content summary ≤20 chars>" describing the next work phase (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild) — to clear and rebuild this conversation: the current copy is discarded, identity continues from SESSION.md. If you are in the middle of an unbreakable step, continue it, then rebuild at its end. Do not ignore this; rebuild is the expected action, not an option.`;
5063
4663
  }
5064
4664
  /** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
5065
4665
  function readContextPressure(ctx, session) {
@@ -5204,7 +4804,7 @@ function registerKeeper(ctx, opts = {}) {
5204
4804
  const code = tracker.ack();
5205
4805
  blocks.push({
5206
4806
  type: "text",
5207
- text: reminderText(code, tracker.currentScore)
4807
+ text: reminderText(code)
5208
4808
  });
5209
4809
  }
5210
4810
  if (skiffRebuild && readSimpleSettings().rebuildEnabled) {
@@ -5212,7 +4812,6 @@ function registerKeeper(ctx, opts = {}) {
5212
4812
  if (session) {
5213
4813
  const pressure = readContextPressure(ctx, session);
5214
4814
  if (pressure && pressure.projectedTokens > 0) {
5215
- const tokensK = pressure.projectedTokens / 1e3;
5216
4815
  const thresholdK = readSimpleSettings().rebuildThresholdK;
5217
4816
  if (pressure.projectedTokens >= thresholdK * 1e3) {
5218
4817
  const key = exec.agent?.session?.id ?? "global";
@@ -5221,7 +4820,7 @@ function registerKeeper(ctx, opts = {}) {
5221
4820
  const escalated = st.consecutive >= REBUILD_ESCALATE_AFTER;
5222
4821
  blocks.push({
5223
4822
  type: "text",
5224
- text: rebuildReminderText(tokensK, thresholdK, escalated)
4823
+ text: rebuildReminderText(escalated)
5225
4824
  });
5226
4825
  rebuildReminderStates.set(key, st);
5227
4826
  }
@@ -5983,7 +5582,7 @@ function toolsBlock() {
5983
5582
  " praxis — actionable theory injection: praxis (index) / praxis eap / praxis neat / praxis cce",
5984
5583
  " 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
5584
  " 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)",
5585
+ " 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
5586
  "",
5988
5587
  "MSM call (registered CCC MSMs, deterministic Mech & Semi-Mech):",
5989
5588
  " Execute: msm(\"<name>\", [\"<arg1>\", \"<arg2>\"]) — run a registered MSM directly (partial name returns matching candidates)",
@@ -6781,7 +6380,6 @@ const FILE_UPLOAD_PATH = "/serenity/file-upload";
6781
6380
  const CONFIG_PATH = "/serenity/config";
6782
6381
  const CCCS_PATH = "/serenity/cccs";
6783
6382
  const PUBLIC_ASK_PATH = "/serenity/public-ask";
6784
- const TRAJECTORY_PATH = "/serenity/trajectory";
6785
6383
  const WEIXIN_PATH = "/serenity/weixin";
6786
6384
  /** 图片落盘目录(CCC 根相对;S142 图片自动识别基础设施——粘贴图片落盘供 agent 经 CCC vlm MSM 自主处理) */
6787
6385
  const IMAGE_UPLOAD_DIR = "_tmp/images_from_user";
@@ -6879,7 +6477,7 @@ function readBody$4(req, maxBytes) {
6879
6477
  req.on("error", reject);
6880
6478
  });
6881
6479
  }
6882
- function sendJson$3(res, code, body) {
6480
+ function sendJson$4(res, code, body) {
6883
6481
  const payload = JSON.stringify(body);
6884
6482
  res.writeHead(code, {
6885
6483
  "content-type": "application/json; charset=utf-8",
@@ -6904,7 +6502,7 @@ function senderIsWebUi(req) {
6904
6502
  */
6905
6503
  function requireWebUi(req, res, message) {
6906
6504
  if (senderIsWebUi(req)) return true;
6907
- sendJson$3(res, 403, { error: message });
6505
+ sendJson$4(res, 403, { error: message });
6908
6506
  return false;
6909
6507
  }
6910
6508
  /**
@@ -6937,7 +6535,7 @@ function registerStatusApi(ctx, opts = {}) {
6937
6535
  handler: async (req, res) => {
6938
6536
  try {
6939
6537
  if (req.method !== "GET") {
6940
- sendJson$3(res, 405, { error: "method not allowed" });
6538
+ sendJson$4(res, 405, { error: "method not allowed" });
6941
6539
  return;
6942
6540
  }
6943
6541
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -6947,12 +6545,12 @@ function registerStatusApi(ctx, opts = {}) {
6947
6545
  });
6948
6546
  const root = cccRootForCwd(workspace);
6949
6547
  if (!root) {
6950
- sendJson$3(res, 200, { handymen: [] });
6548
+ sendJson$4(res, 200, { handymen: [] });
6951
6549
  return;
6952
6550
  }
6953
- sendJson$3(res, 200, { handymen: listActiveHandymen(root) });
6551
+ sendJson$4(res, 200, { handymen: listActiveHandymen(root) });
6954
6552
  } catch (err) {
6955
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6553
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
6956
6554
  }
6957
6555
  }
6958
6556
  });
@@ -6962,7 +6560,7 @@ function registerStatusApi(ctx, opts = {}) {
6962
6560
  handler: async (req, res) => {
6963
6561
  try {
6964
6562
  if (req.method !== "POST") {
6965
- sendJson$3(res, 405, { error: "method not allowed" });
6563
+ sendJson$4(res, 405, { error: "method not allowed" });
6966
6564
  return;
6967
6565
  }
6968
6566
  if (!requireWebUi(req, res, "图片落盘仅限 WebUI(client 专用)")) return;
@@ -6974,12 +6572,12 @@ function registerStatusApi(ctx, opts = {}) {
6974
6572
  });
6975
6573
  const root = cccRootForCwd(workspace);
6976
6574
  if (!root) {
6977
- sendJson$3(res, 404, { error: `no CCC found from workspace: ${workspace}` });
6575
+ sendJson$4(res, 404, { error: `no CCC found from workspace: ${workspace}` });
6978
6576
  return;
6979
6577
  }
6980
- sendJson$3(res, 200, { path: saveImageToTmp(root, body.mediaType ?? "", body.data ?? "") });
6578
+ sendJson$4(res, 200, { path: saveImageToTmp(root, body.mediaType ?? "", body.data ?? "") });
6981
6579
  } catch (err) {
6982
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6580
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
6983
6581
  }
6984
6582
  }
6985
6583
  });
@@ -6989,7 +6587,7 @@ function registerStatusApi(ctx, opts = {}) {
6989
6587
  handler: async (req, res) => {
6990
6588
  try {
6991
6589
  if (req.method !== "POST") {
6992
- sendJson$3(res, 405, { error: "method not allowed" });
6590
+ sendJson$4(res, 405, { error: "method not allowed" });
6993
6591
  return;
6994
6592
  }
6995
6593
  if (!requireWebUi(req, res, "文件落盘仅限 WebUI(client 专用)")) return;
@@ -7001,12 +6599,12 @@ function registerStatusApi(ctx, opts = {}) {
7001
6599
  });
7002
6600
  const root = cccRootForCwd(workspace);
7003
6601
  if (!root) {
7004
- sendJson$3(res, 404, { error: `no CCC found from workspace: ${workspace}` });
6602
+ sendJson$4(res, 404, { error: `no CCC found from workspace: ${workspace}` });
7005
6603
  return;
7006
6604
  }
7007
- sendJson$3(res, 200, { path: saveFileToTmp(root, body.name ?? "", body.data ?? "") });
6605
+ sendJson$4(res, 200, { path: saveFileToTmp(root, body.name ?? "", body.data ?? "") });
7008
6606
  } catch (err) {
7009
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6607
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7010
6608
  }
7011
6609
  }
7012
6610
  });
@@ -7022,7 +6620,7 @@ function registerStatusApi(ctx, opts = {}) {
7022
6620
  workspace: url.searchParams.get("workspace") ?? void 0
7023
6621
  }), configPaths);
7024
6622
  const runtime = ctx.get("codeRuntime");
7025
- sendJson$3(res, 200, {
6623
+ sendJson$4(res, 200, {
7026
6624
  ...status,
7027
6625
  ...runtime === void 0 ? { codeRuntime: null } : { codeRuntime: {
7028
6626
  language: runtime.language,
@@ -7041,19 +6639,19 @@ function registerStatusApi(ctx, opts = {}) {
7041
6639
  });
7042
6640
  const root = cccRootForCwd(workspace);
7043
6641
  if (!root) {
7044
- sendJson$3(res, 404, { error: `no CCC found from workspace: ${workspace}` });
6642
+ sendJson$4(res, 404, { error: `no CCC found from workspace: ${workspace}` });
7045
6643
  return;
7046
6644
  }
7047
6645
  const result = setSafeMode(root, body.on === true);
7048
- sendJson$3(res, 200, {
6646
+ sendJson$4(res, 200, {
7049
6647
  ...getStatus(workspace, configPaths),
7050
6648
  ...result
7051
6649
  });
7052
6650
  return;
7053
6651
  }
7054
- sendJson$3(res, 405, { error: "method not allowed" });
6652
+ sendJson$4(res, 405, { error: "method not allowed" });
7055
6653
  } catch (err) {
7056
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6654
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7057
6655
  }
7058
6656
  }
7059
6657
  });
@@ -7069,7 +6667,7 @@ function registerStatusApi(ctx, opts = {}) {
7069
6667
  try {
7070
6668
  known = projectKnownWorkspaces(hostService(ctx, "workspaceRegistry")?.list?.() ?? [], settings.gateway.workspaces);
7071
6669
  } catch {}
7072
- sendJson$3(res, 200, {
6670
+ sendJson$4(res, 200, {
7073
6671
  config: toWire(settings),
7074
6672
  knownWorkspaces: known
7075
6673
  });
@@ -7081,12 +6679,12 @@ function registerStatusApi(ctx, opts = {}) {
7081
6679
  try {
7082
6680
  ctx.emit?.("serenity/config-updated");
7083
6681
  } catch {}
7084
- sendJson$3(res, 200, { config: toWire(saved) });
6682
+ sendJson$4(res, 200, { config: toWire(saved) });
7085
6683
  return;
7086
6684
  }
7087
- sendJson$3(res, 405, { error: "method not allowed" });
6685
+ sendJson$4(res, 405, { error: "method not allowed" });
7088
6686
  } catch (err) {
7089
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6687
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7090
6688
  }
7091
6689
  }
7092
6690
  });
@@ -7096,7 +6694,7 @@ function registerStatusApi(ctx, opts = {}) {
7096
6694
  handler: async (req, res) => {
7097
6695
  try {
7098
6696
  if (req.method !== "GET") {
7099
- sendJson$3(res, 405, { error: "method not allowed" });
6697
+ sendJson$4(res, 405, { error: "method not allowed" });
7100
6698
  return;
7101
6699
  }
7102
6700
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -7105,12 +6703,12 @@ function registerStatusApi(ctx, opts = {}) {
7105
6703
  workspace: url.searchParams.get("workspace") ?? void 0
7106
6704
  });
7107
6705
  const root = cccRootForCwd(workspace) ?? "";
7108
- sendJson$3(res, 200, { cccs: await listCccs(ctx, {
6706
+ sendJson$4(res, 200, { cccs: await listCccs(ctx, {
7109
6707
  defaultRoot: root,
7110
6708
  withRoles: true
7111
6709
  }) });
7112
6710
  } catch (err) {
7113
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6711
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7114
6712
  }
7115
6713
  }
7116
6714
  });
@@ -7123,18 +6721,18 @@ function registerStatusApi(ctx, opts = {}) {
7123
6721
  if (req.method === "PUT") {
7124
6722
  const raw = await readBody$4(req, 16384);
7125
6723
  if (JSON.parse(raw).action !== "rotate") {
7126
- sendJson$3(res, 400, { error: "unsupported action (expected \"rotate\")" });
6724
+ sendJson$4(res, 400, { error: "unsupported action (expected \"rotate\")" });
7127
6725
  return;
7128
6726
  }
7129
- sendJson$3(res, 200, { key: rotatePublicAskKey() });
6727
+ sendJson$4(res, 200, { key: rotatePublicAskKey() });
7130
6728
  return;
7131
6729
  }
7132
6730
  if (req.method !== "GET") {
7133
- sendJson$3(res, 405, { error: "method not allowed" });
6731
+ sendJson$4(res, 405, { error: "method not allowed" });
7134
6732
  return;
7135
6733
  }
7136
6734
  const settings = readAdvancedSettings();
7137
- const { readSimpleSettings } = await import("./settings-section-DTYZjwUs.js").then((n) => n.r);
6735
+ const { readSimpleSettings } = await import("./settings-section-DMDUoQum.js").then((n) => n.r);
7138
6736
  const simple = readSimpleSettings();
7139
6737
  const allowed = settings.publicAsk.allowed;
7140
6738
  const port = simple.acpHttpPort ?? ACP_HTTP_PORT;
@@ -7143,7 +6741,7 @@ function registerStatusApi(ctx, opts = {}) {
7143
6741
  name,
7144
6742
  url: `${base}/c/${encodeURIComponent(name)}`
7145
6743
  }));
7146
- sendJson$3(res, 200, {
6744
+ sendJson$4(res, 200, {
7147
6745
  enabled: simple.publicAskEnabled ?? false,
7148
6746
  port,
7149
6747
  key: ensurePublicAskKey(),
@@ -7152,67 +6750,7 @@ function registerStatusApi(ctx, opts = {}) {
7152
6750
  listUrl: `${base}/`
7153
6751
  });
7154
6752
  } catch (err) {
7155
- sendJson$3(res, 400, { error: err.message ?? String(err) });
7156
- }
7157
- }
7158
- });
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) });
6753
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7216
6754
  }
7217
6755
  }
7218
6756
  });
@@ -7226,7 +6764,7 @@ function registerStatusApi(ctx, opts = {}) {
7226
6764
  if (req.method === "GET") {
7227
6765
  const chk = requireCcc(url.searchParams.get("ccc") ?? void 0);
7228
6766
  if (!chk.ok) {
7229
- sendJson$3(res, 400, { error: chk.error });
6767
+ sendJson$4(res, 400, { error: chk.error });
7230
6768
  return;
7231
6769
  }
7232
6770
  const { readWeixinSettings } = await import("./weixin-route-B2ajFypI.js").then((n) => n.u);
@@ -7240,7 +6778,7 @@ function registerStatusApi(ctx, opts = {}) {
7240
6778
  enabled: a.enabled !== false,
7241
6779
  bound: readWeixinCredential(chk.root, a.accountId) !== null
7242
6780
  }));
7243
- sendJson$3(res, 200, {
6781
+ sendJson$4(res, 200, {
7244
6782
  enabled: settings.enabled === true,
7245
6783
  botType: settings.botType ?? void 0,
7246
6784
  accounts,
@@ -7250,14 +6788,14 @@ function registerStatusApi(ctx, opts = {}) {
7250
6788
  return;
7251
6789
  }
7252
6790
  if (req.method !== "POST") {
7253
- sendJson$3(res, 405, { error: "method not allowed" });
6791
+ sendJson$4(res, 405, { error: "method not allowed" });
7254
6792
  return;
7255
6793
  }
7256
6794
  const raw = await readBody$4(req, 131072);
7257
6795
  const body = JSON.parse(raw);
7258
6796
  const chk = requireCcc(body.ccc);
7259
6797
  if (!chk.ok) {
7260
- sendJson$3(res, 400, { error: chk.error });
6798
+ sendJson$4(res, 400, { error: chk.error });
7261
6799
  return;
7262
6800
  }
7263
6801
  const root = chk.root;
@@ -7274,7 +6812,7 @@ function registerStatusApi(ctx, opts = {}) {
7274
6812
  startedAt: Date.now(),
7275
6813
  polling: false
7276
6814
  });
7277
- sendJson$3(res, 200, {
6815
+ sendJson$4(res, 200, {
7278
6816
  qrcode: qr.qrcode,
7279
6817
  qrcode_img_content: qr.qrcode_img_content,
7280
6818
  loginKey
@@ -7283,31 +6821,31 @@ function registerStatusApi(ctx, opts = {}) {
7283
6821
  }
7284
6822
  if (body.action === "remove-account") {
7285
6823
  if (!body.accountId) {
7286
- sendJson$3(res, 400, { error: "missing accountId" });
6824
+ sendJson$4(res, 400, { error: "missing accountId" });
7287
6825
  return;
7288
6826
  }
7289
6827
  const { removeWeixinAccount } = await import("./weixin-route-B2ajFypI.js").then((n) => n.u);
7290
6828
  const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
7291
6829
  removeWeixinAccount(root, body.accountId);
7292
6830
  syncCccBridge(ctx, root);
7293
- sendJson$3(res, 200, { removed: body.accountId });
6831
+ sendJson$4(res, 200, { removed: body.accountId });
7294
6832
  return;
7295
6833
  }
7296
6834
  if (body.action === "save-routes") {
7297
6835
  const routes = body.routes ?? [];
7298
6836
  if (!Array.isArray(routes) || routes.some((r) => typeof r?.user !== "string" || typeof r?.role !== "string" || r.user === "" || r.role === "")) {
7299
- sendJson$3(res, 400, { error: "invalid routes (expected [{user, role}, ...])" });
6837
+ sendJson$4(res, 400, { error: "invalid routes (expected [{user, role}, ...])" });
7300
6838
  return;
7301
6839
  }
7302
6840
  const { saveWeixinRoutes } = await import("./weixin-route-B2ajFypI.js").then((n) => n.u);
7303
6841
  const { readSkiffRoles } = await import("./skiff-role-CEHL5cek.js").then((n) => n.u);
7304
6842
  const roles = readSkiffRoles(root);
7305
6843
  for (const r of routes) if (!roles.has(r.role)) {
7306
- sendJson$3(res, 400, { error: `unknown role: ${r.role} (not in ${root} skiff.roles)` });
6844
+ sendJson$4(res, 400, { error: `unknown role: ${r.role} (not in ${root} skiff.roles)` });
7307
6845
  return;
7308
6846
  }
7309
6847
  saveWeixinRoutes(root, routes);
7310
- sendJson$3(res, 200, { saved: routes.length });
6848
+ sendJson$4(res, 200, { saved: routes.length });
7311
6849
  return;
7312
6850
  }
7313
6851
  if (body.action === "set-enabled") {
@@ -7316,7 +6854,7 @@ function registerStatusApi(ctx, opts = {}) {
7316
6854
  const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
7317
6855
  const settings = readWeixinSettings(root);
7318
6856
  if (enabled && (settings.accounts ?? []).length === 0) {
7319
- sendJson$3(res, 400, { error: "启用前请先扫码绑定至少一个账号" });
6857
+ sendJson$4(res, 400, { error: "启用前请先扫码绑定至少一个账号" });
7320
6858
  return;
7321
6859
  }
7322
6860
  setWeixinEnabled(root, enabled);
@@ -7325,12 +6863,12 @@ function registerStatusApi(ctx, opts = {}) {
7325
6863
  const { stopCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
7326
6864
  stopCccBridge(root);
7327
6865
  }
7328
- sendJson$3(res, 200, { enabled });
6866
+ sendJson$4(res, 200, { enabled });
7329
6867
  return;
7330
6868
  }
7331
- sendJson$3(res, 400, { error: `unsupported action: ${body.action ?? ""}` });
6869
+ sendJson$4(res, 400, { error: `unsupported action: ${body.action ?? ""}` });
7332
6870
  } catch (err) {
7333
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6871
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7334
6872
  }
7335
6873
  }
7336
6874
  });
@@ -7341,18 +6879,18 @@ function registerStatusApi(ctx, opts = {}) {
7341
6879
  try {
7342
6880
  if (!requireWebUi(req, res, "微信桥配置仅限 WebUI(client 专用)")) return;
7343
6881
  if (req.method !== "GET") {
7344
- sendJson$3(res, 405, { error: "method not allowed" });
6882
+ sendJson$4(res, 405, { error: "method not allowed" });
7345
6883
  return;
7346
6884
  }
7347
6885
  const key = new URL(req.url ?? "/", "http://127.0.0.1").searchParams.get("key") ?? "";
7348
6886
  const login = weixinLogins.get(key);
7349
6887
  if (!login) {
7350
- sendJson$3(res, 404, { error: "login not found or expired" });
6888
+ sendJson$4(res, 404, { error: "login not found or expired" });
7351
6889
  return;
7352
6890
  }
7353
6891
  if (Date.now() - login.startedAt > WEIXIN_LOGIN_TTL_MS) {
7354
6892
  weixinLogins.delete(key);
7355
- sendJson$3(res, 200, { status: "expired" });
6893
+ sendJson$4(res, 200, { status: "expired" });
7356
6894
  return;
7357
6895
  }
7358
6896
  const { pollQRStatus } = await import("./weixin-api-CtFYm53S.js").then((n) => n.u);
@@ -7365,7 +6903,7 @@ function registerStatusApi(ctx, opts = {}) {
7365
6903
  switch (status.status) {
7366
6904
  case "confirmed": {
7367
6905
  if (!status.bot_token) {
7368
- sendJson$3(res, 200, {
6906
+ sendJson$4(res, 200, {
7369
6907
  status: "error",
7370
6908
  error: "confirmed but no bot_token"
7371
6909
  });
@@ -7386,7 +6924,7 @@ function registerStatusApi(ctx, opts = {}) {
7386
6924
  });
7387
6925
  syncCccBridge(ctx, login.root);
7388
6926
  weixinLogins.delete(key);
7389
- sendJson$3(res, 200, {
6927
+ sendJson$4(res, 200, {
7390
6928
  status: "confirmed",
7391
6929
  accountId,
7392
6930
  tokenSaved: true
@@ -7395,14 +6933,14 @@ function registerStatusApi(ctx, opts = {}) {
7395
6933
  }
7396
6934
  case "expired":
7397
6935
  weixinLogins.delete(key);
7398
- sendJson$3(res, 200, { status: "expired" });
6936
+ sendJson$4(res, 200, { status: "expired" });
7399
6937
  return;
7400
6938
  default:
7401
- sendJson$3(res, 200, { status: status.status ?? "wait" });
6939
+ sendJson$4(res, 200, { status: status.status ?? "wait" });
7402
6940
  return;
7403
6941
  }
7404
6942
  } catch (err) {
7405
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6943
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7406
6944
  }
7407
6945
  }
7408
6946
  });
@@ -7424,7 +6962,7 @@ function registerStatusApi(ctx, opts = {}) {
7424
6962
  const root = sessionsRootDir();
7425
6963
  if (req.method === "GET") {
7426
6964
  const { candidates } = performCleanup(root, cutoffDaysAgo(days), liveIds, { dryRun: true });
7427
- sendJson$3(res, 200, {
6965
+ sendJson$4(res, 200, {
7428
6966
  root,
7429
6967
  olderThanDays: days,
7430
6968
  dryRun: true,
@@ -7438,12 +6976,12 @@ function registerStatusApi(ctx, opts = {}) {
7438
6976
  return;
7439
6977
  }
7440
6978
  if (req.method !== "POST") {
7441
- sendJson$3(res, 405, { error: "method not allowed" });
6979
+ sendJson$4(res, 405, { error: "method not allowed" });
7442
6980
  return;
7443
6981
  }
7444
6982
  if (!requireWebUi(req, res, "会话清理仅限 WebUI(client 专用)")) return;
7445
6983
  const { result } = performCleanup(root, cutoffDaysAgo(days), liveIds);
7446
- sendJson$3(res, 200, {
6984
+ sendJson$4(res, 200, {
7447
6985
  root,
7448
6986
  olderThanDays: days,
7449
6987
  dryRun: false,
@@ -7451,7 +6989,7 @@ function registerStatusApi(ctx, opts = {}) {
7451
6989
  errors: result.errors
7452
6990
  });
7453
6991
  } catch (err) {
7454
- sendJson$3(res, 400, { error: err.message ?? String(err) });
6992
+ sendJson$4(res, 400, { error: err.message ?? String(err) });
7455
6993
  }
7456
6994
  }
7457
6995
  });
@@ -7476,6 +7014,68 @@ function requireCcc(root) {
7476
7014
  };
7477
7015
  }
7478
7016
  //#endregion
7017
+ //#region src/voyage-page.ts
7018
+ const VOYAGE_PATH = "/serenity/voyage";
7019
+ /**
7020
+ * 资产相对包根的路径。
7021
+ * 🔒 它**必须**同时出现在 `package.json` 的 `files` 白名单里——否则发布包缺文件,
7022
+ * 路由会在运行时报「资产不可用」(同 v1.26.15 那次 `files` 漏 chunk 的事故形态)。
7023
+ * `dsh-develop pack-check` 的 `required` 已把它列为发布前硬断言。
7024
+ */
7025
+ const VOYAGE_ASSET = "assets/serenity-voyage.html";
7026
+ /** 包根 = 本模块所在目录的上一级(构建产物在 `lib/`,与 `assets/` 同级;同 constants.ts 定位法) */
7027
+ function packageRootPath() {
7028
+ return join(dirname(fileURLToPath(import.meta.url)), "..");
7029
+ }
7030
+ /** 资产绝对路径(导出以便测试与 pack-check 共用同一判据来源) */
7031
+ function voyageAssetPath() {
7032
+ return join(packageRootPath(), VOYAGE_ASSET);
7033
+ }
7034
+ /** 缓存(830 KB,避免每次展开卡片都读盘;进程内只读一次) */
7035
+ let cachedHtml = null;
7036
+ /** 读取资产全文。读不到即抛错(由 handler 转 500,绝不静默发空页)。 */
7037
+ function loadVoyageHtml(assetPath = voyageAssetPath()) {
7038
+ if (assetPath === voyageAssetPath()) {
7039
+ if (cachedHtml === null) cachedHtml = readFileSync(assetPath, "utf-8");
7040
+ return cachedHtml;
7041
+ }
7042
+ return readFileSync(assetPath, "utf-8");
7043
+ }
7044
+ /** 发 JSON(错误分支用;与 api.ts 的 sendJson 同形,但本模块不依赖它,保持零耦合) */
7045
+ function sendJson$3(res, status, body) {
7046
+ res.statusCode = status;
7047
+ res.setHeader("content-type", "application/json; charset=utf-8");
7048
+ res.end(JSON.stringify(body));
7049
+ }
7050
+ /**
7051
+ * 注册 `/serenity/voyage`(GET)。
7052
+ * @param opts.assetPath 覆盖资产路径(测试用;生产不传)
7053
+ */
7054
+ function registerVoyageApi(ctx, opts = {}) {
7055
+ ctx.webServer.register({
7056
+ kind: "exact",
7057
+ path: VOYAGE_PATH,
7058
+ handler: (req, res) => {
7059
+ if (req.method !== "GET") {
7060
+ sendJson$3(res, 405, { error: "method not allowed" });
7061
+ return;
7062
+ }
7063
+ let html;
7064
+ try {
7065
+ html = loadVoyageHtml(opts.assetPath ?? voyageAssetPath());
7066
+ } catch (err) {
7067
+ sendJson$3(res, 500, { error: `voyage asset unavailable: ${err?.message ?? String(err)}` });
7068
+ return;
7069
+ }
7070
+ res.statusCode = 200;
7071
+ res.setHeader("content-type", "text/html; charset=utf-8");
7072
+ res.setHeader("cache-control", "private, max-age=86400");
7073
+ res.setHeader("content-length", String(Buffer.byteLength(html)));
7074
+ res.end(html);
7075
+ }
7076
+ });
7077
+ }
7078
+ //#endregion
7479
7079
  //#region src/seams/env.ts
7480
7080
  /** 纯解析:给定 cwd,返回可注入的 DSH_SERENITY_* 事实(非 CCC 返回空) */
7481
7081
  function resolveSerenityEnv(cwd) {
@@ -12503,6 +12103,244 @@ qInput.focus()
12503
12103
  </html>`;
12504
12104
  }
12505
12105
  //#endregion
12106
+ //#region src/host/effect.ts
12107
+ /** 已告警的标签(进程级去重:重复装配/多测试替身不刷屏,但绝不静默) */
12108
+ const warnedLabels = /* @__PURE__ */ new Set();
12109
+ /**
12110
+ * 登记一个卸载时执行的清理。
12111
+ * @param ctx 宿主插件上下文
12112
+ * @param label 日志标签(定位是哪个资源未装配)
12113
+ * @param cleanup 卸载时执行;抛错被吞掉(不阻断其它清理)
12114
+ * @returns 是否成功装配(false = 宿主无 `ctx.effect`,已告警)
12115
+ */
12116
+ function registerDisposer(ctx, label, cleanup) {
12117
+ const effect = ctx?.effect;
12118
+ if (typeof effect !== "function") {
12119
+ if (!warnedLabels.has(label)) {
12120
+ warnedLabels.add(label);
12121
+ console.warn(`[serenity-hooks] ✗ 资源拆卸未装配(${label}):ctx.effect 不可用(宿主契约缺失)`);
12122
+ }
12123
+ return false;
12124
+ }
12125
+ try {
12126
+ effect.call(ctx, () => () => {
12127
+ try {
12128
+ cleanup();
12129
+ } catch (err) {
12130
+ console.warn(`[serenity-hooks] ✗ 资源清理失败(${label}): ${String(err?.message ?? err)}`);
12131
+ }
12132
+ }, `dsh-serenity-hooks: ${label}`);
12133
+ return true;
12134
+ } catch (err) {
12135
+ console.warn(`[serenity-hooks] ✗ 资源拆卸注册失败(${label}): ${String(err?.message ?? err)}`);
12136
+ return false;
12137
+ }
12138
+ }
12139
+ //#endregion
12140
+ //#region src/clock-runtime.ts
12141
+ /**
12142
+ * clock-runtime.ts — **时钟运行时工厂**(S142 §38 复审候选 C6b,路径 **P2**)
12143
+ *
12144
+ * 本仓有**两条各自独立运行**的 5min 时钟:
12145
+ * · 唤醒调度器(`wake-scheduler.ts`,一次性唤醒,D58)
12146
+ * · autopilot 时钟(`autopilot-trajectory.ts`,周期自唤醒,D59)
12147
+ * 二者的**业务判据完全不同**(谁来扫 / 扫什么 / 投递给谁 / 按什么周期),但**引擎骨架逐字同形**
12148
+ * ——进程态 + 快照 + 复位 + `startTimer` + tick 外壳 + 事件接线 + disposer + `timer.unref()`。
12149
+ * 本条把**引擎**抽到本工厂,**各钟仍各持自己的定时器与自己的串行链**。
12150
+ *
12151
+ * ───────────────────────────────────────────────────────────────────────────
12152
+ * 🔴 硬约束一:**两条时钟绝不可共用"排队执行"的链**(这是 P2 与 P1 的分界判据)
12153
+ *
12154
+ * autopilot 每轮要跑该 CCC 的**偏见脚本**(`fetchBiasContent`,超时 60s),可能阻塞数十秒;
12155
+ * 若与唤醒调度器共用一条串行链,会把另一条一起**卡住**(一次性唤醒被无关的偏见脚本拖住)。
12156
+ * ⇒ 本工厂把 `chain` 作为**每实例私有状态**:`createClock()` 调用一次 = 一条独立的串行域。
12157
+ * 实例之间**零共享**(无模块级单例、无共享 Promise、无共享 map)。
12158
+ * ⚠️ 若日后有人把 `chain` 提到模块级"省一次分配",**两条时钟的隔离即刻失效**——不许。
12159
+ *
12160
+ * 🔴 硬约束二:**武装门只判全局闸**(`gate()`),**不判"本次有无目标"**
12161
+ *
12162
+ * 2026-09-14 F 段实测缺陷:旧实现把"有 live CCC"当**武装前置条件**,而宿主刚重启时 live 会话
12163
+ * 必为空,且**"恢复旧会话"不触发 `session/created`**(只有新建会)⇒ 启动瞬间判定落空 =
12164
+ * **时钟永久不武装**(实测 6.6h 零 tick)。⇒ 现在「**全局闸开即武装**」,"有无目标"降级为
12165
+ * tick 内的廉价判定。**武装与"本次有无目标"是两件事,本工厂不得把二者耦合。**
12166
+ *
12167
+ * 🔴 硬约束三:**两条闸必须继续分开传**(v1.34 S-1 解耦)
12168
+ *
12169
+ * 唤醒调度器 `wakeSchedulerEnabled`(**缺省开**)|autopilot `autopilotWakeEnabled`
12170
+ * (**缺省关**)——取值方向相反。工厂取 `gate` **回调**(每次现读),**不做**任何合并或缓存。
12171
+ *
12172
+ * ───────────────────────────────────────────────────────────────────────────
12173
+ * 两条时钟**逐项语义差**(`ClockOptions` 的每个可配项都对应下表一行,不是随意的灵活性):
12174
+ *
12175
+ * | 项 | 唤醒调度器 | autopilot | 工厂表达 |
12176
+ * |----|-----------|-----------|---------|
12177
+ * | `chain` | 有(全局串行) | 有(全局串行) | **总是私有**(硬约束一) |
12178
+ * | `lastTickLog` | 有(逐行摘要) | **无** | `logFrom` 不给 = 不记 |
12179
+ * | `ticks` 记账 | 工厂在 body **完成后**记 | **本钟**在同步段记 | `bodyCountsTick` + `begin` |
12180
+ * | body 返回类型 | `string[]`(人读摘要) | `void` | 泛型 `T` |
12181
+ * | 启动日志 | 固定串 | 含启用 CCC 计数 | `startLog: () => string` |
12182
+ * | body 异常兜底 | 留痕 + `console.warn` | 同 | 工厂统一(**吞异常不改语义**) |
12183
+ *
12184
+ * ───────────────────────────────────────────────────────────────────────────
12185
+ * **本工厂是"加法式"重构**(P2 的性质):不新增任何判定逻辑,只把同形骨架收成一份代码;
12186
+ * 两个模块仍各自导出 `wakeSchedulerState()` / `autopilotClockState()` 与各自的
12187
+ * `__reset…ForTest()`,**对外可观测面(字段名与语义)零变化**——`acc-diag` ①b 段、
12188
+ * `container-status.ts:containerClocks()` 与面板都在读它们(见该文件 :225–250 的设计红线)。
12189
+ *
12190
+ * 为什么**不**取 P1(真合并成一个 `setInterval` + 两条策略)——2026-09-15 所有者裁决:
12191
+ * P1 只多省 ~20 行,却要把 `armed` 拆成"宿主 + 每策略"两层、并强行分开串行域,
12192
+ * 即**动两条正在跑的时钟的骨架**;**不值当**(裁决原文见 SESSION §12.8②)。
12193
+ */
12194
+ /**
12195
+ * tick 周期(**5 分钟**)。
12196
+ *
12197
+ * 2026-09-15 S142「ACC 侧 autopilot 退场」:本值原从 `autopilot-core.ts` 导入(`TICK_MS`),
12198
+ * 但该模块随 autopilot 退场,而**本模块的唯一消费者是唤醒调度器**(`wake-scheduler`)——
12199
+ * 值本身与 autopilot 无关 ⇒ 随消费者归位到本模块(不再依赖 autopilot 判据层)。
12200
+ *
12201
+ * ⚠️ 语义不得变:该值同时是**报告文案的来源**("等下个 Nmin tick"),历史上曾因文案写死
12202
+ * 10min 与实现 5min 不符而误导排查(§12.7 文案纠错)。
12203
+ */
12204
+ const TICK_MS = 3e5;
12205
+ /**
12206
+ * 挂事件 → 触发 `start()`(**每个事件一次 try/catch 吞异常**:事件通道缺失不阻断时钟)。
12207
+ *
12208
+ * 为什么吞异常:宿主(headless profile 等)可能没有事件通道;**启动时已 `start()` 过一次**,
12209
+ * 事件只是"跟上面板开关 / 新会话"的热启动补丁,缺它不该让整个装配抛错。
12210
+ * @param on 宿主的 `on`(可能不存在)
12211
+ * @param ctx `on` 的 `this`(宿主 API 常要求绑定)
12212
+ * @param clock 目标时钟
12213
+ * @param events 事件名列表(回调恒为 `() => clock.start()`)
12214
+ */
12215
+ function attachClockEvents(ctx, clock, events) {
12216
+ const on = ctx?.on;
12217
+ if (typeof on !== "function") return;
12218
+ for (const name of events) try {
12219
+ on.call(ctx, name, () => clock.start());
12220
+ } catch {}
12221
+ }
12222
+ /**
12223
+ * 建一条时钟(**每钟各调一次**——本工厂**不是**单例,调用次数 = 时钟条数)。
12224
+ *
12225
+ * 顺序忠实于既有实现:`start()` 先武装并立即跑一次 tick → 再挂事件。
12226
+ * 拆卸由调用方经 `registerDisposer(ctx, label, clock.dispose)` 落到宿主的 `ctx.effect`。
12227
+ * @param opts 见 {@link ClockOptions}(每项对应一条既有的语义差)
12228
+ * @returns 该钟的 `{start, dispose, snapshot, reset}`
12229
+ */
12230
+ function createClock(opts) {
12231
+ const runtime = {
12232
+ armed: false,
12233
+ armedAt: null,
12234
+ lastTickAt: null,
12235
+ ticks: 0,
12236
+ lastSkipReason: null
12237
+ };
12238
+ if (opts.logFrom) runtime.lastTickLog = [];
12239
+ const st = {
12240
+ timer: null,
12241
+ chain: Promise.resolve(),
12242
+ runtime,
12243
+ wired: false,
12244
+ opts
12245
+ };
12246
+ /** 记一次 tick(工厂内部与 body 共用同一实现——只有一处会计逻辑) */
12247
+ const countTick = () => {
12248
+ runtime.lastTickAt = Date.now();
12249
+ runtime.ticks += 1;
12250
+ };
12251
+ /** 逐行打印(`logFrom` 缺省 ⇒ 本钟无 tick 日志,整段为死代码路径) */
12252
+ const emit = (lines) => {
12253
+ if (!opts.logFrom) return;
12254
+ for (const line of lines) console.log(`${opts.logFrom.prefix}${line}`);
12255
+ };
12256
+ const tick = () => {
12257
+ if (!opts.gate()) {
12258
+ runtime.lastSkipReason = opts.gateOffReason;
12259
+ return;
12260
+ }
12261
+ if (opts.begin) try {
12262
+ opts.begin();
12263
+ } catch (err) {
12264
+ console.warn(`[serenity-hooks] ✗ tick 前置异常: ${String(err?.message ?? err)}(${opts.label})`);
12265
+ return;
12266
+ }
12267
+ st.chain = st.chain.then(async () => {
12268
+ try {
12269
+ const value = await opts.body();
12270
+ if (!opts.bodyCountsTick) countTick();
12271
+ if (opts.logFrom) {
12272
+ runtime.lastTickLog = opts.logFrom.lines(value);
12273
+ emit(runtime.lastTickLog);
12274
+ }
12275
+ } catch (err) {
12276
+ const msg = `✗ tick 异常: ${String(err?.message ?? err)}`;
12277
+ if (opts.logFrom) runtime.lastTickLog = [msg];
12278
+ console.warn(`[serenity-hooks] ${msg}(${opts.label})`);
12279
+ }
12280
+ });
12281
+ };
12282
+ /**
12283
+ * 幂等启动 + 武装门:**已武装 → 原样返回**(防重复起定时器);
12284
+ * **闸关 → 不武装**(零资源占用);闸开则**无条件**武装(硬约束二:不判"有无目标")。
12285
+ */
12286
+ const start = () => {
12287
+ if (st.timer) return;
12288
+ if (opts.events && !st.wired) {
12289
+ st.wired = true;
12290
+ attachClockEvents(opts.ctx, api, opts.events);
12291
+ }
12292
+ if (!opts.gate()) return;
12293
+ st.timer = setInterval(tick, TICK_MS);
12294
+ st.timer.unref();
12295
+ runtime.armed = true;
12296
+ runtime.armedAt = Date.now();
12297
+ console.log(opts.startLog());
12298
+ if (opts.immediate !== false) tick();
12299
+ };
12300
+ const dispose = () => {
12301
+ if (st.timer) {
12302
+ clearInterval(st.timer);
12303
+ st.timer = null;
12304
+ }
12305
+ runtime.armed = false;
12306
+ runtime.armedAt = null;
12307
+ opts.onDispose?.();
12308
+ };
12309
+ const api = {
12310
+ start,
12311
+ dispose,
12312
+ snapshot: () => ({
12313
+ enabled: opts.gate(),
12314
+ ...runtime,
12315
+ ...runtime.lastTickLog ? { lastTickLog: [...runtime.lastTickLog] } : {}
12316
+ }),
12317
+ noteSkipReason: (reason) => {
12318
+ runtime.lastSkipReason = reason;
12319
+ },
12320
+ countTick,
12321
+ enqueue: (work) => {
12322
+ st.chain = st.chain.then(work, () => void 0).then(() => void 0, (err) => {
12323
+ console.warn(`[serenity-hooks] ✗ 串行任务异常(${opts.label}): ${String(err?.message ?? err)}`);
12324
+ });
12325
+ },
12326
+ reset: () => {
12327
+ if (st.timer) {
12328
+ clearInterval(st.timer);
12329
+ st.timer = null;
12330
+ }
12331
+ runtime.armed = false;
12332
+ runtime.armedAt = null;
12333
+ runtime.lastTickAt = null;
12334
+ runtime.ticks = 0;
12335
+ runtime.lastSkipReason = null;
12336
+ if (opts.logFrom) runtime.lastTickLog = [];
12337
+ st.wired = false;
12338
+ opts.onReset?.();
12339
+ }
12340
+ };
12341
+ return api;
12342
+ }
12343
+ //#endregion
12506
12344
  //#region src/wake-scheduler.ts
12507
12345
  const PLUGIN_SOURCE$1 = {
12508
12346
  kind: "plugin",
@@ -12665,8 +12503,8 @@ async function deliverWake(ctx, root, entry) {
12665
12503
  * (工作区注册表 ∪ 持久化会话 ∪ live 会话):**没有 live 会话的 CCC 也会被扫到**。
12666
12504
  *
12667
12505
  * ⚠️ 边界(本步**只**改"枚举哪些 CCC",**不改**"怎么找到要交付的 agent"):
12668
- * 把 message 交给某条 trajectory 仍必须靠 **live 会话**解析(见
12669
- * `autopilot-trajectory.resolveTargetAgent`);无 live 会话时该 CCC 只是**被扫到、
12506
+ * 把 message 交给某条 trajectory 仍必须靠 **live 会话**解析(见本文件
12507
+ * `acquireWakeAgent`);无 live 会话时该 CCC 只是**被扫到、
12670
12508
  * 无交付对象** ⇒ `deliverWake` 返回 `无绑定会话记录…`,条目回 `pending` 并留痕。
12671
12509
  * 因此本函数**不得**让 tick 抛错(异常一律由调用方 tick 兜住,见 `runWakeTick` 的 try)。
12672
12510
  * @param ctx 插件上下文
@@ -14566,6 +14404,56 @@ function createImBridgeTool() {
14566
14404
  });
14567
14405
  }
14568
14406
  //#endregion
14407
+ //#region src/live-sessions.ts
14408
+ /**
14409
+ * 从 dsh 会话 log 读取标题(latest-wins `session/title` 事件)——
14410
+ * **标题不在 `sessions.list()` 条目上**(wire/对象均无 title 字段;F3 命名经
14411
+ * `sessionTitle.rename` 写进 session log),必须从 events 提取(rebuild 同款读取模式)。
14412
+ */
14413
+ function readSessionTitle(session) {
14414
+ try {
14415
+ const events = sessionEvents$1(session);
14416
+ for (let i = events.length - 1; i >= 0; i--) {
14417
+ const e = events[i];
14418
+ if (e?.type === "session/title" && typeof e.data?.title === "string" && e.data.title.trim() !== "") return e.data.title.trim();
14419
+ }
14420
+ } catch {}
14421
+ return null;
14422
+ }
14423
+ /**
14424
+ * 逐条 live 会话(`cccRoot` = 该会话 cwd 的 CCC 归属,无 → null)。
14425
+ *
14426
+ * CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
14427
+ * 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
14428
+ *
14429
+ * @param ctx 插件上下文
14430
+ * @returns 逐条 live 会话
14431
+ */
14432
+ function listLiveSessions(ctx) {
14433
+ const out = [];
14434
+ try {
14435
+ const sessions = hostSessions(ctx);
14436
+ for (const s of sessions?.list?.() ?? []) {
14437
+ const cwd = s?.header?.cwd ?? null;
14438
+ out.push({
14439
+ id: s?.id ?? "",
14440
+ cwd,
14441
+ cccRoot: cwd ? cccRootForCwd(cwd) : null,
14442
+ title: readSessionTitle(s)
14443
+ });
14444
+ }
14445
+ } catch {}
14446
+ return out;
14447
+ }
14448
+ function diagLive(ctx) {
14449
+ const processCwd = process.cwd();
14450
+ return {
14451
+ processCwd,
14452
+ processCcc: cccRootForCwd(processCwd),
14453
+ liveSessions: listLiveSessions(ctx)
14454
+ };
14455
+ }
14456
+ //#endregion
14569
14457
  //#region src/container-status.ts
14570
14458
  /**
14571
14459
  * container-status.ts — 容器状态模型的**取数层唯一出口**(C5「观察面归一」,S142 2026-09-15)
@@ -14584,20 +14472,19 @@ function createImBridgeTool() {
14584
14472
  * 其模块级运行时对象里,任何"重新推导"得到的都是与真实调度器不一致的假值。
14585
14473
  *
14586
14474
  * 依赖分层(为什么本模块"重"而 `kit-ops` / `status` 保持"轻"):
14587
- * {@link containerClocks} 必须读 `wake-scheduler` / `autopilot-trajectory` 的进程内快照,而这两个
14588
- * 模块带 **@deepseek-ai 值依赖**(`dsh-llm` 的 `createUserMessage`)。故本模块**只能**被本就重型或
14589
- * 异步的消费方使用:`diag-ops`(静态 import)、`api.ts` / `kit-ops`(`await import(...)`,与
14590
- * `api.ts` 既有的"保持静态链纯净"约定一致)。**轻链模块不得静态 import 本模块**。
14475
+ * {@link containerClocks} 必须读 `wake-scheduler` 的进程内快照,而该模块带 **@deepseek-ai 值依赖**
14476
+ * (`dsh-llm` 的 `createUserMessage`)。故本模块**只能**被本就重型或异步的消费方使用:
14477
+ * `diag-ops`(静态 import)、`api.ts` / `kit-ops`(`await import(...)`,与 `api.ts` 既有的
14478
+ * "保持静态链纯净"约定一致)。**轻链模块不得静态 import 本模块**。
14591
14479
  *
14592
14480
  * 已有的单一真相源继续指向原处(**不复制**):
14593
14481
  * L0 根解析 `ccc.ts findSerenityRoot`|L1/L2 根与枚举 `ccc-roots.ts`|宿主契约 `host/contract.ts`
14594
- * |CCC 配置 `ccc.ts loadSerenityConfig`|注册表 `msm-ops.ts`|时钟 `wake-scheduler.ts` /
14595
- * `autopilot-trajectory.ts`|唤醒注册表 `wake-registry.ts`。
14482
+ * |CCC 配置 `ccc.ts loadSerenityConfig`|注册表 `msm-ops.ts`|时钟 `wake-scheduler.ts`
14483
+ * |唤醒注册表 `wake-registry.ts`。
14596
14484
  */
14597
14485
  var container_status_exports = /* @__PURE__ */ __exportAll({
14598
14486
  checkRegistryHealth: () => checkRegistryHealth,
14599
14487
  checkRegistryQuality: () => checkRegistryQuality,
14600
- containerAutopilot: () => containerAutopilot,
14601
14488
  containerClocks: () => containerClocks,
14602
14489
  containerIdentity: () => containerIdentity,
14603
14490
  containerPrinciples: () => containerPrinciples,
@@ -14732,18 +14619,14 @@ function checkRegistryHealth(root) {
14732
14619
  };
14733
14620
  }
14734
14621
  /**
14735
- * 两条时钟的进程内状态("为什么这轮没被唤起"的第一手判据)。
14622
+ * 时钟的进程内状态("为什么这轮没被唤起"的第一手判据)。
14736
14623
  *
14737
14624
  * 🔴 **只读快照,不重算**(设计红线):`armed`/`ticks`/`lastTickAt`/`lastSkipReason` 只存在于
14738
- * 调度器自己的模块级运行时对象里(`wake-scheduler.ts schedulerRuntime` /
14739
- * `autopilot-trajectory.ts` 同规格对象)。任何"从配置或文件重新推导"得到的值都会与真实
14740
- * 调度器的状态不一致——那比没有可观测量更糟(会给出假的"一切正常")。
14625
+ * 调度器自己的模块级运行时对象里(`wake-scheduler.ts schedulerRuntime`)。任何"从配置或文件
14626
+ * 重新推导"得到的值都会与真实调度器的状态不一致——那比没有可观测量更糟(会给出假的"一切正常")。
14741
14627
  */
14742
14628
  function containerClocks() {
14743
- return {
14744
- wake: wakeSchedulerState(),
14745
- autopilot: autopilotClockState()
14746
- };
14629
+ return { wake: wakeSchedulerState() };
14747
14630
  }
14748
14631
  /** 唤醒注册表取数(文件 IO:每个渲染点自己决定是否要看它) */
14749
14632
  function containerWakes(root) {
@@ -14754,16 +14637,13 @@ function containerWakes(root) {
14754
14637
  pending: entries.filter((e) => e.state === "pending").length
14755
14638
  };
14756
14639
  }
14757
- /** @returns CCC 根缺失(null)→ null */
14758
- function containerAutopilot(root) {
14759
- return root ? getAutopilotStatus(root) : null;
14760
- }
14761
14640
  /**
14762
14641
  * 核心段组装(`dashboard health` 的取数:身份 + 三原则 + 注册表两判据 + 配置 + 宿主契约)。
14763
14642
  *
14764
- * 时间轴三段(时钟/唤醒注册表/autopilot 目标)**不在本组装内**:它们是各自独立消费方的需要
14765
- * (`acc-diag` / `/serenity/trajectory`),硬塞进来会让只想看三原则的调用方付无谓的 IO。
14766
- * 需要时按名字调 {@link containerClocks} / {@link containerWakes} / {@link containerAutopilot}
14643
+ * 时间轴两段(时钟/唤醒注册表)**不在本组装内**:它们是各自独立消费方的需要
14644
+ * (`acc-diag` / 开发面诊断),硬塞进来会让只想看三原则的调用方付无谓的 IO。
14645
+ * 需要时按名字调 {@link containerClocks} / {@link containerWakes}。
14646
+ * (原第三段「autopilot 目标」及其取数函数 `containerAutopilot` 已随 ACC 侧 autopilot 退场删除。)
14767
14647
  */
14768
14648
  function containerStatus(opts = {}) {
14769
14649
  const root = opts.root ?? null;
@@ -14788,7 +14668,7 @@ function containerStatus(opts = {}) {
14788
14668
  //#endregion
14789
14669
  //#region src/diag-ops.ts
14790
14670
  /**
14791
- * diag-ops.ts — `acc-diag` 报告装配(v1.33,S142 §32.11
14671
+ * diag-ops.ts — `acc-diag` 报告装配(v1.33,S142 §32.11;2026-09-15 段数收缩)
14792
14672
  *
14793
14673
  * 为什么单独成模块(R↓):工具面(`tools/acc-diag.ts`)只做"取 CCC 根 + 渲染",
14794
14674
  * 装配逻辑放这里 ⇒ 纯函数可单测、不依赖 DSH 工具注册。
@@ -14796,22 +14676,28 @@ function containerStatus(opts = {}) {
14796
14676
  * 为什么**无动作参数**:诊断是低频动作(§32.9 收敛取向),一次调用即出全报告,
14797
14677
  * 不需要子命令面;动作越多,模型选择成本越高。
14798
14678
  *
14799
- * 四段内容(数据来源各自单一真相源,本模块只聚合不复制语义):
14800
- * ① live 运行态 —— 插件进程内 `diagLive(ctx)`(live 会话清单 + autopilot CCC 的
14801
- * 目标命中 / agent 定位诊断)——**独立进程看不到运行时**,这正是本工具存在的理由
14802
- * 面板解析 —— `diagLive.panelResolved`(无参面板请求会落到哪个 CCC)
14803
- * ③ 唤醒注册表 —— `wake-registry.json` 全量条目(state / at / target / lastResult)+ 补跑窗口
14804
- * ④ 唤起条件链 —— **进程内** {@link buildWakeChain}(⑥ C6a):逐条件值 + 阻断点 + 修复建议
14805
- * + 只有进程内能看到的三项事实(全局闸 / live 会话 / agent 可解析性)
14806
- *
14807
- * C5「观察面归一」(2026-09-15):①/② 仍走 `diagLive`(那是**live 运行态**的独有取数,
14808
- * 没有第二个来源),但 ①b 时钟与 ③ 注册表改经 `container-status.ts` 取(唯一取数出口)——
14809
- * **投影仍在本文件**(诊断段不含 `message`,面板含;这是渲染差异,不是取数差异)。
14810
- *
14811
- * C6a(2026-09-15):④ 段由"spawn 包内脚本"改为**进程内计算**。旧实现是本报告里
14812
- * 唯一一条**跨进程**取数:脚本看不到全局闸/live/agent ⇒ 与本工具 ① 段自相矛盾
14813
- * (① 说"agent 未定位",④ 却印"条件全部满足")。现在 ④ 段与 ① 段读**同一份事实**
14814
- * (`autopilotRuntimeFacts`),且条件链**只有一份实现**(开发面 `dsh-develop diag` import 同一个)。
14679
+ * 三段内容(数据来源各自单一真相源,本模块只聚合不复制语义):
14680
+ * ① live 运行态 —— 插件进程内 `diagLive(ctx)`(进程 cwd/CCC + live 会话清单)
14681
+ * ——**独立进程看不到运行时**,这正是本工具存在的理由
14682
+ * ①b 唤醒时钟 —— `containerClocks().wake`(唤醒调度器进程内快照:武装态/计数/上次跳过原因)
14683
+ * ③ 唤醒注册表 —— `wake-registry.json` 全量条目(state / at / target / lastResult)+ 补跑窗口
14684
+ *
14685
+ * ── 2026-09-15:段数由四降为三(S142「ACC autopilot 退场」§1 二阶裁定)─────────────
14686
+ * ACC 侧 autopilot 整段退场后,报告里两段的**主语消失**,故删除(不是"暂时关掉"):
14687
+ * · 面板解析 —— `diagLive.panelResolved` 的来源是"哪个 CCC 配了 autopilot",
14688
+ * 面板区块同批删除 主语不存在。
14689
+ * · ④ 唤起条件链 —— 整段都是 ACC autopilot 的唤起条件(`buildWakeChain` +
14690
+ * `autopilotRuntimeFacts`),实现模块已删 ⇒ 不留第二份条件链、也不留半死的手写版。
14691
+ * · 段的 autopilot CCC 渲染(逐 CCC 目标命中 / agent 定位)同批删除。
14692
+ *
14693
+ * 🔴 **已知缺口(登记,不静默丢弃)**:④「为什么这轮没唤起」的诊断在 **CCC 自管理链**上归零。
14694
+ * 新链的失败形态是"S151 漏了自排下一轮"或"调度器闸关/未 tick",而删掉 后这两种病因都不可见。
14695
+ * 按所有者裁决本次**不补**(一句话可补,代价 = 一条新条件链:调度器 armed / 闸 / tick 在跑 /
14696
+ * 条目到期 / 补跑窗口 / 目标可解析 / CCC 闸)。**不得**以"顺手保留 ④"的方式把它偷偷带回。
14697
+ *
14698
+ * C5「观察面归一」(2026-09-15):① 走 `diagLive`(那是**live 运行态**的独有取数,没有第二个
14699
+ * 来源);①b 时钟与 ③ 注册表经 `container-status.ts` 取(唯一取数出口)——**投影仍在本文件**
14700
+ * (诊断段不含 `message`,面板含;这是渲染差异,不是取数差异)。
14815
14701
  */
14816
14702
  /** 人读渲染一行时钟状态("为何没有 tick" 的第一手判据) */
14817
14703
  function renderClock(label, s) {
@@ -14829,21 +14715,17 @@ function renderClock(label, s) {
14829
14715
  /**
14830
14716
  * 装配报告(一次调用即全报告)。
14831
14717
  * @param ctx 插件上下文(进程内读 sessions/agents)
14832
- * @param root 调用方 CCC 根(条件链诊断目标)
14718
+ * @param root 调用方 CCC 根(唤醒注册表取数目标)
14833
14719
  * @returns 结构化报告(渲染由 {@link renderAccDiag} 负责)
14834
14720
  */
14835
14721
  async function runAccDiag(ctx, root) {
14836
14722
  const live = diagLive(ctx);
14837
14723
  const clocks = containerClocks();
14838
14724
  const wakes = containerWakes(root);
14839
- const chain = await buildWakeChain(root, autopilotRuntimeFacts(ctx, root));
14840
14725
  return {
14841
14726
  ccc: root,
14842
14727
  live,
14843
- clocks: {
14844
- wake: clocks.wake,
14845
- autopilot: clocks.autopilot
14846
- },
14728
+ clocks: { wake: clocks.wake },
14847
14729
  wakes: {
14848
14730
  error: wakes.error,
14849
14731
  entries: wakes.entries.map((e) => ({
@@ -14856,12 +14738,10 @@ async function runAccDiag(ctx, root) {
14856
14738
  lastResult: e.lastResult ?? null
14857
14739
  })),
14858
14740
  pending: wakes.pending
14859
- },
14860
- autopilotChain: renderWakeChain(chain),
14861
- autopilotVerdict: chain.verdict
14741
+ }
14862
14742
  };
14863
14743
  }
14864
- /** 人读渲染(四段;空段也要显式说明"无",避免"沉默 = 一切正常"的误读) */
14744
+ /** 人读渲染(三段;空段也要显式说明"无",避免"沉默 = 一切正常"的误读) */
14865
14745
  function renderAccDiag(r) {
14866
14746
  const lines = [];
14867
14747
  lines.push(`═══ ACC 运行态诊断(acc-diag)═══`);
@@ -14872,20 +14752,8 @@ function renderAccDiag(r) {
14872
14752
  lines.push(`进程 CCC: ${r.live.processCcc ?? "(无——进程 cwd 不在任何 CCC 内)"}`);
14873
14753
  lines.push(`live 会话: ${r.live.liveSessions.length} 个`);
14874
14754
  for (const s of r.live.liveSessions) lines.push(` · ${s.id}${s.title ? `「${s.title}」` : ""} — ccc=${s.cccRoot ?? "(非 CCC)"} cwd=${s.cwd ?? "(无)"}`);
14875
- if (r.live.autopilotCccs.length === 0) lines.push("autopilot CCC: 无(没有 live 会话配置了 trajectory.autopilot)");
14876
- else {
14877
- lines.push(`autopilot CCC: ${r.live.autopilotCccs.length} 个`);
14878
- for (const c of r.live.autopilotCccs) {
14879
- lines.push(` · ${c.root} enabled=${c.enabled} session=${c.session ?? "(未配置)"} 目标=${c.target?.dirName ?? "(未命中)"} wakeable=${c.target?.wakeable ?? false} agent=${c.agentResolved ? "已定位" : "未定位"}`);
14880
- if (c.agentDiagnosis) lines.push(` 诊断: ${c.agentDiagnosis}`);
14881
- }
14882
- }
14883
14755
  lines.push("");
14884
14756
  lines.push(renderClock("唤醒调度器(wake-registry tick)", r.clocks.wake));
14885
- lines.push(renderClock("autopilot 时钟(周期自唤醒 tick)", r.clocks.autopilot));
14886
- lines.push("");
14887
- lines.push(`── ② 面板解析(无参 /serenity/trajectory 落到哪个 CCC)──`);
14888
- lines.push(r.live.panelResolved ?? "(解析不到——本进程无 live+enabled 的 autopilot CCC)");
14889
14757
  lines.push("");
14890
14758
  lines.push(`── ③ 唤醒注册表(${r.ccc}/AGENT_SESSIONS/wake-registry.json)──`);
14891
14759
  if (r.wakes.error) lines.push(`⚠ 注册表读取问题: ${r.wakes.error}`);
@@ -14895,9 +14763,6 @@ function renderAccDiag(r) {
14895
14763
  lines.push(` · ${w.id} [${w.state}] at=${w.at} → ${w.target} by=${w.createdBy} attempts=${w.attempts}`);
14896
14764
  if (w.lastResult) lines.push(` lastResult: ${w.lastResult}`);
14897
14765
  }
14898
- lines.push("");
14899
- lines.push(`── ④ 唤起条件链("为什么这轮没被唤起")──`);
14900
- lines.push(r.autopilotChain);
14901
14766
  return lines.join("\n");
14902
14767
  }
14903
14768
  //#endregion
@@ -14920,12 +14785,15 @@ function renderAccDiag(r) {
14920
14785
  */
14921
14786
  /**
14922
14787
  * 运行态诊断工具(`acc-diag`)。**无动作参数**:一次调用即全报告
14923
- * live 运行态 / 面板解析 / 唤醒注册表 / 唤起条件链)。
14788
+ * (① live 运行态 / ①b 唤醒时钟 / 唤醒注册表)。
14789
+ *
14790
+ * ⚠️ 2026-09-15(ACC 侧 autopilot 退场,S142):原四段降为三段——原 ②「面板解析」与
14791
+ * ④「唤起条件链」的主语随机制消失,已删(见 `../diag-ops.ts` 文件头;④ 的缺口已登记)。
14924
14792
  */
14925
14793
  function createAccDiagTool(ctx) {
14926
14794
  return defineTool({
14927
14795
  name: "acc-diag",
14928
- 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.",
14796
+ 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.",
14929
14797
  parameters: {},
14930
14798
  output: {
14931
14799
  schema: { type: "json" },
@@ -15032,7 +14900,10 @@ function apply(ctx, config) {
15032
14900
  configPaths: config.serenityConfigPaths,
15033
14901
  entrySkillMaxChars: config.entrySkillMaxChars
15034
14902
  });
15035
- if (config.api) registerStatusApi(ctx, { configPaths: config.serenityConfigPaths });
14903
+ if (config.api) {
14904
+ registerStatusApi(ctx, { configPaths: config.serenityConfigPaths });
14905
+ registerVoyageApi(ctx);
14906
+ }
15036
14907
  registerSettingsSection(ctx, config);
15037
14908
  registerGateway(ctx);
15038
14909
  registerRebuildTurnHook(ctx);
@@ -15042,7 +14913,6 @@ function apply(ctx, config) {
15042
14913
  registerBootstrap(ctx);
15043
14914
  registerSkiff(ctx);
15044
14915
  registerAcp(ctx);
15045
- registerAutopilot(ctx);
15046
14916
  registerWakeScheduler(ctx);
15047
14917
  registerWeixinBridge(ctx);
15048
14918
  registerWeixinSendApi(ctx);