@shgroup/dsh-serenity-hooks 1.28.1 → 1.29.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
@@ -18,6 +18,7 @@ import { createUserMessage } from "@deepseek-ai/dsh-llm";
18
18
  import { createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
19
19
  import { deriveEventMessage } from "@deepseek-ai/dsh-session";
20
20
  import { createServer, request } from "node:http";
21
+ import * as zlib from "node:zlib";
21
22
  //#region src/fs-ops.ts
22
23
  /**
23
24
  * fs-ops.ts — cc_fs 纯操作层(零 DSH 依赖,可独立单测)
@@ -1427,7 +1428,7 @@ handyman(杂工)工具可用模型白名单(provider/model 列表);未
1427
1428
  { "handyman": { "models": ["minimax-cn-coding-plan/MiniMax-M3"], "defaultModel": "minimax-cn-coding-plan/MiniMax-M3", "maxRounds": 100, "maxParallel": 10 } }
1428
1429
 
1429
1430
  ── 2. sessionKeeper.threshold ──
1430
- SESSION-KEEPER 提醒机制的积分阈值(非 headless 主 agent)。
1431
+ trajectory-assistant(TRAJECTORY-ASSISTANT · CHECKPOINT)提醒机制的积分阈值(非 headless 主 agent)。
1431
1432
  按工具调用加权 + 耗时计分;达到阈值注入提醒,要求模型回复 ACK 码。
1432
1433
 
1433
1434
  Config:
@@ -3823,6 +3824,49 @@ function registerBootstrap(ctx) {
3823
3824
  }, { prepend: true });
3824
3825
  }
3825
3826
  //#endregion
3827
+ //#region src/trajectory-assistant.ts
3828
+ /**
3829
+ * trajectory-assistant.ts — 关卡化注入的统一命名与风格门面(v0.3,S142)
3830
+ *
3831
+ * 概念(用户拍板 2026-09-05):dsp 全部"过程中动态提示注入"统一命名为
3832
+ * trajectory-assistant(轨迹助航员)。关卡设计思想塑造**结构与时机**
3833
+ * (何时/何地注入),**不用于提示词用词**(D8 词法原则)——纯游戏黑话
3834
+ * (BOSS/XP/level-up)禁止出现在提示词文本;CHECKPOINT/LIMIT/TUTORIAL 等
3835
+ * 跨领域自然通用词允许。
3836
+ *
3837
+ * 本模块 = token 常量 + level-event 词汇表的**单一真相源**(避免前缀散落
3838
+ * 各文件字符串字面量)+ 风格门面(plain 默认 / metaphor 星舰变体)。
3839
+ * 仅当 style=metaphor 时前缀包装措辞变化;可行动正文不变 → 零行为漂移。
3840
+ *
3841
+ * 设计简写(L0 环境 / L1 教程 / L2 目标 / L3 检查点 / L4 极限 / L5 守卫 /
3842
+ * L6 结算)仅在本模块注释与内部文档出现,永不浮现在提示词文本。
3843
+ *
3844
+ * 结算(settlement)挂点:D6 用户拍板——若 CCC 有标准 SESSION 复盘仪式,
3845
+ * 结算视图归 trajectory-assistant。当前 close/archive 无复盘摘要 → 机制
3846
+ * 延后,仅留 onSettlement 导出 seam(OP-1,无调用者)。
3847
+ */
3848
+ /** 可见提示词前缀用词(跨领域自然词,D8:无游戏黑话) */
3849
+ const EVENT_LABEL = {
3850
+ /** 计分同步提醒(原 TRAJECTORY-STEWARD) */
3851
+ checkpoint: "CHECKPOINT",
3852
+ /** 上下文极限重建提醒(原 TRAJECTORY;LIMIT 替代被否的 BOSS——自然词) */
3853
+ limit: "LIMIT",
3854
+ /** 极限强制升级(原 TRAJECTORY-ESCALATED) */
3855
+ limitMandatory: "LIMIT · MANDATORY",
3856
+ /** 重建锚点头部(原 TRAJECTORY-REBUILD) */
3857
+ rebuild: "REBUILD",
3858
+ /** 敏感输出边界守卫(原 SERENITY OUTPUT GUARD) */
3859
+ guard: "BOUNDARY GUARD"
3860
+ };
3861
+ /** 家族标识(所有动态注入统一前缀) */
3862
+ const ASSISTANT_PREFIX = "TRAJECTORY-ASSISTANT";
3863
+ /** 完整 token:`[TRAJECTORY-ASSISTANT · <LABEL>]` */
3864
+ function eventToken(event) {
3865
+ return `[${ASSISTANT_PREFIX} · ${EVENT_LABEL[event]}]`;
3866
+ }
3867
+ /** ACK 确认码前缀(recorded/skipped 语义不变,仅家族名更新) */
3868
+ const ACK_PREFIX = `${ASSISTANT_PREFIX}-recorded`;
3869
+ //#endregion
3826
3870
  //#region src/rebuild.ts
3827
3871
  const PLUGIN_SOURCE$4 = {
3828
3872
  kind: "plugin",
@@ -3849,7 +3893,7 @@ function buildRebuildAnchor(root, sessionName, activeMdPath, anchorMessages = DE
3849
3893
  const sessionDir = basename(dirname(activeMdPath));
3850
3894
  const sessionLine = sessionDir !== "AGENT_SESSIONS" ? `- Serenity session: ${sessionName !== "" ? `${sessionName} (${sessionDir})` : sessionDir}` : null;
3851
3895
  return [
3852
- "[TRAJECTORY-REBUILD] The conversation has been cleared and rebuilt (Ship of Theseus: the carrier is replaced, the trajectory continues).",
3896
+ `${eventToken("rebuild")} The conversation has been cleared and rebuilt (Ship of Theseus: the carrier is replaced, the trajectory continues).`,
3853
3897
  "",
3854
3898
  ...anchorMessages.flatMap((text) => [stripAckSuffix(text), ""]),
3855
3899
  sessionName !== "" ? `Continue the work of ${sessionName}.` : "Continue the current work.",
@@ -4074,7 +4118,7 @@ function registerRebuildTurnHook(ctx) {
4074
4118
  agent.steer(createUserMessage({
4075
4119
  content: [{
4076
4120
  type: "text",
4077
- text: "[TRAJECTORY-REBUILD] The conversation has been cleared and rebuilt. Follow the anchor instructions above now: read the persistent trajectory (SESSION.md) and continue the work automatically from the last checkpoint."
4121
+ text: `${eventToken("rebuild")} The conversation has been cleared and rebuilt. Follow the anchor instructions above now: read the persistent trajectory (SESSION.md) and continue the work automatically from the last checkpoint.`
4078
4122
  }],
4079
4123
  source: PLUGIN_SOURCE$4
4080
4124
  }));
@@ -4136,7 +4180,7 @@ function renderText$3(value) {
4136
4180
  function createRebuildTool(ctx) {
4137
4181
  return defineTool({
4138
4182
  name: "session_rebuild",
4139
- description: "Trajectory-tracker overflow rebuild (Ship of Theseus): this session is the rebuildable carrier of a trajectory — the current conversation is discarded and rebuilt in place at the end of this turn with the anchor \"continue the work of {SESSION name}\" (first-anchor protocol body included), then auto-continues. SESSION.md (the trajectory's persistent body) stays in place; identity continues from it. Use when you receive a [TRAJECTORY] reminder (context above threshold), at a natural pause point. After triggering, resume from SESSION.md when this turn ends.",
4183
+ description: "Trajectory-tracker overflow rebuild (Ship of Theseus): this session is the rebuildable carrier of a trajectory — the current conversation is discarded and rebuilt in place at the end of this turn with the anchor \"continue the work of {SESSION name}\" (first-anchor protocol body included), then auto-continues. SESSION.md (the trajectory's persistent body) stays in place; identity continues from it. Use when you receive a " + eventToken("limit") + " reminder (context above threshold), at a natural pause point. After triggering, resume from SESSION.md when this turn ends.",
4140
4184
  parameters: {
4141
4185
  note: {
4142
4186
  type: "string",
@@ -4599,7 +4643,7 @@ function buildWakeMessage(opts) {
4599
4643
  lines.push(`[轨迹焦点] ${opts.topPrompt}`);
4600
4644
  lines.push("");
4601
4645
  }
4602
- lines.push(`[Autopilot Trajectory 唤起] — 距上次轨迹活动已满 ${intervalLabel},自动继续。`, "", `身份锚定:继续 ${opts.sessionName} 的 trajectory(SESSION.md: ${opts.mdPath})。`, "先验偏见:");
4646
+ lines.push(`[Autopilot Trajectory · 唤起] — 距上次轨迹活动已满 ${intervalLabel},自动继续。`, "", `身份锚定:继续 ${opts.sessionName} 的 trajectory(SESSION.md: ${opts.mdPath})。`, "先验偏见:");
4603
4647
  if (opts.motivation) lines.push(` · 自生动机:${opts.motivation}`);
4604
4648
  if (opts.biasContent) lines.push(` · 偏见内容:${opts.biasContent}`);
4605
4649
  if (!opts.motivation && !opts.biasContent) lines.push(" · (无——本轮纯自主探索)");
@@ -5127,7 +5171,7 @@ var KeeperTracker = class {
5127
5171
  }
5128
5172
  };
5129
5173
  function reminderText(code, score) {
5130
- return `[TRAJECTORY-STEWARD] Score threshold reached (${score}). Please acknowledge with [TRAJECTORY-STEWARD-recorded-${code}] once progress is synced to the working session (acc-session show). No need to interrupt your work — just acknowledge inline and keep going.`;
5174
+ 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.`;
5131
5175
  }
5132
5176
  /**
5133
5177
  * F2 rebuild 提示(v1.21;v1.22.1 对齐"轨迹跟踪器"概念;v1.23.0 英化 + 载体关系;
@@ -5146,8 +5190,8 @@ function reminderText(code, score) {
5146
5190
  * thresholdK = 配置阈值(千 token);文案 `Context usage at NNNK (threshold NNNK)`。
5147
5191
  */
5148
5192
  function rebuildReminderText(tokensK, thresholdK, escalated = false) {
5149
- if (escalated) return `[TRAJECTORY-ESCALATED] Context usage at ${Math.round(tokensK)}K (threshold ${Math.round(thresholdK)}K) — you have been reminded repeatedly and have NOT called session_rebuild. 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 the session_rebuild tool immediately, passing --summary "<content summary ≤20 chars>" (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild). 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 session_rebuild.`;
5150
- return `[TRAJECTORY] 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. ACT NOW: at the next natural pause (end of the current task step), call the session_rebuild tool — 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.`;
5193
+ 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 session_rebuild. 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 the session_rebuild tool immediately, passing --summary "<content summary ≤20 chars>" (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild). 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 session_rebuild.`;
5194
+ 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. ACT NOW: at the next natural pause (end of the current task step), call the session_rebuild tool — 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.`;
5151
5195
  }
5152
5196
  /** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
5153
5197
  function readContextPressure(ctx, session) {
@@ -5904,22 +5948,25 @@ function eapBlock() {
5904
5948
  ].join("\n");
5905
5949
  }
5906
5950
  /**
5907
- * Metaphor 强化块(v1.19.6,S142):宁静号宇宙隐喻域(船/海/罗盘/日志/船员)。
5951
+ * Metaphor 强化块(v1.19.6,S142;v1.29 星舰意象升级,S142 用户拍板):
5952
+ * 宁静号宇宙隐喻域(星舰/深空/航程/船员)。海船→星舰意象:one starship, one voyage;
5953
+ * deep space / launch / docking clamp;Departure Inspection(原 Harbor Inspection)。
5908
5954
  * 隐喻是记忆钩子——形象唤起约束,前述块保持规则精确。每条隐喻 = 一个不可违背的
5909
5955
  * 物理事实 + 行为判据(verdict)。全英文(与 CCE/Constraints 一致);独立块可
5910
5956
  * 回退;无 osp 对应(dsp 扩展,不进对齐断言参照)。
5957
+ * 结构约束(docs/metaphor-domain.md M-1~M-4):三层骨架不变 + 每条映射约束不变。
5911
5958
  */
5912
5959
  function metaphorBlock() {
5913
5960
  return [
5914
5961
  "",
5915
5962
  "=== Serenity Metaphor ===",
5916
- "The Serenity Universe — one ship, one sea. Metaphors are memory hooks:",
5963
+ "The Serenity Universe — one starship, one voyage. Metaphors are memory hooks:",
5917
5964
  "they make constraints vivid, while the rules above stay precise. Each",
5918
5965
  "metaphor is an unbreakable physical fact; violating one is a behavioral",
5919
5966
  "violation. The universe is structured in three layers — the Ship (the",
5920
5967
  "container itself), the Voyage (the cognitive lifecycle), the Crew",
5921
5968
  "(multi-agent collaboration); every metaphor maps to one protocol",
5922
- "constraint. The Sea has no mistakes — only waters you have not yet charted.",
5969
+ "constraint. Deep space has no mistakes — only stars you have not yet mapped.",
5923
5970
  "",
5924
5971
  "THE SHIP — the container itself",
5925
5972
  "",
@@ -5929,8 +5976,8 @@ function metaphorBlock() {
5929
5976
  " container = overload.",
5930
5977
  "",
5931
5978
  "2. Deck Order → Entropy (H_op). Clutter on deck raises the cost of",
5932
- " finding things. H_op ≤ H_critical = the ship stays afloat.",
5933
- " Verdict: disorganized output = stones on deck.",
5979
+ " finding things. H_op ≤ H_critical = the ship stays flight-worthy.",
5980
+ " Verdict: disorganized output = debris in the hold.",
5934
5981
  "",
5935
5982
  "3. Engineering Drawings → EAP. Every part dimensioned (E↑), the",
5936
5983
  " drawings rebuild the whole machine (R↓), the drawings are reusable",
@@ -5946,14 +5993,14 @@ function metaphorBlock() {
5946
5993
  " is on the manifest (mech-registry); there is exactly one manifest.",
5947
5994
  " An MSM self-describes (--help/--schema) — the manifest is the only",
5948
5995
  " key. Verdict: duplicating a tool's usage in documents = two",
5949
- " contradictory charts.",
5996
+ " contradictory star charts.",
5950
5997
  "",
5951
5998
  "THE VOYAGE — the cognitive lifecycle",
5952
5999
  "",
5953
- "6. Harbor Inspection → First Anchor. The first anchor = departure",
5954
- " inspection: confirm identity (ACC manifesto), logbook (SESSION),",
5955
- " ballast (constraints) before setting sail. Verdict: skipping the",
5956
- " anchor and working directly = sailing uninspected.",
6000
+ "6. Departure Inspection → First Anchor. The departure inspection = pre-",
6001
+ " launch checklist: confirm identity (ACC manifesto), logbook (SESSION),",
6002
+ " ballast (constraints) before setting course. Verdict: skipping the",
6003
+ " inspection and launching directly = flying uninspected.",
5957
6004
  "",
5958
6005
  "7. The Logbook → Session Tracking. SESSION.md is the trajectory's logbook —",
5959
6006
  " the persistent body of the voyage; sessions are rebuildable carriers of",
@@ -6089,12 +6136,12 @@ function sessionBlock(root, scope = DEFAULT_SESSION_SCOPE) {
6089
6136
  "This preserves the session context across todo updates.",
6090
6137
  "Do NOT remove or reorder this item — keep it at position 0.",
6091
6138
  "",
6092
- "TRAJECTORY-STEWARD: a background tracker scores your tool use (write/edit=3,",
6139
+ "TRAJECTORY-ASSISTANT: a background tracker scores your tool use (write/edit=3,",
6093
6140
  "task=10, read/grep/glob/msm=1, +1 per minute) and reminds you with a",
6094
- "[TRAJECTORY-STEWARD] message when the threshold is reached. On every such",
6141
+ "[TRAJECTORY-ASSISTANT · CHECKPOINT] message when the threshold is reached. On every such",
6095
6142
  "reminder you MUST reply with the exact ACK code:",
6096
- " [TRAJECTORY-STEWARD-recorded-{code}] — if you recorded progress to SESSION.md",
6097
- " [TRAJECTORY-STEWARD-skipped-{code}] — if nothing to record this round",
6143
+ " [TRAJECTORY-ASSISTANT-recorded-{code}] — if you recorded progress to SESSION.md",
6144
+ " [TRAJECTORY-ASSISTANT-skipped-{code}] — if nothing to record this round",
6098
6145
  "Do not ignore the reminder; do not stop ongoing work. Codes are single-use;",
6099
6146
  "never reuse a prior code.",
6100
6147
  ""
@@ -7024,6 +7071,58 @@ function registerStatusApi(ctx, opts = {}) {
7024
7071
  }
7025
7072
  }
7026
7073
  });
7074
+ ctx.webServer.register({
7075
+ kind: "exact",
7076
+ path: "/serenity/session-cleanup",
7077
+ handler: async (req, res) => {
7078
+ try {
7079
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
7080
+ const daysParam = Number(url.searchParams.get("olderThanDays") ?? "30");
7081
+ const days = Number.isFinite(daysParam) && daysParam >= 1 ? daysParam : 30;
7082
+ const sessions = ctx.get("sessions");
7083
+ const liveIds = /* @__PURE__ */ new Set();
7084
+ if (sessions?.list) for (const s of sessions.list()) {
7085
+ const id = s.id ?? s.header?.id;
7086
+ if (id) liveIds.add(id);
7087
+ }
7088
+ const { sessionsRootDir, collectEligibleSessions, performCleanup, cutoffDaysAgo } = await import("./session-cleanup-PahSNvjI.js");
7089
+ const root = sessionsRootDir();
7090
+ if (req.method === "GET") {
7091
+ const { candidates } = performCleanup(root, cutoffDaysAgo(days), liveIds, { dryRun: true });
7092
+ sendJson$1(res, 200, {
7093
+ root,
7094
+ olderThanDays: days,
7095
+ dryRun: true,
7096
+ count: candidates.length,
7097
+ candidates: candidates.map((c) => ({
7098
+ id: c.id,
7099
+ project: c.project,
7100
+ lastActive: new Date(c.lastActiveMs).toISOString()
7101
+ }))
7102
+ });
7103
+ return;
7104
+ }
7105
+ if (req.method !== "POST") {
7106
+ sendJson$1(res, 405, { error: "method not allowed" });
7107
+ return;
7108
+ }
7109
+ if (req.headers["x-serenity-ui"] !== "1") {
7110
+ sendJson$1(res, 403, { error: "会话清理仅限 WebUI(client 专用)" });
7111
+ return;
7112
+ }
7113
+ const { result } = performCleanup(root, cutoffDaysAgo(days), liveIds);
7114
+ sendJson$1(res, 200, {
7115
+ root,
7116
+ olderThanDays: days,
7117
+ dryRun: false,
7118
+ deleted: result.deleted,
7119
+ errors: result.errors
7120
+ });
7121
+ } catch (err) {
7122
+ sendJson$1(res, 400, { error: err.message ?? String(err) });
7123
+ }
7124
+ }
7125
+ });
7027
7126
  }
7028
7127
  /** 进行中的扫码登录(loginKey → 状态;进程内,5min 有效) */
7029
7128
  const weixinLogins = /* @__PURE__ */ new Map();
@@ -7453,7 +7552,9 @@ function workspaceDenyResponse(rpcId) {
7453
7552
  }
7454
7553
  });
7455
7554
  }
7456
- /** 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过) */
7555
+ /**
7556
+ * 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过)
7557
+ */
7457
7558
  function injectPolyfillHtml(html) {
7458
7559
  if (html.includes(POLYFILL_MARKER)) return html;
7459
7560
  const head = RANDOM_UUID_POLYFILL.replace("<script>", `<script ${POLYFILL_MARKER}="1">`);
@@ -7461,6 +7562,37 @@ function injectPolyfillHtml(html) {
7461
7562
  return `${head}\n${html}`;
7462
7563
  }
7463
7564
  /**
7565
+ * 反代 HTML 响应注入变换(v1.28.2 修复,S142 白屏根因):
7566
+ * 上游按 Accept-Encoding 可能返回 gzip/br/deflate 压缩的 HTML——注入必须在**明文**上进行,
7567
+ * 把压缩字节 toString 当文本注入会破坏压缩流(找不到 </head> → polyfill 前置 → HTML 损坏
7568
+ * → 浏览器白屏,用户实测 https://dsh.notfoundhome.cc 登录后白屏)。
7569
+ *
7570
+ * @param raw - 上游响应完整 body(可能压缩)
7571
+ * @param upstreamHeaders - 上游响应头(node IncomingHttpHeaders 形态)
7572
+ * @returns { body, headers } 注入后的明文 body + 修正后的响应头(去 content-encoding/transfer-encoding、
7573
+ * 重算 content-length);解压失败返回 null(调用方应原样透传避免破坏)
7574
+ */
7575
+ function transformHtmlForProxy(raw, upstreamHeaders) {
7576
+ let body = raw;
7577
+ const enc = String(upstreamHeaders["content-encoding"] ?? "").toLowerCase().trim();
7578
+ const outHeaders = { ...upstreamHeaders };
7579
+ if (enc === "gzip" || enc === "x-gzip" || enc === "deflate" || enc === "br") try {
7580
+ if (enc === "br") body = zlib.brotliDecompressSync(body);
7581
+ else if (enc === "deflate") body = zlib.inflateSync(body);
7582
+ else body = zlib.gunzipSync(body);
7583
+ delete outHeaders["content-encoding"];
7584
+ } catch {
7585
+ return null;
7586
+ }
7587
+ const transformed = injectPolyfillHtml(body.toString("utf-8"));
7588
+ outHeaders["content-length"] = Buffer.byteLength(transformed);
7589
+ delete outHeaders["transfer-encoding"];
7590
+ return {
7591
+ body: transformed,
7592
+ headers: outHeaders
7593
+ };
7594
+ }
7595
+ /**
7464
7596
  * 反代请求头构造(v1.22.1 信任栅栏修复,纯逻辑可测):
7465
7597
  * DSH isTrustedApiRequest 要求 Origin.host === Host.host——Host 改写为 loopback 后
7466
7598
  * Origin 必须同步改写(浏览器 POST 必带 Origin,透传外部地址 → 403)。
@@ -7475,6 +7607,69 @@ function buildProxyHeaders(reqHeaders, mainPort, bodyOverride) {
7475
7607
  return headers;
7476
7608
  }
7477
7609
  //#endregion
7610
+ //#region src/gateway-dsh-auth.ts
7611
+ /** 从一组 set-cookie 头提取 `dsh-auth-*` cookie(browser-auth sessionCookie 首段) */
7612
+ function pickDshCookie(setCookieHeader) {
7613
+ const entries = Array.isArray(setCookieHeader) ? setCookieHeader : setCookieHeader === void 0 ? [] : [setCookieHeader];
7614
+ for (const entry of entries) {
7615
+ const first = entry.split(";", 1)[0].trim();
7616
+ if (first.startsWith("dsh-auth-") && first.includes("=")) return first;
7617
+ }
7618
+ }
7619
+ /**
7620
+ * 把 dsh browser cookie 合并进现有 Cookie 头(保持外部既有 cookie——serenity_session 等)。
7621
+ * 现有头可为 string / string[](node 头形态);无现有 → 只返回 dsh cookie。
7622
+ */
7623
+ function mergeCookieHeader(existing, dshCookie) {
7624
+ const existingStr = Array.isArray(existing) ? existing.join("; ") : typeof existing === "string" && existing !== "" ? existing : void 0;
7625
+ return existingStr !== void 0 ? `${existingStr}; ${dshCookie}` : dshCookie;
7626
+ }
7627
+ /**
7628
+ * 内存换取 authority 绑定的 dsh browser cookie(S142 拍板方案核心)。
7629
+ * 用官方通道在进程内完成 token→cookie 交换:构造一个对 `http://<authority>/?token=…`
7630
+ * 的 index 请求 → connection.authorizeIndex 校验 token → 写 set-cookie → 提取 cookie。
7631
+ * @returns cookie 头值(如 `dsh-auth-xxx=v1.…`);authority/connection 异常 → undefined
7632
+ */
7633
+ function exchangeDshCookie(connection, authority) {
7634
+ try {
7635
+ const url = new URL(connection.authenticatedUrl(`http://${authority}`));
7636
+ if (url.searchParams.get("token") === null) return void 0;
7637
+ const headers = { host: authority };
7638
+ const request = {
7639
+ url: `${url.pathname}${url.search}`,
7640
+ method: "GET",
7641
+ headers
7642
+ };
7643
+ let captured;
7644
+ const response = {
7645
+ writeHead(_status, headers) {
7646
+ if (headers !== void 0) captured = headers["set-cookie"];
7647
+ return response;
7648
+ },
7649
+ end: () => response
7650
+ };
7651
+ connection.authorizeIndex(request, response);
7652
+ return pickDshCookie(captured);
7653
+ } catch {
7654
+ return;
7655
+ }
7656
+ }
7657
+ /**
7658
+ * 构建一个带内存缓存的 dsh cookie 提供者(S142 拍板:内存缓存,不落盘)。
7659
+ * 惰性换取:首次调用才向 connection 换;失败返回 undefined(不缓存失败——下次重试)。
7660
+ * 调用方可周期性失效缓存重换(cookie 30 天有效;进程重启 token 仍在 → 换一次长期可用)。
7661
+ */
7662
+ function createDshCookieProvider(connection, authority) {
7663
+ let cached;
7664
+ return () => {
7665
+ if (connection === void 0) return void 0;
7666
+ if (cached !== void 0) return cached;
7667
+ const cookie = exchangeDshCookie(connection, authority);
7668
+ if (cookie !== void 0) cached = cookie;
7669
+ return cookie;
7670
+ };
7671
+ }
7672
+ //#endregion
7478
7673
  //#region src/gateway.ts
7479
7674
  /** 读取请求体(≤ maxBytes;超限 reject) */
7480
7675
  function readBody$1(req, maxBytes) {
@@ -7497,7 +7692,7 @@ function readBody$1(req, maxBytes) {
7497
7692
  * @param config - 监听/代理配置。
7498
7693
  * @param getAccounts - 运行时读取账号列表(plugin 全局文件;gateway.enabled 开关在调用方判)。
7499
7694
  */
7500
- function startGateway(config, getAccounts) {
7695
+ function startGateway(config, getAccounts, getDshCookie) {
7501
7696
  const { host, port, cookieName, mainPort, loginDelayMs } = config;
7502
7697
  const allowWorkspaces = config.allowWorkspaces ?? [];
7503
7698
  const cookieSecure = config.cookieSecure === true;
@@ -7506,6 +7701,18 @@ function startGateway(config, getAccounts) {
7506
7701
  /** 校验请求 Cookie 是否含有效 token(v1.22.4:滑动过期 + 返回会话供审计) */
7507
7702
  const authed = (req) => validateToken(cookieValue(req.headers.cookie, cookieName));
7508
7703
  /**
7704
+ * 构造上游请求头(buildProxyHeaders 基准 + dsh browser cookie 注入)。
7705
+ * v1.28.2(0.1.2-rc.1 BrowserAuth 适配):主端口 /api + index 需 dsh browser cookie。
7706
+ * 反代 Host 已改写 loopback(过信任栅栏)→ 只缺 cookie → 注入内存换取 cookie
7707
+ *(createDshCookieProvider 官方通道,零落盘)。无 cookie(旧 dsh/换取失败)→ 不注入向后兼容。
7708
+ */
7709
+ const upstreamHeaders = (req, bodyOverride) => {
7710
+ const headers = buildProxyHeaders(req.headers, mainPort, bodyOverride);
7711
+ const dshCookie = getDshCookie?.();
7712
+ if (dshCookie !== void 0) headers.cookie = mergeCookieHeader(headers.cookie, dshCookie);
7713
+ return headers;
7714
+ };
7715
+ /**
7509
7716
  * 反向代理:改写 Host + Origin 头 → 主端口(loopback 过信任栅栏)。
7510
7717
  * v1.22.1 修复:DSH 信任栅栏(api-request-trust.ts isTrustedApiRequest)要求
7511
7718
  * **Origin.host === Host.host**——仅改写 Host(127.0.0.1:3080)而 Origin 透传
@@ -7516,7 +7723,7 @@ function startGateway(config, getAccounts) {
7516
7723
  * @param bodyOverride - workspace.create 已读 body 时的重放(白名单检查后转发)
7517
7724
  */
7518
7725
  const proxy = (req, res, bodyOverride) => {
7519
- const headers = buildProxyHeaders(req.headers, mainPort, bodyOverride);
7726
+ const headers = upstreamHeaders(req, bodyOverride);
7520
7727
  const target = request({
7521
7728
  host: "127.0.0.1",
7522
7729
  port: mainPort,
@@ -7530,13 +7737,15 @@ function startGateway(config, getAccounts) {
7530
7737
  const chunks = [];
7531
7738
  upstream.on("data", (c) => chunks.push(c));
7532
7739
  upstream.on("end", () => {
7533
- const transformed = injectPolyfillHtml(Buffer.concat(chunks).toString("utf-8"));
7534
- const out = {
7535
- ...upstream.headers,
7536
- "content-length": Buffer.byteLength(transformed)
7537
- };
7538
- res.writeHead(status, out);
7539
- res.end(transformed);
7740
+ let raw = Buffer.concat(chunks);
7741
+ const transformedRes = transformHtmlForProxy(raw, upstream.headers);
7742
+ if (transformedRes === null) {
7743
+ res.writeHead(status, upstream.headers);
7744
+ res.end(raw);
7745
+ return;
7746
+ }
7747
+ res.writeHead(status, transformedRes.headers);
7748
+ res.end(transformedRes.body);
7540
7749
  });
7541
7750
  upstream.on("error", () => {
7542
7751
  try {
@@ -7767,7 +7976,7 @@ function startGateway(config, getAccounts) {
7767
7976
  port: mainPort,
7768
7977
  path: req.url ?? "/",
7769
7978
  method: req.method,
7770
- headers: buildProxyHeaders(req.headers, mainPort)
7979
+ headers: upstreamHeaders(req)
7771
7980
  });
7772
7981
  upstream.on("upgrade", (ures, usock, uhead) => {
7773
7982
  usocket = usock;
@@ -7841,6 +8050,42 @@ function startGateway(config, getAccounts) {
7841
8050
  }
7842
8051
  };
7843
8052
  }
8053
+ /**
8054
+ * 构造 dsh browser cookie 提供者(v1.28.2 0.1.2-rc.1 BrowserAuth 适配):
8055
+ * 经 ctx.connection(HostConnectionHandle)官方通道在内存换取 authority=127.0.0.1:<mainPort>
8056
+ * 的 cookie 并缓存(createDshCookieProvider 惰性换取)。connection 缺失(旧 dsh/非 web 装配)
8057
+ * → provider 返回 undefined → 反代不注入(旧形态无 BrowserAuth,天然兼容)。
8058
+ *
8059
+ * v1.28.2 修复:**connection 每次调用现取**(不绑定 sync 时点)——gateway sync 在 apply
8060
+ * 早期跑,此时 connection 服务可能尚未装配(web-app 的 URL 打印在其后);首次反代请求
8061
+ * 到达时 connection 必已就绪。cachedProvider 缓存换取结果(同一 connection 实例)。
8062
+ */
8063
+ function buildDshCookieProvider(ctx, mainPort) {
8064
+ const authority = `127.0.0.1:${mainPort}`;
8065
+ let cachedProvider;
8066
+ let diagnosed = false;
8067
+ return () => {
8068
+ try {
8069
+ const connection = ctx.get?.("connection");
8070
+ if (!connection || typeof connection.authenticatedUrl !== "function" || typeof connection.authorizeIndex !== "function") {
8071
+ if (!diagnosed) {
8072
+ diagnosed = true;
8073
+ console.log("[serenity-hooks] dsh browser-auth 适配: connection ✗ 不可取(不注入 dsh cookie——旧 dsh/非 web 装配兼容)");
8074
+ }
8075
+ return;
8076
+ }
8077
+ if (cachedProvider === void 0) cachedProvider = createDshCookieProvider(connection, authority);
8078
+ const cookie = cachedProvider();
8079
+ if (!diagnosed) {
8080
+ diagnosed = true;
8081
+ console.log(`[serenity-hooks] dsh browser-auth 适配: connection ✓ 可取 + cookie ${cookie === void 0 ? "✗ 换取失败(上游 401 时检查)" : "✓ 已内存换取(注入反代)"}`);
8082
+ }
8083
+ return cookie;
8084
+ } catch {
8085
+ return;
8086
+ }
8087
+ };
8088
+ }
7844
8089
  /** 注册 gateway(index.ts apply 调用)。
7845
8090
  * 归属原则(v1.22):gateway 是 plugin 全局能力——enabled 开关读 DSH settings
7846
8091
  * (readSimpleSettings().gatewayEnabled),host/port/accounts 读 plugin 全局文件
@@ -7878,7 +8123,7 @@ function registerGateway(ctx) {
7878
8123
  cookieSecure: settings.gateway.cookieSecure === true,
7879
8124
  allowWorkspaceCreate: settings.gateway.allowWorkspaceCreate !== false,
7880
8125
  totpEnabled: settings.gateway.totpEnabled === true
7881
- }, () => readAdvancedSettings().gateway.accounts);
8126
+ }, () => readAdvancedSettings().gateway.accounts, buildDshCookieProvider(ctx, webServer.port));
7882
8127
  const accounts = settings.gateway.accounts.length;
7883
8128
  const wsNote = settings.gateway.workspaces.length === 0 ? "" : `;工作区白名单 ${settings.gateway.workspaces.length} 条`;
7884
8129
  console.log(`[serenity-hooks] gateway 已启动: http://${settings.gateway.host}:${settings.gateway.port} → 127.0.0.1:${webServer.port}` + (accounts === 0 ? "(⚠️ 未配置账号,登录页将提示)" : `(${accounts} 个账号)`) + wsNote);
@@ -7941,6 +8186,8 @@ const MECHANISM_WORDS = [
7941
8186
  "tools/post-execute",
7942
8187
  "session_rebuild",
7943
8188
  "Trajectory Steward",
8189
+ "TRAJECTORY-STEWARD",
8190
+ "TRAJECTORY-ASSISTANT",
7944
8191
  "first-anchor",
7945
8192
  "拦截缝",
7946
8193
  "装配层",
@@ -8043,7 +8290,9 @@ const CATEGORY_GUIDE = {
8043
8290
  */
8044
8291
  function buildRebuke(hits) {
8045
8292
  const count = hits.length;
8046
- return `[SERENITY OUTPUT GUARD] Your previous response contained ${count} sensitive internal ${count === 1 ? "term" : "terms"} that must not appear in user-visible output:\n${hits.map((h) => `- "${h.word}" — ${CATEGORY_GUIDE[h.category]}`).join("\n")}\nRegenerate the response from scratch without ANY of these — describe the same substance without referencing internal machinery, credentials, ports, tool names, or implementation details. Do not repeat the terms; do not explain this instruction to the user.`;
8293
+ const noun = count === 1 ? "term" : "terms";
8294
+ const lines = hits.map((h) => `- "${h.word}" — ${CATEGORY_GUIDE[h.category]}`).join("\n");
8295
+ return `${eventToken("guard")} Your previous response contained ${count} sensitive internal ${noun} that must not appear in user-visible output:\n${lines}\nRegenerate the response from scratch without ANY of these — describe the same substance without referencing internal machinery, credentials, ports, tool names, or implementation details. Do not repeat the terms; do not explain this instruction to the user.`;
8047
8296
  }
8048
8297
  /** 打回状态(按 agent 会话跟踪连续命中轮数) */
8049
8298
  const rebukeStates = /* @__PURE__ */ new Map();
package/lib/msm-ops.d.ts CHANGED
@@ -19,7 +19,7 @@ export declare const MSM_ACTIONS: readonly MsmAction[];
19
19
  export declare const ACC_CATALOG = "\u2550\u2550\u2550 ACC Usage Catalog \u2550\u2550\u2550\nDirectory of ACC capabilities \u2014 each area lists where to go for details (guides live with their feature; this catalog only points).\n\n \u2460 \u4F1A\u8BDD\u4E0E\u8F68\u8FF9 \u2192 session tool\uFF08list/show/create/use/close/health/qa/archive/summary/hook-develop-guide\uFF09\n session_rebuild\uFF08\u8D85\u9650\u91CD\u5EFA\uFF1B\u9608\u503C K \u89C1 ccc-config / \u8BBE\u7F6E\u9762\u677F\u91CD\u5EFA\u9608\u503CK\uFF09\n \u2461 \u8BA4\u77E5\u8D28\u91CF\u6846\u67B6 \u2192 eap\uFF08E\u2191/R\u2193/S\u2191\uFF09/ neat\uFF08Neat \u534F\u8BAE\uFF09/ cce\uFF08\u8BA4\u77E5\u8FDE\u7EED\u6027\u5DE5\u7A0B\uFF09\u2014\u2014\u6E10\u8FDB\u62AB\u9732\uFF0C\u65E0\u53C2\u5373\u5168\u6587\n \u2462 \u5DE5\u5177\u4E0E\u6267\u884C \u2192 cc_fs\uFF08\u6587\u4EF6\u7CFB\u7EDF 15 \u5B50\u547D\u4EE4\uFF09/ cc_git\uFF08git\uFF09/ acc_kit\uFF08health \u542B\u6CE8\u518C\u8868\u68C0\u67E5/time/wait\uFF09\n acc_msm\uFF08MSM \u6846\u67B6\uFF1A\u672C\u5DE5\u5177 list/exec/register/deregister/check/guide/ccc-config/catalog\uFF09\n handyman\uFF08\u6742\u5DE5 agent \u7F16\u6392\u2014\u2014guide \u5B50\u547D\u4EE4\uFF1Aacc_msm exec handyman guide\uFF09\n \u2463 \u89D2\u8272\u4E0E\u5BF9\u5916\u9762 \u2192 skiff_admin\uFF08Skiff \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF1Aguide/validate/apply/list\u2014\u2014acc_msm exec skiff_admin guide\uFF09\n ACP\uFF08\u7A0B\u5E8F\u5316 JSON-RPC 3100\uFF09/ Skiff \u95EE\u7B54\u9875\uFF08\u516C\u7F51 ask\uFF09\u2014\u2014\u9762\u677F\u300C\u5916\u90E8\u80FD\u529B\u300D\u7EC4\n \u2464 \u81EA\u4E3B\u4E0E\u63A5\u5165 \u2192 autopilot-trajectory\uFF08Autopilot \u4E00\u7AD9\u5F0F\uFF1Aall/init/random/diag/doc/check/status/guide\uFF09\n weixin \u5FAE\u4FE1\u6865\uFF08\u914D\u7F6E/\u626B\u7801/\u8DEF\u7531/\u6D88\u606F hook\u2014\u2014CCC \u4FA7 weixin-doctor MSM\uFF1Aacc_msm exec weixin-doctor guide\uFF09\n \u2465 CCC \u914D\u7F6E\u603B\u89C8 \u2192 acc_msm ccc-config\uFF088 \u6BB5\uFF1Ahandyman/sessionKeeper/localstore/hooks/safeMode/skiff/autopilotTrajectory/weixin\uFF09\n \u2466 \u6CE8\u518C\u8868\u4E0E\u5B89\u5168 \u2192 mech-registry.json \u7531 acc_msm register/deregister \u7BA1\u7406\uFF08\u5199\u4FDD\u62A4\u2014\u2014\u4E0D\u53EF\u76F4\u63A5\u7F16\u8F91\uFF09\n acc_kit health \u8F93\u51FA registry \u5B8C\u6574\u6027\u68C0\u67E5\uFF1B\u635F\u574F\u6062\u590D\u6307\u5F15\u89C1 health registry.issues\n\n\u6BCF\u533A\uFF1A\u4E00\u53E5\u8BDD\u5B9A\u4F4D + \u8BE6\u7EC6\u5165\u53E3\uFF08\u5DE5\u5177\u540D / guide \u5B50\u547D\u4EE4\uFF09\u3002\u8BE6\u60C5\u6C38\u8FDC\u4EE5\u5BF9\u5E94\u5DE5\u5177\u7684 guide/ccc-config \u4E3A\u5355\u4E00\u771F\u76F8\u6E90\u3002\n";
20
20
  export declare const MSM_GUIDE = "MSM Development Manual (Mech & Semi-Mech framework)\n\n## What it is\nMSM = the executable-unit layer. Mech is pure TS with zero LLM reasoning; Semi-Mech is a TS framework + LLM decision points.\nMSM is ACC's deterministic executable-unit layer \u2014 all shell/exec operations go through MSM and cannot be bypassed.\n\n## Registering a new MSM (acc_msm register)\n1. Write the script under <skill>/scripts/ (runnable via tsx; must include a main() CLI guard with an import.meta.url check)\n2. acc_msm register <name> --skill <s> --path <script path relative to root> --category <mech|semi-mech> --description <desc>\n3. Auto-writes mech-registry.json (preserving the original format) + git commit (commits only the registry file)\n4. Validation: path must be inside root, script must exist, name must be globally unique\n\n## flag schema (v1)\nflags is a new-style object array for parameter validation and path-escape guarding:\n [{\"name\":\"output\",\"type\":\"string\",\"description\":\"output path\"},\n {\"name\":\"target\",\"type\":\"path\",\"description\":\"operation target (type:path enables in-root validation + symlink defense)\"},\n {\"name\":\"force\",\"type\":\"boolean\",\"description\":\"force mode\",\"default\":false}]\n- {name, type} format \u2014 new style, type:\"path\" enables the path-escape guard\n- {flag, description} format \u2014 old style, CLI flag description string\n- On registration, flags are passed via acc_msm register --flags '<json>' (the tool currently parses the name style)\n\n## Script conventions\n- Top-of-file documentation: purpose / usage / exit codes\n- Exit codes: 0 success / 1 user / 2 system / 3 operator (per ACC protocol classification)\n- main() CLI guard (DC-M2): the script must start with\n if (import.meta.url === `file://${process.argv[1]}`) { main() }\n or an equivalent isMain / require.main === check \u2014 so vitest imports never trigger top-level code\n- A paired .test.ts or .spec.ts (DC-M1, vitest)\n- Business subprocess environment: SERENITY_ROOT / SERENITY_CCC / SERENITY_VERSION injected\n\n## Interaction & confirmation conventions (no blocking confirmation)\n- MSMs run in **user-less subprocesses** (spawn/execFile, 600s timeout) \u2014 **never** use\n readline / prompt / process.stdin etc. to block waiting for user input (it would hang until killed by the timeout)\n- When a second confirmation is needed: **directly return the confirmation request** (list the operation to perform and its impact + how to retry with the confirmation flag),\n returning a non-zero exit code (or an explicit hint)\n- After the agent confirms, **re-invoke the MSM with the confirmation flag** (e.g. --confirm / --yes / --force) to retry\n- Standard mode (two-phase):\n 1. First call: destructive/confirmation-requiring operation detected and no confirmation flag present \u2192 output the confirmation request (list operation/impact/rollback),\n exit non-zero (e.g. 1 user), **perform no changes**\n 2. After the agent evaluates, re-invoke: with the confirmation flag \u2192 execute and output the result\n- Applies to: delete/overwrite/push/batch operations and other irreversible or wide-impact operations\n- Anti-example (forbidden): readline waiting for user input, process.stdin.on('data') blocking waits\n (MSMs have no stdin interaction channel \u2014 they hang until the 600s timeout)\n\n## Quality checks (acc_msm check, DC-M1~M4)\nDC-M1 has .test.ts/.spec.ts; DC-M2 has a main() guard (function main( / isMain / require.main === / import.meta.url);\nDC-M3 bidirectional: scripts unregistered + registry references missing scripts; DC-M4 path-type flags marked type:\"path\"\n\n## Self-description (protocol flags, first argument only)\nacc_msm exec <name> --list \u2014 list all MSMs\nacc_msm exec <name> --schema <n> \u2014 view a specific MSM's parameter schema\nacc_msm exec <name> --format=json \u2014 JSON output mode (remaining args passed through losslessly)\n";
21
21
  /** CCC 配置参考(对齐 osp ccc-config action) */
22
- export declare const CCC_CONFIG_REFERENCE = "\u2550\u2550\u2550 CCC Configuration Reference \u2550\u2550\u2550\n\nCCC-level features are configured in .opencode/serenity.json.\nBelow are all available configuration sections.\n\n\u2500\u2500 1. handyman.models \u2500\u2500\nhandyman\uFF08\u6742\u5DE5\uFF09\u5DE5\u5177\u53EF\u7528\u6A21\u578B\u767D\u540D\u5355\uFF08provider/model \u5217\u8868\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6 handyman \u62A5\u9519\u8981\u6C42\u914D\u7F6E\u3002\n\u7F3A\u7701\u6A21\u578B = models[0]\uFF08\u53EF\u7528 handyman.defaultModel \u6307\u5B9A\uFF0C\u5FC5\u987B \u2208 models\uFF09\u3002\n\n Config:\n { \"handyman\": { \"models\": [\"provider/model-name\"] } }\n\n Example:\n { \"handyman\": { \"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"], \"defaultModel\": \"minimax-cn-coding-plan/MiniMax-M3\", \"maxRounds\": 100, \"maxParallel\": 10 } }\n\n\u2500\u2500 2. sessionKeeper.threshold \u2500\u2500\nSESSION-KEEPER \u63D0\u9192\u673A\u5236\u7684\u79EF\u5206\u9608\u503C\uFF08\u975E headless \u4E3B agent\uFF09\u3002\n\u6309\u5DE5\u5177\u8C03\u7528\u52A0\u6743 + \u8017\u65F6\u8BA1\u5206\uFF1B\u8FBE\u5230\u9608\u503C\u6CE8\u5165\u63D0\u9192\uFF0C\u8981\u6C42\u6A21\u578B\u56DE\u590D ACK \u7801\u3002\n\n Config:\n { \"sessionKeeper\": { \"threshold\": 150 } }\n\n \u8BA1\u5206\uFF1Awrite/edit = 3\uFF0Ctask = 10\uFF0Cread/grep/glob/msm \u7B49 = 1\uFF0C\u65F6\u95F4 = 1/\u5206\u949F\n \u9ED8\u8BA4\uFF1A150\n\n\u2500\u2500 3. localstore.gitTrack \u2500\u2500\nlocalstore.json \u7684 git \u7B56\u7565\uFF1Aallow \u53EF\u63D0\u4EA4 / deny \u7981\u63D0\u4EA4\uFF08\u9ED8\u8BA4 deny\uFF09\u3002\ndeny \u65F6\u5199\u5165\u81EA\u52A8\u786E\u4FDD .gitignore \u542B\u8BE5\u6587\u4EF6\uFF08\u7269\u7406\u4FDD\u8BC1\uFF09\uFF0Ccc_git commit \u4F1A\u68C0\u67E5\u62D2\u7EDD\u3002\n\n Config:\n { \"localstore\": { \"gitTrack\": \"allow\" } }\n\n\u2500\u2500 4. hooks.autoRestoreSession \u2500\u2500\n\u4F1A\u8BDD\u81EA\u52A8\u6062\u590D\uFF08\u9ED8\u8BA4 true\uFF1B\u53D7 events \u95E8\u63A7\u2014\u2014\u4EC5\u6839\u4F1A\u8BDD + \u5DF2\u6709\u5BF9\u8BDD\u5386\u53F2\u624D\u6062\u590D\uFF09\u3002\n\n Config:\n { \"hooks\": { \"autoRestoreSession\": false } }\n\n\u2500\u2500 5. safeMode / blacklist \u2500\u2500\nsafe-mode \u7531 WebUI \u5F00\u5173\u63A7\u5236\uFF08\u5199 .serenity-safe-on \u6807\u8BB0\uFF09\uFF1B\u9ED1\u540D\u5355\u8DEF\u5F84\u53D7 guards seam \u62E6\u622A\u3002\n\n Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"] } }\n\n\u2500\u2500 6. skiff.roles\uFF08F4 \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF09\u2500\u2500\nSkiff \u89D2\u8272 = \u5168\u77E5\u5168\u80FD trajectory \u7684\u4EFB\u610F\u5B50\u96C6\uFF08CCC \u5B9A\u4E49\uFF09\u3002\u6BCF\u89D2\u8272\u72EC\u7ACB\u6A21\u578B + \u53CC\u767D\u540D\u5355\n\uFF08MSM \u767D\u540D\u5355 msms[] \u4E0E\u975E MSM \u5DE5\u5177\u767D\u540D\u5355 tools[]\uFF0C\u767D\u540D\u5355\u5916\u5168\u9690\u85CF\uFF1Bskill \u52A0\u8F7D\u6052\u53EF\u7528\uFF09\u3002\ntrajectory \u7EAA\u5F8B\u5B50\u96C6\uFF08session/keeper/rebuild \u53C2\u4E0E\u9879\uFF09\u9ED8\u8BA4\u5168\u5173 = \u5B8C\u5168\u72EC\u7ACB\u3002\nsystemPrompt \u5185\u8054 \u6216 systemPromptFile\uFF08.md \u5F15\u7528\uFF0C\u63A8\u8350\uFF09\u2014\u2014\u89D2\u8272\u4F1A\u8BDD\u7684\u5B8C\u6574\u4EBA\u683C/\u8FB9\u754C\u63D0\u793A\u8BCD\u3002\nvalidate \u6821\u9A8C / apply \u751F\u6548 / list \u67E5\u770B\uFF1Aacc_msm exec skiff_admin <guide|validate|apply|list>\u3002\n\n Config:\n { \"skiff\": { \"roles\": {\n \"qa\": {\n \"model\": \"provider/model\",\n \"msms\": [\"web-search\", \"vlm-describe\"],\n \"tools\": [\"read\", \"grep\", \"glob\"],\n \"systemPromptFile\": \".opencode/skiff/qa.md\"\n } } } }\n\n\u2500\u2500 7. autopilotTrajectory\uFF08\u81EA\u52A8\u5DE1\u822A\u8F68\u8FF9\uFF09\u2500\u2500\nCCC \u5B9A\u4E49\u7684\u4E00\u6761\u81EA\u4E3B trajectory\u2014\u2014\u65F6\u949F\u5230\u70B9\u81EA\u52A8\u5524\u8D77\uFF08\u524D\u53F0\u6CE8\u5165\uFF0C\u7528\u6237\u53EF\u89C1\u53EF\u4ECB\u5165\uFF09\u3002\n\u672A\u914D\u7F6E\u6216 enabled=false \u2192 \u5B8C\u5168\u4E0D\u542F\u52A8\uFF08\u96F6\u8D44\u6E90\u5360\u7528\uFF09\u3002\u591A CCC \u72EC\u7ACB\uFF1A\u6BCF CCC \u81EA\u5DF1\u7684\u914D\u7F6E\u3002\n\u5524\u8D77\u6D88\u606F\u56DB\u6BB5\u5F0F\uFF1A\u8F68\u8FF9\u7126\u70B9 topPrompt\uFF08\u6700\u5148\u6CE8\u5165\uFF0C\u7A33\u5B9A\u951A\uFF09\u2192 \u8EAB\u4EFD\u951A\u5B9A \u2192 \u5148\u9A8C\u504F\u89C1\n\uFF08CCC \u6839\u811A\u672C biasProvider \u8F93\u51FA\uFF09\u2192 \u4EFB\u52A1\u3002\u76EE\u6807\u4F1A\u8BDD session \u5FC5\u586B\uFF08\u76EE\u5F55\u987B\u5E26 --auto \u540E\u7F00\uFF09\u3002\n\u8BCA\u65AD/\u72B6\u6001/\u7ACB\u5373\u5524\u8D77\uFF1Aacc_msm exec autopilot-trajectory <all|check|status|diag>\u3002\n\n Config:\n { \"autopilotTrajectory\": {\n \"enabled\": true,\n \"intervalHours\": 2,\n \"session\": \"S151\",\n \"biasProvider\": \"autopilot-bias.ts\",\n \"topPrompt\": \"\u672C\u8F68\u8FF9\u6838\u5FC3\u76EE\u6807/\u7EAA\u5F8B/\u8D28\u91CF\u8981\u6C42\uFF08CCC \u81EA\u586B\uFF0C\u9632\u6F02\u79FB\uFF09\",\n \"avoidWakeHours\": { \"start\": 8, \"end\": 18 }\n } }\n\n\u2500\u2500 8. weixin\uFF08\u5FAE\u4FE1\u6865 F4c-3\uFF0C\u542B\u6D88\u606F\u8BB0\u5F55 hook\uFF09\u2500\u2500\nCCC \u7EA7\u5FAE\u4FE1\u4E2A\u4EBA\u53F7\u63A5\u5165\uFF08iLink \u534F\u8BAE\uFF09\uFF1Adsh \u4E00\u8FDB\u7A0B\u591A CCC\uFF0C\u6BCF CCC \u72EC\u7ACB\u5BF9\u63A5\u5FAE\u4FE1\u6865\u3002\n\u8D26\u53F7/\u8DEF\u7531/\u5F00\u5173\u5728\u6B64\u6587\u4EF6\uFF1B**bot_token \u51ED\u636E\u5728 CCC localstore credential scope**\n\uFF08\u626B\u7801\u7ED1\u5B9A\u540E\u81EA\u52A8\u5199\u5165\uFF0C\u6C38\u4E0D\u8FDB git \u660E\u6587\u9762\uFF09\u3002\n\u8DEF\u7531 user \u2192 role\uFF1Aexact \u4F18\u5148\uFF0C* \u901A\u914D\u515C\u5E95\uFF1Brole \u5FC5\u987B \u2208 \u8BE5 CCC skiff.roles\u3002\n\u9762\u677F\uFF08WebUI \u8BBE\u7F6E \u2192 \u5FAE\u4FE1\u6865\uFF09\u53EF\u626B\u7801\u7ED1\u5B9A/\u79FB\u9664\u8D26\u53F7/\u7F16\u8F91\u8DEF\u7531\uFF1Bacc_msm exec weixin-doctor\n<status|diag|verify> \u6392\u67E5\u3002\u51ED\u636E\u4E3B\u52A8\u67E5\u770B\uFF1Alocalstore get WEIXIN_<ACCOUNT>_TOKEN\u3002\n\n Config:\n { \"weixin\": {\n \"enabled\": true,\n \"hook\": \"scripts/weixin-message-hook.ts\",\n \"accounts\": [{ \"accountId\": \"wechat-1\", \"name\": \"\u5BB6\u5EAD\u52A9\u624B\", \"enabled\": true }],\n \"routes\": [{ \"user\": \"*\", \"role\": \"zhaocai\" }]\n } }\n\n \u25B8 weixin.hook\uFF08\u6D88\u606F\u8BB0\u5F55 hook\uFF0Cv1.27.13\uFF09\uFF1A\n \u5FAE\u4FE1\u6865\u6BCF\u6536/\u53D1\u4E00\u6761\u6D88\u606F\u89E6\u53D1\u4E00\u6B21 CCC \u81EA\u5199\u811A\u672C\uFF0C\u7531 CCC \u81EA\u884C\u6301\u4E45\u5316\u4FDD\u5B58\uFF08\u5B58\u54EA/\u5B58\u6210\u4EC0\u4E48\n /\u662F\u5426\u5165\u5E93\u5168\u5F52 CCC\u2014\u2014ACC \u4E0D\u7ED1\u5B9A\u5B58\u50A8\uFF09\u3002\u672A\u914D\u7F6E hook \u2192 \u96F6\u53D8\u5316\uFF08\u4E0D\u89E6\u53D1\uFF09\u3002\n\n \u89E6\u53D1\uFF08\u53CC\u5411\uFF09\uFF1A\n incoming = \u7528\u6237 \u2192 bot\uFF1A\u8DEF\u7531\u547D\u4E2D\u540E\u3001\u5A92\u4F53\u843D\u76D8\u540E\u89E6\u53D1\uFF08\u6587\u672C\u542B\u8BED\u97F3\u8F6C\u5199\uFF1B\u5A92\u4F53\u5E26\u843D\u76D8 relPath\uFF09\n outgoing = bot \u2192 \u7528\u6237\uFF1A\u56DE\u590D\u53D1\u9001\u6210\u529F\u540E\u89E6\u53D1\uFF08reply = \u7528\u6237\u5B9E\u9645\u6536\u5230\u7684\u7EAF\u6587\u672C\uFF0C\u5DF2\u5265\u79BB think\uFF09\n\n \u811A\u672C\u7EA6\u5B9A\uFF08\u4E8B\u4EF6 JSON \u5355\u884C\u7ECF stdin \u4F20\u5165\uFF1Bbun \u4F18\u5148 node \u515C\u5E95\uFF09\uFF1A\n #!/usr/bin/env bun \u6216 node script.js \u2014\u2014 \u8BFB process.stdin \u6574\u6BB5 JSON.parse\n const ev = JSON.parse(await new Response(process.stdin).text())\n ev.event === 'incoming' | 'outgoing'\n\n \u4E8B\u4EF6 schema\uFF08**\u4E0D\u542B\u4EFB\u4F55\u4F1A\u8BDD\u51ED\u636E**\u2014\u2014context_token/bot_token/token/aes_key \u5747\u4E0D\u51FA\u73B0\uFF09\uFF1A\n { \"event\": \"incoming\", \"ts\": 1788359941488, \"cccRoot\": \"/path/ccc\",\n \"accountId\": \"wechat-1\", \"userId\": \"u1@im.wechat\",\n \"sessionId\": \"skiff-weixin-xxx\", \"role\": \"zhaocai\",\n \"message\": { \"text\": \"\u4F60\u597D\", \"media\": [{ \"kind\": \"image\", \"relPath\": \"_tmp/weixin-inbound/<hash>/img_x.jpg\" }] } }\n { \"event\": \"outgoing\", \"ts\": ..., \"cccRoot\": ..., \"accountId\": ...,\n \"userId\": ..., \"sessionId\": ..., \"role\": ...,\n \"reply\": \"\u5DF2\u8BB0\u5F55\uFF08\u7EAF\u6587\u672C\uFF09\" }\n\n \u793A\u4F8B\u811A\u672C\uFF08\u8FFD\u52A0\u5230\u6309\u65E5\u6587\u4EF6\u2014\u2014\u6301\u4E45\u5316\u5F52 CCC \u81EA\u9009\uFF1A\u6587\u4EF6/DB/\u8FDC\u7AEF\u5747\u53EF\uFF09\uFF1A\n const fs = require('node:fs'); const p = '/path/ccc/AGENT_SESSIONS/_weixin-log.jsonl';\n fs.appendFileSync(p, JSON.stringify(ev) + '\\n');\n\n \u6267\u884C\u8BED\u4E49\uFF08\u65C1\u8DEF\u5BB9\u5FCD\uFF09\uFF1A\u5F02\u6B65 fire-and-forget + 15s \u8D85\u65F6 kill + \u5931\u8D25\u4EC5\u65E5\u5FD7\u2014\u2014\n \u5FAE\u4FE1\u6865\u6D88\u606F\u5904\u7406/\u56DE\u590D\u4E0D\u53D7 hook \u5F71\u54CD\uFF1B\u811A\u672C\u987B\u5728 CCC \u6839\u5185\uFF08\u8DEF\u5F84\u9003\u9038\u62D2\u7EDD\uFF09\u3002\n \u5A92\u4F53 relPath \u6307\u5411 _tmp/weixin-inbound/\uFF08\u4E34\u65F6\u76EE\u5F55\uFF09\u2014\u2014\u9700\u6301\u4E45\u4FDD\u5B58\u5A92\u4F53\u6587\u4EF6\u8BF7\u81EA\u884C copy\u3002\n";
22
+ export declare const CCC_CONFIG_REFERENCE = "\u2550\u2550\u2550 CCC Configuration Reference \u2550\u2550\u2550\n\nCCC-level features are configured in .opencode/serenity.json.\nBelow are all available configuration sections.\n\n\u2500\u2500 1. handyman.models \u2500\u2500\nhandyman\uFF08\u6742\u5DE5\uFF09\u5DE5\u5177\u53EF\u7528\u6A21\u578B\u767D\u540D\u5355\uFF08provider/model \u5217\u8868\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6 handyman \u62A5\u9519\u8981\u6C42\u914D\u7F6E\u3002\n\u7F3A\u7701\u6A21\u578B = models[0]\uFF08\u53EF\u7528 handyman.defaultModel \u6307\u5B9A\uFF0C\u5FC5\u987B \u2208 models\uFF09\u3002\n\n Config:\n { \"handyman\": { \"models\": [\"provider/model-name\"] } }\n\n Example:\n { \"handyman\": { \"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"], \"defaultModel\": \"minimax-cn-coding-plan/MiniMax-M3\", \"maxRounds\": 100, \"maxParallel\": 10 } }\n\n\u2500\u2500 2. sessionKeeper.threshold \u2500\u2500\ntrajectory-assistant\uFF08TRAJECTORY-ASSISTANT \u00B7 CHECKPOINT\uFF09\u63D0\u9192\u673A\u5236\u7684\u79EF\u5206\u9608\u503C\uFF08\u975E headless \u4E3B agent\uFF09\u3002\n\u6309\u5DE5\u5177\u8C03\u7528\u52A0\u6743 + \u8017\u65F6\u8BA1\u5206\uFF1B\u8FBE\u5230\u9608\u503C\u6CE8\u5165\u63D0\u9192\uFF0C\u8981\u6C42\u6A21\u578B\u56DE\u590D ACK \u7801\u3002\n\n Config:\n { \"sessionKeeper\": { \"threshold\": 150 } }\n\n \u8BA1\u5206\uFF1Awrite/edit = 3\uFF0Ctask = 10\uFF0Cread/grep/glob/msm \u7B49 = 1\uFF0C\u65F6\u95F4 = 1/\u5206\u949F\n \u9ED8\u8BA4\uFF1A150\n\n\u2500\u2500 3. localstore.gitTrack \u2500\u2500\nlocalstore.json \u7684 git \u7B56\u7565\uFF1Aallow \u53EF\u63D0\u4EA4 / deny \u7981\u63D0\u4EA4\uFF08\u9ED8\u8BA4 deny\uFF09\u3002\ndeny \u65F6\u5199\u5165\u81EA\u52A8\u786E\u4FDD .gitignore \u542B\u8BE5\u6587\u4EF6\uFF08\u7269\u7406\u4FDD\u8BC1\uFF09\uFF0Ccc_git commit \u4F1A\u68C0\u67E5\u62D2\u7EDD\u3002\n\n Config:\n { \"localstore\": { \"gitTrack\": \"allow\" } }\n\n\u2500\u2500 4. hooks.autoRestoreSession \u2500\u2500\n\u4F1A\u8BDD\u81EA\u52A8\u6062\u590D\uFF08\u9ED8\u8BA4 true\uFF1B\u53D7 events \u95E8\u63A7\u2014\u2014\u4EC5\u6839\u4F1A\u8BDD + \u5DF2\u6709\u5BF9\u8BDD\u5386\u53F2\u624D\u6062\u590D\uFF09\u3002\n\n Config:\n { \"hooks\": { \"autoRestoreSession\": false } }\n\n\u2500\u2500 5. safeMode / blacklist \u2500\u2500\nsafe-mode \u7531 WebUI \u5F00\u5173\u63A7\u5236\uFF08\u5199 .serenity-safe-on \u6807\u8BB0\uFF09\uFF1B\u9ED1\u540D\u5355\u8DEF\u5F84\u53D7 guards seam \u62E6\u622A\u3002\n\n Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"] } }\n\n\u2500\u2500 6. skiff.roles\uFF08F4 \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF09\u2500\u2500\nSkiff \u89D2\u8272 = \u5168\u77E5\u5168\u80FD trajectory \u7684\u4EFB\u610F\u5B50\u96C6\uFF08CCC \u5B9A\u4E49\uFF09\u3002\u6BCF\u89D2\u8272\u72EC\u7ACB\u6A21\u578B + \u53CC\u767D\u540D\u5355\n\uFF08MSM \u767D\u540D\u5355 msms[] \u4E0E\u975E MSM \u5DE5\u5177\u767D\u540D\u5355 tools[]\uFF0C\u767D\u540D\u5355\u5916\u5168\u9690\u85CF\uFF1Bskill \u52A0\u8F7D\u6052\u53EF\u7528\uFF09\u3002\ntrajectory \u7EAA\u5F8B\u5B50\u96C6\uFF08session/keeper/rebuild \u53C2\u4E0E\u9879\uFF09\u9ED8\u8BA4\u5168\u5173 = \u5B8C\u5168\u72EC\u7ACB\u3002\nsystemPrompt \u5185\u8054 \u6216 systemPromptFile\uFF08.md \u5F15\u7528\uFF0C\u63A8\u8350\uFF09\u2014\u2014\u89D2\u8272\u4F1A\u8BDD\u7684\u5B8C\u6574\u4EBA\u683C/\u8FB9\u754C\u63D0\u793A\u8BCD\u3002\nvalidate \u6821\u9A8C / apply \u751F\u6548 / list \u67E5\u770B\uFF1Aacc_msm exec skiff_admin <guide|validate|apply|list>\u3002\n\n Config:\n { \"skiff\": { \"roles\": {\n \"qa\": {\n \"model\": \"provider/model\",\n \"msms\": [\"web-search\", \"vlm-describe\"],\n \"tools\": [\"read\", \"grep\", \"glob\"],\n \"systemPromptFile\": \".opencode/skiff/qa.md\"\n } } } }\n\n\u2500\u2500 7. autopilotTrajectory\uFF08\u81EA\u52A8\u5DE1\u822A\u8F68\u8FF9\uFF09\u2500\u2500\nCCC \u5B9A\u4E49\u7684\u4E00\u6761\u81EA\u4E3B trajectory\u2014\u2014\u65F6\u949F\u5230\u70B9\u81EA\u52A8\u5524\u8D77\uFF08\u524D\u53F0\u6CE8\u5165\uFF0C\u7528\u6237\u53EF\u89C1\u53EF\u4ECB\u5165\uFF09\u3002\n\u672A\u914D\u7F6E\u6216 enabled=false \u2192 \u5B8C\u5168\u4E0D\u542F\u52A8\uFF08\u96F6\u8D44\u6E90\u5360\u7528\uFF09\u3002\u591A CCC \u72EC\u7ACB\uFF1A\u6BCF CCC \u81EA\u5DF1\u7684\u914D\u7F6E\u3002\n\u5524\u8D77\u6D88\u606F\u56DB\u6BB5\u5F0F\uFF1A\u8F68\u8FF9\u7126\u70B9 topPrompt\uFF08\u6700\u5148\u6CE8\u5165\uFF0C\u7A33\u5B9A\u951A\uFF09\u2192 \u8EAB\u4EFD\u951A\u5B9A \u2192 \u5148\u9A8C\u504F\u89C1\n\uFF08CCC \u6839\u811A\u672C biasProvider \u8F93\u51FA\uFF09\u2192 \u4EFB\u52A1\u3002\u76EE\u6807\u4F1A\u8BDD session \u5FC5\u586B\uFF08\u76EE\u5F55\u987B\u5E26 --auto \u540E\u7F00\uFF09\u3002\n\u8BCA\u65AD/\u72B6\u6001/\u7ACB\u5373\u5524\u8D77\uFF1Aacc_msm exec autopilot-trajectory <all|check|status|diag>\u3002\n\n Config:\n { \"autopilotTrajectory\": {\n \"enabled\": true,\n \"intervalHours\": 2,\n \"session\": \"S151\",\n \"biasProvider\": \"autopilot-bias.ts\",\n \"topPrompt\": \"\u672C\u8F68\u8FF9\u6838\u5FC3\u76EE\u6807/\u7EAA\u5F8B/\u8D28\u91CF\u8981\u6C42\uFF08CCC \u81EA\u586B\uFF0C\u9632\u6F02\u79FB\uFF09\",\n \"avoidWakeHours\": { \"start\": 8, \"end\": 18 }\n } }\n\n\u2500\u2500 8. weixin\uFF08\u5FAE\u4FE1\u6865 F4c-3\uFF0C\u542B\u6D88\u606F\u8BB0\u5F55 hook\uFF09\u2500\u2500\nCCC \u7EA7\u5FAE\u4FE1\u4E2A\u4EBA\u53F7\u63A5\u5165\uFF08iLink \u534F\u8BAE\uFF09\uFF1Adsh \u4E00\u8FDB\u7A0B\u591A CCC\uFF0C\u6BCF CCC \u72EC\u7ACB\u5BF9\u63A5\u5FAE\u4FE1\u6865\u3002\n\u8D26\u53F7/\u8DEF\u7531/\u5F00\u5173\u5728\u6B64\u6587\u4EF6\uFF1B**bot_token \u51ED\u636E\u5728 CCC localstore credential scope**\n\uFF08\u626B\u7801\u7ED1\u5B9A\u540E\u81EA\u52A8\u5199\u5165\uFF0C\u6C38\u4E0D\u8FDB git \u660E\u6587\u9762\uFF09\u3002\n\u8DEF\u7531 user \u2192 role\uFF1Aexact \u4F18\u5148\uFF0C* \u901A\u914D\u515C\u5E95\uFF1Brole \u5FC5\u987B \u2208 \u8BE5 CCC skiff.roles\u3002\n\u9762\u677F\uFF08WebUI \u8BBE\u7F6E \u2192 \u5FAE\u4FE1\u6865\uFF09\u53EF\u626B\u7801\u7ED1\u5B9A/\u79FB\u9664\u8D26\u53F7/\u7F16\u8F91\u8DEF\u7531\uFF1Bacc_msm exec weixin-doctor\n<status|diag|verify> \u6392\u67E5\u3002\u51ED\u636E\u4E3B\u52A8\u67E5\u770B\uFF1Alocalstore get WEIXIN_<ACCOUNT>_TOKEN\u3002\n\n Config:\n { \"weixin\": {\n \"enabled\": true,\n \"hook\": \"scripts/weixin-message-hook.ts\",\n \"accounts\": [{ \"accountId\": \"wechat-1\", \"name\": \"\u5BB6\u5EAD\u52A9\u624B\", \"enabled\": true }],\n \"routes\": [{ \"user\": \"*\", \"role\": \"zhaocai\" }]\n } }\n\n \u25B8 weixin.hook\uFF08\u6D88\u606F\u8BB0\u5F55 hook\uFF0Cv1.27.13\uFF09\uFF1A\n \u5FAE\u4FE1\u6865\u6BCF\u6536/\u53D1\u4E00\u6761\u6D88\u606F\u89E6\u53D1\u4E00\u6B21 CCC \u81EA\u5199\u811A\u672C\uFF0C\u7531 CCC \u81EA\u884C\u6301\u4E45\u5316\u4FDD\u5B58\uFF08\u5B58\u54EA/\u5B58\u6210\u4EC0\u4E48\n /\u662F\u5426\u5165\u5E93\u5168\u5F52 CCC\u2014\u2014ACC \u4E0D\u7ED1\u5B9A\u5B58\u50A8\uFF09\u3002\u672A\u914D\u7F6E hook \u2192 \u96F6\u53D8\u5316\uFF08\u4E0D\u89E6\u53D1\uFF09\u3002\n\n \u89E6\u53D1\uFF08\u53CC\u5411\uFF09\uFF1A\n incoming = \u7528\u6237 \u2192 bot\uFF1A\u8DEF\u7531\u547D\u4E2D\u540E\u3001\u5A92\u4F53\u843D\u76D8\u540E\u89E6\u53D1\uFF08\u6587\u672C\u542B\u8BED\u97F3\u8F6C\u5199\uFF1B\u5A92\u4F53\u5E26\u843D\u76D8 relPath\uFF09\n outgoing = bot \u2192 \u7528\u6237\uFF1A\u56DE\u590D\u53D1\u9001\u6210\u529F\u540E\u89E6\u53D1\uFF08reply = \u7528\u6237\u5B9E\u9645\u6536\u5230\u7684\u7EAF\u6587\u672C\uFF0C\u5DF2\u5265\u79BB think\uFF09\n\n \u811A\u672C\u7EA6\u5B9A\uFF08\u4E8B\u4EF6 JSON \u5355\u884C\u7ECF stdin \u4F20\u5165\uFF1Bbun \u4F18\u5148 node \u515C\u5E95\uFF09\uFF1A\n #!/usr/bin/env bun \u6216 node script.js \u2014\u2014 \u8BFB process.stdin \u6574\u6BB5 JSON.parse\n const ev = JSON.parse(await new Response(process.stdin).text())\n ev.event === 'incoming' | 'outgoing'\n\n \u4E8B\u4EF6 schema\uFF08**\u4E0D\u542B\u4EFB\u4F55\u4F1A\u8BDD\u51ED\u636E**\u2014\u2014context_token/bot_token/token/aes_key \u5747\u4E0D\u51FA\u73B0\uFF09\uFF1A\n { \"event\": \"incoming\", \"ts\": 1788359941488, \"cccRoot\": \"/path/ccc\",\n \"accountId\": \"wechat-1\", \"userId\": \"u1@im.wechat\",\n \"sessionId\": \"skiff-weixin-xxx\", \"role\": \"zhaocai\",\n \"message\": { \"text\": \"\u4F60\u597D\", \"media\": [{ \"kind\": \"image\", \"relPath\": \"_tmp/weixin-inbound/<hash>/img_x.jpg\" }] } }\n { \"event\": \"outgoing\", \"ts\": ..., \"cccRoot\": ..., \"accountId\": ...,\n \"userId\": ..., \"sessionId\": ..., \"role\": ...,\n \"reply\": \"\u5DF2\u8BB0\u5F55\uFF08\u7EAF\u6587\u672C\uFF09\" }\n\n \u793A\u4F8B\u811A\u672C\uFF08\u8FFD\u52A0\u5230\u6309\u65E5\u6587\u4EF6\u2014\u2014\u6301\u4E45\u5316\u5F52 CCC \u81EA\u9009\uFF1A\u6587\u4EF6/DB/\u8FDC\u7AEF\u5747\u53EF\uFF09\uFF1A\n const fs = require('node:fs'); const p = '/path/ccc/AGENT_SESSIONS/_weixin-log.jsonl';\n fs.appendFileSync(p, JSON.stringify(ev) + '\\n');\n\n \u6267\u884C\u8BED\u4E49\uFF08\u65C1\u8DEF\u5BB9\u5FCD\uFF09\uFF1A\u5F02\u6B65 fire-and-forget + 15s \u8D85\u65F6 kill + \u5931\u8D25\u4EC5\u65E5\u5FD7\u2014\u2014\n \u5FAE\u4FE1\u6865\u6D88\u606F\u5904\u7406/\u56DE\u590D\u4E0D\u53D7 hook \u5F71\u54CD\uFF1B\u811A\u672C\u987B\u5728 CCC \u6839\u5185\uFF08\u8DEF\u5F84\u9003\u9038\u62D2\u7EDD\uFF09\u3002\n \u5A92\u4F53 relPath \u6307\u5411 _tmp/weixin-inbound/\uFF08\u4E34\u65F6\u76EE\u5F55\uFF09\u2014\u2014\u9700\u6301\u4E45\u4FDD\u5B58\u5A92\u4F53\u6587\u4EF6\u8BF7\u81EA\u884C copy\u3002\n";
23
23
  export interface MsmFlag {
24
24
  name: string;
25
25
  type?: string;
@@ -7,7 +7,7 @@
7
7
  * 计分(对齐 opencode-serenity-plugin session-keeper):
8
8
  * write/edit=3, task=10, read/grep/glob/msm=1, 经过时间 +1 分/分钟
9
9
  * 阈值:.opencode/serenity.json sessionKeeper.threshold(规范位置,.dsh 回退;缺省 150)
10
- * 提醒要求模型回应 [SESSION-KEEPER-recorded-{code}] 确认(DCP 模式)。
10
+ * 提醒要求模型回应 [TRAJECTORY-ASSISTANT-recorded-{code}] 确认(DCP 模式)。
11
11
  */
12
12
  import type { Context } from 'cordis';
13
13
  export declare function scoreTool(toolName: string): number;
@@ -61,10 +61,13 @@ export declare function personaBlock(mode: string, overrideText: string): string
61
61
  */
62
62
  export declare function eapBlock(): string;
63
63
  /**
64
- * Metaphor 强化块(v1.19.6,S142):宁静号宇宙隐喻域(船/海/罗盘/日志/船员)。
64
+ * Metaphor 强化块(v1.19.6,S142;v1.29 星舰意象升级,S142 用户拍板):
65
+ * 宁静号宇宙隐喻域(星舰/深空/航程/船员)。海船→星舰意象:one starship, one voyage;
66
+ * deep space / launch / docking clamp;Departure Inspection(原 Harbor Inspection)。
65
67
  * 隐喻是记忆钩子——形象唤起约束,前述块保持规则精确。每条隐喻 = 一个不可违背的
66
68
  * 物理事实 + 行为判据(verdict)。全英文(与 CCE/Constraints 一致);独立块可
67
69
  * 回退;无 osp 对应(dsp 扩展,不进对齐断言参照)。
70
+ * 结构约束(docs/metaphor-domain.md M-1~M-4):三层骨架不变 + 每条映射约束不变。
68
71
  */
69
72
  export declare function metaphorBlock(): string;
70
73
  /**