@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/README.md +14 -9
- package/dsh.plugin.json +1 -1
- package/experiments/autotrajectory/SKILL.md +106 -0
- package/experiments/autotrajectory/scripts/autotrajectory-exp.ts +383 -0
- package/lib/index.js +404 -69
- package/package.json +4 -2
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$
|
|
525
|
+
function agentCwd$10(exec) {
|
|
526
526
|
return exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
527
527
|
}
|
|
528
|
-
function renderText$
|
|
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$
|
|
600
|
+
render: (args, value) => renderText$12(value)
|
|
601
601
|
},
|
|
602
602
|
async execute(args, exec) {
|
|
603
|
-
const root = findSerenityRoot(agentCwd$
|
|
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$
|
|
1131
|
+
function agentCwd$9(exec) {
|
|
1132
1132
|
return exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
1133
1133
|
}
|
|
1134
|
-
function renderText$
|
|
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$
|
|
1157
|
+
render: (args, value) => renderText$11(value)
|
|
1158
1158
|
},
|
|
1159
1159
|
async execute(args, exec) {
|
|
1160
|
-
return await runKit(findSerenityRoot(agentCwd$
|
|
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$
|
|
1620
|
+
function agentCwd$8(exec) {
|
|
1621
1621
|
return exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
1622
1622
|
}
|
|
1623
|
-
function renderText$
|
|
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$
|
|
1662
|
+
render: (args, value) => renderText$10(value)
|
|
1663
1663
|
},
|
|
1664
1664
|
async execute(args, exec) {
|
|
1665
|
-
const root = findSerenityRoot(agentCwd$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
2683
|
+
render: (args, value) => renderText$9(value)
|
|
2684
2684
|
},
|
|
2685
2685
|
async execute(args, exec) {
|
|
2686
|
-
const root = findSerenityRoot(agentCwd$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
2968
|
+
function agentCwd$6(exec) {
|
|
2969
2969
|
return (exec.agent?.session)?.header?.cwd ?? process.cwd();
|
|
2970
2970
|
}
|
|
2971
|
-
function renderText$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
6224
|
+
render: (args, value) => renderText$4(value)
|
|
6225
6225
|
},
|
|
6226
6226
|
async execute(args, exec) {
|
|
6227
|
-
const root = findSerenityRoot(agentCwd$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
6822
|
+
render: (args, value) => renderText$3(value)
|
|
6823
6823
|
},
|
|
6824
6824
|
async execute(args, exec) {
|
|
6825
|
-
const root = findSerenityRoot(agentCwd$
|
|
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$
|
|
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$
|
|
6862
|
+
function agentCwd$3(exec) {
|
|
6863
6863
|
return exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
6864
6864
|
}
|
|
6865
|
-
function renderText$
|
|
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$
|
|
6897
|
+
render: (args, value) => renderText$2(value)
|
|
6898
6898
|
},
|
|
6899
6899
|
async execute(args, exec) {
|
|
6900
|
-
const root = findSerenityRoot(agentCwd$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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)
|
|
9714
|
-
|
|
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
|
|
9719
|
-
for (const p of
|
|
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)
|
|
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
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
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
|
|
9742
|
-
*
|
|
9743
|
-
*
|
|
9744
|
-
*
|
|
9745
|
-
*
|
|
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
|
-
|
|
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 持久化)。
|