@shgroup/dsh-serenity-hooks 1.23.3 → 1.23.5
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/client.js +4 -11
- package/lib/index.js +38 -4
- 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.23.
|
|
3
|
+
"version": "1.23.5",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/loop/session_rebuild + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
package/lib/client.js
CHANGED
|
@@ -8,7 +8,7 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SafeModePanel
|
|
11
|
-
const css$3 = "/* SafeModePanel — 会话头部 Serenity 状态卡片(v1.22 UX 重构)\n * 卡片 → 点击 → 官方 Modal(body portal 遮罩 + 居中)\n * 模态内:tab 栏 + 分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 前缀 sp- 防冲突。 */\n\n/* ── 头部状态卡片(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n
|
|
11
|
+
const css$3 = "/* SafeModePanel — 会话头部 Serenity 状态卡片(v1.22 UX 重构)\n * 卡片 → 点击 → 官方 Modal(body portal 遮罩 + 居中)\n * 模态内:tab 栏 + 分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 前缀 sp- 防冲突。 */\n\n/* ── 头部状态卡片(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n/* safe-mode 状态点(safe 分离出卡片:safe on=琥珀警告色,off=绿,非 CCC=灰) */\n.sp-dotWarn {\n background: var(--dsw-alias-state-warning-fg, #f59e0b);\n box-shadow: 0 0 0 3px var(--dsw-alias-state-warning-bg, rgba(245, 158, 11, 0.18));\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n}\n\n/* ── 官方 Modal 定制(宽度 + 内容紧凑) ── */\n\n.sp-modal {\n width: 520px;\n max-width: calc(100vw - 48px);\n}\n\n/* ── tab 栏 ── */\n\n.sp-tabs {\n display: flex;\n gap: 6px;\n padding: 0 2px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.sp-tab {\n flex: 1;\n padding: 8px 0;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-tab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-tabOn {\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.2));\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n\n/* ── 模态内容 ── */\n\n.sp-modalBody {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding-top: 14px;\n /* 内容超出视口时 body 区滚动(兜底——不再截断);整卡不滚动 */\n overflow-y: auto;\n max-height: calc(100vh - 200px);\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-module);\n}\n\n.sp-label {\n flex: none;\n width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-module);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-warning-bg, rgba(245, 158, 11, 0.15));\n color: var(--dsw-alias-state-warning-fg, #f59e0b);\n border-color: var(--dsw-alias-state-warning-border, rgba(245, 158, 11, 0.4));\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* loop 运行状态区块 */\n\n.sp-loopsToggle {\n flex: none;\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n cursor: pointer;\n}\n\n.sp-loopsToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-loopsList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-loopLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-loopResp {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sp-loopsMore {\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n padding: 4px 12px;\n}\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
12
12
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SafeModePanel\"]") === null) {
|
|
13
13
|
const tag = document.createElement("style");
|
|
14
14
|
tag.setAttribute("data-sp-css", "SafeModePanel");
|
|
@@ -92,27 +92,20 @@ window.__ModuleLoader__.load({
|
|
|
92
92
|
});
|
|
93
93
|
const inCcc = status.root !== null;
|
|
94
94
|
const runningLoops = loops.filter((l) => !l.done);
|
|
95
|
+
const dotClass = !inCcc ? "sp-dotOff" : status.safeModeOn ? "sp-dotWarn" : "sp-dotOn";
|
|
95
96
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
96
97
|
type: "button",
|
|
97
98
|
className: cx("sp-card"),
|
|
98
99
|
"aria-haspopup": "dialog",
|
|
99
100
|
"aria-expanded": open,
|
|
100
101
|
onClick: () => setOpen(true),
|
|
101
|
-
title: inCcc ? `Serenity v${status.accVersion} — ${status.root}` : "Serenity(未激活)",
|
|
102
|
+
title: inCcc ? `Serenity v${status.accVersion} — ${status.root}${status.safeModeOn ? "(safe-mode ON)" : "(safe-mode OFF)"}` : "Serenity(未激活)",
|
|
102
103
|
children: [
|
|
103
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cx("sp-dot",
|
|
104
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cx("sp-dot", dotClass) }),
|
|
104
105
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
105
106
|
className: cx("sp-brand"),
|
|
106
107
|
children: ["Serenity v", status.accVersion]
|
|
107
108
|
}),
|
|
108
|
-
inCcc && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
109
|
-
className: cx("sp-sm", status.safeModeOn ? "sp-smOn" : "sp-smOff"),
|
|
110
|
-
title: status.safeModeOn ? "safe-mode 已开启(隐藏写工具)" : "safe-mode 关闭",
|
|
111
|
-
children: [status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
|
|
112
|
-
size: 11,
|
|
113
|
-
className: cx("sp-smIcon")
|
|
114
|
-
}), status.safeModeOn ? "safe" : "safe-off"]
|
|
115
|
-
}),
|
|
116
109
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
|
|
117
110
|
size: 12,
|
|
118
111
|
className: cx("sp-chev")
|
package/lib/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
10
10
|
import { createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
11
|
+
import { deriveEventMessage } from "@deepseek-ai/dsh-session";
|
|
11
12
|
import { createServer, request } from "node:http";
|
|
12
13
|
//#region src/ccc.ts
|
|
13
14
|
/**
|
|
@@ -5106,13 +5107,13 @@ function parseSessionContextFromEvents(events) {
|
|
|
5106
5107
|
const idx = s.indexOf(SESSION_CONTEXT_MARKER);
|
|
5107
5108
|
if (idx < 0) continue;
|
|
5108
5109
|
const dirName = s.slice(idx + 28).trim().split("\n")[0]?.trim() ?? "";
|
|
5109
|
-
const mdMatch = s.match(/SESSION\.md path:\s*(\
|
|
5110
|
+
const mdMatch = s.match(/SESSION\.md path:\s*([^\r\n]+)/);
|
|
5110
5111
|
if (/^\d{4}-\d{2}-\d{2}--/.test(dirName) && mdMatch) {
|
|
5111
5112
|
const idMatch = dirName.match(/--S(\d{3,})--/);
|
|
5112
5113
|
return {
|
|
5113
5114
|
sessionId: idMatch ? `S${idMatch[1]}` : basename(dirName),
|
|
5114
5115
|
dirName,
|
|
5115
|
-
mdPath: mdMatch[1]
|
|
5116
|
+
mdPath: mdMatch[1].trim()
|
|
5116
5117
|
};
|
|
5117
5118
|
}
|
|
5118
5119
|
}
|
|
@@ -5988,11 +5989,14 @@ function stripAckSuffix(text) {
|
|
|
5988
5989
|
*/
|
|
5989
5990
|
function buildRebuildAnchor(root, sessionName, activeMdPath, anchorMessages = DEFAULT_ANCHOR_MESSAGES) {
|
|
5990
5991
|
const rel = activeMdPath.startsWith(root) ? activeMdPath.slice(root.length + 1) : activeMdPath;
|
|
5992
|
+
const sessionDir = basename(dirname(activeMdPath));
|
|
5993
|
+
const sessionLine = sessionDir !== "AGENT_SESSIONS" ? `- Serenity session: ${sessionName !== "" ? `${sessionName} (${sessionDir})` : sessionDir}` : null;
|
|
5991
5994
|
return [
|
|
5992
5995
|
"[TRAJECTORY-REBUILD] The conversation has been cleared and rebuilt (Ship of Theseus: the carrier is replaced, the trajectory continues).",
|
|
5993
5996
|
"",
|
|
5994
5997
|
...anchorMessages.flatMap((text) => [stripAckSuffix(text), ""]),
|
|
5995
5998
|
sessionName !== "" ? `Continue the work of ${sessionName}.` : "Continue the current work.",
|
|
5999
|
+
...sessionLine ? [sessionLine] : [],
|
|
5996
6000
|
`- Persistent trajectory (SESSION.md, unmoved): ${rel}`,
|
|
5997
6001
|
`- Read that SESSION.md first (goal/decisions/progress/unresolved), then continue from the last checkpoint.`
|
|
5998
6002
|
].join("\n");
|
|
@@ -6037,11 +6041,39 @@ async function queueRebuild(ctx, opts) {
|
|
|
6037
6041
|
* 执行真正的 surface replace(v1.22.4 定稿语义第二步,turn-stopping 时调用):
|
|
6038
6042
|
* 当前 turn 已完整结束(所有 tool/result 已 append)→ 全部节点替换为锚点消息。
|
|
6039
6043
|
* 同一会话 id 原地重建——同工作区天然满足,无新/旧会话之分。
|
|
6044
|
+
*
|
|
6045
|
+
* **shadow-price 协议(v1.23.5 修复,S142 用户问询)**:DSH token-meter 的
|
|
6046
|
+
* `foldSurfaceProjection` 对 surface `replace` 要求**紧邻其前的 metering 事件**
|
|
6047
|
+
* (`compaction/summary` 或 `compaction/prune`)声明被替换范围的启发式 token 价,
|
|
6048
|
+
* 否则 claim 缺失 → `deltaTokens=0` → `contextBreakdown.messageTokens` 永不扣减
|
|
6049
|
+
* → UI「对话消息」虚高累计、往复 rebuild 计量爆炸(用户截图实证:总 15% 但
|
|
6050
|
+
* 对话消息 ~1M)。官方先例 = `compaction-tool-result-pruner`(append
|
|
6051
|
+
* `compaction/prune` → 紧接 replace)。此处同款:先 append `compaction/prune`
|
|
6052
|
+
* (定价全部被替换节点,经 ctx.tokenMeter.estimateMessage 逐节点累加),再
|
|
6053
|
+
* append 锚点 replace——meter 不可用时退化(无 claim,仅计量漂移,会话功能不受影响)。
|
|
6054
|
+
*
|
|
6040
6055
|
* @returns 是否执行了 replace(false = 无 pending 或 surface 空)
|
|
6041
6056
|
*/
|
|
6042
|
-
function performRebuild(session, pending) {
|
|
6057
|
+
function performRebuild(session, pending, meter) {
|
|
6043
6058
|
const nodes = [...session.surface.nodes];
|
|
6044
6059
|
if (nodes.length === 0) return false;
|
|
6060
|
+
if (meter) {
|
|
6061
|
+
let shadowedTokenCount = 0;
|
|
6062
|
+
for (const seq of nodes) {
|
|
6063
|
+
const event = session.events?.[seq];
|
|
6064
|
+
if (!event) continue;
|
|
6065
|
+
const message = deriveEventMessage(event);
|
|
6066
|
+
if (message) shadowedTokenCount += meter.estimateMessage(message);
|
|
6067
|
+
}
|
|
6068
|
+
session.append("compaction/prune", {
|
|
6069
|
+
shadowedRange: {
|
|
6070
|
+
start: nodes[0],
|
|
6071
|
+
end: nodes[nodes.length - 1]
|
|
6072
|
+
},
|
|
6073
|
+
shadowedSeqs: nodes,
|
|
6074
|
+
shadowedTokenCount
|
|
6075
|
+
});
|
|
6076
|
+
}
|
|
6045
6077
|
session.append("user/message", {
|
|
6046
6078
|
content: [{
|
|
6047
6079
|
type: "text",
|
|
@@ -6078,7 +6110,9 @@ function registerRebuildTurnHook(ctx) {
|
|
|
6078
6110
|
}
|
|
6079
6111
|
pendingRebuilds.delete(id);
|
|
6080
6112
|
try {
|
|
6081
|
-
|
|
6113
|
+
const tokenMeter = ctx.get?.("tokenMeter");
|
|
6114
|
+
const meter = tokenMeter && typeof tokenMeter.estimateMessage === "function" ? { estimateMessage: (m) => tokenMeter.estimateMessage(m) } : void 0;
|
|
6115
|
+
if (performRebuild(agent.session, pending, meter)) {
|
|
6082
6116
|
agent.steer(createUserMessage({
|
|
6083
6117
|
content: [{
|
|
6084
6118
|
type: "text",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.5",
|
|
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": {
|