@shgroup/dsh-serenity-hooks 1.26.10 → 1.26.12

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
@@ -522,10 +522,10 @@ function runCcFs(root, args) {
522
522
  * 进程内注册(取代 v0.1 的 bash spawn runner):zod/schemastery 参数校验、
523
523
  * 规范 JSON 输出、纯 render 投影。逻辑在 fs-ops.ts(可单测)。
524
524
  */
525
- function agentCwd$9(exec) {
525
+ function agentCwd$10(exec) {
526
526
  return exec.agent?.session?.header?.cwd ?? process.cwd();
527
527
  }
528
- function renderText$11(value) {
528
+ function renderText$12(value) {
529
529
  return [{
530
530
  type: "text",
531
531
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -597,10 +597,10 @@ const ccFsTool = defineTool({
597
597
  },
598
598
  output: {
599
599
  schema: { type: "json" },
600
- render: (args, value) => renderText$11(value)
600
+ render: (args, value) => renderText$12(value)
601
601
  },
602
602
  async execute(args, exec) {
603
- const root = findSerenityRoot(agentCwd$9(exec));
603
+ const root = findSerenityRoot(agentCwd$10(exec));
604
604
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
605
605
  return runCcFs(root, args);
606
606
  }
@@ -1128,10 +1128,10 @@ async function runKit(root, args) {
1128
1128
  /**
1129
1129
  * kit.ts — acc_kit 真实 DSH 工具定义(defineTool)
1130
1130
  */
1131
- function agentCwd$8(exec) {
1131
+ function agentCwd$9(exec) {
1132
1132
  return exec.agent?.session?.header?.cwd ?? process.cwd();
1133
1133
  }
1134
- function renderText$10(value) {
1134
+ function renderText$11(value) {
1135
1135
  return [{
1136
1136
  type: "text",
1137
1137
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -1154,10 +1154,10 @@ const kitTool = defineTool({
1154
1154
  },
1155
1155
  output: {
1156
1156
  schema: { type: "json" },
1157
- render: (args, value) => renderText$10(value)
1157
+ render: (args, value) => renderText$11(value)
1158
1158
  },
1159
1159
  async execute(args, exec) {
1160
- return await runKit(findSerenityRoot(agentCwd$8(exec)), args);
1160
+ return await runKit(findSerenityRoot(agentCwd$9(exec)), args);
1161
1161
  }
1162
1162
  });
1163
1163
  //#endregion
@@ -1617,10 +1617,10 @@ function runGit(root, args) {
1617
1617
  /**
1618
1618
  * git.ts — cc_git 真实 DSH 工具定义(defineTool)
1619
1619
  */
1620
- function agentCwd$7(exec) {
1620
+ function agentCwd$8(exec) {
1621
1621
  return exec.agent?.session?.header?.cwd ?? process.cwd();
1622
1622
  }
1623
- function renderText$9(value) {
1623
+ function renderText$10(value) {
1624
1624
  return [{
1625
1625
  type: "text",
1626
1626
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -1659,10 +1659,10 @@ const gitTool = defineTool({
1659
1659
  },
1660
1660
  output: {
1661
1661
  schema: { type: "json" },
1662
- render: (args, value) => renderText$9(value)
1662
+ render: (args, value) => renderText$10(value)
1663
1663
  },
1664
1664
  async execute(args, exec) {
1665
- const root = findSerenityRoot(agentCwd$7(exec));
1665
+ const root = findSerenityRoot(agentCwd$8(exec));
1666
1666
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
1667
1667
  return runGit(root, args);
1668
1668
  }
@@ -2407,7 +2407,7 @@ function listActiveHandymen(root) {
2407
2407
  }
2408
2408
  //#endregion
2409
2409
  //#region src/skiff-core.ts
2410
- const PLUGIN_SOURCE$4 = {
2410
+ const PLUGIN_SOURCE$5 = {
2411
2411
  kind: "plugin",
2412
2412
  plugin: "dsh-serenity-hooks"
2413
2413
  };
@@ -2577,7 +2577,7 @@ async function askSkiff(ctx, agent, question, eventsStart, options) {
2577
2577
  type: "text",
2578
2578
  text: question
2579
2579
  }],
2580
- source: PLUGIN_SOURCE$4
2580
+ source: PLUGIN_SOURCE$5
2581
2581
  }));
2582
2582
  await waitIdle$1(ctx, agent);
2583
2583
  const answer = lastAssistantText$2(agent);
@@ -2622,13 +2622,13 @@ function skiffMsmGate(root, sessionId, action, name) {
2622
2622
  /**
2623
2623
  * msm.ts — acc_msm 真实 DSH 工具定义(defineTool)
2624
2624
  */
2625
- function agentCwd$6(exec) {
2625
+ function agentCwd$7(exec) {
2626
2626
  return exec.agent?.session?.header?.cwd ?? process.cwd();
2627
2627
  }
2628
2628
  function agentSessionId$1(exec) {
2629
2629
  return exec.agent?.session?.id ?? "";
2630
2630
  }
2631
- function renderText$8(value) {
2631
+ function renderText$9(value) {
2632
2632
  return [{
2633
2633
  type: "text",
2634
2634
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -2680,10 +2680,10 @@ const msmTool = defineTool({
2680
2680
  },
2681
2681
  output: {
2682
2682
  schema: { type: "json" },
2683
- render: (args, value) => renderText$8(value)
2683
+ render: (args, value) => renderText$9(value)
2684
2684
  },
2685
2685
  async execute(args, exec) {
2686
- const root = findSerenityRoot(agentCwd$6(exec));
2686
+ const root = findSerenityRoot(agentCwd$7(exec));
2687
2687
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
2688
2688
  const gate = skiffMsmGate(root, agentSessionId$1(exec), args.action, args.name);
2689
2689
  if (gate.reject) throw new Error(gate.reject);
@@ -2733,7 +2733,7 @@ CCC (home-serenity etc.) encodes cognitive content as skills/SESSIONs/design doc
2733
2733
 
2734
2734
  ## Reference
2735
2735
  https://github.com/tellmewhattodo/theory-eap`;
2736
- function renderText$7(value) {
2736
+ function renderText$8(value) {
2737
2737
  return [{
2738
2738
  type: "text",
2739
2739
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -2753,7 +2753,7 @@ const eapTool = defineTool({
2753
2753
  } },
2754
2754
  output: {
2755
2755
  schema: { type: "string" },
2756
- render: (_args, value) => renderText$7(value)
2756
+ render: (_args, value) => renderText$8(value)
2757
2757
  },
2758
2758
  async execute(args) {
2759
2759
  if (args.section === "variables") return EAP_CONTENT.split("## Pre-Output Self-Check Checklist")[0];
@@ -2794,7 +2794,7 @@ Requirements → Scope → Solution → Interface → Implementation
2794
2794
  2. Wait for confirmation or correction (small step)
2795
2795
  3. After confirmation, record the decision (into session SESSION.md or a design doc)
2796
2796
  4. Move to the next decision point`;
2797
- function renderText$6(value) {
2797
+ function renderText$7(value) {
2798
2798
  return [{
2799
2799
  type: "text",
2800
2800
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -2810,7 +2810,7 @@ const neatTool = defineTool({
2810
2810
  } },
2811
2811
  output: {
2812
2812
  schema: { type: "string" },
2813
- render: (_args, value) => renderText$6(value)
2813
+ render: (_args, value) => renderText$7(value)
2814
2814
  },
2815
2815
  async execute(args) {
2816
2816
  if (args.section === "rules") return NEAT_CONTENT.match(/## Four Iron Rules[\s\S]*?(?=## )/)?.[0] ?? NEAT_CONTENT;
@@ -2873,7 +2873,7 @@ CCE answers "how structured knowledge should keep evolving across time without l
2873
2873
  ## Relationship with Serenity
2874
2874
  Serenity's session system, session tracking, and entropy management mechanisms (SQC quality loop) are all engineering implementations of CCE;
2875
2875
  the behavioral constraints embedded in CCC system prompts come from CCE.`;
2876
- function renderText$5(value) {
2876
+ function renderText$6(value) {
2877
2877
  return [{
2878
2878
  type: "text",
2879
2879
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -2894,7 +2894,7 @@ const cceTool = defineTool({
2894
2894
  } },
2895
2895
  output: {
2896
2896
  schema: { type: "string" },
2897
- render: (_args, value) => renderText$5(value)
2897
+ render: (_args, value) => renderText$6(value)
2898
2898
  },
2899
2899
  async execute(args) {
2900
2900
  const section = args.section;
@@ -2965,10 +2965,10 @@ function handymanPresetInheritance(parentCtx) {
2965
2965
  * preset 继承 + 工具收窄:setup 钩子里对子 agent 执行 agentPresets.composeFrom(对齐
2966
2966
  * subagent 先例)+ tools.restrict deny handyman(worker 内部看不到 handyman 工具)。
2967
2967
  */
2968
- function agentCwd$5(exec) {
2968
+ function agentCwd$6(exec) {
2969
2969
  return (exec.agent?.session)?.header?.cwd ?? process.cwd();
2970
2970
  }
2971
- function renderText$4(value) {
2971
+ function renderText$5(value) {
2972
2972
  return [{
2973
2973
  type: "text",
2974
2974
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -3185,11 +3185,11 @@ function createHandymanTool(ctx) {
3185
3185
  },
3186
3186
  output: {
3187
3187
  schema: { type: "json" },
3188
- render: (_args, value) => renderText$4(value)
3188
+ render: (_args, value) => renderText$5(value)
3189
3189
  },
3190
3190
  async execute(args, exec) {
3191
3191
  if (args.guide) return { guide: HANDYMAN_GUIDE };
3192
- const root = findSerenityRoot(agentCwd$5(exec));
3192
+ const root = findSerenityRoot(agentCwd$6(exec));
3193
3193
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
3194
3194
  const hc = readHandymanConfig(root, DEFAULT_SERENITY_CONFIG_PATHS);
3195
3195
  if (hc === null) throw new Error("handyman requires a model whitelist: configure .opencode/serenity.json \"handyman.models\" (e.g. {\"handyman\": {\"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"], \"defaultModel\": \"minimax-cn-coding-plan/MiniMax-M3\"}})");
@@ -5987,7 +5987,7 @@ function qaCheck(root, key) {
5987
5987
  * 同步把当前 dsh 会话重命名为该 SESSION 目录名**(sessionTitle.rename,user source
5988
5988
  * pin 住标题)。非创建时预命名。
5989
5989
  */
5990
- function agentCwd$4(exec) {
5990
+ function agentCwd$5(exec) {
5991
5991
  return exec.agent?.session?.header?.cwd ?? process.cwd();
5992
5992
  }
5993
5993
  /** 当前 dsh 会话 id(use/close 按会话隔离的 scope) */
@@ -6087,7 +6087,7 @@ function renameDshSessionForActive(ctx, exec, info) {
6087
6087
  console.warn(`[serenity-hooks] dsh 会话重命名异常: ${String(err?.message ?? err)}`);
6088
6088
  }
6089
6089
  }
6090
- function renderText$3(value) {
6090
+ function renderText$4(value) {
6091
6091
  return [{
6092
6092
  type: "text",
6093
6093
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -6221,10 +6221,10 @@ function createSessionTool(ctx) {
6221
6221
  },
6222
6222
  output: {
6223
6223
  schema: { type: "json" },
6224
- render: (args, value) => renderText$3(value)
6224
+ render: (args, value) => renderText$4(value)
6225
6225
  },
6226
6226
  async execute(args, exec) {
6227
- const root = findSerenityRoot(agentCwd$4(exec));
6227
+ const root = findSerenityRoot(agentCwd$5(exec));
6228
6228
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
6229
6229
  const entries = loadMsmEntries(root);
6230
6230
  const hasSessionTool = entries.some((e) => e.name === "session-tool");
@@ -6584,7 +6584,7 @@ function registerBootstrap(ctx) {
6584
6584
  }
6585
6585
  //#endregion
6586
6586
  //#region src/rebuild.ts
6587
- const PLUGIN_SOURCE$3 = {
6587
+ const PLUGIN_SOURCE$4 = {
6588
6588
  kind: "plugin",
6589
6589
  plugin: "dsh-serenity-hooks"
6590
6590
  };
@@ -6770,7 +6770,7 @@ function registerRebuildTurnHook(ctx) {
6770
6770
  type: "text",
6771
6771
  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."
6772
6772
  }],
6773
- source: PLUGIN_SOURCE$3
6773
+ source: PLUGIN_SOURCE$4
6774
6774
  }));
6775
6775
  console.log(`[serenity-hooks] session_rebuild executed with auto-continue (turn ${payload.turn ?? "?"} ended): ${id}`);
6776
6776
  }
@@ -6796,13 +6796,13 @@ function registerRebuildTurnHook(ctx) {
6796
6796
  * (turn 结束前所有 tool/result 已 append → 无孤儿;S141 INVALID_REQUEST 根治)。
6797
6797
  * 服务端逻辑在 ../rebuild.ts(queueRebuild/performRebuild/registerRebuildTurnHook)。
6798
6798
  */
6799
- function agentCwd$3(exec) {
6799
+ function agentCwd$4(exec) {
6800
6800
  return exec.agent?.session?.header?.cwd ?? process.cwd();
6801
6801
  }
6802
6802
  function agentSessionId(exec) {
6803
6803
  return exec.agent?.session?.id ?? "";
6804
6804
  }
6805
- function renderText$2(value) {
6805
+ function renderText$3(value) {
6806
6806
  return [{
6807
6807
  type: "text",
6808
6808
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -6819,17 +6819,17 @@ function createRebuildTool(ctx) {
6819
6819
  } },
6820
6820
  output: {
6821
6821
  schema: { type: "json" },
6822
- render: (args, value) => renderText$2(value)
6822
+ render: (args, value) => renderText$3(value)
6823
6823
  },
6824
6824
  async execute(args, exec) {
6825
- const root = findSerenityRoot(agentCwd$3(exec));
6825
+ const root = findSerenityRoot(agentCwd$4(exec));
6826
6826
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
6827
6827
  const dshSessionId = agentSessionId(exec);
6828
6828
  if (!dshSessionId) throw new Error("Unable to determine the current dsh session id");
6829
6829
  const result = await queueRebuild(ctx, {
6830
6830
  root,
6831
6831
  note: args.note,
6832
- agentCwd: agentCwd$3(exec),
6832
+ agentCwd: agentCwd$4(exec),
6833
6833
  dshSessionId
6834
6834
  });
6835
6835
  return {
@@ -6859,10 +6859,10 @@ const ACTIONS = [
6859
6859
  "show",
6860
6860
  "doc"
6861
6861
  ];
6862
- function agentCwd$2(exec) {
6862
+ function agentCwd$3(exec) {
6863
6863
  return exec.agent?.session?.header?.cwd ?? process.cwd();
6864
6864
  }
6865
- function renderText$1(value) {
6865
+ function renderText$2(value) {
6866
6866
  return [{
6867
6867
  type: "text",
6868
6868
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -6894,10 +6894,10 @@ const localstoreTool = defineTool({
6894
6894
  },
6895
6895
  output: {
6896
6896
  schema: { type: "json" },
6897
- render: (args, value) => renderText$1(value)
6897
+ render: (args, value) => renderText$2(value)
6898
6898
  },
6899
6899
  async execute(args, exec) {
6900
- const root = findSerenityRoot(agentCwd$2(exec));
6900
+ const root = findSerenityRoot(agentCwd$3(exec));
6901
6901
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
6902
6902
  return runLocalStore(root, args);
6903
6903
  }
@@ -6912,10 +6912,10 @@ const localstoreTool = defineTool({
6912
6912
  *
6913
6913
  * 归属:ACC 机制工具(教会 + 校验),角色内容仍归 CCC 配置(.opencode/serenity.json skiff.roles)。
6914
6914
  */
6915
- function agentCwd$1(exec) {
6915
+ function agentCwd$2(exec) {
6916
6916
  return exec.agent?.session?.header?.cwd ?? process.cwd();
6917
6917
  }
6918
- function renderText(value) {
6918
+ function renderText$1(value) {
6919
6919
  return [{
6920
6920
  type: "text",
6921
6921
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -7070,10 +7070,10 @@ const skiffAdminTool = defineTool({
7070
7070
  } },
7071
7071
  output: {
7072
7072
  schema: { type: "json" },
7073
- render: (args, value) => renderText(value)
7073
+ render: (args, value) => renderText$1(value)
7074
7074
  },
7075
7075
  async execute(args, exec) {
7076
- const root = findSerenityRoot(agentCwd$1(exec));
7076
+ const root = findSerenityRoot(agentCwd$2(exec));
7077
7077
  if (!root) throw new Error("No CCC found: no .serenity file from agent cwd");
7078
7078
  switch (args.action) {
7079
7079
  case "guide": return { guide: SKIFF_GUIDE };
@@ -7085,6 +7085,82 @@ const skiffAdminTool = defineTool({
7085
7085
  }
7086
7086
  });
7087
7087
  //#endregion
7088
+ //#region src/tools/autotrajectory-exp.ts
7089
+ /**
7090
+ * autotrajectory-exp.ts — 自主轨迹实验一站式管理工具(v1.26.12 实验提案,默认关)
7091
+ *
7092
+ * 定位:dsp **只提供工具与知识**,不向 CCC 自动安装任何东西(实验可能失败,不污染 CCC)——
7093
+ * 实验是 CCC 的自选动作:agent 调本工具(doc/全报告)即懂实验,init/random/check 辅助,
7094
+ * 实际执行(写配置/写偏见脚本/标记会话)由 CCC 自己决定、自己用现有工具完成。
7095
+ *
7096
+ * 实现:薄封装——exec 包内静态脚本(npm files 含 experiments/),脚本是单一真相源。
7097
+ * 环境注入 SERENITY_ROOT(当前 CCC 根)供脚本定位;bun 优先(可直跑 TS)。
7098
+ */
7099
+ /** 包内实验脚本(lib/tools/ → ../../experiments/...;npm files 分发) */
7100
+ const EXP_SCRIPT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "experiments", "autotrajectory", "scripts", "autotrajectory-exp.ts");
7101
+ const AUTO_TRAJECTORY_EXP_ACTIONS = [
7102
+ "all",
7103
+ "init",
7104
+ "random",
7105
+ "doc",
7106
+ "check",
7107
+ "status",
7108
+ "guide"
7109
+ ];
7110
+ function agentCwd$1(exec) {
7111
+ return exec.agent?.session?.header?.cwd ?? process.cwd();
7112
+ }
7113
+ function renderText(value) {
7114
+ return [{
7115
+ type: "text",
7116
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
7117
+ }];
7118
+ }
7119
+ const autoTrajectoryExpTool = defineTool({
7120
+ name: "autotrajectory-exp",
7121
+ description: "自主轨迹实验(Self-Sustaining Trajectory)一站式管理——实验提案 v1.26.12,默认关。无参/action=all:全报告(背景摘要 + 就绪检查 + 状态 + 下一步)——CCC agent 看一次即完整理解实验并知道怎么开始;init:初始化辅助(写配置 + 生成偏见提供者脚本模板);random:运行偏见提供者脚本输出当前偏见内容;doc:实验定义全文;check/status/guide:单项。实验是 CCC 的自选动作——dsp 只提供工具与知识,不自动安装任何东西。",
7122
+ parameters: { action: {
7123
+ type: "string",
7124
+ enum: [...AUTO_TRAJECTORY_EXP_ACTIONS],
7125
+ required: true,
7126
+ description: "Subcommand"
7127
+ } },
7128
+ output: {
7129
+ schema: { type: "json" },
7130
+ render: (_args, value) => renderText(value)
7131
+ },
7132
+ async execute(args, exec) {
7133
+ const root = findSerenityRoot(agentCwd$1(exec));
7134
+ const result = {};
7135
+ if (!existsSync(EXP_SCRIPT)) {
7136
+ result.error = `autotrajectory-exp 脚本缺失(${EXP_SCRIPT})——实验包未随安装分发,请检查 npm 包完整性`;
7137
+ return result;
7138
+ }
7139
+ const env = { ...process.env };
7140
+ if (root) env.SERENITY_ROOT = root;
7141
+ const r = spawnSync("bun", [EXP_SCRIPT, args.action ?? "all"], {
7142
+ encoding: "utf-8",
7143
+ timeout: 6e5,
7144
+ env,
7145
+ stdio: [
7146
+ "ignore",
7147
+ "pipe",
7148
+ "pipe"
7149
+ ]
7150
+ });
7151
+ if (r.status === 0) {
7152
+ result.output = r.stdout?.trim() || "(empty)";
7153
+ return result;
7154
+ }
7155
+ if (r.error?.code === "ENOENT") {
7156
+ result.error = "autotrajectory-exp 需要 bun 运行时(bun not found in PATH)";
7157
+ return result;
7158
+ }
7159
+ result.error = r.stderr?.trim() || r.stdout?.trim() || `exit ${r.status ?? "?"}`;
7160
+ return result;
7161
+ }
7162
+ });
7163
+ //#endregion
7088
7164
  //#region src/seams/keeper.ts
7089
7165
  const SCORES = {
7090
7166
  write: 3,
@@ -7167,7 +7243,7 @@ function readContextPressure(ctx, session) {
7167
7243
  return null;
7168
7244
  }
7169
7245
  }
7170
- const PLUGIN_SOURCE$2 = {
7246
+ const PLUGIN_SOURCE$3 = {
7171
7247
  kind: "plugin",
7172
7248
  plugin: "dsh-serenity-hooks"
7173
7249
  };
@@ -7230,7 +7306,7 @@ function registerKeeper(ctx, opts = {}) {
7230
7306
  if (blocks.length === 0) return downstream;
7231
7307
  const reminderAll = createUserMessage({
7232
7308
  content: blocks,
7233
- source: PLUGIN_SOURCE$2
7309
+ source: PLUGIN_SOURCE$3
7234
7310
  });
7235
7311
  if (downstream.kind === "block") return {
7236
7312
  kind: "block",
@@ -8241,7 +8317,7 @@ function accIdentityText(root, configPaths = DEFAULT_SERENITY_CONFIG_PATHS, entr
8241
8317
  }
8242
8318
  return lines.join("\n");
8243
8319
  }
8244
- const PLUGIN_SOURCE$1 = {
8320
+ const PLUGIN_SOURCE$2 = {
8245
8321
  kind: "plugin",
8246
8322
  plugin: "dsh-serenity-hooks"
8247
8323
  };
@@ -8258,7 +8334,7 @@ function accMessage(root, configPaths, entrySkillMaxChars) {
8258
8334
  }];
8259
8335
  return createUserMessage({
8260
8336
  content,
8261
- source: PLUGIN_SOURCE$1
8337
+ source: PLUGIN_SOURCE$2
8262
8338
  });
8263
8339
  }
8264
8340
  /** 每 agent 是否已注入过(进程内存态) */
@@ -9705,53 +9781,89 @@ const MECHANISM_PORTS = [
9705
9781
  function buildSensitiveTable(root) {
9706
9782
  const exact = /* @__PURE__ */ new Set();
9707
9783
  const substring = [];
9784
+ const credentialWords = [];
9785
+ const mechanismWords = [...MECHANISM_WORDS];
9786
+ const portWords = [...MECHANISM_PORTS];
9787
+ const msmWords = [];
9708
9788
  try {
9709
9789
  const p = join(root, "localstore.json");
9710
9790
  if (existsSync(p)) {
9711
9791
  const creds = JSON.parse(readFileSync(p, "utf-8").replace(/^\uFEFF/, ""))?.credentials;
9712
9792
  if (creds && typeof creds === "object") for (const [name, value] of Object.entries(creds)) {
9713
- if (name) exact.add(name);
9714
- if (typeof value === "string" && value !== "") exact.add(value);
9793
+ if (name) {
9794
+ exact.add(name);
9795
+ credentialWords.push(name);
9796
+ }
9797
+ if (typeof value === "string" && value !== "") {
9798
+ exact.add(value);
9799
+ credentialWords.push(value);
9800
+ }
9715
9801
  }
9716
9802
  }
9717
9803
  } catch {}
9718
- for (const w of MECHANISM_WORDS) substring.push(w);
9719
- for (const p of MECHANISM_PORTS) substring.push(p);
9804
+ for (const w of mechanismWords) substring.push(w);
9805
+ for (const p of portWords) substring.push(p);
9720
9806
  try {
9721
- for (const entry of loadMsmEntries(root)) if (entry.name) exact.add(entry.name);
9807
+ for (const entry of loadMsmEntries(root)) if (entry.name) {
9808
+ exact.add(entry.name);
9809
+ msmWords.push(entry.name);
9810
+ }
9722
9811
  } catch {}
9723
9812
  return {
9724
9813
  exact,
9725
9814
  substring,
9815
+ credentialWords,
9816
+ mechanismWords,
9817
+ portWords,
9818
+ msmWords,
9726
9819
  sources: {
9727
9820
  exact: [...exact],
9728
9821
  substring
9729
9822
  }
9730
9823
  };
9731
9824
  }
9732
- /** 检测文本中命中的敏感词(返回命中列表;未命中返回空数组) */
9825
+ /**
9826
+ * 检测文本中命中的敏感词(v1.26.11:返回带分类的命中——credential/mechanism/port/msm,
9827
+ * 打回消息据此给语义化规避指引;未命中返回空数组)
9828
+ */
9733
9829
  function detectSensitive(text, table) {
9734
9830
  if (!text) return [];
9735
- const hits = /* @__PURE__ */ new Set();
9736
- for (const w of table.exact) if (w !== "" && text.includes(w)) hits.add(w);
9737
- for (const w of table.substring) if (w !== "" && text.includes(w)) hits.add(w);
9738
- return [...hits];
9831
+ const hits = /* @__PURE__ */ new Map();
9832
+ const hit = (w, cat) => {
9833
+ if (w !== "" && text.includes(w) && !hits.has(w)) hits.set(w, cat);
9834
+ };
9835
+ for (const w of table.credentialWords) hit(w, "credential");
9836
+ for (const w of table.msmWords) hit(w, "msm");
9837
+ for (const w of table.mechanismWords) hit(w, "mechanism");
9838
+ for (const w of table.portWords) hit(w, "port");
9839
+ return [...hits].map(([word, category]) => ({
9840
+ word,
9841
+ category
9842
+ }));
9739
9843
  }
9844
+ /** 分类 → 规避指引(v1.26.11:告诉模型这个词**是什么**、**怎么规避**——裸词无法行动) */
9845
+ const CATEGORY_GUIDE = {
9846
+ credential: "credential identifier (a name, key, token, or password) — never mention credential names or values",
9847
+ mechanism: "internal mechanism term — never mention internal implementation/machinery names or config paths",
9848
+ port: "internal service port or address — never mention internal ports, addresses, or service endpoints",
9849
+ msm: "internal tool name — never mention internal tool names"
9850
+ };
9740
9851
  /**
9741
- * 打回消息(steer 内容):**告知命中词并指令重新生成**(v1.26.10 用户调整——
9742
- * 原 v1.26.3 设计"不含命中词防二次泄露"导致模型不知道改什么;命中词本就已在被拦截的
9743
- * 回复中进入会话(已暴露面),打回消息重复一次不扩大暴露,模型据此精准重写)。
9744
- * 命中词已落入模型上下文(它刚写过),此消息不回显给用户(steer 是 plugin source 消息,
9745
- * 3100 对外响应 v1.26.10 起已不含 trajectory → 打回文本不会到达外部用户)。
9852
+ * 打回消息(steer 内容):**逐词告知命中词 + 分类规避指引**(v1.26.11 用户调整——
9853
+ * v1.26.10 只列裸词(如 "3080")模型仍不知道是什么/怎么规避;现在按类说明:
9854
+ * "3080" → 内部服务端口,不要提及内部端口/地址/端点)。
9855
+ * R↓ 安全性:命中词本就已在被拦截回复中进入会话(已暴露面),打回重复一次不扩大暴露;
9856
+ * steer 是 plugin source 消息不回显给用户(3100 对外响应已不含 trajectory → 打回文本不达外部用户)。
9746
9857
  */
9747
9858
  function buildRebuke(hits) {
9748
- return `[SERENITY OUTPUT GUARD] Your previous response contained ${hits.length} sensitive internal term(s) (mechanism/credential identifiers) that must not appear in user-visible output: ${[...new Set(hits)].join(", ")}. Regenerate the response from scratch without ANY of these terms — describe the same substance without referencing internal machinery, credentials, tool names, or implementation details. Do not repeat the terms; do not explain this instruction to the user.`;
9859
+ const count = hits.length;
9860
+ 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.`;
9749
9861
  }
9750
9862
  /** 打回状态(按 agent 会话跟踪连续命中轮数) */
9751
9863
  const rebukeStates = /* @__PURE__ */ new Map();
9752
9864
  //#endregion
9753
9865
  //#region src/output-guard-seam.ts
9754
- const PLUGIN_SOURCE = {
9866
+ const PLUGIN_SOURCE$1 = {
9755
9867
  kind: "plugin",
9756
9868
  plugin: "dsh-serenity-hooks"
9757
9869
  };
@@ -9807,7 +9919,7 @@ function registerOutputGuardHook(ctx) {
9807
9919
  type: "text",
9808
9920
  text: buildRebuke(hits)
9809
9921
  }],
9810
- source: PLUGIN_SOURCE
9922
+ source: PLUGIN_SOURCE$1
9811
9923
  }));
9812
9924
  } catch (error) {
9813
9925
  console.warn(`[serenity-hooks] output-guard steer failed: ${String(error?.message ?? error)}`);
@@ -12704,6 +12816,227 @@ qInput.focus()
12704
12816
  </html>`;
12705
12817
  }
12706
12818
  //#endregion
12819
+ //#region src/autotrajectory.ts
12820
+ const PLUGIN_SOURCE = {
12821
+ kind: "plugin",
12822
+ plugin: "dsh-serenity-hooks"
12823
+ };
12824
+ /** 自主轨迹会话目录后缀标志(用户拍板:后缀) */
12825
+ const AUTO_DIR_SUFFIX = "--auto";
12826
+ /** 调度 tick 周期(10min——验证形态"一个就够") */
12827
+ const TICK_MS = 6e5;
12828
+ /** SESSION.md 内「下一轮动机」段标记(自生偏见载体——轨迹预设自己的未来) */
12829
+ const MOTIVATION_MARKER = "下一轮动机";
12830
+ /** 缺省避开的高峰时段(北京时间 [start, end) 不唤起——用量峰谷省钱) */
12831
+ const DEFAULT_AVOID_HOURS = {
12832
+ start: 8,
12833
+ end: 18
12834
+ };
12835
+ function readAutoTrajectorySettings(root) {
12836
+ return loadSerenityConfig(root).autotrajectory ?? null;
12837
+ }
12838
+ /**
12839
+ * 北京时间(UTC+8)当前小时——不依赖服务器时区(服务器可能 UTC/本地任意)。
12840
+ */
12841
+ function beijingHour(nowMs) {
12842
+ return Math.floor((nowMs + 288e5) % 864e5 / 36e5);
12843
+ }
12844
+ /**
12845
+ * 唤起窗口判定:北京时间 [avoidStart, avoidEnd) 内不唤起(缺省 8~18)。
12846
+ * 单段避开(start<=end):窗口 = [0,start) ∪ [end,24);跨零点避开(start>end):窗口 = [end,start)。
12847
+ */
12848
+ function inAllowedWakeWindow(nowMs, avoid) {
12849
+ const start = avoid?.start ?? DEFAULT_AVOID_HOURS.start;
12850
+ const end = avoid?.end ?? DEFAULT_AVOID_HOURS.end;
12851
+ const h = beijingHour(nowMs);
12852
+ if (start <= end) return h < start || h >= end;
12853
+ return h >= end && h < start;
12854
+ }
12855
+ /** 标志位判定:SESSION.md 所在目录名以 --auto 结尾 → 自主轨迹形态 */
12856
+ function isAutoTrajectorySession(mdPath) {
12857
+ return basename(dirname(mdPath)).endsWith(AUTO_DIR_SUFFIX);
12858
+ }
12859
+ /**
12860
+ * 目标会话定位:**必须配置 cfg.session**(S###/目录名)——CCC 日常有多条 trajectory 在跑,
12861
+ * 绝不默认唤起(缺省最近活跃会误伤其他正在运行的轨迹;用户拍板:必须配置才生效)。
12862
+ * 未配置 session 或未命中 → null(不唤起)。
12863
+ */
12864
+ function resolveTargetMd(root, cfg) {
12865
+ if (!cfg?.session) return null;
12866
+ const found = findSession(sessionsRoot(root), cfg.session);
12867
+ if (!found) return null;
12868
+ const md = join(found.path, "SESSION.md");
12869
+ return existsSync(md) ? md : null;
12870
+ }
12871
+ /**
12872
+ * 唤起条件(纯逻辑,可测):enabled + 未在运行 + 目录标志 + mtime 超间隔 + 窗口允许。
12873
+ */
12874
+ function shouldWake(settings, mdPath, nowMs, running) {
12875
+ if (!settings?.enabled || running) return false;
12876
+ if (!mdPath || !isAutoTrajectorySession(mdPath)) return false;
12877
+ if (!inAllowedWakeWindow(nowMs, settings.avoidWakeHours)) return false;
12878
+ try {
12879
+ const mtime = statSync(mdPath).mtimeMs;
12880
+ const hours = Math.max(1, settings.intervalHours ?? 12);
12881
+ return nowMs - mtime >= hours * 36e5;
12882
+ } catch {
12883
+ return false;
12884
+ }
12885
+ }
12886
+ /** 自生动机读取:SESSION.md「下一轮动机」段内容(到下一个二级标题或文件尾;无 → null) */
12887
+ function readSelfGeneratedMotivation(mdPath) {
12888
+ try {
12889
+ const content = readFileSync(mdPath, "utf-8");
12890
+ const idx = content.indexOf(MOTIVATION_MARKER);
12891
+ if (idx < 0) return null;
12892
+ const rest = content.slice(idx + 5);
12893
+ const nextHeading = rest.search(/\n## /);
12894
+ return (nextHeading >= 0 ? rest.slice(0, nextHeading) : rest).trim() || null;
12895
+ } catch {
12896
+ return null;
12897
+ }
12898
+ }
12899
+ /**
12900
+ * 偏见内容:直接运行 CCC 根目录下偏见提供者脚本(biasProvider,缺省 autotrajectory-bias.ts)→ stdout。
12901
+ * 脚本缺失 → 返回 { text: null, error: 提示实现 }(唤起侧报错要求实现,不静默跳过)。
12902
+ * 路径逃逸校验(resolveInside);bun 优先,node 兜底;600s 超时。
12903
+ */
12904
+ async function fetchBiasContent(root, providerRel) {
12905
+ let scriptAbs;
12906
+ try {
12907
+ scriptAbs = resolveInside(root, providerRel);
12908
+ } catch {
12909
+ return {
12910
+ text: null,
12911
+ error: `biasProvider 路径逃逸(须在 CCC 根内): ${providerRel}`
12912
+ };
12913
+ }
12914
+ if (!existsSync(scriptAbs)) return {
12915
+ text: null,
12916
+ error: `请在 CCC 根目录实现偏见内容提供者脚本: ${providerRel}(stdout 输出偏见内容一行;acc_msm exec autotrajectory-exp init 可生成模板)`
12917
+ };
12918
+ const runs = [["bun", [scriptAbs]], [process.execPath, [scriptAbs]]];
12919
+ for (const [cmd, args] of runs) try {
12920
+ const r = spawnSync(cmd, args, {
12921
+ encoding: "utf-8",
12922
+ timeout: 6e5,
12923
+ stdio: [
12924
+ "ignore",
12925
+ "pipe",
12926
+ "pipe"
12927
+ ]
12928
+ });
12929
+ if (r.status === 0) return {
12930
+ text: (r.stdout ?? "").trim() || null,
12931
+ error: null
12932
+ };
12933
+ if (r.error?.code === "ENOENT") continue;
12934
+ return {
12935
+ text: null,
12936
+ error: `偏见内容提供者脚本执行失败(exit ${r.status ?? "?"}): ${r.stderr?.trim() || r.stdout?.trim() || ""}`
12937
+ };
12938
+ } catch {
12939
+ continue;
12940
+ }
12941
+ return {
12942
+ text: null,
12943
+ error: "偏见内容提供者脚本无法运行(bun 与 node 均不可用)"
12944
+ };
12945
+ }
12946
+ /** 唤起消息(三段式:身份锚定 / 先验偏见[自生动机+偏见内容] / 任务)——注入前台会话,用户可见 */
12947
+ function buildWakeMessage(opts) {
12948
+ const lines = [
12949
+ `[自主轨迹唤起] — 距上次轨迹活动已满 ${opts.intervalHours} 小时,自动继续。`,
12950
+ "",
12951
+ `身份锚定:继续 ${opts.sessionName} 的 trajectory(SESSION.md: ${opts.mdPath})。`,
12952
+ "先验偏见:"
12953
+ ];
12954
+ if (opts.motivation) lines.push(` · 自生动机:${opts.motivation}`);
12955
+ if (opts.biasContent) lines.push(` · 偏见内容:${opts.biasContent}`);
12956
+ if (!opts.motivation && !opts.biasContent) lines.push(" · (无——本轮纯自主探索)");
12957
+ lines.push("", "任务:执行一轮自主认知(探索/反事实检验),把产出写入 SESSION.md", "「自主探索日志」段,并预写「下一轮动机」段。完成后自然结束。");
12958
+ return lines.join("\n");
12959
+ }
12960
+ /**
12961
+ * 装配(index.ts apply 调用):仅当 CCC 配置 autotrajectory.enabled=true 时启动定时器;
12962
+ * 否则零资源占用。定时器 tick 检查唤起条件 → 注入活跃会话(agent.steer,前台可见)。
12963
+ */
12964
+ function registerAutoTrajectory(ctx) {
12965
+ const root = resolveAutoTrajectoryRoot(ctx);
12966
+ const settings = root ? readAutoTrajectorySettings(root) : null;
12967
+ if (!settings?.enabled) return;
12968
+ let running = false;
12969
+ const tick = () => {
12970
+ if (running) return;
12971
+ const mdPath = resolveTargetMd(root, settings);
12972
+ if (!shouldWake(settings, mdPath, Date.now(), running)) return;
12973
+ const agent = resolveTargetAgent(ctx, mdPath);
12974
+ if (!agent) {
12975
+ console.warn("[serenity-hooks] ✗ 自主轨迹唤起跳过:目标会话 agent 不可得(session use 激活后重试)");
12976
+ return;
12977
+ }
12978
+ running = true;
12979
+ (async () => {
12980
+ try {
12981
+ const provider = settings.biasProvider?.trim() || "autotrajectory-bias.ts";
12982
+ const biasRes = await fetchBiasContent(root, provider);
12983
+ if (biasRes.error) {
12984
+ console.error(`[serenity-hooks] ✗ 自主轨迹唤起中止(偏见内容缺失): ${biasRes.error}`);
12985
+ return;
12986
+ }
12987
+ const motivation = readSelfGeneratedMotivation(mdPath);
12988
+ const message = buildWakeMessage({
12989
+ sessionName: basename(dirname(mdPath)),
12990
+ mdPath,
12991
+ intervalHours: settings.intervalHours ?? 12,
12992
+ motivation,
12993
+ biasContent: biasRes.text
12994
+ });
12995
+ agent.steer(createUserMessage({
12996
+ content: [{
12997
+ type: "text",
12998
+ text: message
12999
+ }],
13000
+ source: PLUGIN_SOURCE
13001
+ }));
13002
+ console.log(`[serenity-hooks] ✓ 自主轨迹唤起(${basename(dirname(mdPath))},偏见提供者 ${provider})`);
13003
+ } catch (err) {
13004
+ console.warn(`[serenity-hooks] ✗ 自主轨迹唤起失败: ${String(err?.message ?? err)}`);
13005
+ } finally {
13006
+ running = false;
13007
+ }
13008
+ })();
13009
+ };
13010
+ setInterval(tick, TICK_MS).unref();
13011
+ tick();
13012
+ }
13013
+ /** 目标 agent:从 SESSION.md 反向定位 dsh 会话(标题 F3 命名 S###-日期 匹配);不可得 → null */
13014
+ function resolveTargetAgent(ctx, mdPath) {
13015
+ const idMatch = basename(dirname(mdPath)).match(/--S(\d{3,})--/);
13016
+ const sid = idMatch ? `S${idMatch[1]}` : null;
13017
+ try {
13018
+ const sessions = ctx.sessions;
13019
+ for (const s of sessions?.list?.() ?? []) if (sid && (s.title === sid || s.title?.startsWith(`${sid}-`))) {
13020
+ const agent = ctx.agents?.get?.(s.id ?? "");
13021
+ if (agent) return agent;
13022
+ }
13023
+ } catch {}
13024
+ return null;
13025
+ }
13026
+ /** 自主轨迹绑定的 CCC 根:进程 cwd 上溯 .serenity 优先,回退任一 live 会话 root */
13027
+ function resolveAutoTrajectoryRoot(ctx) {
13028
+ const fromCwd = findSerenityRoot(process.cwd());
13029
+ if (fromCwd) return fromCwd;
13030
+ try {
13031
+ const sessions = ctx.sessions;
13032
+ for (const s of sessions?.list?.() ?? []) {
13033
+ const r = findSerenityRoot(s?.header?.cwd ?? "");
13034
+ if (r) return r;
13035
+ }
13036
+ } catch {}
13037
+ return null;
13038
+ }
13039
+ //#endregion
12707
13040
  //#region src/index.ts
12708
13041
  const name = "dsh-serenity-hooks";
12709
13042
  /** 主动调用的服务;其余(agent 事件)随 harness 装配必然存在 */
@@ -12759,6 +13092,7 @@ function apply(ctx, config) {
12759
13092
  ctx.tools.register(createRebuildTool(ctx));
12760
13093
  ctx.tools.register(localstoreTool);
12761
13094
  ctx.tools.register(skiffAdminTool);
13095
+ ctx.tools.register(autoTrajectoryExpTool);
12762
13096
  }
12763
13097
  if (config.guards) registerGuards(ctx, { configPaths: config.serenityConfigPaths });
12764
13098
  if (config.keeper) registerKeeper(ctx, {
@@ -12784,6 +13118,7 @@ function apply(ctx, config) {
12784
13118
  registerBootstrap(ctx);
12785
13119
  registerSkiff(ctx);
12786
13120
  registerAcp(ctx);
13121
+ registerAutoTrajectory(ctx);
12787
13122
  }
12788
13123
  /**
12789
13124
  * F4 Skiff 调试服务装配:启停 = 人工(设置面板 Skiff 区块开关,settings 持久化)。