@shgroup/dsh-serenity-hooks 1.24.10 → 1.24.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/dsh.plugin.json +1 -1
- package/lib/index.js +119 -33
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.12",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/handyman/session_rebuild + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
package/lib/index.js
CHANGED
|
@@ -5227,26 +5227,38 @@ function closeSession(root, key, confirm, scope = DEFAULT_SESSION_SCOPE) {
|
|
|
5227
5227
|
return `Session "${session.dirName}" closed and marked as completed.`;
|
|
5228
5228
|
}
|
|
5229
5229
|
/**
|
|
5230
|
-
* 从会话历史(events
|
|
5231
|
-
*
|
|
5230
|
+
* 从会话历史(events)解析会话身份(进程重启恢复;只扫**当前会话**自己的历史——无跨会话串台)。
|
|
5231
|
+
*
|
|
5232
|
+
* v1.24.11 稳固化(S142 用户需求:重建后新会话必须准确知道从哪个 SESSION 恢复):
|
|
5233
|
+
* **路径规范行即可,不再要求 [SESSION CONTEXT] 标记**——`session use` 上下文与重建锚点
|
|
5234
|
+
* (buildRebuildAnchor 的 `- Persistent trajectory — SESSION.md path: <rel>` 行)同格式,
|
|
5235
|
+
* 因此**仅靠重建锚点的会话(从未显式 use)同样可恢复**。从**尾到头**扫描,最后一条
|
|
5236
|
+
* 合法路径胜出(时间序最新);会话目录名/ID 从路径本身派生(单一真相源,不猜)。
|
|
5237
|
+
* 路径可为相对(重建锚点存 rel)——绝对化与存在性校验在调用方(知道 root)执行。
|
|
5232
5238
|
*/
|
|
5233
5239
|
function parseSessionContextFromEvents(events) {
|
|
5234
5240
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
5235
5241
|
const strs = [];
|
|
5236
5242
|
collectStrings(events[i], strs);
|
|
5237
5243
|
for (const s of strs) {
|
|
5238
|
-
const
|
|
5239
|
-
if (
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
if (
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5244
|
+
const md = extractSessionMdPathFromText(s);
|
|
5245
|
+
if (!md) continue;
|
|
5246
|
+
let dirName;
|
|
5247
|
+
let filePath;
|
|
5248
|
+
if (basename(md) === SESSION_MD) {
|
|
5249
|
+
dirName = basename(dirname(md));
|
|
5250
|
+
filePath = md;
|
|
5251
|
+
} else if (isSessionDirName(basename(md))) {
|
|
5252
|
+
dirName = basename(md);
|
|
5253
|
+
filePath = join(md, SESSION_MD);
|
|
5254
|
+
} else continue;
|
|
5255
|
+
if (!isSessionDirName(dirName)) continue;
|
|
5256
|
+
const idMatch = dirName.match(/--S(\d{3,})--/);
|
|
5257
|
+
return {
|
|
5258
|
+
sessionId: idMatch ? `S${idMatch[1]}` : dirName,
|
|
5259
|
+
dirName,
|
|
5260
|
+
mdPath: filePath
|
|
5261
|
+
};
|
|
5250
5262
|
}
|
|
5251
5263
|
}
|
|
5252
5264
|
return null;
|
|
@@ -5259,6 +5271,35 @@ function collectStrings(v, out) {
|
|
|
5259
5271
|
}
|
|
5260
5272
|
if (v && typeof v === "object") for (const val of Object.values(v)) collectStrings(val, out);
|
|
5261
5273
|
}
|
|
5274
|
+
/** 规范行正则:`SESSION.md path: <路径>`(路径可含空格 → [^\r\n]+ 整行匹配) */
|
|
5275
|
+
const SESSION_MD_PATH_RE = /SESSION\.md path:\s*([^\r\n]+)/;
|
|
5276
|
+
/** 从文本提取 SESSION.md 路径(use 上下文 / 重建锚点规范行通用;无匹配返回 null)。
|
|
5277
|
+
* 同行已知尾注(如系统提示词 Session 块的 persistent-body 注释)剥除——规范行只取路径本体。 */
|
|
5278
|
+
function extractSessionMdPathFromText(text) {
|
|
5279
|
+
const m = text.match(SESSION_MD_PATH_RE);
|
|
5280
|
+
if (!m) return null;
|
|
5281
|
+
let p = m[1].trim();
|
|
5282
|
+
const suffix = p.search(/ \(the trajectory's persistent body/);
|
|
5283
|
+
if (suffix > 0) p = p.slice(0, suffix).trim();
|
|
5284
|
+
return p;
|
|
5285
|
+
}
|
|
5286
|
+
/** 会话目录名形态校验(createSession 恒带日期前缀 `YYYY-MM-DD--`) */
|
|
5287
|
+
function isSessionDirName(dirName) {
|
|
5288
|
+
return /^\d{4}-\d{2}-\d{2}--/.test(dirName) && dirName.length > 11;
|
|
5289
|
+
}
|
|
5290
|
+
/**
|
|
5291
|
+
* 约定回退(v1.24.11):AGENT_SESSIONS 下最新修改的**未完成**会话的 SESSION.md。
|
|
5292
|
+
* readAllSessions 已按「未完成优先 + mtime 降序」排序 → 首个未完成且含 SESSION.md 即最新活动。
|
|
5293
|
+
* 只作最后手段(内存/events/锚点全缺时),保证重建锚点至少指向一个真实存在的轨迹。
|
|
5294
|
+
*/
|
|
5295
|
+
function findLatestActiveSessionMd(root) {
|
|
5296
|
+
for (const s of readAllSessions(sessionsRoot(root))) {
|
|
5297
|
+
if (s.status.completed) continue;
|
|
5298
|
+
const md = join(s.path, SESSION_MD);
|
|
5299
|
+
if (existsSync(md)) return md;
|
|
5300
|
+
}
|
|
5301
|
+
return null;
|
|
5302
|
+
}
|
|
5262
5303
|
/** health 子命令(对齐 osp healthCheck:stale/stalled/ghost/drift 四类检查,文本输出) */
|
|
5263
5304
|
function healthCheck(root) {
|
|
5264
5305
|
const sessions = readAllSessions(sessionsRoot(root));
|
|
@@ -6128,10 +6169,53 @@ function buildRebuildAnchor(root, sessionName, activeMdPath, anchorMessages = DE
|
|
|
6128
6169
|
...anchorMessages.flatMap((text) => [stripAckSuffix(text), ""]),
|
|
6129
6170
|
sessionName !== "" ? `Continue the work of ${sessionName}.` : "Continue the current work.",
|
|
6130
6171
|
...sessionLine ? [sessionLine] : [],
|
|
6131
|
-
`- Persistent trajectory
|
|
6172
|
+
`- Persistent trajectory — SESSION.md path: ${rel}`,
|
|
6173
|
+
` (the trajectory's persistent body — stays in place through rebuilds)`,
|
|
6132
6174
|
`- Read that SESSION.md first (goal/decisions/progress/unresolved), then continue from the last checkpoint.`
|
|
6133
6175
|
].join("\n");
|
|
6134
6176
|
}
|
|
6177
|
+
/** 从会话 surface 首条 user 消息(重建锚点)解析 SESSION.md 路径(events 异常/缺失时的兜底) */
|
|
6178
|
+
function parseAnchorMdPath(session) {
|
|
6179
|
+
try {
|
|
6180
|
+
const nodes = [...session.surface.nodes];
|
|
6181
|
+
if (nodes.length === 0) return null;
|
|
6182
|
+
const events = session.events;
|
|
6183
|
+
if (!Array.isArray(events)) return null;
|
|
6184
|
+
const event = events[nodes[0]];
|
|
6185
|
+
if (!event) return null;
|
|
6186
|
+
const message = deriveEventMessage(event);
|
|
6187
|
+
if (!message) return null;
|
|
6188
|
+
return extractSessionMdPathFromText(message.content?.map((c) => c.type === "text" ? c.text ?? "" : "").join("") ?? "");
|
|
6189
|
+
} catch {
|
|
6190
|
+
return null;
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
/** 从 SESSION.md 绝对路径派生会话名(S###;issue 目录回退完整目录名) */
|
|
6194
|
+
function sessionNameFromMdPath(mdPath) {
|
|
6195
|
+
const dirName = basename(dirname(mdPath));
|
|
6196
|
+
const idMatch = dirName.match(/--S(\d{3,})--/);
|
|
6197
|
+
return idMatch ? `S${idMatch[1]}` : dirName;
|
|
6198
|
+
}
|
|
6199
|
+
/**
|
|
6200
|
+
* 稳固的 SESSION.md 定位(多层候选 + 存在性校验,**绝不输出虚假路径**):
|
|
6201
|
+
* ① 内存活跃会话(本会话显式 use 过)→ ② events 恢复(use 标记 + 重建锚点规范行,进程重启后)
|
|
6202
|
+
* → ③ surface 首条锚点(events 异常时兜底)→ ④ AGENT_SESSIONS 约定回退(最新未完成活动目录)。
|
|
6203
|
+
* 每候选 resolve 后 existsSync 校验(相对路径按 root 解析);全部失败返回 null → 调用方报错引导。
|
|
6204
|
+
*/
|
|
6205
|
+
function resolveSessionMdPath(root, scope, session) {
|
|
6206
|
+
const candidates = [];
|
|
6207
|
+
candidates.push(getActiveSessionInfo(scope)?.mdPath ?? null);
|
|
6208
|
+
const events = session.events;
|
|
6209
|
+
if (Array.isArray(events)) candidates.push(parseSessionContextFromEvents(events)?.mdPath ?? null);
|
|
6210
|
+
candidates.push(parseAnchorMdPath(session));
|
|
6211
|
+
candidates.push(findLatestActiveSessionMd(root));
|
|
6212
|
+
for (const c of candidates) {
|
|
6213
|
+
if (!c) continue;
|
|
6214
|
+
const abs = c.startsWith(root) ? c : resolve(root, c);
|
|
6215
|
+
if (existsSync(abs)) return abs;
|
|
6216
|
+
}
|
|
6217
|
+
return null;
|
|
6218
|
+
}
|
|
6135
6219
|
const pendingRebuilds = /* @__PURE__ */ new Map();
|
|
6136
6220
|
/** 陈旧队列存活时长(毫秒):超过则丢弃(turn 异常结束/agent 崩溃时防残留误清空) */
|
|
6137
6221
|
const PENDING_TTL_MS = 6e5;
|
|
@@ -6145,19 +6229,11 @@ const PENDING_TTL_MS = 6e5;
|
|
|
6145
6229
|
async function queueRebuild(ctx, opts) {
|
|
6146
6230
|
if (!readSimpleSettings().rebuildEnabled) throw new Error("session_rebuild is disabled (rebuild.enabled=false — enable it in the dsh settings panel)");
|
|
6147
6231
|
const { root, note, dshSessionId } = opts;
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
const active = getActiveSessionInfo(scope);
|
|
6154
|
-
if (active?.mdPath) {
|
|
6155
|
-
sessionMdPath = active.mdPath;
|
|
6156
|
-
sessionName = active.sessionId ?? "";
|
|
6157
|
-
}
|
|
6158
|
-
} catch {}
|
|
6159
|
-
const mdPath = sessionMdPath ?? join(root, "AGENT_SESSIONS", "SESSION.md");
|
|
6160
|
-
const anchor = buildRebuildAnchor(root, sessionName, mdPath);
|
|
6232
|
+
const session = ctx.sessions?.get?.(dshSessionId);
|
|
6233
|
+
if (!session) throw new Error(`Unable to locate dsh session ${dshSessionId} (session may be closed)`);
|
|
6234
|
+
const mdPath = resolveSessionMdPath(root, dshSessionId, session);
|
|
6235
|
+
if (!mdPath) throw new Error("Unable to determine the active SESSION.md — no session context found in this conversation. Run \"session use <S###>\" first to activate the trajectory to resume, then retry session_rebuild.");
|
|
6236
|
+
const anchor = buildRebuildAnchor(root, getActiveSessionInfo(dshSessionId)?.sessionId ?? sessionNameFromMdPath(mdPath), mdPath);
|
|
6161
6237
|
pendingRebuilds.set(dshSessionId, {
|
|
6162
6238
|
anchor,
|
|
6163
6239
|
queuedAt: Date.now()
|
|
@@ -6165,7 +6241,7 @@ async function queueRebuild(ctx, opts) {
|
|
|
6165
6241
|
return {
|
|
6166
6242
|
queued: true,
|
|
6167
6243
|
anchor,
|
|
6168
|
-
sessionMdPath
|
|
6244
|
+
sessionMdPath: mdPath
|
|
6169
6245
|
};
|
|
6170
6246
|
}
|
|
6171
6247
|
/**
|
|
@@ -6436,14 +6512,18 @@ function reminderText(code, score) {
|
|
|
6436
6512
|
* ("[TRAJECTORY] Context usage at N%..."),模型当成系统状态而非行动请求,可一直忽略。
|
|
6437
6513
|
* 新文案 = 明确的行动指令(ACT NOW + 何时执行 + 必须执行),对齐 steward ACK 协议风格。
|
|
6438
6514
|
*
|
|
6515
|
+
* v1.24.12 沉淀协议(S142 用户需求):rebuild 前若掌握值得沉淀的认知——修订 CCC 现有
|
|
6516
|
+
* skill(eap 结构化);若需新建 skill——**不自行创建**,写提案进 SESSION.md 供用户参考。
|
|
6517
|
+
* 简短留自由度(不提"加载 eap 工具",模型自知;只说修订 skill + 新建 skill 落 SESSION)。
|
|
6518
|
+
*
|
|
6439
6519
|
* escalated=true(v1.23.3):连续多轮超阈值仍未 rebuild → 升级强制语气
|
|
6440
6520
|
* (STOP and rebuild now,持续注入直到调用 session_rebuild)。
|
|
6441
6521
|
*/
|
|
6442
6522
|
function rebuildReminderText(ratio, threshold, escalated = false) {
|
|
6443
6523
|
const pct = (ratio * 100).toFixed(0);
|
|
6444
6524
|
const thr = (threshold * 100).toFixed(0);
|
|
6445
|
-
if (escalated) return `[TRAJECTORY-ESCALATED] Context usage at ${pct}% (threshold ${thr}%) — you have been reminded repeatedly and have NOT called session_rebuild. This is now mandatory: STOP at the current task step
|
|
6446
|
-
return `[TRAJECTORY] Context usage at ${pct}% (threshold ${thr}%). This session is the rebuildable carrier of the trajectory: SESSION.md is the persistent body, this conversation is only a temporary work copy. ACT NOW: at the next natural pause (end of the current task step), call the session_rebuild tool 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.`;
|
|
6525
|
+
if (escalated) return `[TRAJECTORY-ESCALATED] Context usage at ${pct}% (threshold ${thr}%) — 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. 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.`;
|
|
6526
|
+
return `[TRAJECTORY] Context usage at ${pct}% (threshold ${thr}%). 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 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.`;
|
|
6447
6527
|
}
|
|
6448
6528
|
/** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
|
|
6449
6529
|
function readContextPressure(ctx, session) {
|
|
@@ -7495,8 +7575,14 @@ function registerContext(ctx, opts = {}) {
|
|
|
7495
7575
|
if (loadSerenityConfig(root, configPaths).hooks?.autoRestoreSession ?? true) {
|
|
7496
7576
|
const info = parseSessionContextFromEvents(agent.session.events ?? []);
|
|
7497
7577
|
if (info) {
|
|
7498
|
-
|
|
7499
|
-
|
|
7578
|
+
const abs = info.mdPath.startsWith(root) ? info.mdPath : resolve(root, info.mdPath);
|
|
7579
|
+
if (existsSync(abs)) {
|
|
7580
|
+
setActiveSessionInfo(scope, {
|
|
7581
|
+
...info,
|
|
7582
|
+
mdPath: abs
|
|
7583
|
+
});
|
|
7584
|
+
console.log(`[serenity-hooks] ↻ 从历史恢复激活会话: ${info.dirName}`);
|
|
7585
|
+
}
|
|
7500
7586
|
}
|
|
7501
7587
|
}
|
|
7502
7588
|
} catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.12",
|
|
4
4
|
"description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|