@shgroup/dsh-serenity-hooks 1.59.3 → 1.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/lib/host/contract.d.ts +11 -0
- package/lib/index.js +207 -36
- package/lib/seams/bootstrap.d.ts +39 -3
- package/lib/seams/guards.d.ts +19 -0
- 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.
|
|
3
|
+
"version": "1.61.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1 与 0.2.0-rc.1(双档,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
package/lib/host/contract.d.ts
CHANGED
|
@@ -23,6 +23,17 @@ type HostAccess = 'injected' | 'lazy';
|
|
|
23
23
|
interface HostMember {
|
|
24
24
|
name: string;
|
|
25
25
|
kind: 'function' | 'value';
|
|
26
|
+
/**
|
|
27
|
+
* 成员所属的「互斥族」标签(可选)。**同族成员满足任一即可**。
|
|
28
|
+
*
|
|
29
|
+
* 🔴 为什么需要(v1.61,S151 2026-10-17 报告):
|
|
30
|
+
* 宿主会**给同一个能力改名**并保留旧名作回退。`subagents` 就是实例:
|
|
31
|
+
* `0.2.1-rc.*+` 的正门叫 `startActivation`,`start` 仅作旧宿主回退
|
|
32
|
+
* (`tools/cheap-worker.ts` 的 `resolveSubagentStarter()` 是**双路**的)。
|
|
33
|
+
* 若契约把某一个具体名字钉成"必须有",宿主改名后 ⇒ **探针报假故障,而功能是好的**。
|
|
34
|
+
* ⇒ 判据必须是「**这一族里至少有一个能用**」,不是「这个名字必须在」。
|
|
35
|
+
*/
|
|
36
|
+
anyOf?: string;
|
|
26
37
|
}
|
|
27
38
|
interface HostServiceContract {
|
|
28
39
|
/** 稳定标识(报告用) */
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { c as hostInjected, d as hostSessions, f as hostSettings, h as hostWebServer, i as cccRootForExec, l as hostLlm, m as hostWeb, n as agentCwdFor, o as listCccs, p as hostSubagents, r as cccRootForCwd, s as hostAgents, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-GIJ3A_tw.js";
|
|
3
3
|
import { _ as resolveInside, a as findSerenityRoot$1, c as matchBlacklist, d as readCccName$1, f as readCheapWorkerConfig, h as readTrajectorySkills, i as findGitRoot, l as pathInside, m as readSafeModeHiddenTools, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readExclusiveTools, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-B0ZGL9lx.js";
|
|
4
|
-
import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSession, O as localFileStamp, S as sessionsRoot, T as summarize, _ as namingTitleFor, a as pruneMissingBindings, b as resolveSessionByTitle, c as setBindingStore, d as clearActiveSessionInfo, f as createSession, g as listSessions, h as getActiveSessionInfo, i as listBoundSessionIds, k as localHuman, l as DEFAULT_SESSION_SCOPE, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionTrajectoryLabel,
|
|
4
|
+
import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSession, O as localFileStamp, S as sessionsRoot, T as summarize, _ as namingTitleFor, a as pruneMissingBindings, b as resolveSessionByTitle, c as setBindingStore, d as clearActiveSessionInfo, f as createSession, g as listSessions, h as getActiveSessionInfo, i as listBoundSessionIds, k as localHuman, l as DEFAULT_SESSION_SCOPE, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionTrajectoryLabel, u as TRAJECTORY_ACTIONS, v as parseSessionContextFromEvents, w as showSession, x as sessionEvents$1, y as readActiveSessionMd } from "./message-source-B4P3Uw6D.js";
|
|
5
5
|
import { a as readGitTrack, c as unsetEntry, i as localstorePath, l as writeEntry, n as checkLocalstoreGitCompliance, o as readStore, r as getEntry, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./localstore-ops-DMs_ovHo.js";
|
|
6
6
|
import { a as FACE_PORTS, c as MECHANISM_PORTS, d as registerSkiffSession$1, f as skiffRoleFor$1, h as ACC_VERSION, i as ACP_PORT, l as SKIFF_DEBUG_PORT, m as unregisterSkiffSession$1, n as readSimpleSettings, o as GATEWAY_PORT, p as skiffSessionInfo$1, r as registerSettingsSection, s as MAIN_WEB_PORT, u as WEIXIN_SEND_PORT } from "./settings-section-DGUQZo5j.js";
|
|
7
7
|
import { a as readSkiffRoles, c as roleMsmWhitelist, d as systemPromptSource, f as trajectorySubset, i as isSkiffSessionId, l as roleToolWhitelist, n as buildSkiffBasePrompt, o as resolveRoleSystemPrompt, r as createRolePromptReader, s as resolveSkiffKind } from "./skiff-role-B615L9MV.js";
|
|
@@ -9196,7 +9196,7 @@ function parseForegroundJobs(raw) {
|
|
|
9196
9196
|
function resolveSubagentStarter(subagents) {
|
|
9197
9197
|
if (typeof subagents.startActivation === "function") {
|
|
9198
9198
|
const fn = subagents.startActivation;
|
|
9199
|
-
return (req) => fn({
|
|
9199
|
+
return (req) => fn.call(subagents, {
|
|
9200
9200
|
provider: "spawn",
|
|
9201
9201
|
label: req.label,
|
|
9202
9202
|
request: {
|
|
@@ -9388,7 +9388,7 @@ function createCheapWorkerTool(ctx) {
|
|
|
9388
9388
|
done,
|
|
9389
9389
|
jobs: results,
|
|
9390
9390
|
usage: {
|
|
9391
|
-
how: `foreground parallel mode starts ${results.length} child agents at once through the host delegation service (ctx.subagents.start "spawn"), awaits all of them, and returns each child's final text. No loop, no completion-code validation, no progress file. Cap = ${FOREGROUND_MAX_PARALLEL}.`,
|
|
9391
|
+
how: `foreground parallel mode starts ${results.length} child agents at once through the host delegation service (ctx.subagents.startActivation on 0.2.1-rc.*+, falling back to ctx.subagents.start "spawn" on older hosts), awaits all of them, and returns each child's final text. No loop, no completion-code validation, no progress file. Cap = ${FOREGROUND_MAX_PARALLEL}.`,
|
|
9392
9392
|
model: "NOT selectable per call — resolved from the CCC config cheap-worker.models (cheap-worker.defaultModel, else models[0])",
|
|
9393
9393
|
next: done ? "All children completed; use each job's output directly (results are in the same order as the jobs you passed)" : "Some children did not complete — inspect each job's diagnostic/output (results keep the order you passed)"
|
|
9394
9394
|
}
|
|
@@ -9406,7 +9406,7 @@ function createCheapWorkerTool(ctx) {
|
|
|
9406
9406
|
return {
|
|
9407
9407
|
...res,
|
|
9408
9408
|
usage: {
|
|
9409
|
-
how: "foreground mode starts ONE serial child agent through the host delegation service (ctx.subagents.start \"spawn\") with the CCC-configured model; it awaits the run and returns the child's final text. No loop, no completion-code validation, no progress file.",
|
|
9409
|
+
how: "foreground mode starts ONE serial child agent through the host delegation service (ctx.subagents.startActivation on 0.2.1-rc.*+, falling back to ctx.subagents.start \"spawn\" on older hosts) with the CCC-configured model; it awaits the run and returns the child's final text. No loop, no completion-code validation, no progress file.",
|
|
9410
9410
|
model: "NOT selectable per call — resolved from the CCC config cheap-worker.models (cheap-worker.defaultModel, else models[0])",
|
|
9411
9411
|
next: res.done ? "Child completed; use its output directly" : `Child did not complete (stopReason=${res.stopReason}); inspect diagnostic/output and decide whether to retry`
|
|
9412
9412
|
}
|
|
@@ -13773,6 +13773,69 @@ function forgetImBridgeVisibility(sessionId) {
|
|
|
13773
13773
|
imBridgeRestrictions.delete(sessionId);
|
|
13774
13774
|
}
|
|
13775
13775
|
/**
|
|
13776
|
+
* 宿主 `dsh-tool-schedule` 注册的 4 个模型可见工具名(**逐字**,来自该包 `lib/index.js` 的
|
|
13777
|
+
* `defineTool({ name: … })`:`schedule_create` / `schedule_list` / `schedule_update` / `schedule_delete`)。
|
|
13778
|
+
*
|
|
13779
|
+
* 🔴 **为什么要在 CCC 内屏蔽(owner 原话:「这和 CRO 冲突,要求在宁静号激活情况下屏蔽掉」)**:
|
|
13780
|
+
* 宿主从 `0.2.1-rc.2` 起自带一套**模型可见的定时器工具**(`ctx.schedule` 服务 + 这 4 个工具)。
|
|
13781
|
+
* 而宁静号的唤醒/巡航走的是**另一套**机制:`container_trajectory send-later` → 落 CCC 的
|
|
13782
|
+
* `AGENT_SESSIONS/wake-registry.json` → 由**中心调度器 5min tick 统一投递**(D58)。
|
|
13783
|
+
* 两套并存会产生**三个具体问题**(不是"重复功能"这种审美问题):
|
|
13784
|
+
* ① **两处真相源**:宿主的 schedule 状态在宿主域、我们的在 CCC 文件里,**谁在跑说不清**;
|
|
13785
|
+
* ② **CRO 判据失真**:CRO 程序读的是 `wake-registry.json`,宿主定时器触发的唤起**它看不见**
|
|
13786
|
+
* ⇒ 巡检/记账全部遗漏(本轨已有同族教训:「CRO 只在 `wake===true` 时算」那条被推翻);
|
|
13787
|
+
* ③ **安静失败**:模型可能顺手用 `schedule_create` 排一个唤醒,**它不在任何人的账本里**。
|
|
13788
|
+
* ⇒ 故**在 CCC 内隐藏**(从模型清单移除),非 CCC 会话**不受影响**(那是宿主的正常能力)。
|
|
13789
|
+
*
|
|
13790
|
+
* ⚠️ **一处必须防的坑**(与 `hideAllFor` 同族):宿主 `restrict` 对**未注册的名字**会让**整次调用失败**
|
|
13791
|
+
* (`dsh-tools` 契约逐字:"Empty filters, unknown names … fail")。
|
|
13792
|
+
* 这 4 个名字是否在册取决于**宿主是否装了该 tool 包**(不同宿主版本/裁剪可能没有)
|
|
13793
|
+
* ⇒ 所以**逐个名字单独发 restrict**,某个失败就跳过那个,**绝不整批发**
|
|
13794
|
+
* (整批发一旦撞到缺名的宿主 ⇒ 一个都不隐藏 + 只有一行 log ⇒ 缺陷静默)。
|
|
13795
|
+
*/
|
|
13796
|
+
const HOST_SCHEDULE_TOOL_NAMES = [
|
|
13797
|
+
"schedule_create",
|
|
13798
|
+
"schedule_list",
|
|
13799
|
+
"schedule_update",
|
|
13800
|
+
"schedule_delete"
|
|
13801
|
+
];
|
|
13802
|
+
/** `${sessionId}::${toolName}` → restrict disposer(逐工具,便于某个名字不成立时单独跳过) */
|
|
13803
|
+
const hostScheduleRestrictions = /* @__PURE__ */ new Map();
|
|
13804
|
+
/**
|
|
13805
|
+
* 在 **CCC 内**屏蔽宿主自带的定时器工具(v1.59.5)。
|
|
13806
|
+
*
|
|
13807
|
+
* 判据 = **会话是否落在某个 CCC 内**(`root !== null`)——与 `syncCccToolVisibility` 互补:
|
|
13808
|
+
* 那个管"不在 CCC ⇒ 隐藏**我们自己的**全部工具",本函数管"**在** CCC ⇒ 隐藏**宿主的**定时器工具"。
|
|
13809
|
+
*
|
|
13810
|
+
* 幂等;失败不阻断 step(最坏 = 那一个工具仍可见,但那只是"宿主的正常能力没被藏起来",
|
|
13811
|
+
* 不会让会话坏掉)。逐个名字独立发 restrict ⇒ 任何一个名字不在册都不会连累其余三个。
|
|
13812
|
+
*
|
|
13813
|
+
* @param agent 目标会话
|
|
13814
|
+
* @param root 该会话的 CCC 根;`null` ⇒ 非 CCC ⇒ **不动**(宿主的定时器在别处是正常能力)
|
|
13815
|
+
*/
|
|
13816
|
+
function syncHostScheduleToolHiding(agent, root) {
|
|
13817
|
+
if (root === null) return;
|
|
13818
|
+
const sessionId = agent.session.id ?? "global";
|
|
13819
|
+
for (const tool of HOST_SCHEDULE_TOOL_NAMES) {
|
|
13820
|
+
const key = `${sessionId}::${tool}`;
|
|
13821
|
+
if (hostScheduleRestrictions.has(key)) continue;
|
|
13822
|
+
try {
|
|
13823
|
+
hostScheduleRestrictions.set(key, agent.ctx.tools.restrict({ deny: [tool] }));
|
|
13824
|
+
} catch {}
|
|
13825
|
+
}
|
|
13826
|
+
}
|
|
13827
|
+
/** 会话销毁清理(lifecycle 调用;按 `${sessionId}::` 前缀回收) */
|
|
13828
|
+
function forgetHostScheduleToolHiding(sessionId) {
|
|
13829
|
+
const prefix = `${sessionId}::`;
|
|
13830
|
+
for (const [key, dispose] of [...hostScheduleRestrictions]) {
|
|
13831
|
+
if (!key.startsWith(prefix)) continue;
|
|
13832
|
+
try {
|
|
13833
|
+
dispose();
|
|
13834
|
+
} catch {}
|
|
13835
|
+
hostScheduleRestrictions.delete(key);
|
|
13836
|
+
}
|
|
13837
|
+
}
|
|
13838
|
+
/**
|
|
13776
13839
|
* ACC 注册但**默认对所有 CCC 隐藏**的专属工具清单。
|
|
13777
13840
|
*
|
|
13778
13841
|
* 语义(通用机制,不绑定任何具体 CCC):只有在 CCC 的 `.opencode/serenity.json`
|
|
@@ -14038,6 +14101,88 @@ const DEFAULT_ANCHOR_MESSAGES = ["You are the operator agent of Serenity — a c
|
|
|
14038
14101
|
function hasUserMessageHistory(session) {
|
|
14039
14102
|
return sessionEvents$1(session).some((event) => event.type === "user/message");
|
|
14040
14103
|
}
|
|
14104
|
+
/** 造出来的 assistant 回复正文。锚定轮要求"只回确认、不做事"。 */
|
|
14105
|
+
const ANCHOR_ACK_TEXT = "acknowledge";
|
|
14106
|
+
/** 占位 system 文本:宿主 `SystemPromptProjection.project()` 会把它**原地 replace** 成真提示词。 */
|
|
14107
|
+
const ANCHOR_SYSTEM_PLACEHOLDER = "<!-- serenity:anchor-placeholder -->";
|
|
14108
|
+
/**
|
|
14109
|
+
* 把两轮 first-anchor 对话**直接写进会话日志**,取代原来的「真跑两轮」。
|
|
14110
|
+
*
|
|
14111
|
+
* ## 为什么可以这样(三条取证,全部打在真 `@deepseek-ai/dsh-session` 上)
|
|
14112
|
+
*
|
|
14113
|
+
* 1. **system 必须是 surface node 0**,而 `agent/pre-step` 跑在宿主提交 system **之前**
|
|
14114
|
+
* ⇒ 若直接写 [anchor…],system 会被追加到**末尾**(实测:`systemAtIndex: 4`,
|
|
14115
|
+
* 且**不报错**、静默走偏)。解法 = **先写一条占位 system**:宿主随后看到 node0
|
|
14116
|
+
* 已有 system 且文本不同 ⇒ 走 `replace(head)` 换成真提示词,锚定历史原样留在其后。
|
|
14117
|
+
* 2. **`contentGeneration` 只在 replace 时涨,append 不涨**;而 `buildRequest()`
|
|
14118
|
+
* 取 generation 快照**晚于** `systemPrompt.project()` ⇒ 那次 replace 不会被
|
|
14119
|
+
* 误判成「系列断裂」(不会误触发 `startsSeries`)。
|
|
14120
|
+
* 3. **造出来的 `assistant/message` 合法**:`stream: []` 通过校验;整段历史之后
|
|
14121
|
+
* 仍可被 compaction 的 `surfaceOp: replace` 整体覆盖(不炸)。
|
|
14122
|
+
*
|
|
14123
|
+
* ## ⚠️ 为什么不发 turn/start、turn/end
|
|
14124
|
+
*
|
|
14125
|
+
* 宿主的 turn 计数是**构造时缓存**的:`phase.lastTurn` 在本钩子触发前已读定(0),
|
|
14126
|
+
* 之后 `turn()` 恒用 `phase.turn + 1`。我们若自己发 `turn/start{1}{2}`,宿主随后
|
|
14127
|
+
* 仍会再发一条 `turn/start{1}` ⇒ **重复编号 ∧ 两个 turn 同时开着**。
|
|
14128
|
+
* ⇒ 只给消息打 **turn 标签**(模型可见顺序不依赖 turn 事件,`deriveMessages` 只看
|
|
14129
|
+
* surface 节点),**不碰宿主的 turn 账本**。
|
|
14130
|
+
*
|
|
14131
|
+
* @param session 会话(需有 append)
|
|
14132
|
+
* @param provider 当前路由 provider(写入 assistant 消息来源)
|
|
14133
|
+
* @param model 当前模型 id
|
|
14134
|
+
* @returns 是否成功写入
|
|
14135
|
+
*/
|
|
14136
|
+
function appendAnchorHistory(session, provider, model, anchorMessages = DEFAULT_ANCHOR_MESSAGES) {
|
|
14137
|
+
const target = session;
|
|
14138
|
+
if (typeof target?.append !== "function") return false;
|
|
14139
|
+
const append = target.append.bind(target);
|
|
14140
|
+
const rid = (prefix) => `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
14141
|
+
append("system/message", {
|
|
14142
|
+
turn: 1,
|
|
14143
|
+
step: 1,
|
|
14144
|
+
message: {
|
|
14145
|
+
id: rid("anchor-system"),
|
|
14146
|
+
role: "system",
|
|
14147
|
+
content: [{
|
|
14148
|
+
type: "text",
|
|
14149
|
+
text: ANCHOR_SYSTEM_PLACEHOLDER
|
|
14150
|
+
}],
|
|
14151
|
+
source: { kind: "system-prompt" }
|
|
14152
|
+
}
|
|
14153
|
+
}, { surfaceOp: "append" });
|
|
14154
|
+
for (let i = 0; i < anchorMessages.length; i++) {
|
|
14155
|
+
const turn = i + 1;
|
|
14156
|
+
append("user/message", {
|
|
14157
|
+
id: rid(`anchor-user-${turn}`),
|
|
14158
|
+
role: "user",
|
|
14159
|
+
content: [{
|
|
14160
|
+
type: "text",
|
|
14161
|
+
text: anchorMessages[i]
|
|
14162
|
+
}],
|
|
14163
|
+
source: { kind: "user" }
|
|
14164
|
+
}, { surfaceOp: "append" });
|
|
14165
|
+
append("assistant/message", {
|
|
14166
|
+
turn,
|
|
14167
|
+
step: 1,
|
|
14168
|
+
message: {
|
|
14169
|
+
id: rid(`anchor-assistant-${turn}`),
|
|
14170
|
+
role: "assistant",
|
|
14171
|
+
content: [{
|
|
14172
|
+
type: "text",
|
|
14173
|
+
text: ANCHOR_ACK_TEXT
|
|
14174
|
+
}],
|
|
14175
|
+
source: {
|
|
14176
|
+
kind: "model",
|
|
14177
|
+
provider,
|
|
14178
|
+
model
|
|
14179
|
+
}
|
|
14180
|
+
},
|
|
14181
|
+
stream: []
|
|
14182
|
+
}, { surfaceOp: "append" });
|
|
14183
|
+
}
|
|
14184
|
+
return true;
|
|
14185
|
+
}
|
|
14041
14186
|
/**
|
|
14042
14187
|
* 构建一个 epoch 感知晋升跟踪器(纯逻辑,可单测)。
|
|
14043
14188
|
* requiredSignals:晋升所需信号数(boundary 后累计;默认 1)。
|
|
@@ -14150,9 +14295,16 @@ const PROMOTE_EVENTS = {
|
|
|
14150
14295
|
"assistant-message": /* @__PURE__ */ new Set(["assistant/message"]),
|
|
14151
14296
|
either: /* @__PURE__ */ new Set(["tool/call", "assistant/message"])
|
|
14152
14297
|
};
|
|
14153
|
-
/** 协议固有 bootstrap
|
|
14154
|
-
*
|
|
14155
|
-
*
|
|
14298
|
+
/** 协议固有 bootstrap 设置:**zeroTools=false**(v1.60 起)。
|
|
14299
|
+
*
|
|
14300
|
+
* 🔴 **为什么从 true 改成 false**(owner 令 2026-10-17,造历史取代真跑):
|
|
14301
|
+
* 锚定历史现在**直接写进日志**,因此 `assistant/message` 晋升信号在首请求前
|
|
14302
|
+
* 就已满足 ⇒ `boundary < 0` 分支实际不可达。但**不能依赖"恰好满足"这个副作用**:
|
|
14303
|
+
* 若哪天锚定历史没写成(例如非 CCC 会话/异常降级),`zeroTools: true` 会退回到
|
|
14304
|
+
* 「首请求 0 工具」,那是**另一种**退化。显式关掉 ⇒ 任何情况下首请求都拿完整目录。
|
|
14305
|
+
*
|
|
14306
|
+
* ⚠️ 这意味着 `bootstrapTools` 窄化也随之失效 —— 它的作用本来就是"首请求窄目录",
|
|
14307
|
+
* 而造历史的目的正是**取消这个断点**。保留字段只为不破坏旧测试的引用面。 */
|
|
14156
14308
|
function resolveBootstrapSettings() {
|
|
14157
14309
|
return {
|
|
14158
14310
|
bootstrapTools: [...DEFAULT_BOOTSTRAP_TOOLS],
|
|
@@ -14161,7 +14313,7 @@ function resolveBootstrapSettings() {
|
|
|
14161
14313
|
suppressedSources: new Set(DEFAULT_SUPPRESSED_SOURCES),
|
|
14162
14314
|
compactionTools: [...DEFAULT_COMPACTION_TOOLS],
|
|
14163
14315
|
anchorMessages: [...DEFAULT_ANCHOR_MESSAGES],
|
|
14164
|
-
zeroTools:
|
|
14316
|
+
zeroTools: false
|
|
14165
14317
|
};
|
|
14166
14318
|
}
|
|
14167
14319
|
/**
|
|
@@ -14213,23 +14365,11 @@ function registerBootstrap(ctx) {
|
|
|
14213
14365
|
if (hasUserMessageHistory(session)) return;
|
|
14214
14366
|
} else if (sid !== void 0 && anchoredSessions.has(sid)) return;
|
|
14215
14367
|
if (message?.source?.kind === "plugin:dsh-serenity-hooks") return;
|
|
14216
|
-
const
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
content: [{
|
|
14222
|
-
type: "text",
|
|
14223
|
-
text: SETTINGS.anchorMessages[i]
|
|
14224
|
-
}],
|
|
14225
|
-
source: {
|
|
14226
|
-
kind: ACC_MESSAGE_KIND,
|
|
14227
|
-
form: "notice",
|
|
14228
|
-
summary: "bootstrap anchor turn"
|
|
14229
|
-
}
|
|
14230
|
-
});
|
|
14231
|
-
if (sid !== void 0) anchoredSessions.add(sid);
|
|
14232
|
-
warnOnce(`anchor turns injected: ${SETTINGS.anchorMessages.length} 条(${SETTINGS.anchorMessages[0].slice(0, 40)}…)`);
|
|
14368
|
+
const route = agent.options;
|
|
14369
|
+
const wrote = appendAnchorHistory(session, typeof route?.provider === "string" ? route.provider : "unknown", typeof route?.model === "string" ? route.model : "unknown");
|
|
14370
|
+
if (wrote && sid !== void 0) anchoredSessions.add(sid);
|
|
14371
|
+
if (wrote) warnOnce(`anchor history fabricated: ${SETTINGS.anchorMessages.length} 轮(不再真跑)`);
|
|
14372
|
+
return;
|
|
14233
14373
|
} catch {}
|
|
14234
14374
|
});
|
|
14235
14375
|
let warned = false;
|
|
@@ -14267,11 +14407,11 @@ function registerBootstrap(ctx) {
|
|
|
14267
14407
|
tools: tools.filter((tool) => typeof tool.name === "string" && keep.has(tool.name))
|
|
14268
14408
|
};
|
|
14269
14409
|
}
|
|
14270
|
-
|
|
14271
|
-
|
|
14410
|
+
if (status.boundary < 0) return assembled;
|
|
14411
|
+
const keep = new Set(SETTINGS.compactionTools);
|
|
14272
14412
|
const missing = [...keep].filter((name) => !available.has(name));
|
|
14273
14413
|
if (missing.length > 0) {
|
|
14274
|
-
warnOnce(`expected
|
|
14414
|
+
warnOnce(`expected compaction tools missing=${JSON.stringify(missing)} — bootstrap disabled, full catalog exposed`);
|
|
14275
14415
|
return assembled;
|
|
14276
14416
|
}
|
|
14277
14417
|
return {
|
|
@@ -14877,6 +15017,9 @@ function registerContext(ctx, opts = {}) {
|
|
|
14877
15017
|
try {
|
|
14878
15018
|
syncExclusiveToolsVisibility(agent, root);
|
|
14879
15019
|
} catch {}
|
|
15020
|
+
try {
|
|
15021
|
+
syncHostScheduleToolHiding(agent, root);
|
|
15022
|
+
} catch {}
|
|
14880
15023
|
};
|
|
14881
15024
|
if (opts.seedOnStart ?? true) ctx.on("agent/created", async (payload) => {
|
|
14882
15025
|
try {
|
|
@@ -14903,6 +15046,9 @@ function registerContext(ctx, opts = {}) {
|
|
|
14903
15046
|
try {
|
|
14904
15047
|
syncExclusiveToolsVisibility(agent, root);
|
|
14905
15048
|
} catch {}
|
|
15049
|
+
try {
|
|
15050
|
+
syncHostScheduleToolHiding(agent, root);
|
|
15051
|
+
} catch {}
|
|
14906
15052
|
registerEntrySkillSection(agent, root);
|
|
14907
15053
|
registerTrajectorySkillSection(agent, root);
|
|
14908
15054
|
}
|
|
@@ -16072,8 +16218,13 @@ const HOST_SERVICES = [
|
|
|
16072
16218
|
name: "subagents",
|
|
16073
16219
|
access: "lazy",
|
|
16074
16220
|
members: [{
|
|
16221
|
+
name: "startActivation",
|
|
16222
|
+
kind: "function",
|
|
16223
|
+
anyOf: "spawn-starter"
|
|
16224
|
+
}, {
|
|
16075
16225
|
name: "start",
|
|
16076
|
-
kind: "function"
|
|
16226
|
+
kind: "function",
|
|
16227
|
+
anyOf: "spawn-starter"
|
|
16077
16228
|
}],
|
|
16078
16229
|
impact: "cheap-worker 委派不可用(前台委派的正门,响亮报错)",
|
|
16079
16230
|
required: false
|
|
@@ -16319,13 +16470,30 @@ function probeHostContract(ctx, hostVersion = null) {
|
|
|
16319
16470
|
for (const member of svc.members) {
|
|
16320
16471
|
checked += 1;
|
|
16321
16472
|
const actual = value[member.name];
|
|
16322
|
-
if (!(member.kind === "function" ? typeof actual === "function" : actual !== void 0))
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16473
|
+
if (!(member.kind === "function" ? typeof actual === "function" : actual !== void 0)) {
|
|
16474
|
+
if (member.anyOf !== void 0) {
|
|
16475
|
+
const siblings = svc.members.filter((m) => m.anyOf === member.anyOf);
|
|
16476
|
+
if (siblings.some((m) => {
|
|
16477
|
+
const v = value[m.name];
|
|
16478
|
+
return m.kind === "function" ? typeof v === "function" : v !== void 0;
|
|
16479
|
+
})) continue;
|
|
16480
|
+
issues.push({
|
|
16481
|
+
id: `${svc.id}.${member.anyOf}`,
|
|
16482
|
+
kind: "member",
|
|
16483
|
+
detail: `service "${svc.name}" is missing every ${member.kind} of "${member.anyOf}" (${siblings.map((m) => m.name).join(" | ")})`,
|
|
16484
|
+
impact: svc.impact,
|
|
16485
|
+
required: svc.required
|
|
16486
|
+
});
|
|
16487
|
+
continue;
|
|
16488
|
+
}
|
|
16489
|
+
issues.push({
|
|
16490
|
+
id: `${svc.id}.${member.name}`,
|
|
16491
|
+
kind: "member",
|
|
16492
|
+
detail: `service "${svc.name}" is missing ${member.kind} "${member.name}"`,
|
|
16493
|
+
impact: svc.impact,
|
|
16494
|
+
required: svc.required
|
|
16495
|
+
});
|
|
16496
|
+
}
|
|
16329
16497
|
}
|
|
16330
16498
|
}
|
|
16331
16499
|
const version = checkHostVersion(hostVersion);
|
|
@@ -23125,6 +23293,9 @@ function cleanupSessionState(sessionId) {
|
|
|
23125
23293
|
try {
|
|
23126
23294
|
forgetNonCccToolVisibility(sessionId);
|
|
23127
23295
|
} catch {}
|
|
23296
|
+
try {
|
|
23297
|
+
forgetHostScheduleToolHiding(sessionId);
|
|
23298
|
+
} catch {}
|
|
23128
23299
|
try {
|
|
23129
23300
|
forgetLogbookCompactionState(sessionId);
|
|
23130
23301
|
} catch {}
|
package/lib/seams/bootstrap.d.ts
CHANGED
|
@@ -59,6 +59,35 @@ export declare const DEFAULT_ANCHOR_MESSAGES: string[];
|
|
|
59
59
|
* 统一经 sessionEvents() 读取(snapshotEvents() 优先,.events 兜底)。
|
|
60
60
|
*/
|
|
61
61
|
export declare function hasUserMessageHistory(session: unknown): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* 把两轮 first-anchor 对话**直接写进会话日志**,取代原来的「真跑两轮」。
|
|
64
|
+
*
|
|
65
|
+
* ## 为什么可以这样(三条取证,全部打在真 `@deepseek-ai/dsh-session` 上)
|
|
66
|
+
*
|
|
67
|
+
* 1. **system 必须是 surface node 0**,而 `agent/pre-step` 跑在宿主提交 system **之前**
|
|
68
|
+
* ⇒ 若直接写 [anchor…],system 会被追加到**末尾**(实测:`systemAtIndex: 4`,
|
|
69
|
+
* 且**不报错**、静默走偏)。解法 = **先写一条占位 system**:宿主随后看到 node0
|
|
70
|
+
* 已有 system 且文本不同 ⇒ 走 `replace(head)` 换成真提示词,锚定历史原样留在其后。
|
|
71
|
+
* 2. **`contentGeneration` 只在 replace 时涨,append 不涨**;而 `buildRequest()`
|
|
72
|
+
* 取 generation 快照**晚于** `systemPrompt.project()` ⇒ 那次 replace 不会被
|
|
73
|
+
* 误判成「系列断裂」(不会误触发 `startsSeries`)。
|
|
74
|
+
* 3. **造出来的 `assistant/message` 合法**:`stream: []` 通过校验;整段历史之后
|
|
75
|
+
* 仍可被 compaction 的 `surfaceOp: replace` 整体覆盖(不炸)。
|
|
76
|
+
*
|
|
77
|
+
* ## ⚠️ 为什么不发 turn/start、turn/end
|
|
78
|
+
*
|
|
79
|
+
* 宿主的 turn 计数是**构造时缓存**的:`phase.lastTurn` 在本钩子触发前已读定(0),
|
|
80
|
+
* 之后 `turn()` 恒用 `phase.turn + 1`。我们若自己发 `turn/start{1}{2}`,宿主随后
|
|
81
|
+
* 仍会再发一条 `turn/start{1}` ⇒ **重复编号 ∧ 两个 turn 同时开着**。
|
|
82
|
+
* ⇒ 只给消息打 **turn 标签**(模型可见顺序不依赖 turn 事件,`deriveMessages` 只看
|
|
83
|
+
* surface 节点),**不碰宿主的 turn 账本**。
|
|
84
|
+
*
|
|
85
|
+
* @param session 会话(需有 append)
|
|
86
|
+
* @param provider 当前路由 provider(写入 assistant 消息来源)
|
|
87
|
+
* @param model 当前模型 id
|
|
88
|
+
* @returns 是否成功写入
|
|
89
|
+
*/
|
|
90
|
+
export declare function appendAnchorHistory(session: unknown, provider: string, model: string, anchorMessages?: string[]): boolean;
|
|
62
91
|
interface PromotionStatus {
|
|
63
92
|
/** 最后一次 compaction/end 的 seq(-1 = 未压缩过) */
|
|
64
93
|
boundary: number;
|
|
@@ -85,9 +114,16 @@ interface PromotionTracker {
|
|
|
85
114
|
* 通常 ≤ 2 < 默认兜底 3,不受影响。
|
|
86
115
|
*/
|
|
87
116
|
export declare function createEpochPromotion(promoteEvents: Set<'tool/call' | 'assistant/message'>, requiredSignals?: number, maxRoundsFallback?: number): PromotionTracker;
|
|
88
|
-
/** 协议固有 bootstrap
|
|
89
|
-
*
|
|
90
|
-
*
|
|
117
|
+
/** 协议固有 bootstrap 设置:**zeroTools=false**(v1.60 起)。
|
|
118
|
+
*
|
|
119
|
+
* 🔴 **为什么从 true 改成 false**(owner 令 2026-10-17,造历史取代真跑):
|
|
120
|
+
* 锚定历史现在**直接写进日志**,因此 `assistant/message` 晋升信号在首请求前
|
|
121
|
+
* 就已满足 ⇒ `boundary < 0` 分支实际不可达。但**不能依赖"恰好满足"这个副作用**:
|
|
122
|
+
* 若哪天锚定历史没写成(例如非 CCC 会话/异常降级),`zeroTools: true` 会退回到
|
|
123
|
+
* 「首请求 0 工具」,那是**另一种**退化。显式关掉 ⇒ 任何情况下首请求都拿完整目录。
|
|
124
|
+
*
|
|
125
|
+
* ⚠️ 这意味着 `bootstrapTools` 窄化也随之失效 —— 它的作用本来就是"首请求窄目录",
|
|
126
|
+
* 而造历史的目的正是**取消这个断点**。保留字段只为不破坏旧测试的引用面。 */
|
|
91
127
|
export declare function resolveBootstrapSettings(): BootstrapSettings;
|
|
92
128
|
export declare function registerBootstrap(ctx: Context): void;
|
|
93
129
|
export {};
|
package/lib/seams/guards.d.ts
CHANGED
|
@@ -72,6 +72,25 @@ export declare function getImBridgeVisibilityDiagnostics(): {
|
|
|
72
72
|
export declare function syncImBridgeVisibility(agent: Agent, root: string): void;
|
|
73
73
|
/** 会话销毁清理(lifecycle 调用;防 per-会话 Map 无界增长——review F-08 同族) */
|
|
74
74
|
export declare function forgetImBridgeVisibility(sessionId: string): void;
|
|
75
|
+
/** 诊断面:本会话已屏蔽的宿主定时器工具(排查"工具为什么不见了") */
|
|
76
|
+
export declare function getHostScheduleHiddenDiagnostics(): {
|
|
77
|
+
hidden: string[];
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* 在 **CCC 内**屏蔽宿主自带的定时器工具(v1.59.5)。
|
|
81
|
+
*
|
|
82
|
+
* 判据 = **会话是否落在某个 CCC 内**(`root !== null`)——与 `syncCccToolVisibility` 互补:
|
|
83
|
+
* 那个管"不在 CCC ⇒ 隐藏**我们自己的**全部工具",本函数管"**在** CCC ⇒ 隐藏**宿主的**定时器工具"。
|
|
84
|
+
*
|
|
85
|
+
* 幂等;失败不阻断 step(最坏 = 那一个工具仍可见,但那只是"宿主的正常能力没被藏起来",
|
|
86
|
+
* 不会让会话坏掉)。逐个名字独立发 restrict ⇒ 任何一个名字不在册都不会连累其余三个。
|
|
87
|
+
*
|
|
88
|
+
* @param agent 目标会话
|
|
89
|
+
* @param root 该会话的 CCC 根;`null` ⇒ 非 CCC ⇒ **不动**(宿主的定时器在别处是正常能力)
|
|
90
|
+
*/
|
|
91
|
+
export declare function syncHostScheduleToolHiding(agent: Agent, root: string | null): void;
|
|
92
|
+
/** 会话销毁清理(lifecycle 调用;按 `${sessionId}::` 前缀回收) */
|
|
93
|
+
export declare function forgetHostScheduleToolHiding(sessionId: string): void;
|
|
75
94
|
/** 专属工具隐藏诊断(排查"工具为什么不在清单里") */
|
|
76
95
|
export declare function getExclusiveToolsDiagnostics(): {
|
|
77
96
|
hidden: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.61.0",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1 与 0.2.0-rc.1(双档)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|