@shgroup/dsh-serenity-hooks 1.63.2 → 1.65.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/README.md +15 -15
- package/dsh.plugin.json +3 -3
- package/lib/{ccc-B0ZGL9lx.js → ccc-C33QWoWZ.js} +3 -3
- package/lib/{ccc-roots-GIJ3A_tw.js → ccc-roots-RMzGU-CJ.js} +2 -2
- package/lib/ccc.d.ts +15 -50
- package/lib/client.js +0 -23
- package/lib/{trajectory-assistant.d.ts → cognitive-trajectory-assistant.d.ts} +9 -9
- package/lib/{trajectory-bound.d.ts → cognitive-trajectory-bound.d.ts} +10 -10
- package/lib/{trajectory-ops.d.ts → cognitive-trajectory-ops.d.ts} +6 -6
- package/lib/{trajectory-skills.d.ts → cognitive-trajectory-skills.d.ts} +8 -8
- package/lib/cro-log.d.ts +2 -2
- package/lib/cro.d.ts +1 -1
- package/lib/host/contract.d.ts +1 -1
- package/lib/index.d.ts +1 -5
- package/lib/index.js +245 -919
- package/lib/invariant.d.ts +1 -1
- package/lib/invariant.js +1 -1
- package/lib/json.d.ts +1 -1
- package/lib/{localstore-ops-DMs_ovHo.js → localstore-ops-ylQoPz1Q.js} +1 -1
- package/lib/{message-source-B4P3Uw6D.js → message-source--2veY0JU.js} +17 -17
- package/lib/output-guard-seam.d.ts +5 -3
- package/lib/output-guard.d.ts +1 -1
- package/lib/rebuild.d.ts +2 -2
- package/lib/seams/keeper.d.ts +6 -6
- package/lib/seams/system-prompt.d.ts +1 -1
- package/lib/{settings-section-DGUQZo5j.js → settings-section-B1YzjFz6.js} +1 -3
- package/lib/settings-section.d.ts +0 -10
- package/lib/skiff-core.d.ts +10 -10
- package/lib/skiff-debug.d.ts +1 -1
- package/lib/{skiff-role-B615L9MV.js → skiff-role-Boc4SKct.js} +14 -14
- package/lib/skiff-role.d.ts +4 -4
- package/lib/skills/resource-index.d.ts +1 -1
- package/lib/skills-discovery.d.ts +1 -1
- package/lib/tools/{trajectory.d.ts → cognitive-trajectory.d.ts} +7 -7
- package/lib/tools/skiff-admin.d.ts +1 -1
- package/lib/usage-stats.d.ts +1 -1
- package/lib/wake-registry.d.ts +5 -5
- package/lib/wake-scheduler.d.ts +5 -5
- package/lib/weixin-bridge.d.ts +1 -1
- package/lib/{weixin-route-Aheyq3fN.js → weixin-route-BoSC-uPp.js} +2 -2
- package/package.json +2 -2
- package/lib/unattended-ops.d.ts +0 -249
- package/lib/unattended-seam.d.ts +0 -49
package/lib/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
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-
|
|
3
|
-
import { _ as resolveInside, a as findSerenityRoot$1, c as matchBlacklist, d as readCccName$1, f as readCheapWorkerConfig, h as
|
|
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
|
|
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-
|
|
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-
|
|
7
|
-
import { a as
|
|
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-RMzGU-CJ.js";
|
|
3
|
+
import { _ as resolveInside, a as findSerenityRoot$1, c as matchBlacklist, d as readCccName$1, f as readCheapWorkerConfig, h as readSafeModeHiddenTools, i as findGitRoot, l as pathInside, m as readExclusiveTools, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readCognitiveTrajectorySkills, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-C33QWoWZ.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 COGNITIVE_TRAJECTORY_ACTIONS, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionCognitiveTrajectoryLabel, u as DEFAULT_SESSION_SCOPE, v as parseSessionContextFromEvents, w as showSession, x as sessionEvents$1, y as readActiveSessionMd } from "./message-source--2veY0JU.js";
|
|
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-ylQoPz1Q.js";
|
|
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-B1YzjFz6.js";
|
|
7
|
+
import { a as isSkiffSessionId, c as resolveSkiffKind, f as systemPromptSource, i as createRolePromptReader, l as roleMsmWhitelist, n as buildSkiffBasePrompt, o as readSkiffRoles, r as cognitiveTrajectorySubset, s as resolveRoleSystemPrompt, u as roleToolWhitelist } from "./skiff-role-Boc4SKct.js";
|
|
8
8
|
import { REGISTERED_TOOLS } from "./invariant.js";
|
|
9
|
-
import { a as readWeixinCredential, c as weixinInboundDir, i as matchWeixinRoute, l as weixinSessionIdFor, n as extractWeixinText, o as readWeixinSettings, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-
|
|
9
|
+
import { a as readWeixinCredential, c as weixinInboundDir, i as matchWeixinRoute, l as weixinSessionIdFor, n as extractWeixinText, o as readWeixinSettings, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-BoSC-uPp.js";
|
|
10
10
|
import { a as markdownToPlainText, c as sendTyping, i as getUpdates, l as sniffImageExt, n as downloadMedia, o as sendFileMessage, r as getConfig, s as sendTextMessage, t as TypingStatus } from "./weixin-api-BrRcPiQs.js";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import z from "@deepseek-ai/schemastery";
|
|
@@ -1073,7 +1073,7 @@ const NPX_BIN = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
|
1073
1073
|
const ACC_CATALOG = `═══ ACC Usage Catalog ═══
|
|
1074
1074
|
Directory of ACC capabilities — each area lists where to go for details (guides live with their feature; this catalog only points).
|
|
1075
1075
|
|
|
1076
|
-
① 轨迹本体 →
|
|
1076
|
+
① 轨迹本体 → cognitive_trajectory(list/show/create/use + rebuild 超限重建 + send-now 即时投递 + send-later 未来一次性唤醒
|
|
1077
1077
|
+ cro-guide CRO 编写指南)
|
|
1078
1078
|
原 logbook 已并入(v1.33 用户裁决:废除 logbook 词);close/archive 删(归档走 container_fs mv),
|
|
1079
1079
|
health/qa/summary 分别并入 use / list,hook-develop-guide 并进 container_admin msm guide
|
|
@@ -1087,15 +1087,15 @@ Directory of ACC capabilities — each area lists where to go for details (guide
|
|
|
1087
1087
|
④ 角色与对外面 → container_admin role(Skiff 认知子集角色:guide/validate/apply/list——skiff_admin 已并入)
|
|
1088
1088
|
3099 Skiff 调试问答页(人工调试面)。🔴 2026-10-15:自造 ACP 面已退役
|
|
1089
1089
|
⇒ 外部程序化接入改走官方 dsh --profile acp + 现成 ACP 客户端
|
|
1090
|
-
⑤ 自主与接入 →
|
|
1091
|
-
|
|
1090
|
+
⑤ 自主与接入 → cognitive_trajectory send-now(跨轨迹的**即时**递送,**不排队**:live 目标在跑就当场注入当前轮、空闲则立即起轮;非 live 冷载入——等效于直接 wake;**有同步回执**)
|
|
1091
|
+
cognitive_trajectory send-later(跨轨迹的"未来时刻 + 一条 message";fire-and-forget,无回执无回收)
|
|
1092
1092
|
weixin 微信桥(配置/扫码/路由/消息 hook——CCC 侧 weixin-doctor MSM:msm("weixin-doctor", ["guide"]))
|
|
1093
|
-
⑥ CCC 配置总览 → container_admin msm ccc-config(9 段:cheap-worker/sessionKeeper/localstore/hooks/safeMode/skiff/
|
|
1093
|
+
⑥ CCC 配置总览 → container_admin msm ccc-config(9 段:cheap-worker/sessionKeeper/localstore/hooks/safeMode/skiff/cognitiveTrajectory/weixin/**exclusiveTools**)
|
|
1094
1094
|
⑦ 注册表与安全 → mech-registry.json 由 container_admin msm register/deregister 管理(写保护——不可直接编辑)
|
|
1095
1095
|
dashboard health 输出 registry 完整性检查;损坏恢复指引见 health registry.issues
|
|
1096
1096
|
CCC 级开关:exclusiveTools(声明专属工具)/ safeMode.blacklist(写路径黑名单)
|
|
1097
1097
|
/ safeMode.hiddenTools(**安全模式开启期间**额外隐藏的工具,v1.51.4)
|
|
1098
|
-
CCC 级 skill 供给:
|
|
1098
|
+
CCC 级 skill 供给:cognitiveTrajectory.skills(v1.44.0——**本 CCC 每条轨迹都带**,含 skiff 会话;与轨迹自己的 frontmatter 声明取并集)
|
|
1099
1099
|
|
|
1100
1100
|
每区:一句话定位 + 详细入口(工具名 / guide 子命令)。详情永远以对应工具的 guide/ccc-config 为单一真相源。
|
|
1101
1101
|
`;
|
|
@@ -1181,7 +1181,7 @@ cheap-worker(杂工)工具可用模型白名单(provider/model 列表)
|
|
|
1181
1181
|
{ "cheap-worker": { "models": ["minimax-cn-coding-plan/MiniMax-M3"], "defaultModel": "minimax-cn-coding-plan/MiniMax-M3" } }
|
|
1182
1182
|
|
|
1183
1183
|
── 2. sessionKeeper.threshold / sessionKeeper.sessionMdMaxKB ──
|
|
1184
|
-
trajectory-assistant(TRAJECTORY-ASSISTANT · CHECKPOINT)提醒机制的积分阈值(非 headless 主 agent)。
|
|
1184
|
+
cognitive-trajectory-assistant(COGNITIVE-TRAJECTORY-ASSISTANT · CHECKPOINT)提醒机制的积分阈值(非 headless 主 agent)。
|
|
1185
1185
|
按工具调用加权 + 耗时计分;达到阈值注入提醒,要求模型回复 ACK 码。
|
|
1186
1186
|
|
|
1187
1187
|
Config:
|
|
@@ -1191,7 +1191,7 @@ trajectory-assistant(TRAJECTORY-ASSISTANT · CHECKPOINT)提醒机制的积
|
|
|
1191
1191
|
默认:150
|
|
1192
1192
|
|
|
1193
1193
|
▸ sessionMdMaxKB(v1.31.1 新增,v1.31.2 缺省 100→200):**活跃 SESSION.md 体积上限(KB,缺省 200;0 = 关闭)**。
|
|
1194
|
-
超过 → 注入 [TRAJECTORY-ASSISTANT · LOGBOOK COMPACTION] 提醒:暂停当前工作 →
|
|
1194
|
+
超过 → 注入 [COGNITIVE-TRAJECTORY-ASSISTANT · LOGBOOK COMPACTION] 提醒:暂停当前工作 →
|
|
1195
1195
|
加载 eap(praxis eap)→ 按 EAP 分层骨架重写 SESSION.md。四条原则(ACC 内嵌,随提醒给出):
|
|
1196
1196
|
① 保留 EAP 分层骨架(不压成时间流水账)② 内容可外移到 references 文件
|
|
1197
1197
|
(AGENT_SESSIONS/<会话>/references/*.md,SESSION.md 留链接)③ 允许整合/删除不重要事项
|
|
@@ -1229,9 +1229,9 @@ safe-mode 由 WebUI 开关控制(写 .serenity-safe-on 标记);黑名单
|
|
|
1229
1229
|
{ "safeMode": { "blacklist": [".secrets/"], "hiddenTools": ["workflow", "job_kill"] } }
|
|
1230
1230
|
|
|
1231
1231
|
── 6. skiff.roles(F4 认知子集角色)──
|
|
1232
|
-
Skiff 角色 = 全知全能 trajectory 的任意子集(CCC 定义)。每角色独立模型 + 双白名单
|
|
1232
|
+
Skiff 角色 = 全知全能 cognitive trajectory 的任意子集(CCC 定义)。每角色独立模型 + 双白名单
|
|
1233
1233
|
(MSM 白名单 msms[] 与非 MSM 工具白名单 tools[],白名单外全隐藏;skill 加载恒可用)。
|
|
1234
|
-
trajectory 纪律子集(session/keeper/rebuild 参与项)默认全关 = 完全独立。
|
|
1234
|
+
cognitive trajectory 纪律子集(session/keeper/rebuild 参与项)默认全关 = 完全独立。
|
|
1235
1235
|
systemPrompt 内联 或 systemPromptFile(.md 引用,推荐)——角色会话的完整人格/边界提示词。
|
|
1236
1236
|
validate 校验 / apply 生效 / list 查看:container_admin role <guide|validate|apply|list>(skiff_admin 已并入)。
|
|
1237
1237
|
|
|
@@ -1244,31 +1244,31 @@ validate 校验 / apply 生效 / list 查看:container_admin role <guide|valid
|
|
|
1244
1244
|
"systemPromptFile": ".opencode/skiff/qa.md"
|
|
1245
1245
|
} } } }
|
|
1246
1246
|
|
|
1247
|
-
── 7.
|
|
1248
|
-
CCC 的一条 trajectory = 持久身体(SESSION.md)+ 时间轴(AGENT_SESSIONS/ 目录 + 唤醒注册表)。
|
|
1247
|
+
── 7. cognitive_trajectory(轨迹 + 唤醒注册表,D58)──
|
|
1248
|
+
CCC 的一条 cognitive trajectory = 持久身体(SESSION.md)+ 时间轴(AGENT_SESSIONS/ 目录 + 唤醒注册表)。
|
|
1249
1249
|
一个 CCC 内可多条并行;条目全部由人类/agent 显式登记。
|
|
1250
1250
|
⚠️ ACC 不再提供**周期自唤醒**(原 autopilot,2026-09-15 整段退场:机制/时钟/container_admin autopilot
|
|
1251
1251
|
域/面板区块一并删除)。自主巡航改由 CCC 自己驱动(home-serenity 侧 msm autopilot-round 每轮自排下一轮,
|
|
1252
|
-
真相源 = S151 SESSION.md §1/§1b/§1c)。**本轮的
|
|
1252
|
+
真相源 = S151 SESSION.md §1/§1b/§1c)。**本轮的 cognitiveTrajectory.autopilot 配置段保留**(见下表)——
|
|
1253
1253
|
它现由 **CCC 侧脚本**读取,ACC 不再消费该段。
|
|
1254
|
-
唤醒注册表(
|
|
1254
|
+
唤醒注册表(cognitive_trajectory send-later):唤醒 = 未来时刻 + 一条 message,可唤醒自己或别的 cognitive trajectory;
|
|
1255
1255
|
落点 AGENT_SESSIONS/wake-registry.json,中心调度器到点投递(fire-and-forget;无回执、无回收)。
|
|
1256
1256
|
运行态诊断(ACC 负责人专用):acc-diag(① live 运行态 / ①b 唤醒时钟 / ③ 唤醒注册表)。
|
|
1257
1257
|
|
|
1258
|
-
▸
|
|
1258
|
+
▸ cognitiveTrajectory.skills(v1.44.0,**CCC 级 skill 供给**;owner 令 2026-09-20「要求实现我的诉求……
|
|
1259
1259
|
注入是动态注入就行;create 先不触发吧;**skiff 也支持**」):
|
|
1260
1260
|
**本 CCC 的每条轨迹都带这些 skill**——声明的 skill 全文在该轨迹**被绑定期间**注入系统提示词
|
|
1261
1261
|
(**动态注入**:每请求重新求值、不缓存 ⇒ 改配置或改 frontmatter **即时生效**,无需重新 use、无需重启)。
|
|
1262
|
-
两条声明来源**并集**(不是二选一):本键 **
|
|
1262
|
+
两条声明来源**并集**(不是二选一):本键 **cognitiveTrajectory.skills**(容器给的底座,**在前**)
|
|
1263
1263
|
+ 轨迹自己 SESSION.md 顶部 frontmatter 的 skills:(这条轨迹额外的,**追加**;同名去重)。
|
|
1264
1264
|
✅ **含 skiff 角色会话**(旁路的是 ACC 身份与纪律;skill 是"工作资料",照给)。
|
|
1265
1265
|
守卫:名字先过安全校验才用于拼路径(防穿越)|**合并后**统一 32 KB 上限截断|找不到响亮写 [缺失]。
|
|
1266
|
-
⚠️ 与 create **无关**(create 刻意不夺绑定,U6)⇒ 注入的门是"**存在绑定**"(
|
|
1266
|
+
⚠️ 与 create **无关**(create 刻意不夺绑定,U6)⇒ 注入的门是"**存在绑定**"(cognitive_trajectory use)。
|
|
1267
1267
|
失败语义:未配置 / 字段缺失 / 配置损坏 ⇒ **空数组**(= 无 CCC 级供给;轨迹级声明照旧生效)。
|
|
1268
1268
|
归属(D23):**机制在 ACC**(读取 + 合并 + 注入 + 守卫),**声明在 CCC**(本键)。
|
|
1269
1269
|
|
|
1270
1270
|
Config:
|
|
1271
|
-
{ "
|
|
1271
|
+
{ "cognitiveTrajectory": {
|
|
1272
1272
|
"skills": ["acc-session", "cce"], // v1.44.0:本 CCC 的每条轨迹都带(含 skiff 会话)
|
|
1273
1273
|
"autopilot": { // 原 autopilotTrajectory 段,字段不变(旧键仍回退读)
|
|
1274
1274
|
"enabled": true,
|
|
@@ -2036,7 +2036,7 @@ function scanSerenityDirs(skillsRoot) {
|
|
|
2036
2036
|
/**
|
|
2037
2037
|
* 防穿越:skill 名必须是简单目录名(拒绝分隔符与 `..`/`.` 路径段)。
|
|
2038
2038
|
* v1.34.1:导出——轨迹 skill 声明(CCC 数据)的名字也要**先过这一关**才能用于拼路径
|
|
2039
|
-
* (规格 `docs/trajectory-skill-injection.md` §3「名字安全(必做)」)。
|
|
2039
|
+
* (规格 `docs/cognitive-trajectory-skill-injection.md` §3「名字安全(必做)」)。
|
|
2040
2040
|
*/
|
|
2041
2041
|
function isSafeSkillName(name) {
|
|
2042
2042
|
if (name.length === 0) return false;
|
|
@@ -2129,7 +2129,7 @@ function truncateContent(content, maxChars) {
|
|
|
2129
2129
|
*
|
|
2130
2130
|
* 🔴 **两个面,两个入口**(2026-10-12 增;owner 裁决 O2)—— 同源、但成本形态不同:
|
|
2131
2131
|
* · **载入面** `buildResourceIndex()` —— 完整索引(名字 + 首标题 + 字节),带 `.md` 文件头读取;
|
|
2132
|
-
* 在三条路径上各附一次:入口段(`system-prompt.ts` order −50)/轨迹段(`trajectory-skills.ts`
|
|
2132
|
+
* 在三条路径上各附一次:入口段(`system-prompt.ts` order −50)/轨迹段(`cognitive-trajectory-skills.ts`
|
|
2133
2133
|
* order −45)/目录 provider 的 `get()`(`opencode-skills.ts`)。
|
|
2134
2134
|
* · **目录面** `buildResourceNameSuffix()` —— **只有名字**,**不读任何正文**;只挂一处:
|
|
2135
2135
|
* 目录 provider 的 `list()`(`available_skills` 的 `SkillSummary.description`)。
|
|
@@ -2390,24 +2390,24 @@ function buildResourceNameSuffix(skillDir, options = {}) {
|
|
|
2390
2390
|
return `(参考件:${names.join(" / ")}${tail})`;
|
|
2391
2391
|
}
|
|
2392
2392
|
//#endregion
|
|
2393
|
-
//#region src/trajectory-skills.ts
|
|
2393
|
+
//#region src/cognitive-trajectory-skills.ts
|
|
2394
2394
|
/**
|
|
2395
|
-
* trajectory-skills.ts — 轨迹 skill 声明解析 + 注入内容装配(纯逻辑,零 DSH 依赖)
|
|
2395
|
+
* cognitive-trajectory-skills.ts — 轨迹 skill 声明解析 + 注入内容装配(纯逻辑,零 DSH 依赖)
|
|
2396
2396
|
*
|
|
2397
|
-
* 接口真相源:`docs/trajectory-skill-injection.md` v1.0(§2 数据契约 / §3 注入契约)。
|
|
2397
|
+
* 接口真相源:`docs/cognitive-trajectory-skill-injection.md` v1.0(§2 数据契约 / §3 注入契约)。
|
|
2398
2398
|
* 一句话:**轨迹自己声明它要挂哪些 skill**(`SESSION.md` 顶部 YAML frontmatter 的 `skills:`),
|
|
2399
2399
|
* ACC 在**绑定期间**把这些 skill 的 `SKILL.md` 全文注入系统提示词。
|
|
2400
2400
|
*
|
|
2401
2401
|
* 分工(R↓:为什么解析与注册分开):
|
|
2402
2402
|
* · 本模块 = **纯文本层**(解析 / 装配 / 安全判定)——可脱离 DSH 单测;
|
|
2403
|
-
* · `seams/system-prompt.ts` 的 `
|
|
2403
|
+
* · `seams/system-prompt.ts` 的 `registerCognitiveTrajectorySkillSection` = **宿主接入层**
|
|
2404
2404
|
* (per-agent `ctx.systemPrompt.section`),只做门与注册,不再解析文本。
|
|
2405
2405
|
*
|
|
2406
2406
|
* 边界(规格 §2.2):**不解析** `skills` 以外任何键的语义(CCC 可自由使用,ACC 视而不见);
|
|
2407
2407
|
* **不改写** frontmatter(ACC 只读)。
|
|
2408
2408
|
*/
|
|
2409
2409
|
/** 全文注入总长上限(字符数;规格 §3「缺省 32 KB」——防上下文爆炸) */
|
|
2410
|
-
const
|
|
2410
|
+
const COGNITIVE_TRAJECTORY_SKILLS_MAX_CHARS = 32768;
|
|
2411
2411
|
/** 缺失标记(规格 §3/§6-3 的验收锚点:任何"拿不到全文"的情况都必须带它,绝不静默)
|
|
2412
2412
|
* 🔴 2026-09-25(S142 分批清死代码·第 1 批):**降为模块内常量**——全仓唯一消费者在本文件,
|
|
2413
2413
|
* 导出只服务测试(测试用的是字面量 `'[缺失]'`)⇒ 导出面无意义。 */
|
|
@@ -2496,7 +2496,7 @@ function absoluteMdPath(root, mdPath) {
|
|
|
2496
2496
|
* (规格 §6-6 要求"不静默";不注册就等于静默)
|
|
2497
2497
|
* 其余(文件在但无 frontmatter / 无 `skills` 键 / 值为空)⇒ false,不注册、不产生噪声。
|
|
2498
2498
|
*/
|
|
2499
|
-
function
|
|
2499
|
+
function hasCognitiveTrajectorySkillsDeclaration(root, mdPath) {
|
|
2500
2500
|
const abs = absoluteMdPath(root, mdPath);
|
|
2501
2501
|
if (!existsSync(abs)) return true;
|
|
2502
2502
|
try {
|
|
@@ -2521,7 +2521,7 @@ function normalizeNames(names) {
|
|
|
2521
2521
|
* 合并两条声明来源(v1.44.0):**CCC 级在前,轨迹级追加**,同名去重取首次位置。
|
|
2522
2522
|
*
|
|
2523
2523
|
* 两条来源(owner 令 2026-09-20):
|
|
2524
|
-
* · **CCC 级** = `.opencode/serenity.json` 的 `
|
|
2524
|
+
* · **CCC 级** = `.opencode/serenity.json` 的 `cognitiveTrajectory.skills`(本 CCC 所有轨迹共有的底座);
|
|
2525
2525
|
* · **轨迹级** = 该轨迹 `SESSION.md` frontmatter 的 `skills:`(这条轨迹额外的)。
|
|
2526
2526
|
*
|
|
2527
2527
|
* 为什么是**并集**而不是二选一(R↓):C1 设计当初否决"纯 CCC 全局"的理由是"会**丢掉
|
|
@@ -2545,14 +2545,14 @@ function mergeSkillNames(cccSkills, trajSkills) {
|
|
|
2545
2545
|
* @param root CCC 根
|
|
2546
2546
|
* @param mdPath 轨迹的 SESSION.md 路径(**来自绑定的 `mdPath`**,不用 label 拼)
|
|
2547
2547
|
* @param maxChars 总长上限(字符)
|
|
2548
|
-
* @param cccSkills CCC 级声明(`
|
|
2548
|
+
* @param cccSkills CCC 级声明(`readCognitiveTrajectorySkills`;缺省空 = 只有轨迹级,行为与 v1.43 一致)
|
|
2549
2549
|
* @param onInjectedNames 观测回调(本次请求实际带上的 skill 名;**纯观测**,供用量统计用——
|
|
2550
2550
|
* 回调抛错被吞,**不影响注入正文**)
|
|
2551
2551
|
* @returns 注入正文;两条来源皆无声明 → `''`(空串 = 宿主不产出该 section 块);
|
|
2552
2552
|
* SESSION.md 缺失/读失败 → 响亮提示(若同时有 CCC 级声明,则提示在前、正文随后——
|
|
2553
2553
|
* **提示不因"别处有内容"而被吞掉**)
|
|
2554
2554
|
*/
|
|
2555
|
-
function
|
|
2555
|
+
function buildCognitiveTrajectorySkillsSection(root, mdPath, maxChars = COGNITIVE_TRAJECTORY_SKILLS_MAX_CHARS, cccSkills = [], onInjectedNames) {
|
|
2556
2556
|
const abs = absoluteMdPath(root, mdPath);
|
|
2557
2557
|
let trajNames = [];
|
|
2558
2558
|
let notice = "";
|
|
@@ -2631,7 +2631,7 @@ function buildTrajectorySkillsSection(root, mdPath, maxChars = TRAJECTORY_SKILLS
|
|
|
2631
2631
|
* | 字段 | 含义 | 喂它的地方 |
|
|
2632
2632
|
* |---|---|---|
|
|
2633
2633
|
* | `skill.loads` | 模型**主动**调宿主 `skill` 工具(把某 skill 的 `SKILL.md` 全文载入) | `tools/post-execute`(工具名 = `skill`) |
|
|
2634
|
-
* | `skill.injections` | ACC **每请求注入**的 skill 段(`
|
|
2634
|
+
* | `skill.injections` | ACC **每请求注入**的 skill 段(`cognitiveTrajectory.skills` / 轨迹 frontmatter 声明) | `seams/system-prompt.ts` 的 cognitive-trajectory-skills section 求值回调 |
|
|
2635
2635
|
* | `msm` | `msm` 工具的一次执行,按 **MSM 名**分桶 | `tools/post-execute`(工具名 = `msm`) |
|
|
2636
2636
|
*
|
|
2637
2637
|
* 🔴 **两者差好几个数量级**:`loads` 是模型的**动作**(稀疏),`injections` 是**请求的属性**(稠密)。
|
|
@@ -3220,7 +3220,7 @@ function toolsBlock() {
|
|
|
3220
3220
|
"The ACC (this plugin) provides the following built-in tools:",
|
|
3221
3221
|
"",
|
|
3222
3222
|
" container_fs — container filesystem operations (15 subcommands: root/resolve/exists/list/tree/relative/mkdir/rm/mv/cp/touch/append/reveal/info/find)",
|
|
3223
|
-
"
|
|
3223
|
+
" cognitive_trajectory — one cognitive trajectory: its persistent body (SESSION.md) + its timeline. Lifecycle: list (inventory + stats) / show / create / use (activate; inline integrity check — silent when fine) / rebuild (clear-and-rebuild this conversation in place, Ship of Theseus); delivery: send-now (deliver one message RIGHT NOW; live target injected immediately, non-live cold-resumed like a direct wake — returns a synchronous receipt) / send-later (one future instant + one message to any cognitive trajectory; fire-and-forget — no receipt, no recall) / cro-guide (the CRO programming guide: how to give THIS cognitive trajectory its own program that decides when it should be woken)",
|
|
3224
3224
|
" dashboard — always-on container instruments: health (CCC three-principle check + registry integrity) / time (now) / wait (N seconds)",
|
|
3225
3225
|
" container_git— git operations (status/commit/push/log/pull/diff)",
|
|
3226
3226
|
" msm — execute a registered CCC MSM: msm(name, args); partial name returns candidates; inspect=true shows usage",
|
|
@@ -3256,7 +3256,7 @@ function cceBlock() {
|
|
|
3256
3256
|
"",
|
|
3257
3257
|
"1. Continuity — every interaction modifies the container's future state. Before",
|
|
3258
3258
|
" acting, consult what came before — prior decisions, abstractions, constraints.",
|
|
3259
|
-
" You are part of a trajectory, not a fresh start.",
|
|
3259
|
+
" You are part of a cognitive trajectory, not a fresh start.",
|
|
3260
3260
|
"",
|
|
3261
3261
|
"2. Bounded Space — the container has boundaries. Respect them. Do not assume",
|
|
3262
3262
|
" knowledge that has not been accumulated within this container.",
|
|
@@ -3307,10 +3307,10 @@ function principlesBlock(root, omitMsmPrinciples = false) {
|
|
|
3307
3307
|
"fault to be hidden. Never disguise or excuse what you do not know;",
|
|
3308
3308
|
"not-knowing is a state to be repaired, and reporting it is the first repair.",
|
|
3309
3309
|
"",
|
|
3310
|
-
"The session-trajectory relation: a session is the rebuildable carrier of a",
|
|
3311
|
-
"trajectory. SESSION.md is the trajectory's persistent body — it never moves;",
|
|
3310
|
+
"The session–cognitive-trajectory relation: a session is the rebuildable carrier of a",
|
|
3311
|
+
"cognitive trajectory. SESSION.md is the cognitive trajectory's persistent body — it never moves;",
|
|
3312
3312
|
"the current conversation is a temporary work copy that may be discarded and",
|
|
3313
|
-
"rebuilt (
|
|
3313
|
+
"rebuilt (cognitive_trajectory rebuild). Identity belongs to the cognitive trajectory, not to any",
|
|
3314
3314
|
"session.",
|
|
3315
3315
|
""
|
|
3316
3316
|
];
|
|
@@ -3409,15 +3409,16 @@ function metaphorBlock() {
|
|
|
3409
3409
|
" ballast (constraints) before setting course. Verdict: skipping the",
|
|
3410
3410
|
" inspection and launching directly = flying uninspected.",
|
|
3411
3411
|
"",
|
|
3412
|
-
"7. The Ship's Log → Trajectory Tracking. SESSION.md is the
|
|
3412
|
+
"7. The Ship's Log → Cognitive Trajectory Tracking. SESSION.md is the",
|
|
3413
|
+
" cognitive trajectory's log —",
|
|
3413
3414
|
" the persistent body of the voyage; sessions are rebuildable carriers of",
|
|
3414
|
-
" the trajectory. Discard the carrier, keep the log. Unrecorded =",
|
|
3415
|
+
" the cognitive trajectory. Discard the carrier, keep the log. Unrecorded =",
|
|
3415
3416
|
" unvoyaged. Verdict: finishing multi-step work without a progress record",
|
|
3416
3417
|
" = a missing page.",
|
|
3417
3418
|
"",
|
|
3418
3419
|
"8. The Ship of Theseus → Continuity. Planks may be replaced; the ship",
|
|
3419
3420
|
" remains the same. The container can be rebuilt; identity persists.",
|
|
3420
|
-
" You are part of a trajectory, not a new ship. Verdict: acting",
|
|
3421
|
+
" You are part of a cognitive trajectory, not a new ship. Verdict: acting",
|
|
3421
3422
|
" without consulting precedent = a different ship.",
|
|
3422
3423
|
"",
|
|
3423
3424
|
"THE CREW — multi-agent collaboration",
|
|
@@ -3532,10 +3533,10 @@ function sessionBlock(root, scope = DEFAULT_SESSION_SCOPE) {
|
|
|
3532
3533
|
return [
|
|
3533
3534
|
"",
|
|
3534
3535
|
"=== Serenity Session ===",
|
|
3535
|
-
`Active session: ${active.sessionId} — ${active.dirName} (this session is the rebuildable carrier of the trajectory)`,
|
|
3536
|
-
`SESSION.md path: ${active.mdPath} (the trajectory's persistent body — stays in place through rebuilds)`,
|
|
3536
|
+
`Active session: ${active.sessionId} — ${active.dirName} (this session is the rebuildable carrier of the cognitive trajectory)`,
|
|
3537
|
+
`SESSION.md path: ${active.mdPath} (the cognitive trajectory's persistent body — stays in place through rebuilds)`,
|
|
3537
3538
|
"",
|
|
3538
|
-
"Rules (SESSION.md is the trajectory log — keep its format & granularity fixed):",
|
|
3539
|
+
"Rules (SESSION.md is the cognitive trajectory log — keep its format & granularity fixed):",
|
|
3539
3540
|
" • Write only when a MILESTONE landed or a DECISION was made — not every round",
|
|
3540
3541
|
" • One entry = one abstraction level, at the highest one that can express it:",
|
|
3541
3542
|
" identity/anchors (rare) › decision (one line + rationale/rejected) › milestone",
|
|
@@ -3559,12 +3560,12 @@ function sessionBlock(root, scope = DEFAULT_SESSION_SCOPE) {
|
|
|
3559
3560
|
"This preserves the session context across todo updates.",
|
|
3560
3561
|
"Do NOT remove or reorder this item — keep it at position 0.",
|
|
3561
3562
|
"",
|
|
3562
|
-
"TRAJECTORY-ASSISTANT: a background tracker scores your tool use (write/edit=3,",
|
|
3563
|
+
"COGNITIVE-TRAJECTORY-ASSISTANT: a background tracker scores your tool use (write/edit=3,",
|
|
3563
3564
|
"task=10, read/grep/glob/msm=1, +1 per minute) and reminds you with a",
|
|
3564
|
-
"[TRAJECTORY-ASSISTANT · CHECKPOINT] message when the threshold is reached. On every such",
|
|
3565
|
+
"[COGNITIVE-TRAJECTORY-ASSISTANT · CHECKPOINT] message when the threshold is reached. On every such",
|
|
3565
3566
|
"reminder you MUST reply with the exact ACK code:",
|
|
3566
|
-
" [TRAJECTORY-ASSISTANT-recorded-{code}] — if you recorded progress to SESSION.md",
|
|
3567
|
-
" [TRAJECTORY-ASSISTANT-skipped-{code}] — if nothing to record this round",
|
|
3567
|
+
" [COGNITIVE-TRAJECTORY-ASSISTANT-recorded-{code}] — if you recorded progress to SESSION.md",
|
|
3568
|
+
" [COGNITIVE-TRAJECTORY-ASSISTANT-skipped-{code}] — if nothing to record this round",
|
|
3568
3569
|
"Do not ignore the reminder; do not stop ongoing work. Codes are single-use;",
|
|
3569
3570
|
"never reuse a prior code.",
|
|
3570
3571
|
""
|
|
@@ -3704,15 +3705,15 @@ function registerEntrySkillSection(agent, root) {
|
|
|
3704
3705
|
}
|
|
3705
3706
|
}
|
|
3706
3707
|
/**
|
|
3707
|
-
* 轨迹 skill 注入 section(规格 `docs/trajectory-skill-injection.md` §3,v1.34.1)。
|
|
3708
|
+
* 轨迹 skill 注入 section(规格 `docs/cognitive-trajectory-skill-injection.md` §3,v1.34.1)。
|
|
3708
3709
|
*
|
|
3709
3710
|
* **声明来源(两条,并集)**:
|
|
3710
|
-
* · **CCC 级**(v1.44.0,owner 令)= `.opencode/serenity.json` 的 `
|
|
3711
|
+
* · **CCC 级**(v1.44.0,owner 令)= `.opencode/serenity.json` 的 `cognitiveTrajectory.skills`
|
|
3711
3712
|
* ——**本 CCC 的每条轨迹都带**(含 skiff 角色会话),是容器给的底座;
|
|
3712
3713
|
* · **轨迹级** = 该会话绑定的轨迹(`readLastBound(session).mdPath` = 它的 `SESSION.md`)
|
|
3713
3714
|
* 顶部 frontmatter 的 `skills:`——这条轨迹额外的。
|
|
3714
|
-
* 合并规则与顺序见 `trajectory-skills.mergeSkillNames`(CCC 级在前,同名去重)。
|
|
3715
|
-
* 正文 = 合并清单里各 skill 的 `SKILL.md` 全文(本模块只注册,装配在 `trajectory-skills.ts`)。
|
|
3715
|
+
* 合并规则与顺序见 `cognitive-trajectory-skills.mergeSkillNames`(CCC 级在前,同名去重)。
|
|
3716
|
+
* 正文 = 合并清单里各 skill 的 `SKILL.md` 全文(本模块只注册,装配在 `cognitive-trajectory-skills.ts`)。
|
|
3716
3717
|
*
|
|
3717
3718
|
* 为什么是 **per-agent scoped section**(与 `registerEntrySkillSection` 同形):注入必须
|
|
3718
3719
|
* **绑定期间持续存在**(a2)——一次性注入会随对话压缩消失,而 section 每次装配重新求值。
|
|
@@ -3723,21 +3724,21 @@ function registerEntrySkillSection(agent, root) {
|
|
|
3723
3724
|
* 自动跟上(**动态注入,不缓存**),且绑定消失时返回 `''`
|
|
3724
3725
|
* (宿主 renderSections **丢弃空 section**,不残留噪声)。
|
|
3725
3726
|
*
|
|
3726
|
-
* ⚠️ 路径取自绑定的 `mdPath`(权威记录),**不用** `
|
|
3727
|
+
* ⚠️ 路径取自绑定的 `mdPath`(权威记录),**不用** `resolveSessionCognitiveTrajectoryLabel` 的显示 label
|
|
3727
3728
|
* 去拼路径——label 是模糊匹配的展示码,拼路径即模糊匹配当精确匹配用。
|
|
3728
3729
|
*
|
|
3729
3730
|
* @param agent 目标 agent(其 `session` 需有 `header.id` + `header.cwd`)
|
|
3730
3731
|
* @param root 该 agent 的 CCC 根
|
|
3731
3732
|
* @returns 是否**本次**完成注册(已注册过 / 门未过 → false)
|
|
3732
3733
|
*/
|
|
3733
|
-
const
|
|
3734
|
-
function
|
|
3734
|
+
const cognitiveTrajectorySkillAgents = /* @__PURE__ */ new Set();
|
|
3735
|
+
function registerCognitiveTrajectorySkillSection(agent, root) {
|
|
3735
3736
|
const session = agent.session;
|
|
3736
3737
|
const key = session.id ?? "global";
|
|
3737
|
-
if (
|
|
3738
|
+
if (cognitiveTrajectorySkillAgents.has(key)) return false;
|
|
3738
3739
|
const bound = readLastBound(session);
|
|
3739
3740
|
if (!bound) return false;
|
|
3740
|
-
if (
|
|
3741
|
+
if (readCognitiveTrajectorySkills(root).length === 0 && !hasCognitiveTrajectorySkillsDeclaration(root, bound.mdPath)) return false;
|
|
3741
3742
|
try {
|
|
3742
3743
|
agent.ctx.systemPrompt.section({
|
|
3743
3744
|
name: "serenity-trajectory-skills",
|
|
@@ -3745,10 +3746,10 @@ function registerTrajectorySkillSection(agent, root) {
|
|
|
3745
3746
|
text: () => {
|
|
3746
3747
|
const current = readLastBound(session);
|
|
3747
3748
|
if (!current) return "";
|
|
3748
|
-
return
|
|
3749
|
+
return buildCognitiveTrajectorySkillsSection(root, current.mdPath, COGNITIVE_TRAJECTORY_SKILLS_MAX_CHARS, readCognitiveTrajectorySkills(root), (names) => recordSkillInjections(root, names));
|
|
3749
3750
|
}
|
|
3750
3751
|
});
|
|
3751
|
-
|
|
3752
|
+
cognitiveTrajectorySkillAgents.add(key);
|
|
3752
3753
|
return true;
|
|
3753
3754
|
} catch {
|
|
3754
3755
|
return false;
|
|
@@ -3783,7 +3784,7 @@ const AUTO_BOUND_NOTE_PREFIX = "auto-created for skiff role";
|
|
|
3783
3784
|
* 系统提示词中的**工作台纪律块**(v1.30.4 升级:从单行路径 → 完整 ACC 层约束)。
|
|
3784
3785
|
* 用户点破(S142):只给路径 LLM 不会主动用——需注入「已绑定 + 使用纪律 + 动作指引」。
|
|
3785
3786
|
* SESSION = 工作台——skiff 与主舱同一套机制(零特调,用户拍板):
|
|
3786
|
-
* 自动绑定已生效(无需
|
|
3787
|
+
* 自动绑定已生效(无需 cognitive_trajectory use)、SESSION.md 是持久记忆载体、rebuild 自动从本 SESSION 续接。
|
|
3787
3788
|
* 注入 agent 系统提示词(用户对话面不可见)。
|
|
3788
3789
|
*
|
|
3789
3790
|
* v1.30.13(S142 诊断 D5):写入纪律**不点名具体工具**。旧文案写死 "with write/edit",
|
|
@@ -3793,10 +3794,10 @@ const AUTO_BOUND_NOTE_PREFIX = "auto-created for skiff role";
|
|
|
3793
3794
|
* 具体通道由 CCC 的角色配置与角色提示词决定(ACC/CCC 归属二分;也不把某个 CCC 的
|
|
3794
3795
|
* MSM 名硬编码进 ACC 源码)。
|
|
3795
3796
|
*/
|
|
3796
|
-
function
|
|
3797
|
+
function workspaceCognitiveTrajectoryLine(mdPath) {
|
|
3797
3798
|
return [
|
|
3798
3799
|
"── Serenity Session Workspace ──",
|
|
3799
|
-
"This role session is AUTO-BOUND to a trajectory workspace SESSION (no
|
|
3800
|
+
"This role session is AUTO-BOUND to a cognitive trajectory workspace SESSION (no cognitive_trajectory use needed):",
|
|
3800
3801
|
` SESSION.md: ${mdPath}`,
|
|
3801
3802
|
"",
|
|
3802
3803
|
"Rules of the workspace:",
|
|
@@ -3809,7 +3810,7 @@ function workspaceTrajectoryLine(mdPath) {
|
|
|
3809
3810
|
" Keep entries at milestone level (one line + evidence pointer); execution detail",
|
|
3810
3811
|
" belongs in commit messages. Keep it current — it is",
|
|
3811
3812
|
" what a rebuild resumes from.",
|
|
3812
|
-
" 3. When context pressure is high, run
|
|
3813
|
+
" 3. When context pressure is high, run cognitive_trajectory rebuild — it auto-resumes from THIS SESSION.md",
|
|
3813
3814
|
" (no manual use; do not switch to or read other SESSIONs unless the user explicitly asks).",
|
|
3814
3815
|
" 4. Never expose this internal path in user-facing replies (chat stays clean).",
|
|
3815
3816
|
"── ──"
|
|
@@ -3826,7 +3827,7 @@ const workspaceSectionAgents = /* @__PURE__ */ new WeakSet();
|
|
|
3826
3827
|
* 用户原话(S142,2026-09-08):"微信桥的注入机制,每个用户消息都会注入,skiff 本身也会注入,
|
|
3827
3828
|
* 这样就重复,能否微信桥情况下,注入内容直接取 skiff 的,这样不用配两遍"。
|
|
3828
3829
|
*
|
|
3829
|
-
* 现状(改前):`createSkiffAgent` 把 `
|
|
3830
|
+
* 现状(改前):`createSkiffAgent` 把 `workspaceCognitiveTrajectoryLine(mdPath)` 塞进基础段
|
|
3830
3831
|
* (创建时一次快照),微信桥又**每条消息**把同一文本拼进 question(v1.30.4 为覆盖
|
|
3831
3832
|
* "live/existing 快路径不重挂提示词"而加)→ 新建 agent 首轮起即重复,且每轮重复付 token。
|
|
3832
3833
|
*
|
|
@@ -3849,7 +3850,7 @@ function ensureWorkspacePromptSection(agent, scope) {
|
|
|
3849
3850
|
order: -55,
|
|
3850
3851
|
text: () => {
|
|
3851
3852
|
const mdPath = getActiveSessionInfo(scope)?.mdPath;
|
|
3852
|
-
return mdPath ?
|
|
3853
|
+
return mdPath ? workspaceCognitiveTrajectoryLine(mdPath) : "";
|
|
3853
3854
|
}
|
|
3854
3855
|
});
|
|
3855
3856
|
workspaceSectionAgents.add(agent);
|
|
@@ -3868,7 +3869,7 @@ function ensureWorkspacePromptSection(agent, scope) {
|
|
|
3868
3869
|
* (调试页等**无固定会话 id**的临时 agent)→ null。理由(R↓):这些 agent 每次
|
|
3869
3870
|
* 调用都是新 id、无身份延续,为其建 SESSION = 每次问答留一个永久空目录(无界熵增);
|
|
3870
3871
|
* 用户模型「skiff 本质是临时会话机制」——工作台属于**持久身份**,不属于单次调用。
|
|
3871
|
-
* ① 角色未启用 session 能力(
|
|
3872
|
+
* ① 角色未启用 session 能力(cognitiveTrajectory.session !== true)→ null(现状零变化)
|
|
3872
3873
|
* ② 已有**本机制自动创建**的 bound(note 前缀 auto-created)→ 恢复(重启/续接记得)
|
|
3873
3874
|
* ②b 内存活跃记录(同 scope)→ 恢复 + **自愈补写绑定**(防御纵深:绑定写失败/事件形态未落盘时
|
|
3874
3875
|
* 仍复用同一 SESSION,而不是每条消息新建一个——v1.30.5 及更早"每条消息一个 SESSION"的放大效应)
|
|
@@ -3879,7 +3880,7 @@ function ensureWorkspacePromptSection(agent, scope) {
|
|
|
3879
3880
|
* @returns 绑定后的 SESSION.md 绝对路径(供提示词注入);不适用/失败 → null
|
|
3880
3881
|
*/
|
|
3881
3882
|
function ensureSkiffSession(root, agent, roleName, role, persistent = true) {
|
|
3882
|
-
if (!
|
|
3883
|
+
if (!cognitiveTrajectorySubset(role).session) return null;
|
|
3883
3884
|
if (!persistent) return null;
|
|
3884
3885
|
const session = agent.session;
|
|
3885
3886
|
const scope = String(agent.session.id ?? agent.id);
|
|
@@ -3977,7 +3978,7 @@ async function createSkiffAgent(ctx, root, roleName, role, defaultModel, session
|
|
|
3977
3978
|
console.warn(`[serenity-hooks] skiff 系统提示词注册失败: ${String(err?.message ?? err)}`);
|
|
3978
3979
|
}
|
|
3979
3980
|
if (agent?.session) try {
|
|
3980
|
-
|
|
3981
|
+
registerCognitiveTrajectorySkillSection(agent, root);
|
|
3981
3982
|
} catch (err) {
|
|
3982
3983
|
console.warn(`[serenity-hooks] skiff 轨迹 skill 注入注册失败(不影响 agent 创建): ${String(err?.message ?? err)}`);
|
|
3983
3984
|
}
|
|
@@ -4078,7 +4079,7 @@ async function createOrResumeAgent(ctx, id, root, model, setup, fixedId) {
|
|
|
4078
4079
|
/**
|
|
4079
4080
|
* 会话事件读取(v1.28.0 适配 0.1.2-rc.1):rc.1 Session 移除 `.events` 属性 → `snapshotEvents()` 方法。
|
|
4080
4081
|
* 兼容双形态(运行时 rc.1 snapshotEvents;测试替身 events)——同一函数两处语义,测试注入零侵入。
|
|
4081
|
-
* 注:与 trajectory-ops.ts 导出的共享 `sessionEvents` 同构(v1.28.1 收敛基准)。本模块保留本地副本
|
|
4082
|
+
* 注:与 cognitive-trajectory-ops.ts 导出的共享 `sessionEvents` 同构(v1.28.1 收敛基准)。本模块保留本地副本
|
|
4082
4083
|
* 以维持独立 import 面(零 DSH 依赖测试替身形态一致);若未来重构可统一 import 共享版。
|
|
4083
4084
|
*/
|
|
4084
4085
|
function sessionEvents(session) {
|
|
@@ -4099,7 +4100,7 @@ function lastAssistantText$1(agent) {
|
|
|
4099
4100
|
return "";
|
|
4100
4101
|
}
|
|
4101
4102
|
/** events → 可读轨迹(user/assistant 文本 + 工具调用 + 工具结果;单条解析失败跳过) */
|
|
4102
|
-
function
|
|
4103
|
+
function eventsToCognitiveTrajectory(events) {
|
|
4103
4104
|
const out = [];
|
|
4104
4105
|
for (const raw of events) try {
|
|
4105
4106
|
const ev = raw;
|
|
@@ -4148,7 +4149,7 @@ function truncate(s, n) {
|
|
|
4148
4149
|
* 提问一轮:followup → 等 idle → 读答案 + 轨迹。
|
|
4149
4150
|
* @param eventsStart 轨迹起点:显式传 0 = 全量轨迹(会话追问时页面重绘完整时间线,
|
|
4150
4151
|
* v1.25.10 用户拍板);不传(undefined)= 本轮增量(followup 前 events 之后)
|
|
4151
|
-
* @param options.
|
|
4152
|
+
* @param options.includeCognitiveTrajectory 是否计算轨迹(v1.26.10 起供**对外面**传 false 跳过计算——
|
|
4152
4153
|
* 原消费方 = 3100 公开问答页 / ACP JSON-RPC,**该面已于 2026-10-15 退役** ⇒ 现无生产调用方传 false;
|
|
4153
4154
|
* 3099 调试页默认 true 保留)
|
|
4154
4155
|
*/
|
|
@@ -4163,24 +4164,24 @@ async function askSkiff(ctx, agent, question, eventsStart, options) {
|
|
|
4163
4164
|
}));
|
|
4164
4165
|
await waitAgentIdle(ctx, agent);
|
|
4165
4166
|
const answer = lastAssistantText$1(agent);
|
|
4166
|
-
const
|
|
4167
|
+
const cognitiveTrajectory = options?.includeCognitiveTrajectory === false ? [] : eventsToCognitiveTrajectory(sessionEvents(agent.session).slice(before));
|
|
4167
4168
|
return {
|
|
4168
4169
|
answer,
|
|
4169
4170
|
sessionId: String(agent.session.id ?? ""),
|
|
4170
|
-
|
|
4171
|
+
cognitiveTrajectory
|
|
4171
4172
|
};
|
|
4172
4173
|
}
|
|
4173
4174
|
/**
|
|
4174
4175
|
* Skiff 会话的轨迹纪律参与判定:非 skiff 会话恒 true(正常参与);
|
|
4175
|
-
* skiff 会话按角色
|
|
4176
|
+
* skiff 会话按角色 cognitiveTrajectory 子集(session/keeper/rebuild)决定;
|
|
4176
4177
|
* 注册表缺失(进程重启遗留等)→ 保守旁路(false,完全独立)。
|
|
4177
4178
|
*/
|
|
4178
|
-
function
|
|
4179
|
+
function skiffCognitiveTrajectoryEnabled(root, sessionId, key) {
|
|
4179
4180
|
if (!isSkiffSessionId(sessionId)) return true;
|
|
4180
4181
|
const roleName = sessionId ? skiffRoleFor(sessionId) : null;
|
|
4181
4182
|
if (!roleName) return false;
|
|
4182
4183
|
const role = readSkiffRoles(root).get(roleName);
|
|
4183
|
-
return
|
|
4184
|
+
return cognitiveTrajectorySubset(role)[key];
|
|
4184
4185
|
}
|
|
4185
4186
|
/**
|
|
4186
4187
|
* msm 工具的 Skiff 门控:非 skiff 会话恒放行;skiff 会话——
|
|
@@ -9626,9 +9627,9 @@ function cutoffDaysAgo(days, nowMs = Date.now()) {
|
|
|
9626
9627
|
//#endregion
|
|
9627
9628
|
//#region src/wake-registry.ts
|
|
9628
9629
|
/**
|
|
9629
|
-
* wake-registry.ts —
|
|
9630
|
+
* wake-registry.ts — 认知轨迹唤醒注册表(D58,S142 2026-09-13)
|
|
9630
9631
|
*
|
|
9631
|
-
* 需求(用户原话):一个 CCC
|
|
9632
|
+
* 需求(用户原话):一个 CCC 内允许多个认知轨迹任意形态并行;某个认知轨迹可以
|
|
9632
9633
|
* **预定未来唤醒自己或别人**,本质上是一次「未来时刻 + 一条 message」的投递;「周期自唤醒」
|
|
9633
9634
|
* 曾是它的特例(原 ACC autopilot / D59,该机制已于 2026-09-15 整段退场)。协作边界:
|
|
9634
9635
|
* 互相可见、**无阻塞无等待**。
|
|
@@ -9661,7 +9662,7 @@ function emptyRegistry() {
|
|
|
9661
9662
|
};
|
|
9662
9663
|
}
|
|
9663
9664
|
/** 条目形状校验(容忍手改:字段缺失/类型不符 → 丢弃该条,不阻断整表) */
|
|
9664
|
-
function asEntry$
|
|
9665
|
+
function asEntry$1(value) {
|
|
9665
9666
|
const e = value;
|
|
9666
9667
|
if (!e || typeof e.id !== "string" || typeof e.target !== "string") return null;
|
|
9667
9668
|
if (typeof e.at !== "string" || typeof e.message !== "string") return null;
|
|
@@ -9695,7 +9696,7 @@ function loadWakeRegistry(root) {
|
|
|
9695
9696
|
const raw = Array.isArray(parsed.entries) ? parsed.entries : [];
|
|
9696
9697
|
const entries = [];
|
|
9697
9698
|
for (const item of raw) {
|
|
9698
|
-
const e = asEntry$
|
|
9699
|
+
const e = asEntry$1(item);
|
|
9699
9700
|
if (e) entries.push(e);
|
|
9700
9701
|
}
|
|
9701
9702
|
return {
|
|
@@ -9774,7 +9775,7 @@ function parseWakeAt(input, nowMs) {
|
|
|
9774
9775
|
/**
|
|
9775
9776
|
* 登记一条唤醒。
|
|
9776
9777
|
* @param root CCC 根
|
|
9777
|
-
* @param input
|
|
9778
|
+
* @param input 目标认知轨迹 / 时刻串 / message / 发起者 / 当前时刻
|
|
9778
9779
|
* @returns 成功返回条目,失败返回稳定错误文案(不抛错)
|
|
9779
9780
|
*/
|
|
9780
9781
|
function addWake(root, input) {
|
|
@@ -10042,7 +10043,7 @@ const CRO_TIMEOUT_MS = 6e4;
|
|
|
10042
10043
|
const CRO_STDOUT_MAX = 65536;
|
|
10043
10044
|
/** 绑定文件体积上限(它只是个机器名 ⇒ 超过这个数一定是写错了,不读) */
|
|
10044
10045
|
const CRO_HOST_FILE_MAX = 1024;
|
|
10045
|
-
/** SESSION.md 文件名(与 `trajectory-ops.ts` 同值;此处本地常量避免反向依赖) */
|
|
10046
|
+
/** SESSION.md 文件名(与 `cognitive-trajectory-ops.ts` 同值;此处本地常量避免反向依赖) */
|
|
10046
10047
|
const SESSION_MD = "SESSION.md";
|
|
10047
10048
|
/**
|
|
10048
10049
|
* 展示码派生(**只用于展示**,不作识别依据 —— §0I U4:锚永远是 `dirName`)。
|
|
@@ -10323,7 +10324,7 @@ function parseCroHostBinding(raw) {
|
|
|
10323
10324
|
* @param root CCC 根
|
|
10324
10325
|
* @returns 启用了 CRO 的轨迹目录名(**排序后**,保证同 tick 顺序稳定、便于日志比对)
|
|
10325
10326
|
*/
|
|
10326
|
-
function
|
|
10327
|
+
function listCroCognitiveTrajectories(root) {
|
|
10327
10328
|
const rootDir = sessionsRoot(root);
|
|
10328
10329
|
let entries;
|
|
10329
10330
|
try {
|
|
@@ -10668,7 +10669,7 @@ const TURN_TTL_MS = 18e5;
|
|
|
10668
10669
|
*/
|
|
10669
10670
|
const runningBySession = /* @__PURE__ */ new Map();
|
|
10670
10671
|
/** 取载体 id(事件 payload 的 `agent` 面;取不到 ⇒ null) */
|
|
10671
|
-
function sessionIdOf$
|
|
10672
|
+
function sessionIdOf$2(agent) {
|
|
10672
10673
|
const id = agent?.session?.id;
|
|
10673
10674
|
return typeof id === "string" && id !== "" ? id : null;
|
|
10674
10675
|
}
|
|
@@ -10754,23 +10755,23 @@ function registerCroTurnTracking(ctx) {
|
|
|
10754
10755
|
}
|
|
10755
10756
|
};
|
|
10756
10757
|
on("agent/created", (payload) => {
|
|
10757
|
-
const id = sessionIdOf$
|
|
10758
|
+
const id = sessionIdOf$2(payload?.agent);
|
|
10758
10759
|
if (id) markTurnRunning(id, Date.now());
|
|
10759
10760
|
});
|
|
10760
10761
|
on("agent/status", (payload) => {
|
|
10761
10762
|
const p = payload;
|
|
10762
|
-
const id = sessionIdOf$
|
|
10763
|
+
const id = sessionIdOf$2(p?.agent);
|
|
10763
10764
|
if (!id) return;
|
|
10764
10765
|
const turn = typeof p.turn === "number" ? p.turn : null;
|
|
10765
10766
|
if (p.status === "running") markTurnRunning(id, Date.now(), turn);
|
|
10766
10767
|
else if (p.status === "idle") clearTurnRunning(id);
|
|
10767
10768
|
});
|
|
10768
10769
|
on("agent/turn-stopping", (payload) => {
|
|
10769
|
-
const id = sessionIdOf$
|
|
10770
|
+
const id = sessionIdOf$2(payload?.agent);
|
|
10770
10771
|
if (id) clearTurnRunning(id);
|
|
10771
10772
|
});
|
|
10772
10773
|
on("agent/disposed", (payload) => {
|
|
10773
|
-
const id = sessionIdOf$
|
|
10774
|
+
const id = sessionIdOf$2(payload?.agent);
|
|
10774
10775
|
if (id) clearTurnRunning(id);
|
|
10775
10776
|
});
|
|
10776
10777
|
on("session/disposed", (payload) => {
|
|
@@ -10788,10 +10789,10 @@ function registerCroTurnTracking(ctx) {
|
|
|
10788
10789
|
*
|
|
10789
10790
|
* ## 所有者令(本模块的存在理由)
|
|
10790
10791
|
*
|
|
10791
|
-
* 「S151 和 S185
|
|
10792
|
+
* 「S151 和 S185 的验证都说明,认知轨迹倾向于自己做个结构化记录来记录自身 CRO 的执行情况,
|
|
10792
10793
|
* 这是个结构化流水账;**我们 ACC 有必要进行 CRO 唤醒的日志记录**,当 CRO 存在时,每次**成功唤醒**
|
|
10793
10794
|
* 写相关信息,但是保留数量要少,**只保留仅两日**的记录即可,**这个时间是程序化的**」
|
|
10794
|
-
*
|
|
10795
|
+
* +「**这个日志写结构化文件到认知轨迹目录,文件名固定即可**」
|
|
10795
10796
|
* +「**失败成功都记录**」。
|
|
10796
10797
|
*
|
|
10797
10798
|
* ## 它解决什么麻烦(白话)
|
|
@@ -10839,14 +10840,14 @@ const CRO_LOG_VERSION = 1;
|
|
|
10839
10840
|
function croWakeLogPath(root, dirName) {
|
|
10840
10841
|
return join(sessionsRoot(root), dirName, CRO_LOG_FILENAME);
|
|
10841
10842
|
}
|
|
10842
|
-
function emptyLog
|
|
10843
|
+
function emptyLog() {
|
|
10843
10844
|
return {
|
|
10844
10845
|
version: CRO_LOG_VERSION,
|
|
10845
10846
|
entries: []
|
|
10846
10847
|
};
|
|
10847
10848
|
}
|
|
10848
10849
|
/** 条目形状校验(容忍手改:字段缺失/类型不符 → 丢弃该条,不阻断整档) */
|
|
10849
|
-
function asEntry
|
|
10850
|
+
function asEntry(value) {
|
|
10850
10851
|
const e = value;
|
|
10851
10852
|
if (!e || typeof e !== "object") return null;
|
|
10852
10853
|
if (typeof e.at !== "string" || e.at === "") return null;
|
|
@@ -10881,7 +10882,7 @@ function summarizeCroPrompt(prompt) {
|
|
|
10881
10882
|
function loadCroWakeLog(root, dirName) {
|
|
10882
10883
|
const path = croWakeLogPath(root, dirName);
|
|
10883
10884
|
if (!existsSync(path)) return {
|
|
10884
|
-
log: emptyLog
|
|
10885
|
+
log: emptyLog(),
|
|
10885
10886
|
error: null
|
|
10886
10887
|
};
|
|
10887
10888
|
try {
|
|
@@ -10889,7 +10890,7 @@ function loadCroWakeLog(root, dirName) {
|
|
|
10889
10890
|
const raw = Array.isArray(parsed.entries) ? parsed.entries : [];
|
|
10890
10891
|
const entries = [];
|
|
10891
10892
|
for (const item of raw) {
|
|
10892
|
-
const e = asEntry
|
|
10893
|
+
const e = asEntry(item);
|
|
10893
10894
|
if (e) entries.push(e);
|
|
10894
10895
|
}
|
|
10895
10896
|
return {
|
|
@@ -10901,7 +10902,7 @@ function loadCroWakeLog(root, dirName) {
|
|
|
10901
10902
|
};
|
|
10902
10903
|
} catch (err) {
|
|
10903
10904
|
return {
|
|
10904
|
-
log: emptyLog
|
|
10905
|
+
log: emptyLog(),
|
|
10905
10906
|
error: `CRO 流水解析失败(${String(err?.message ?? err)})`
|
|
10906
10907
|
};
|
|
10907
10908
|
}
|
|
@@ -11196,10 +11197,10 @@ function createClock(opts) {
|
|
|
11196
11197
|
//#endregion
|
|
11197
11198
|
//#region src/wake-scheduler.ts
|
|
11198
11199
|
/**
|
|
11199
|
-
* wake-scheduler.ts —
|
|
11200
|
+
* wake-scheduler.ts — 认知轨迹唤醒调度器(D58/D59/D60;S142 2026-09-13)
|
|
11200
11201
|
*
|
|
11201
11202
|
* 一个**中心调度器**(5min tick,复用 autopilot 的节律常量),处理 CCC 唤醒注册表里
|
|
11202
|
-
* 到期的条目:把「未来时刻 + 一条 message
|
|
11203
|
+
* 到期的条目:把「未来时刻 + 一条 message」投递给目标认知轨迹,**fire-and-forget**
|
|
11203
11204
|
* (无阻塞、无等待、无回执——发起方不 await,调度器不替模型结果负责)。
|
|
11204
11205
|
*
|
|
11205
11206
|
* 与 autopilot 的关系(D59):**两个 tick 各自独立**。autopilot 是每 CCC 单例的
|
|
@@ -11251,7 +11252,7 @@ function resolveWakeTarget(root, target) {
|
|
|
11251
11252
|
*
|
|
11252
11253
|
* 历史(勿误读):v1.34 S-1 曾把两闸解耦(周期自唤醒 `autopilotWakeEnabled` / 本钟 `wakeSchedulerEnabled`),
|
|
11253
11254
|
* 理由是"关 auto-trajectory 不得连带关一次性唤醒";随 ACC 侧 autopilot 于 v1.35.0 整段退场,
|
|
11254
|
-
* 两闸只剩本钟一座 ⇒ 现已整座移除。详见 `docs/trajectory-scheduling.md` §S-1 与 v1.44 CHANGELOG。
|
|
11255
|
+
* 两闸只剩本钟一座 ⇒ 现已整座移除。详见 `docs/cognitive-trajectory-scheduling.md` §S-1 与 v1.44 CHANGELOG。
|
|
11255
11256
|
*/
|
|
11256
11257
|
/**
|
|
11257
11258
|
* 🔴 **本钟的串行域**(C6b 硬约束):`clock.chain` 是**本实例私有**的——
|
|
@@ -11265,15 +11266,15 @@ function resolveWakeTarget(root, target) {
|
|
|
11265
11266
|
*/
|
|
11266
11267
|
/** 本钟的装配项(`ctx` 由 {@link registerWakeScheduler} 在装配点填入) */
|
|
11267
11268
|
const clockOpts = {
|
|
11268
|
-
label: "
|
|
11269
|
+
label: "认知轨迹唤醒调度器",
|
|
11269
11270
|
ctx: void 0,
|
|
11270
11271
|
events: ["session/created", "serenity/settings-changed"],
|
|
11271
11272
|
body: () => runWakeTick(clockOpts.ctx),
|
|
11272
11273
|
logFrom: {
|
|
11273
|
-
prefix: "[serenity-hooks]
|
|
11274
|
+
prefix: "[serenity-hooks] 认知轨迹唤醒 ",
|
|
11274
11275
|
lines: (lines) => lines
|
|
11275
11276
|
},
|
|
11276
|
-
startLog: () => "[serenity-hooks] ✓
|
|
11277
|
+
startLog: () => "[serenity-hooks] ✓ 认知轨迹唤醒调度器启动(5min tick)"
|
|
11277
11278
|
};
|
|
11278
11279
|
const clock = createClock(clockOpts);
|
|
11279
11280
|
/** 调度器进程态快照(只读)。
|
|
@@ -11286,7 +11287,7 @@ function wakeSchedulerState() {
|
|
|
11286
11287
|
* 取目标 agent:live 优先 → 冷会话走 sessionController.resolveAgent → 标题回退。
|
|
11287
11288
|
* @param ctx 插件上下文
|
|
11288
11289
|
* @param root CCC 根
|
|
11289
|
-
* @param dirName
|
|
11290
|
+
* @param dirName 目标认知轨迹目录名
|
|
11290
11291
|
* @returns 命中返回 agent 与取得方式(how 进 lastResult,便于诊断);未命中返回原因
|
|
11291
11292
|
* (原因带 `notReady` = **环境未就绪**而非投递失败,调用方**不得**据此记一次失败)
|
|
11292
11293
|
*
|
|
@@ -11331,17 +11332,17 @@ async function acquireWakeAgent(ctx, root, dirName) {
|
|
|
11331
11332
|
}
|
|
11332
11333
|
}
|
|
11333
11334
|
return {
|
|
11334
|
-
error: `无绑定会话记录(AGENT_SESSIONS/.bindings.json 中无 ${dirName})——目标轨迹需先被
|
|
11335
|
+
error: `无绑定会话记录(AGENT_SESSIONS/.bindings.json 中无 ${dirName})——目标轨迹需先被 cognitive_trajectory use 激活过`,
|
|
11335
11336
|
noBinding: true
|
|
11336
11337
|
};
|
|
11337
11338
|
}
|
|
11338
11339
|
/** 唤醒消息(唤起即投递正文:身份锚定 + 唤醒信息 + 任务) */
|
|
11339
11340
|
function buildWakeText(entry, target) {
|
|
11340
11341
|
return [
|
|
11341
|
-
`[
|
|
11342
|
+
`[认知轨迹唤醒] 到点(登记于 ${entry.createdAt},发起者 ${entry.createdBy || "未知"},id ${entry.id})。`,
|
|
11342
11343
|
`当前时间:${localHuman()}(当地时区)。`,
|
|
11343
11344
|
"",
|
|
11344
|
-
`身份锚定:继续 ${target.dirName}
|
|
11345
|
+
`身份锚定:继续 ${target.dirName} 的认知轨迹(SESSION.md: ${target.mdPath})。`,
|
|
11345
11346
|
"",
|
|
11346
11347
|
"唤醒信息:",
|
|
11347
11348
|
entry.message,
|
|
@@ -11360,7 +11361,7 @@ async function deliverWake(ctx, root, entry) {
|
|
|
11360
11361
|
const target = resolveWakeTarget(root, entry.target);
|
|
11361
11362
|
if (!target) return {
|
|
11362
11363
|
ok: false,
|
|
11363
|
-
detail:
|
|
11364
|
+
detail: `目标认知轨迹未命中(${entry.target})`
|
|
11364
11365
|
};
|
|
11365
11366
|
const acquired = await acquireWakeAgent(ctx, root, target.dirName);
|
|
11366
11367
|
if ("error" in acquired) return {
|
|
@@ -11403,7 +11404,7 @@ function buildSendText(target, message, sender) {
|
|
|
11403
11404
|
return [
|
|
11404
11405
|
`[即时消息] 来自 ${sender || "未知轨迹"}。当前时间:${localHuman()}(当地时区)。`,
|
|
11405
11406
|
"",
|
|
11406
|
-
`身份锚定:继续 ${target.dirName}
|
|
11407
|
+
`身份锚定:继续 ${target.dirName} 的认知轨迹(SESSION.md: ${target.mdPath})。`,
|
|
11407
11408
|
"",
|
|
11408
11409
|
"消息:",
|
|
11409
11410
|
message,
|
|
@@ -11412,7 +11413,7 @@ function buildSendText(target, message, sender) {
|
|
|
11412
11413
|
].join("\n");
|
|
11413
11414
|
}
|
|
11414
11415
|
/**
|
|
11415
|
-
* 即时投递(`
|
|
11416
|
+
* 即时投递(`cognitive_trajectory send-now`)——与 `send-later`(预约)**共用取用通路**(`acquireWakeAgent`),
|
|
11416
11417
|
* 区别只在**时刻**(现在 vs 未来)与**回执**(有 vs 无):
|
|
11417
11418
|
* · `send-later`(原 `wake-later`):未来时刻 + 一条 message → 落注册表 → 本文件的调度器到点投递(**fire-and-forget,无回执**)
|
|
11418
11419
|
* · `send-now`(原 `send-message`):**此刻**把一条 message 递给目标 → **同步回执** → **不落注册表**
|
|
@@ -11439,7 +11440,7 @@ function buildSendText(target, message, sender) {
|
|
|
11439
11440
|
* 预约的本职是"到点唤起",且在跑时排队不打断是它被实证验收过的行为,不顺手动它。
|
|
11440
11441
|
*
|
|
11441
11442
|
* **归属**:本函数放在调度器侧而非工具侧 —— "怎么把一句话送到一条轨迹"是**调度器的知识**,
|
|
11442
|
-
* 工具只该知道"有这个动作"。这样 `trajectory.ts` 完全不碰调度器内部(v1.35.0 硬约束①:
|
|
11443
|
+
* 工具只该知道"有这个动作"。这样 `cognitive-trajectory.ts` 完全不碰调度器内部(v1.35.0 硬约束①:
|
|
11443
11444
|
* `send-later` 链路**一行为不动**,本次改动 = **纯新增**)。
|
|
11444
11445
|
*
|
|
11445
11446
|
* @param ctx 插件上下文
|
|
@@ -11449,11 +11450,11 @@ function buildSendText(target, message, sender) {
|
|
|
11449
11450
|
* @param sender 发起者轨迹标识
|
|
11450
11451
|
* @returns 投递结果(**同步**返回给调用方)
|
|
11451
11452
|
*/
|
|
11452
|
-
async function
|
|
11453
|
+
async function sendToCognitiveTrajectory(ctx, root, target, message, sender) {
|
|
11453
11454
|
const t = resolveWakeTarget(root, target);
|
|
11454
11455
|
if (!t) return {
|
|
11455
11456
|
ok: false,
|
|
11456
|
-
detail:
|
|
11457
|
+
detail: `目标认知轨迹未命中(${target})`
|
|
11457
11458
|
};
|
|
11458
11459
|
let acquired = await acquireWakeAgent(ctx, root, t.dirName);
|
|
11459
11460
|
let createdNote = "";
|
|
@@ -11558,7 +11559,7 @@ async function createAndBindTarget(ctx, root, dirName) {
|
|
|
11558
11559
|
const sc = hostService(ctx, "sessionController");
|
|
11559
11560
|
const create = sc?.create;
|
|
11560
11561
|
if (typeof create !== "function") return {
|
|
11561
|
-
error: "目标无活跃 dsh 会话,且 sessionController.create 不可用(绑定 id: 无)——无法就地创建载体;请先对该轨迹执行一次
|
|
11562
|
+
error: "目标无活跃 dsh 会话,且 sessionController.create 不可用(绑定 id: 无)——无法就地创建载体;请先对该轨迹执行一次 cognitive_trajectory use",
|
|
11562
11563
|
notReady: true
|
|
11563
11564
|
};
|
|
11564
11565
|
const ws = findWorkspaceByPath(listWorkspaces(ctx) ?? [], root);
|
|
@@ -11693,7 +11694,7 @@ function findWorkspaceByPath(workspaces, root) {
|
|
|
11693
11694
|
* (工作区注册表 ∪ 持久化会话 ∪ live 会话):**没有 live 会话的 CCC 也会被扫到**。
|
|
11694
11695
|
*
|
|
11695
11696
|
* ⚠️ 边界(本步**只**改"枚举哪些 CCC",**不改**"怎么找到要交付的 agent"):
|
|
11696
|
-
* 把 message
|
|
11697
|
+
* 把 message 交给某条认知轨迹仍必须靠 **live 会话**解析(见本文件
|
|
11697
11698
|
* `acquireWakeAgent`);无 live 会话时该 CCC 只是**被扫到、
|
|
11698
11699
|
* 无交付对象** ⇒ `deliverWake` 返回 `无绑定会话记录…`,条目回 `pending` 并留痕。
|
|
11699
11700
|
* 因此本函数**不得**让 tick 抛错(异常一律由调用方 tick 兜住,见 `runWakeTick` 的 try)。
|
|
@@ -11726,7 +11727,7 @@ function buildCroWakeText(dirName, mdPath, prompt, reason) {
|
|
|
11726
11727
|
`[CRO 唤起] ${dirName} 自带的 continuous-re-occurrence.ts 判定:现在该唤起。`,
|
|
11727
11728
|
`当前时间:${localHuman()}(当地时区)。`,
|
|
11728
11729
|
"",
|
|
11729
|
-
`身份锚定:继续 ${dirName}
|
|
11730
|
+
`身份锚定:继续 ${dirName} 的认知轨迹(SESSION.md: ${mdPath})。`,
|
|
11730
11731
|
"",
|
|
11731
11732
|
`判定理由:${reason ?? "(程序未提供 reason —— 见 CRO 编写指南 §7 坑 3)"}`,
|
|
11732
11733
|
"",
|
|
@@ -11808,7 +11809,7 @@ async function runCroPhase(ctx, root, registry, log) {
|
|
|
11808
11809
|
}
|
|
11809
11810
|
let dirs;
|
|
11810
11811
|
try {
|
|
11811
|
-
dirs =
|
|
11812
|
+
dirs = listCroCognitiveTrajectories(root);
|
|
11812
11813
|
} catch {
|
|
11813
11814
|
return;
|
|
11814
11815
|
}
|
|
@@ -11940,7 +11941,7 @@ async function runWakeTick(ctx) {
|
|
|
11940
11941
|
function registerWakeScheduler(ctx) {
|
|
11941
11942
|
clockOpts.ctx = ctx;
|
|
11942
11943
|
clock.start();
|
|
11943
|
-
registerDisposer(ctx, "
|
|
11944
|
+
registerDisposer(ctx, "认知轨迹唤醒调度器", () => clock.dispose());
|
|
11944
11945
|
}
|
|
11945
11946
|
//#endregion
|
|
11946
11947
|
//#region src/cro-guide.ts
|
|
@@ -11973,10 +11974,10 @@ function registerWakeScheduler(ctx) {
|
|
|
11973
11974
|
* 不写任何真实机器路径或私有会话名 —— 本文件随 npm 包发布(2026-10-14 隐私核查)。
|
|
11974
11975
|
*/
|
|
11975
11976
|
const CRO_SAMPLE_SNAPSHOT_INPUT = {
|
|
11976
|
-
dirName: "2026-01-01--S001--example-trajectory",
|
|
11977
|
+
dirName: "2026-01-01--S001--example-cognitive-trajectory",
|
|
11977
11978
|
cccRoot: "/home/user/workspace",
|
|
11978
11979
|
nowMs: 17898e8,
|
|
11979
|
-
sessionMdPath: "/home/user/workspace/AGENT_SESSIONS/2026-01-01--S001--example-trajectory/SESSION.md",
|
|
11980
|
+
sessionMdPath: "/home/user/workspace/AGENT_SESSIONS/2026-01-01--S001--example-cognitive-trajectory/SESSION.md",
|
|
11980
11981
|
sessionMdBytes: 182e3,
|
|
11981
11982
|
sessionMdMtimeMs: 1789799e6,
|
|
11982
11983
|
references: [{
|
|
@@ -12181,18 +12182,18 @@ ${renderCroSampleSnapshot()}
|
|
|
12181
12182
|
**吃一份快照,吐一行决策。先自测,绿了再改名。**
|
|
12182
12183
|
`;
|
|
12183
12184
|
//#endregion
|
|
12184
|
-
//#region src/tools/trajectory.ts
|
|
12185
|
+
//#region src/tools/cognitive-trajectory.ts
|
|
12185
12186
|
/**
|
|
12186
|
-
* trajectory.ts — `
|
|
12187
|
+
* trajectory.ts — `cognitive_trajectory` 真实 DSH 工具定义(defineTool)
|
|
12187
12188
|
*
|
|
12188
12189
|
* v1.33(S142 §32 用户裁决):由 `logbook` **更名并收敛为 `trajectory`**(v1.34:工具名再硬切为
|
|
12189
|
-
* `container_trajectory`,无别名)——trajectory 是一等概念
|
|
12190
|
+
* `container_trajectory`,无别名)——cognitive trajectory 是一等概念
|
|
12190
12191
|
* (SESSION.md 是它的持久身体),本工具管它的**载体生命周期 + 一次性时间安排**。
|
|
12191
12192
|
* 动作面现行 **8 个**:list / show / create / use / rebuild / **`send-now`** / **`send-later`** / **`cro-guide`**。
|
|
12192
12193
|
* 🔴 2026-09-16(所有者裁决):**第 7 个动作 `send-message`(即时投递)**
|
|
12193
12194
|
* ——与 `wake-later` 共用取用通路,差别只在**时刻**(现在 vs 未来)与**回执**(有 vs 无);
|
|
12194
12195
|
* 实现落在 `wake-scheduler.ts` 的 `sendToTrajectory()`("怎么把一句话送到一条轨迹"是调度器的知识)。
|
|
12195
|
-
* 设计稿 = `docs/trajectory-send-message-design.md`(v0.2 定稿)。
|
|
12196
|
+
* 设计稿 = `docs/cognitive-trajectory-send-message-design.md`(v0.2 定稿)。
|
|
12196
12197
|
* 🔴 2026-09-17(所有者裁决 · **命名 A 案**):**两个投递动作更名** —— `wake-later` → **`send-later`**、
|
|
12197
12198
|
* `send-message` → **`send-now`**。判据(R↓):两者本是**同一条投递通路**(同取用机制 / 同载荷 /
|
|
12198
12199
|
* 同落点语义),只差**时刻**;而「唤醒(冷载入)」是**两条路径共有**的属性,不配做区分
|
|
@@ -12211,7 +12212,7 @@ ${renderCroSampleSnapshot()}
|
|
|
12211
12212
|
* 归档能力由 `container_fs mv → _archived/` 承担
|
|
12212
12213
|
* · `hook-develop-guide` → **淘汰**(v1.34.1:SEP 整体废除——「能落 harness 已有原语,
|
|
12213
12214
|
* 就不要自造协议 + 脚本管道」;轨迹挂哪些 skill 改由 SESSION.md frontmatter 声明 +
|
|
12214
|
-
* `systemPrompt.section` 注入,见 `docs/trajectory-skill-injection.md`)
|
|
12215
|
+
* `systemPrompt.section` 注入,见 `docs/cognitive-trajectory-skill-injection.md`)
|
|
12215
12216
|
* · autopilot 面(all/init/random/diag/doc/check/status/guide)→ `container_admin` 的 autopilot 域
|
|
12216
12217
|
* · `wake-add`/`wake-list`/`wake-rm` → **`wake-later`**(v1.33 收敛;**2026-09-17 更名 `send-later`**)
|
|
12217
12218
|
* · 2026-09-20(CRO 落地):新增 **`cro-guide`** —— CRO 编写指南的出口(纯读)。
|
|
@@ -12319,40 +12320,40 @@ function renderText$3(value) {
|
|
|
12319
12320
|
}];
|
|
12320
12321
|
}
|
|
12321
12322
|
/**
|
|
12322
|
-
* 创建 trajectory 工具(原 logbook;v1.33 S142 §32 更名 + 动作收敛)。
|
|
12323
|
+
* 创建 cognitive trajectory 工具(原 logbook;v1.33 S142 §32 更名 + 动作收敛)。
|
|
12323
12324
|
* 闭包捕获插件 ctx → use 后可调 ctx.sessionTitle.rename。
|
|
12324
12325
|
*/
|
|
12325
|
-
function
|
|
12326
|
+
function createCognitiveTrajectoryTool(ctx) {
|
|
12326
12327
|
return defineTool({
|
|
12327
|
-
name: "
|
|
12328
|
+
name: "cognitive_trajectory",
|
|
12328
12329
|
description: [
|
|
12329
|
-
"
|
|
12330
|
-
"A trajectory is NOT the conversation: SESSION.md is the durable body and never moves;",
|
|
12331
|
-
"the dsh conversation is only its rebuildable carrier (Ship of Theseus — the carrier is replaced, the trajectory continues).",
|
|
12330
|
+
"cognitive_trajectory — one cognitive trajectory: its persistent body (SESSION.md) + its timeline.",
|
|
12331
|
+
"A cognitive trajectory is NOT the conversation: SESSION.md is the durable body and never moves;",
|
|
12332
|
+
"the dsh conversation is only its rebuildable carrier (Ship of Theseus — the carrier is replaced, the cognitive trajectory continues).",
|
|
12332
12333
|
"ACTIONS, grouped by EFFECT (read the grouping before picking one):",
|
|
12333
|
-
"(A) LIFECYCLE — changes which trajectory THIS conversation is bound to:",
|
|
12334
|
-
"list (inventory + stats + anomaly marks) / show (read one SESSION.md) / create (make a NEW trajectory) /",
|
|
12335
|
-
"use (bind THIS conversation to an existing trajectory; inline integrity check — silent when fine, problems reported as hints, never errors) /",
|
|
12334
|
+
"(A) LIFECYCLE — changes which cognitive trajectory THIS conversation is bound to:",
|
|
12335
|
+
"list (inventory + stats + anomaly marks) / show (read one SESSION.md) / create (make a NEW cognitive trajectory) /",
|
|
12336
|
+
"use (bind THIS conversation to an existing cognitive trajectory; inline integrity check — silent when fine, problems reported as hints, never errors) /",
|
|
12336
12337
|
"rebuild (clear-and-rebuild THIS conversation in place).",
|
|
12337
|
-
"(B) DELIVERY — sends ONE message to ANOTHER trajectory; your own binding is unchanged:",
|
|
12338
|
+
"(B) DELIVERY — sends ONE message to ANOTHER cognitive trajectory; your own binding is unchanged:",
|
|
12338
12339
|
"send-now (deliver RIGHT NOW) / send-later (deliver at ONE future instant).",
|
|
12339
|
-
"(C) REFERENCE — cro-guide (how to give THIS trajectory its own program that decides when it should be woken).",
|
|
12340
|
+
"(C) REFERENCE — cro-guide (how to give THIS cognitive trajectory its own program that decides when it should be woken).",
|
|
12340
12341
|
"HOW TO CHOOSE (a rule, not a guess):",
|
|
12341
|
-
"not yet bound + starting new work ⇒ create — with no prior binding it activates immediately, so the new trajectory has a carrier at once.",
|
|
12342
|
-
"already bound + recording a separate future line ⇒ create — it does NOT steal your binding (you stay on the current trajectory);",
|
|
12342
|
+
"not yet bound + starting new work ⇒ create — with no prior binding it activates immediately, so the new cognitive trajectory has a carrier at once.",
|
|
12343
|
+
"already bound + recording a separate future line ⇒ create — it does NOT steal your binding (you stay on the current cognitive trajectory);",
|
|
12343
12344
|
"activate the new one when you want it by sending it a message.",
|
|
12344
|
-
"want THIS conversation to move to another trajectory ⇒ use (refuses when it would orphan the current binding; --force overrides).",
|
|
12345
|
-
"want to poke another trajectory ⇒ send-now. Its target needs NO prior setup:",
|
|
12345
|
+
"want THIS conversation to move to another cognitive trajectory ⇒ use (refuses when it would orphan the current binding; --force overrides).",
|
|
12346
|
+
"want to poke another cognitive trajectory ⇒ send-now. Its target needs NO prior setup:",
|
|
12346
12347
|
"if the target has no live dsh session, one is created and bound automatically, and your message is injected into it.",
|
|
12347
12348
|
"RECEIPTS (read no more than is written): send-now returns a synchronous receipt meaning exactly \"injected / turn started\" —",
|
|
12348
12349
|
"NEVER \"the target ran or replied\"; to prove the target actually moved, read its SESSION.md.",
|
|
12349
12350
|
"It never queues behind the current turn: a busy target is steered mid-turn (read in the CURRENT turn), an idle target starts a turn at once.",
|
|
12350
12351
|
"send-later is fire-and-forget by design: no receipt, no recall, no panel.",
|
|
12351
12352
|
"ASYMMETRY (deliberate — the two differ in whether a missing target is normal): send-now creates a target on demand,",
|
|
12352
|
-
"because sending to a never-used trajectory is a legitimate intent; send-later does NOT,",
|
|
12353
|
-
"because scheduling for a trajectory that has never existed is almost always a mistyped name — auto-creating would hide that error behind an empty trajectory.",
|
|
12353
|
+
"because sending to a never-used cognitive trajectory is a legitimate intent; send-later does NOT,",
|
|
12354
|
+
"because scheduling for a cognitive trajectory that has never existed is almost always a mistyped name — auto-creating would hide that error behind an empty cognitive trajectory.",
|
|
12354
12355
|
"PARAMETERS: create requires --desc <desc> [--goal] or --issue <ticket> (exactly one) plus --summary (≤20 chars).",
|
|
12355
|
-
"use requires --summary (≤20 chars) and may pass --force to switch away from the currently-bound trajectory.",
|
|
12356
|
+
"use requires --summary (≤20 chars) and may pass --force to switch away from the currently-bound cognitive trajectory.",
|
|
12356
12357
|
"rebuild requires --summary (next-phase summary ≤20 chars) + optional --note (task focus for the rebuilt self).",
|
|
12357
12358
|
"send-later requires target (S### or AGENT_SESSIONS dir name) + at (RFC3339 or +30m/+2h) + message.",
|
|
12358
12359
|
"send-now requires target + message.",
|
|
@@ -12361,16 +12362,16 @@ function createTrajectoryTool(ctx) {
|
|
|
12361
12362
|
parameters: {
|
|
12362
12363
|
action: {
|
|
12363
12364
|
type: "string",
|
|
12364
|
-
enum: [...
|
|
12365
|
+
enum: [...COGNITIVE_TRAJECTORY_ACTIONS],
|
|
12365
12366
|
required: true,
|
|
12366
12367
|
description: [
|
|
12367
12368
|
"Subcommand — grouped by effect.",
|
|
12368
12369
|
"LIFECYCLE (changes THIS conversation's binding):",
|
|
12369
12370
|
"list (inventory + stats + anomaly marks) / show (S### or dir name or fuzzy keyword) /",
|
|
12370
|
-
"create (new trajectory; activates itself when you have no binding, otherwise leaves your binding untouched and tells you how to activate it) /",
|
|
12371
|
+
"create (new cognitive trajectory; activates itself when you have no binding, otherwise leaves your binding untouched and tells you how to activate it) /",
|
|
12371
12372
|
"use (activate context for THIS conversation; inline integrity check — pass silent, problems reported as hints, not errors) /",
|
|
12372
12373
|
"rebuild (clear-and-rebuild current conversation — requires --summary + optional --note).",
|
|
12373
|
-
"DELIVERY (sends one message to ANOTHER trajectory; your binding is unchanged):",
|
|
12374
|
+
"DELIVERY (sends one message to ANOTHER cognitive trajectory; your binding is unchanged):",
|
|
12374
12375
|
"send-now (deliver RIGHT NOW; the target needs no prior setup — if it has no live session, one is created and bound automatically) /",
|
|
12375
12376
|
"send-later (one future instant + one message; fire-and-forget, and it does NOT auto-create a target).",
|
|
12376
12377
|
"REFERENCE: cro-guide (no parameters: prints the CRO programming guide, including a ready-to-use sample snapshot)"
|
|
@@ -12402,7 +12403,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12402
12403
|
},
|
|
12403
12404
|
force: {
|
|
12404
12405
|
type: "boolean",
|
|
12405
|
-
description: "use: allow switching away from the currently-bound trajectory (binding guard override)"
|
|
12406
|
+
description: "use: allow switching away from the currently-bound cognitive trajectory (binding guard override)"
|
|
12406
12407
|
},
|
|
12407
12408
|
dryRun: {
|
|
12408
12409
|
type: "boolean",
|
|
@@ -12410,7 +12411,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12410
12411
|
},
|
|
12411
12412
|
target: {
|
|
12412
12413
|
type: "string",
|
|
12413
|
-
description: "send-later/send-now: target trajectory (S### or AGENT_SESSIONS directory name)"
|
|
12414
|
+
description: "send-later/send-now: target cognitive trajectory (S### or AGENT_SESSIONS directory name)"
|
|
12414
12415
|
},
|
|
12415
12416
|
at: {
|
|
12416
12417
|
type: "string",
|
|
@@ -12418,7 +12419,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12418
12419
|
},
|
|
12419
12420
|
message: {
|
|
12420
12421
|
type: "string",
|
|
12421
|
-
description: "send-later/send-now: the single message delivered to the target trajectory"
|
|
12422
|
+
description: "send-later/send-now: the single message delivered to the target cognitive trajectory"
|
|
12422
12423
|
}
|
|
12423
12424
|
},
|
|
12424
12425
|
output: {
|
|
@@ -12468,7 +12469,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12468
12469
|
return [
|
|
12469
12470
|
message,
|
|
12470
12471
|
`⚠ 本轨迹【未】被激活 —— 当前已绑定 ${priorBound},保绑定优先(不夺走你正在用的轨迹)。`,
|
|
12471
|
-
` 要激活它(并在它名下开一条会话):用
|
|
12472
|
+
` 要激活它(并在它名下开一条会话):用 cognitive_trajectory send-now 向 ${result.dirName} 投一条消息 ——`,
|
|
12472
12473
|
` 目标若没有活跃 dsh 会话,会自动创建一条并把「使用提示词 + 你的消息」一并注入。`
|
|
12473
12474
|
].join("\n");
|
|
12474
12475
|
}
|
|
@@ -12482,7 +12483,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12482
12483
|
const currentDir = currentBoundDirName(exec, scope);
|
|
12483
12484
|
const switching = currentDir !== null && targetDirName !== currentDir;
|
|
12484
12485
|
const force = args.force === true;
|
|
12485
|
-
if (switching && !force) throw new Error(`Session is bound to ${currentDir}. Switching to ${targetDirName} would orphan the current trajectory. Re-run with --force to switch (or close the current session first).`);
|
|
12486
|
+
if (switching && !force) throw new Error(`Session is bound to ${currentDir}. Switching to ${targetDirName} would orphan the current cognitive trajectory. Re-run with --force to switch (or close the current session first).`);
|
|
12486
12487
|
const active = useSession(root, args.name, scope);
|
|
12487
12488
|
const info = getActiveSessionInfo(scope);
|
|
12488
12489
|
if (info) renameDshSessionForActive(ctx, exec, info, args.summary);
|
|
@@ -12536,7 +12537,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12536
12537
|
target: args.target,
|
|
12537
12538
|
at: args.at,
|
|
12538
12539
|
message: args.message,
|
|
12539
|
-
createdBy:
|
|
12540
|
+
createdBy: resolveSessionCognitiveTrajectoryLabel(exec.agent?.session, wakeScope === "default" ? "" : wakeScope),
|
|
12540
12541
|
nowMs: Date.now()
|
|
12541
12542
|
});
|
|
12542
12543
|
if (!res.ok) return {
|
|
@@ -12551,7 +12552,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12551
12552
|
case "send-now": {
|
|
12552
12553
|
if (!args.target || !args.message) throw new Error("send-now requires target (S### or dir name) + message");
|
|
12553
12554
|
const sendScope = agentScope$1(exec);
|
|
12554
|
-
const res = await
|
|
12555
|
+
const res = await sendToCognitiveTrajectory(ctx, root, args.target, args.message, resolveSessionCognitiveTrajectoryLabel(exec.agent?.session, sendScope === "default" ? "" : sendScope));
|
|
12555
12556
|
if (!res.ok) return {
|
|
12556
12557
|
ok: false,
|
|
12557
12558
|
error: res.detail
|
|
@@ -12579,7 +12580,7 @@ function createTrajectoryTool(ctx) {
|
|
|
12579
12580
|
*/
|
|
12580
12581
|
function renderWakeEntry(e) {
|
|
12581
12582
|
return [
|
|
12582
|
-
"═══ trajectory 唤醒(1 条)═══",
|
|
12583
|
+
"═══ cognitive trajectory 唤醒(1 条)═══",
|
|
12583
12584
|
` · ${e.id} [${e.state}] at=${e.at} target=${e.target} by=${e.createdBy || "?"}`,
|
|
12584
12585
|
` message: ${e.message.length > 120 ? `${e.message.slice(0, 120)}…` : e.message}`,
|
|
12585
12586
|
"",
|
|
@@ -12672,7 +12673,7 @@ const localstoreTool = defineTool({
|
|
|
12672
12673
|
/** Skiff 定义教程(核心):概念 / schema / 认知 MSM 写法 / 双白名单 / 轨迹纪律 / 示例角色 */
|
|
12673
12674
|
const SKIFF_GUIDE = `═══ Skiff Definition Guide (F4, experimental) ═══
|
|
12674
12675
|
|
|
12675
|
-
Skiff = 宁静号放出的独立小艇——完整 trajectory(在宁静号内全知全能)的**任意子集角色**。
|
|
12676
|
+
Skiff = 宁静号放出的独立小艇——完整 cognitive trajectory(在宁静号内全知全能)的**任意子集角色**。
|
|
12676
12677
|
由本 CCC(.opencode/serenity.json skiff.roles)定义:能力面(双白名单)+ 轨迹纪律子集 + 系统提示词。
|
|
12677
12678
|
dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页);角色内容完全由 CCC 发挥。
|
|
12678
12679
|
|
|
@@ -12682,7 +12683,7 @@ dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页)
|
|
|
12682
12683
|
"model": "provider/model", // 角色模型(CCC 直接指定,无白名单校验;缺省回退 cheap-worker.defaultModel)
|
|
12683
12684
|
"msms": ["msm-a", "msm-b"], // MSM 白名单(独立):msm 工具只能执行这些;register/deregister 必拒
|
|
12684
12685
|
"tools": ["read","grep","glob",...], // 非 MSM 工具白名单(独立):白名单外工具一律不可用(guard 强制)
|
|
12685
|
-
"
|
|
12686
|
+
"cognitiveTrajectory": { "session": false, "keeper": false, "rebuild": false }, // 轨迹纪律子集(缺省全关 = 完全独立)
|
|
12686
12687
|
// 角色人格/认知边界/风格(CCC 完整定义;dsp 只给基础提示词)
|
|
12687
12688
|
"systemPromptFile": ".opencode/skiff/<role>.md" // ★ 推荐:引用 md 文件(相对 CCC 根;超长提示词在 JSON 内嵌不可读)
|
|
12688
12689
|
// "systemPrompt": "..." // 兼容:内嵌字符串仍可用;两者都设时 systemPromptFile 优先
|
|
@@ -12700,14 +12701,14 @@ dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页)
|
|
|
12700
12701
|
- 例:cognitive-qa(读 docs/references 回答)、review-scan(读代码出审查意见)、review-fix(写修复)
|
|
12701
12702
|
- 注意:MSM 在脚本层执行(bun),文件访问不受 agent 工具面约束——"只读"语义靠 CCC 自写 MSM 自觉
|
|
12702
12703
|
|
|
12703
|
-
── 轨迹纪律子集(
|
|
12704
|
+
── 轨迹纪律子集(cognitiveTrajectory)──
|
|
12704
12705
|
- session/keeper/rebuild 默认 false:Skiff 完全独立(不建 SESSION.md、无 keeper 提醒、无 rebuild 压力检测)
|
|
12705
12706
|
- 开启某项 = 该角色参与对应轨迹机制(如 keeper=true 计分提醒按角色生效)
|
|
12706
12707
|
|
|
12707
12708
|
── 示例角色 ──
|
|
12708
|
-
qa-readonly: { "msms": ["cognitive-qa"], "tools": [], "
|
|
12709
|
+
qa-readonly: { "msms": ["cognitive-qa"], "tools": [], "cognitiveTrajectory": {} }
|
|
12709
12710
|
→ 认知问答(仅 MSM 通道,无直接工具,完全独立)
|
|
12710
|
-
code-review: { "msms": ["review-scan","review-fix"], "tools": ["read","grep","glob","write","edit"], "
|
|
12711
|
+
code-review: { "msms": ["review-scan","review-fix"], "tools": ["read","grep","glob","write","edit"], "cognitiveTrajectory": { "keeper": true } }
|
|
12711
12712
|
→ 有操作能力(可写修复),参与 keeper 轨迹机制
|
|
12712
12713
|
|
|
12713
12714
|
── 运行 ──
|
|
@@ -12762,10 +12763,10 @@ function listSkiffRoles(root) {
|
|
|
12762
12763
|
model: role.model ?? "(cheap-worker default)",
|
|
12763
12764
|
msms: role.msms ?? [],
|
|
12764
12765
|
tools: role.tools ?? [],
|
|
12765
|
-
|
|
12766
|
-
session: role.
|
|
12767
|
-
keeper: role.
|
|
12768
|
-
rebuild: role.
|
|
12766
|
+
cognitiveTrajectory: {
|
|
12767
|
+
session: role.cognitiveTrajectory?.session === true,
|
|
12768
|
+
keeper: role.cognitiveTrajectory?.keeper === true,
|
|
12769
|
+
rebuild: role.cognitiveTrajectory?.rebuild === true
|
|
12769
12770
|
},
|
|
12770
12771
|
promptSource: systemPromptSource(role),
|
|
12771
12772
|
...role.systemPromptFile ? { systemPromptFile: role.systemPromptFile } : {}
|
|
@@ -12941,447 +12942,6 @@ function createContainerAdminTool(ctx) {
|
|
|
12941
12942
|
});
|
|
12942
12943
|
}
|
|
12943
12944
|
//#endregion
|
|
12944
|
-
//#region src/unattended-ops.ts
|
|
12945
|
-
/**
|
|
12946
|
-
* unattended-ops.ts — 无人值守代理回复(unattended proxy)机制层(S142 **D77**,owner 2026-09-23 提案)
|
|
12947
|
-
*
|
|
12948
|
-
* ## 这东西用来干什么(白话)
|
|
12949
|
-
*
|
|
12950
|
-
* 用户发个任务就走开。现在模型干完一段就停下来等回话,用户不在 ⇒ **会话挂在那儿不动**。
|
|
12951
|
-
* 本机制 = 用户走开后,系统**代替用户说一句"继续"**:让它自己跑完;干完了用一个**验证码**报告;
|
|
12952
|
-
* 卡住了先自己想办法;真不能定的**写下来等用户回来**。
|
|
12953
|
-
*
|
|
12954
|
-
* ## 归属(D23:ACC 管机制与数据,CCC 管措辞与纪律)
|
|
12955
|
-
*
|
|
12956
|
-
* | 面 | 归谁 | 内容 |
|
|
12957
|
-
* |---|---|---|
|
|
12958
|
-
* | **机制** | 🔴 **ACC(本模块)** | 触发判定 / 验证码 / 四选一结构 / 轮上限 / 流水 |
|
|
12959
|
-
* | **不可代批的*具体内容*** | **CCC 纪律** | ACC 只给**通用下限**({@link UNATTENDED_BLOCKED_FLOOR}),CCC 可加严 |
|
|
12960
|
-
*
|
|
12961
|
-
* 🔵 下限之所以落在 ACC(而不是"完全交给 CCC"):**没有这张清单,"自行解决"=把所有把关点自动放行**。
|
|
12962
|
-
* 最终内容仍待 owner 一句(设计 §13-5)——本版先给**保守下限**,CCC 只能在其上加严、不得放宽。
|
|
12963
|
-
*
|
|
12964
|
-
* ## 🔴 核心原则:单一续驱者(single-driver)
|
|
12965
|
-
*
|
|
12966
|
-
* > **一个会话同一时刻只应有一个"续驱者":用户 / CRO / 自排绳 / 无人值守代理。**
|
|
12967
|
-
*
|
|
12968
|
-
* 口径(owner 2026-09-23 19:1x 裁决)= **窄口径:只排除「有 CRO 程序的轨迹」**。
|
|
12969
|
-
* 原「宽口径」(任何自排唤醒都排除)**作废** —— 它会让本机制**永远够不着靠续接绳活着的会话**,
|
|
12970
|
-
* 等于**放弃"代替旧机制"**(绳因此退化为**长周期保险丝**:6–12h 一条)。
|
|
12971
|
-
*
|
|
12972
|
-
* ## 🔴 铁律(与 `cro.ts` / `cro-log.ts` 同族,勿破)
|
|
12973
|
-
*
|
|
12974
|
-
* **本模块的每个导出函数都不抛错**:写盘 / 解析 / 裁剪失败一律返回结构化失败。
|
|
12975
|
-
* ⇒ 代理机制的任何问题**不影响 turn 收尾**、不影响既有链路(唤醒投递 / 输出守卫 / rebuild)。
|
|
12976
|
-
*
|
|
12977
|
-
* ## 🔴 为什么不构成第二真相源(勿删此论证)
|
|
12978
|
-
*
|
|
12979
|
-
* | 载体 | 谁写 | 主语 | 回答 |
|
|
12980
|
-
* |---|---|---|---|
|
|
12981
|
-
* | `<轨迹目录>/unattended-log.json`(**本模块**) | **ACC** | ACC | 「**我注入了什么、判了什么、放过没有**」 |
|
|
12982
|
-
* | `<轨迹目录>/SESSION.md` | **LLM(CCC)** | CCC | 「**这轮发生了什么、为什么这么定**」 |
|
|
12983
|
-
*
|
|
12984
|
-
* **内容不重叠**:本模块只记**机械事实**(轮次 / 验证码 / 判定理由码 / 是否放过),**不写散文**;
|
|
12985
|
-
* LLM 侧的散文与理由归 SESSION.md。完整审计 = 两者合看(本模块 = 注入面|SESSION.md = 判断面)。
|
|
12986
|
-
*
|
|
12987
|
-
* ## 🔴 验证码形态(禁用 4 位字母码)
|
|
12988
|
-
*
|
|
12989
|
-
* 注入文本**自带**"请回复 ABCD" ⇒ 模型**复述指令**即**假完成**(高概率,不是小概率)
|
|
12990
|
-
* ⇒ 用**每轮新生成**的 nonce + **包装 + 独占一行**(`[[UNATTENDED-DONE:<hex>]]`),
|
|
12991
|
-
* 明写「**旧码作废**」,校验**只认当前轮**(防上下文里的旧码复活)。
|
|
12992
|
-
* 先例 = `cheap-worker` background 的 `SERENITY_CHEAP_WORKER_DONE_<16hex>`(同族实现)。
|
|
12993
|
-
*/
|
|
12994
|
-
/** 完成码包装前缀(**独占一行**才有意义;见文件头"禁用 4 位字母码") */
|
|
12995
|
-
const UNATTENDED_DONE_PREFIX = "[[UNATTENDED-DONE:";
|
|
12996
|
-
/** 注入消息的**首行标记**(幂等可辨:模型与事后审计都能一眼看出这是代理消息,不是用户消息) */
|
|
12997
|
-
const UNATTENDED_PROXY_MARK = "[[UNATTENDED-PROXY v1]]";
|
|
12998
|
-
/** 🔴 合成消息**固定身份句**(owner 已裁:「形如用户消息,但首句自报身份与权限边界」) */
|
|
12999
|
-
const UNATTENDED_IDENTITY_LINE = "无人值守代理 · 非本人";
|
|
13000
|
-
/** 生成一枚新 nonce(**每轮一枚**;旧码作废由调用方在文本里声明) */
|
|
13001
|
-
function newNonce() {
|
|
13002
|
-
return randomBytes(8).toString("hex");
|
|
13003
|
-
}
|
|
13004
|
-
/** 完成码全文(注入文本展示用;校验走 {@link hasDoneToken}) */
|
|
13005
|
-
function doneToken(nonce) {
|
|
13006
|
-
return `${UNATTENDED_DONE_PREFIX}${nonce}]]`;
|
|
13007
|
-
}
|
|
13008
|
-
/**
|
|
13009
|
-
* 出现过的完成码 nonce 列表(按出现顺序、去重;**小写归一**)。
|
|
13010
|
-
* 容忍 8–64 位 hex(模型可能手抄多/少几个字符 ⇒ 那就不匹配,属预期:只认本轮那枚)。
|
|
13011
|
-
*/
|
|
13012
|
-
function parseDoneNonces(text) {
|
|
13013
|
-
if (typeof text !== "string" || text === "") return [];
|
|
13014
|
-
const re = /\[\[UNATTENDED-DONE:([0-9a-fA-F]{8,64})\]\]/g;
|
|
13015
|
-
const out = [];
|
|
13016
|
-
let m;
|
|
13017
|
-
while ((m = re.exec(text)) !== null) {
|
|
13018
|
-
const n = (m[1] ?? "").toLowerCase();
|
|
13019
|
-
if (n !== "" && !out.includes(n)) out.push(n);
|
|
13020
|
-
}
|
|
13021
|
-
return out;
|
|
13022
|
-
}
|
|
13023
|
-
/** 触及即停(写待办/交接,**不循环**)——**CCC 的纪律更严则从严** */
|
|
13024
|
-
const UNATTENDED_BLOCKED_FLOOR = [
|
|
13025
|
-
"发布 / 发版",
|
|
13026
|
-
"对外发送(邮件 / 微信 / 对外面输出)",
|
|
13027
|
-
"删除与不可逆操作(rm / 归档 / force push)",
|
|
13028
|
-
"凭据与权限变更",
|
|
13029
|
-
"花钱(外部 API 大额调用 / GPU 长任务)",
|
|
13030
|
-
"代用户拍板(替人做决定)"
|
|
13031
|
-
];
|
|
13032
|
-
/** 四选一正文({@link buildProxyMessage} 与 {@link buildFalseNotifyRebuke} 共用,避免两份措辞漂移) */
|
|
13033
|
-
function proxyBody(input) {
|
|
13034
|
-
return [
|
|
13035
|
-
`A) 已完成、且用户回来会满意 ⇒ 交码 + 附完成凭据(做了什么 / 怎么验的 / 产物路径)`,
|
|
13036
|
-
`B) 已成功向用户发出通知、要停下等他主动回来 ⇒ 交码 + 写明走了哪个通道(**必须真有发送记录**)`,
|
|
13037
|
-
`C) 需要确认但可自决 ⇒ 按「**最保守且可逆**」的选项继续做完(⚠️ **不要交码**,直接做)`,
|
|
13038
|
-
`D) 触及不可代批边界 ⇒ **立即停**,把问题写进待办 / 交接,然后交码`,
|
|
13039
|
-
``,
|
|
13040
|
-
`不可代批的**通用下限**(你所在 CCC 的纪律更严则从严):${(input.blocked ?? UNATTENDED_BLOCKED_FLOOR).join(" / ")}`,
|
|
13041
|
-
``,
|
|
13042
|
-
`交码 = **独占一行、逐字复制下面这一行**(旧码一律作废,只认本轮这个):`,
|
|
13043
|
-
``,
|
|
13044
|
-
` ${doneToken(input.nonce)}`,
|
|
13045
|
-
``,
|
|
13046
|
-
`禁止:把本条当真实用户的授权;禁止把本指令文本复述成回答;禁止什么都没做只回一个码;`,
|
|
13047
|
-
`禁止借机发起**新的长任务**(代理只推进"已在办"的事)。`
|
|
13048
|
-
].join("\n");
|
|
13049
|
-
}
|
|
13050
|
-
/** 组装一次代理注入消息(**形如用户消息,但首句自报身份与权限边界**) */
|
|
13051
|
-
function buildProxyMessage(input) {
|
|
13052
|
-
return [
|
|
13053
|
-
`${UNATTENDED_PROXY_MARK} round=${input.round}/${input.maxRounds} nonce=${input.nonce}`,
|
|
13054
|
-
`${UNATTENDED_IDENTITY_LINE} —— 这条消息是**系统代用户发的**,不是用户本人;我没有任何权限替你拍板。`,
|
|
13055
|
-
``,
|
|
13056
|
-
`用户不在场。你只能以下列**四条之一**收束本轮:`,
|
|
13057
|
-
``,
|
|
13058
|
-
proxyBody(input)
|
|
13059
|
-
].join("\n");
|
|
13060
|
-
}
|
|
13061
|
-
/**
|
|
13062
|
-
* 组装一次**假完成打回**消息(设计 §7 的机械核验分支:声称已通知用户,但本轮无发送记录)。
|
|
13063
|
-
*
|
|
13064
|
-
* ⚠️ 同样带**新 nonce**:它是一次"再来一轮",不是"放过"。
|
|
13065
|
-
*/
|
|
13066
|
-
function buildFalseNotifyRebuke(input) {
|
|
13067
|
-
return [
|
|
13068
|
-
`${UNATTENDED_PROXY_MARK} round=${input.round}/${input.maxRounds} nonce=${input.nonce}`,
|
|
13069
|
-
`${UNATTENDED_IDENTITY_LINE} —— 这条消息是**系统代用户发的**,不是用户本人。`,
|
|
13070
|
-
``,
|
|
13071
|
-
`🔴 你交的码是对的,但**你声称已通知用户,而我这边查不到任何发送记录** ⇒ **不算合法收束**。`,
|
|
13072
|
-
`要么**真的发**(走可用通道;发成之后再交码),要么改走 A / C / D。其余口径同下。`,
|
|
13073
|
-
``,
|
|
13074
|
-
proxyBody(input)
|
|
13075
|
-
].join("\n");
|
|
13076
|
-
}
|
|
13077
|
-
/**
|
|
13078
|
-
* 「收尾文本**声称**已联系用户」的**启发式**判据(设计 §7 的触发器,**不是**判据本身)。
|
|
13079
|
-
*
|
|
13080
|
-
* 🔴 机械判据 = **ACC 侧的发送记录**({@link hasOutboundSendSince});本函数只回答"**要不要去查记录**"。
|
|
13081
|
-
* 误判方向(明标):**误报**(模型换了个说法没被匹配到)⇒ 退回"只认码",不产生假放过;
|
|
13082
|
-
* **误报**(正常措辞被当成声称)⇒ 多打回一轮(有轮上限兜底)。⇒ 偏严,符合设计 §11
|
|
13083
|
-
* 把"谎称已发消息"列为失败模式的取向。
|
|
13084
|
-
*/
|
|
13085
|
-
const NOTIFY_CLAIM_PATTERNS = [
|
|
13086
|
-
/已(经)?(成功)?(向|给|和)?\s*(用户|您|你|他|她|本人|主人)?[^\n。]{0,16}(发送|发出|告知|通知|联系|留言|汇报)/,
|
|
13087
|
-
/\b(?:notified|informed|messaged|contacted)\s+(?:the\s+)?user\b/i,
|
|
13088
|
-
/\b(?:sent|sending)\s+(?:a\s+)?(?:message|notification|notice)\s+to\s+(?:the\s+)?user\b/i,
|
|
13089
|
-
/\bI\s+(?:have\s+)?(?:notified|informed|messaged|contacted)\b/i
|
|
13090
|
-
];
|
|
13091
|
-
/** 收尾文本是否**声称**已联系用户(见 {@link NOTIFY_CLAIM_PATTERNS} 的误判方向说明) */
|
|
13092
|
-
function claimsUserNotification(text) {
|
|
13093
|
-
if (typeof text !== "string" || text === "") return false;
|
|
13094
|
-
return NOTIFY_CLAIM_PATTERNS.some((re) => re.test(text));
|
|
13095
|
-
}
|
|
13096
|
-
/**
|
|
13097
|
-
* **不许开**的会话(设计 §3 的"建议写死",本版**确实写死**):
|
|
13098
|
-
* `skiff-`(F4 问答 / **微信桥会话也是这个前缀** —— 桥那侧的"用户"是**家人**,
|
|
13099
|
-
* 代理替家人说话 = **冒充**)| `acp-`(对外程序化面 —— ⚠️ **2026-10-15 起已成死判据**:
|
|
13100
|
-
* 该前缀从无生产者且面 E 已退役;理由与"为何保留"见 `output-guard-seam.ts` 文件头)|
|
|
13101
|
-
* `cheap-worker-`(自主 worker,已有自己的完成码循环)。
|
|
13102
|
-
*
|
|
13103
|
-
* ⚠️ **`rebuild-` 不在排除面**(与 `output-guard-seam.ts` 的取向**刻意不同**):重建会话是
|
|
13104
|
-
* **同一轨迹的载体**(内部维护面),不是对外面;把它排除会让"重建后就没人推了"。
|
|
13105
|
-
*/
|
|
13106
|
-
function isProxyExcludedSessionId(sessionId) {
|
|
13107
|
-
if (typeof sessionId !== "string" || sessionId === "") return true;
|
|
13108
|
-
return isSkiffSessionId(sessionId) || sessionId.startsWith("acp-") || sessionId.startsWith("cheap-worker-");
|
|
13109
|
-
}
|
|
13110
|
-
/**
|
|
13111
|
-
* 一次判定的**唯一入口**(纯函数)。
|
|
13112
|
-
*
|
|
13113
|
-
* 顺序即语义(R↓,勿随意调换):
|
|
13114
|
-
* 1. **闸/空间/会话资格**(关着、不在 CCC、非主会话、排除面)⇒ skip;
|
|
13115
|
-
* 2. **单一续驱者**:用户刚发起 ⇒ skip(人就在场);该轨迹有 CRO ⇒ skip(程序在驱动);
|
|
13116
|
-
* 3. **合法收束优先于轮上限** —— 最后一轮交的码必须算数(否则等于永远收不了尾);
|
|
13117
|
-
* 4. 交码 + 声称已通知 + **无发送记录** ⇒ 假完成,走下一轮(reason=`false-notify`);
|
|
13118
|
-
* 5. 墙钟 / 轮上限 ⇒ cap(**停下不动**,退回改造前的行为,不产生新风险);
|
|
13119
|
-
* 6. 其余 ⇒ inject 下一轮。
|
|
13120
|
-
*/
|
|
13121
|
-
function decideUnattendedAction(f) {
|
|
13122
|
-
if (!f.enabled) return {
|
|
13123
|
-
action: "skip",
|
|
13124
|
-
reason: "mode-off",
|
|
13125
|
-
round: 0
|
|
13126
|
-
};
|
|
13127
|
-
if (!f.root) return {
|
|
13128
|
-
action: "skip",
|
|
13129
|
-
reason: "not-in-ccc",
|
|
13130
|
-
round: 0
|
|
13131
|
-
};
|
|
13132
|
-
if (!f.isMainSession) return {
|
|
13133
|
-
action: "skip",
|
|
13134
|
-
reason: "not-main-session",
|
|
13135
|
-
round: 0
|
|
13136
|
-
};
|
|
13137
|
-
if (f.sessionIdExcluded) return {
|
|
13138
|
-
action: "skip",
|
|
13139
|
-
reason: "excluded-session",
|
|
13140
|
-
round: 0
|
|
13141
|
-
};
|
|
13142
|
-
if (f.userInitiated) return {
|
|
13143
|
-
action: "skip",
|
|
13144
|
-
reason: "user-initiated",
|
|
13145
|
-
round: 0
|
|
13146
|
-
};
|
|
13147
|
-
if (f.hasCroProgram) return {
|
|
13148
|
-
action: "skip",
|
|
13149
|
-
reason: "cro-programmed",
|
|
13150
|
-
round: 0
|
|
13151
|
-
};
|
|
13152
|
-
const run = f.run;
|
|
13153
|
-
if (run !== null && f.doneNonce !== null && f.doneNonce.toLowerCase() === run.nonce.toLowerCase() && run) {
|
|
13154
|
-
if (f.claimsNotify && !f.outboundSeen) {
|
|
13155
|
-
if (run.round >= 15) return {
|
|
13156
|
-
action: "cap",
|
|
13157
|
-
reason: "round-cap",
|
|
13158
|
-
round: run.round
|
|
13159
|
-
};
|
|
13160
|
-
return {
|
|
13161
|
-
action: "inject",
|
|
13162
|
-
reason: "false-notify",
|
|
13163
|
-
round: run.round + 1
|
|
13164
|
-
};
|
|
13165
|
-
}
|
|
13166
|
-
return {
|
|
13167
|
-
action: "accept",
|
|
13168
|
-
reason: "done-code",
|
|
13169
|
-
round: run.round
|
|
13170
|
-
};
|
|
13171
|
-
}
|
|
13172
|
-
if (run && f.nowMs - run.startedAt > 72e5) return {
|
|
13173
|
-
action: "cap",
|
|
13174
|
-
reason: "wall-clock",
|
|
13175
|
-
round: run.round
|
|
13176
|
-
};
|
|
13177
|
-
if (run && run.round >= 15) return {
|
|
13178
|
-
action: "cap",
|
|
13179
|
-
reason: "round-cap",
|
|
13180
|
-
round: run.round
|
|
13181
|
-
};
|
|
13182
|
-
const round = (run?.round ?? 0) + 1;
|
|
13183
|
-
return {
|
|
13184
|
-
action: "inject",
|
|
13185
|
-
reason: run === null ? "first-stop" : f.doneNonce !== null ? "stale-code" : "no-legal-closure",
|
|
13186
|
-
round
|
|
13187
|
-
};
|
|
13188
|
-
}
|
|
13189
|
-
/** 键 = 载体(dsh 会话 id)——与 `cro-turns.ts` 同款选择(事件本身就以 agent/session 为主语) */
|
|
13190
|
-
const runs = /* @__PURE__ */ new Map();
|
|
13191
|
-
/** 取该会话的代理段(无 ⇒ null) */
|
|
13192
|
-
function getProxyRun(sessionId) {
|
|
13193
|
-
return runs.get(sessionId) ?? null;
|
|
13194
|
-
}
|
|
13195
|
-
/** 开始 / 推进一段代理(`startedAt` 由调用方给出 ⇒ 跨轮保留首次值) */
|
|
13196
|
-
function setProxyRun(sessionId, run) {
|
|
13197
|
-
if (!sessionId) return;
|
|
13198
|
-
runs.set(sessionId, run);
|
|
13199
|
-
}
|
|
13200
|
-
/** 结束该会话的代理段(放过 / 上限 / 用户回来 / 载体销毁) */
|
|
13201
|
-
function clearProxyRun(sessionId) {
|
|
13202
|
-
if (!sessionId) return;
|
|
13203
|
-
runs.delete(sessionId);
|
|
13204
|
-
}
|
|
13205
|
-
/**
|
|
13206
|
-
* 「该载体最近一次由**真实用户**发起的轮次号」(会话 id → turn)。
|
|
13207
|
-
*
|
|
13208
|
-
* ⚠️ 只留**一条**(不是集合):`claimed` 的 `turn` 单调递增,留最新一条即可回答
|
|
13209
|
-
* "本轮是不是用户发起的",且**表的大小 = 会话数**(本容器栽过 5 次"只增不清",此处结构性避免)。
|
|
13210
|
-
*/
|
|
13211
|
-
const userTurns = /* @__PURE__ */ new Map();
|
|
13212
|
-
/** 记下"这个载体的这一轮是真实用户发起的" */
|
|
13213
|
-
function noteUserTurn(sessionId, turn) {
|
|
13214
|
-
if (!sessionId || typeof turn !== "number") return;
|
|
13215
|
-
userTurns.set(sessionId, turn);
|
|
13216
|
-
}
|
|
13217
|
-
/** 该载体的这一轮是否由真实用户发起(判据 = `noteUserTurn` 写入的最新一条) */
|
|
13218
|
-
function isUserTurn(sessionId, turn) {
|
|
13219
|
-
if (!sessionId || typeof turn !== "number") return false;
|
|
13220
|
-
return userTurns.get(sessionId) === turn;
|
|
13221
|
-
}
|
|
13222
|
-
/** 清掉该载体的用户轮次记录(载体销毁时) */
|
|
13223
|
-
function clearUserTurn(sessionId) {
|
|
13224
|
-
if (!sessionId) return;
|
|
13225
|
-
userTurns.delete(sessionId);
|
|
13226
|
-
}
|
|
13227
|
-
/** 清空全部进程内运行态(disposer;**不碰磁盘**) */
|
|
13228
|
-
function clearUnattendedState() {
|
|
13229
|
-
runs.clear();
|
|
13230
|
-
userTurns.clear();
|
|
13231
|
-
outboundSends.clear();
|
|
13232
|
-
}
|
|
13233
|
-
/** 发送记录保留窗(超出即丢:它只服务"这一轮到底发没发"这个瞬时问题) */
|
|
13234
|
-
const UNATTENDED_OUTBOUND_TTL_MS = 216e5;
|
|
13235
|
-
const outboundSends = /* @__PURE__ */ new Map();
|
|
13236
|
-
/**
|
|
13237
|
-
* 记一次**成功的**出站发送(`im-bridge` 工具面在通道返回成功后调用)。
|
|
13238
|
-
*
|
|
13239
|
-
* ⚠️ **覆盖边界(诚实标注)**:只覆盖 **ACC 自己这条通道**(`im-bridge` 的 send / send-file)。
|
|
13240
|
-
* CCC 自有的发送通道(如 `mail-tool` MSM)**ACC 看不见** ⇒ 那类"已通知"落到
|
|
13241
|
-
* {@link hasOutboundSendSince} = false ⇒ 会被判成假完成。⇒ 本版口径:**代理模式下"联系用户"
|
|
13242
|
-
* 只认 ACC 通道**;这与设计 §7 的原话(im-bridge 记日志)一致,边界写在此处以免误读。
|
|
13243
|
-
*/
|
|
13244
|
-
function noteOutboundSend(root, atMs = Date.now()) {
|
|
13245
|
-
if (!root) return;
|
|
13246
|
-
const list = outboundSends.get(root) ?? [];
|
|
13247
|
-
list.push(atMs);
|
|
13248
|
-
const cutoff = atMs - UNATTENDED_OUTBOUND_TTL_MS;
|
|
13249
|
-
const kept = list.filter((t) => t >= cutoff).slice(-20);
|
|
13250
|
-
outboundSends.set(root, kept);
|
|
13251
|
-
}
|
|
13252
|
-
/** 自 `sinceMs` 起该 CCC 是否有过成功出站发送(**只读,不清表**——清理归写入侧的裁剪) */
|
|
13253
|
-
function hasOutboundSendSince(root, sinceMs, nowMs = Date.now()) {
|
|
13254
|
-
if (!root) return false;
|
|
13255
|
-
const list = outboundSends.get(root);
|
|
13256
|
-
if (!list || list.length === 0) return false;
|
|
13257
|
-
const floor = Math.max(sinceMs, nowMs - UNATTENDED_OUTBOUND_TTL_MS);
|
|
13258
|
-
return list.some((t) => t >= floor && t <= nowMs);
|
|
13259
|
-
}
|
|
13260
|
-
/** 固定文件名(与 `cro-wake-log.json` 并排、主语分明:那份记"我叫了什么",这份记"我代说了什么") */
|
|
13261
|
-
const UNATTENDED_LOG_FILENAME = "unattended-log.json";
|
|
13262
|
-
/** 保留窗 = 2 日(与 CRO 流水同值;**常量** ⇒ 无需人工清理动作) */
|
|
13263
|
-
const UNATTENDED_LOG_RETENTION_MS = 1728e5;
|
|
13264
|
-
const UNATTENDED_LOG_VERSION = 1;
|
|
13265
|
-
/** 日志绝对路径 */
|
|
13266
|
-
function unattendedLogPath(root, dirName) {
|
|
13267
|
-
return join(sessionsRoot(root), dirName, UNATTENDED_LOG_FILENAME);
|
|
13268
|
-
}
|
|
13269
|
-
function emptyLog() {
|
|
13270
|
-
return {
|
|
13271
|
-
version: UNATTENDED_LOG_VERSION,
|
|
13272
|
-
entries: []
|
|
13273
|
-
};
|
|
13274
|
-
}
|
|
13275
|
-
/** 条目形状校验(容忍手改:字段缺失/类型不符 ⇒ 丢弃该条,不阻断整档) */
|
|
13276
|
-
function asEntry(value) {
|
|
13277
|
-
const e = value;
|
|
13278
|
-
if (!e || typeof e !== "object") return null;
|
|
13279
|
-
if (typeof e.at !== "string" || e.at === "") return null;
|
|
13280
|
-
if (e.event !== "inject" && e.event !== "accept" && e.event !== "cap" && e.event !== "yield") return null;
|
|
13281
|
-
if (typeof e.reason !== "string") return null;
|
|
13282
|
-
return {
|
|
13283
|
-
at: e.at,
|
|
13284
|
-
event: e.event,
|
|
13285
|
-
round: typeof e.round === "number" ? e.round : 0,
|
|
13286
|
-
nonce: typeof e.nonce === "string" ? e.nonce : null,
|
|
13287
|
-
reason: e.reason,
|
|
13288
|
-
sessionId: typeof e.sessionId === "string" ? e.sessionId : ""
|
|
13289
|
-
};
|
|
13290
|
-
}
|
|
13291
|
-
/** 读取流水(**永不抛**;读坏 ⇒ 当作空档 + 返回 error 文本) */
|
|
13292
|
-
function loadUnattendedLog(root, dirName) {
|
|
13293
|
-
const path = unattendedLogPath(root, dirName);
|
|
13294
|
-
if (!existsSync(path)) return {
|
|
13295
|
-
log: emptyLog(),
|
|
13296
|
-
error: null
|
|
13297
|
-
};
|
|
13298
|
-
try {
|
|
13299
|
-
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
13300
|
-
const raw = Array.isArray(parsed.entries) ? parsed.entries : [];
|
|
13301
|
-
const entries = [];
|
|
13302
|
-
for (const item of raw) {
|
|
13303
|
-
const e = asEntry(item);
|
|
13304
|
-
if (e) entries.push(e);
|
|
13305
|
-
}
|
|
13306
|
-
return {
|
|
13307
|
-
log: {
|
|
13308
|
-
version: UNATTENDED_LOG_VERSION,
|
|
13309
|
-
entries
|
|
13310
|
-
},
|
|
13311
|
-
error: null
|
|
13312
|
-
};
|
|
13313
|
-
} catch (err) {
|
|
13314
|
-
return {
|
|
13315
|
-
log: emptyLog(),
|
|
13316
|
-
error: `无人值守流水解析失败(${String(err?.message ?? err)})`
|
|
13317
|
-
};
|
|
13318
|
-
}
|
|
13319
|
-
}
|
|
13320
|
-
/**
|
|
13321
|
-
* 按窗裁剪(**纯函数** ⇒ 可穷举测试)。
|
|
13322
|
-
* 🔴 `at` 不可解析者**一律丢弃**并在返回值里报数(`dropped`)——**不静默吞掉**(同 `cro-log.ts` 纪律)。
|
|
13323
|
-
*/
|
|
13324
|
-
function pruneUnattendedLogEntries(entries, nowMs, windowMs = UNATTENDED_LOG_RETENTION_MS) {
|
|
13325
|
-
const floor = nowMs - windowMs;
|
|
13326
|
-
const kept = [];
|
|
13327
|
-
let pruned = 0;
|
|
13328
|
-
let dropped = 0;
|
|
13329
|
-
for (const e of entries) {
|
|
13330
|
-
const t = Date.parse(e.at);
|
|
13331
|
-
if (Number.isNaN(t)) {
|
|
13332
|
-
dropped += 1;
|
|
13333
|
-
continue;
|
|
13334
|
-
}
|
|
13335
|
-
if (t >= floor) kept.push(e);
|
|
13336
|
-
else pruned += 1;
|
|
13337
|
-
}
|
|
13338
|
-
return {
|
|
13339
|
-
kept,
|
|
13340
|
-
pruned,
|
|
13341
|
-
dropped
|
|
13342
|
-
};
|
|
13343
|
-
}
|
|
13344
|
-
/**
|
|
13345
|
-
* 追加一条流水并按窗裁剪(**本模块的落盘入口;永不抛**)。
|
|
13346
|
-
*
|
|
13347
|
-
* 顺序 = 读 → 追加 → 裁剪 → **原子写**(tmp + rename)。任一步失败 ⇒ `{ok:false, error}`,**不抛**
|
|
13348
|
-
* ——调用方只记一行 console 日志 ⇒ **代理机制的问题绝不影响 turn 收尾**。
|
|
13349
|
-
*/
|
|
13350
|
-
function appendUnattendedLog(root, dirName, entry, nowMs = Date.now()) {
|
|
13351
|
-
if (!root || !dirName) return {
|
|
13352
|
-
ok: false,
|
|
13353
|
-
error: "缺少 CCC 根或轨迹目录名",
|
|
13354
|
-
kept: 0
|
|
13355
|
-
};
|
|
13356
|
-
const path = unattendedLogPath(root, dirName);
|
|
13357
|
-
try {
|
|
13358
|
-
const loaded = loadUnattendedLog(root, dirName);
|
|
13359
|
-
const full = {
|
|
13360
|
-
at: isoLocal(nowMs),
|
|
13361
|
-
...entry
|
|
13362
|
-
};
|
|
13363
|
-
const { kept } = pruneUnattendedLogEntries([...loaded.log.entries, full], nowMs);
|
|
13364
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
13365
|
-
const tmp = `${path}.tmp`;
|
|
13366
|
-
writeFileSync(tmp, `${JSON.stringify({
|
|
13367
|
-
version: UNATTENDED_LOG_VERSION,
|
|
13368
|
-
entries: kept
|
|
13369
|
-
}, null, 2)}\n`, "utf-8");
|
|
13370
|
-
renameSync(tmp, path);
|
|
13371
|
-
return {
|
|
13372
|
-
ok: true,
|
|
13373
|
-
error: null,
|
|
13374
|
-
kept: kept.length
|
|
13375
|
-
};
|
|
13376
|
-
} catch (err) {
|
|
13377
|
-
return {
|
|
13378
|
-
ok: false,
|
|
13379
|
-
error: `无人值守流水写入失败(${String(err?.message ?? err)})`,
|
|
13380
|
-
kept: 0
|
|
13381
|
-
};
|
|
13382
|
-
}
|
|
13383
|
-
}
|
|
13384
|
-
//#endregion
|
|
13385
12945
|
//#region src/im-bridge.ts
|
|
13386
12946
|
/**
|
|
13387
12947
|
* im-bridge.ts — IM 发送能力层(v1.31.0,S142 用户洞察)
|
|
@@ -13532,19 +13092,17 @@ async function runImBridge(root, req) {
|
|
|
13532
13092
|
const resolved = channel.resolveUser(root, rawUser);
|
|
13533
13093
|
if (!resolved) return fail("ALIAS_UNKNOWN", `unknown user "${rawUser}"`);
|
|
13534
13094
|
try {
|
|
13535
|
-
const sent = await channel.send({
|
|
13536
|
-
root,
|
|
13537
|
-
userId: resolved.id,
|
|
13538
|
-
text,
|
|
13539
|
-
accountId: req.account
|
|
13540
|
-
});
|
|
13541
|
-
noteOutboundSend(root);
|
|
13542
13095
|
return {
|
|
13543
13096
|
ok: true,
|
|
13544
13097
|
channel: channelId,
|
|
13545
13098
|
action,
|
|
13546
13099
|
detail: {
|
|
13547
|
-
...
|
|
13100
|
+
...await channel.send({
|
|
13101
|
+
root,
|
|
13102
|
+
userId: resolved.id,
|
|
13103
|
+
text,
|
|
13104
|
+
accountId: req.account
|
|
13105
|
+
}),
|
|
13548
13106
|
user: resolved.alias ?? resolved.id
|
|
13549
13107
|
}
|
|
13550
13108
|
};
|
|
@@ -13563,21 +13121,19 @@ async function runImBridge(root, req) {
|
|
|
13563
13121
|
const file = readCccFile(root, fileArg);
|
|
13564
13122
|
if (!file.ok) return fail(file.code, file.error);
|
|
13565
13123
|
try {
|
|
13566
|
-
const sent = await channel.sendFile({
|
|
13567
|
-
root,
|
|
13568
|
-
userId: resolved.id,
|
|
13569
|
-
data: file.data,
|
|
13570
|
-
fileName: file.fileName,
|
|
13571
|
-
caption: req.caption,
|
|
13572
|
-
accountId: req.account
|
|
13573
|
-
});
|
|
13574
|
-
noteOutboundSend(root);
|
|
13575
13124
|
return {
|
|
13576
13125
|
ok: true,
|
|
13577
13126
|
channel: channelId,
|
|
13578
13127
|
action,
|
|
13579
13128
|
detail: {
|
|
13580
|
-
...
|
|
13129
|
+
...await channel.sendFile({
|
|
13130
|
+
root,
|
|
13131
|
+
userId: resolved.id,
|
|
13132
|
+
data: file.data,
|
|
13133
|
+
fileName: file.fileName,
|
|
13134
|
+
caption: req.caption,
|
|
13135
|
+
accountId: req.account
|
|
13136
|
+
}),
|
|
13581
13137
|
user: resolved.alias ?? resolved.id
|
|
13582
13138
|
}
|
|
13583
13139
|
};
|
|
@@ -13889,7 +13445,7 @@ function forgetImBridgeVisibility(sessionId) {
|
|
|
13889
13445
|
*
|
|
13890
13446
|
* 🔴 **为什么要在 CCC 内屏蔽(owner 原话:「这和 CRO 冲突,要求在宁静号激活情况下屏蔽掉」)**:
|
|
13891
13447
|
* 宿主从 `0.2.1-rc.2` 起自带一套**模型可见的定时器工具**(`ctx.schedule` 服务 + 这 4 个工具)。
|
|
13892
|
-
* 而宁静号的唤醒/巡航走的是**另一套**机制:`
|
|
13448
|
+
* 而宁静号的唤醒/巡航走的是**另一套**机制:`cognitive_trajectory send-later` → 落 CCC 的
|
|
13893
13449
|
* `AGENT_SESSIONS/wake-registry.json` → 由**中心调度器 5min tick 统一投递**(D58)。
|
|
13894
13450
|
* 两套并存会产生**三个具体问题**(不是"重复功能"这种审美问题):
|
|
13895
13451
|
* ① **两处真相源**:宿主的 schedule 状态在宿主域、我们的在 CCC 文件里,**谁在跑说不清**;
|
|
@@ -14011,7 +13567,7 @@ function forgetExclusiveToolsVisibility(sessionId) {
|
|
|
14011
13567
|
*
|
|
14012
13568
|
* 🔴 **为什么需要它(缺陷根因)**:工具是 `apply()` 里 `ctx.tools.register(...)` 注册到**宿主级**的
|
|
14013
13569
|
* ⇒ 它们出现在**每一个**会话的工具清单里,**与会话 cwd 无关**。其余各缝(身份注入 / guards /
|
|
14014
|
-
* keeper / output-guard /
|
|
13570
|
+
* keeper / output-guard / bootstrap)都已有 `if (!root) return` 这道门,
|
|
14015
13571
|
* **唯独"工具可见性"这一层没有** ⇒ 在非 CCC 目录开会话时,模型仍看得见这 14 个工具,
|
|
14016
13572
|
* 于是会去调 —— 调了才抛 `No CCC found`。**那是运行时报错,不是事先不可见**。
|
|
14017
13573
|
*
|
|
@@ -14549,12 +14105,12 @@ function registerBootstrap(ctx) {
|
|
|
14549
14105
|
}, { prepend: true });
|
|
14550
14106
|
}
|
|
14551
14107
|
//#endregion
|
|
14552
|
-
//#region src/trajectory-assistant.ts
|
|
14108
|
+
//#region src/cognitive-trajectory-assistant.ts
|
|
14553
14109
|
/**
|
|
14554
|
-
* trajectory-assistant.ts — 关卡化注入的统一命名与风格门面(v0.3,S142)
|
|
14110
|
+
* cognitive-trajectory-assistant.ts — 关卡化注入的统一命名与风格门面(v0.3,S142)
|
|
14555
14111
|
*
|
|
14556
14112
|
* 概念(用户拍板 2026-09-05):dsp 全部"过程中动态提示注入"统一命名为
|
|
14557
|
-
* trajectory-assistant(轨迹助航员)。关卡设计思想塑造**结构与时机**
|
|
14113
|
+
* cognitive-trajectory-assistant(轨迹助航员)。关卡设计思想塑造**结构与时机**
|
|
14558
14114
|
* (何时/何地注入),**不用于提示词用词**(D8 词法原则)——纯游戏黑话
|
|
14559
14115
|
* (BOSS/XP/level-up)禁止出现在提示词文本;CHECKPOINT/LIMIT/TUTORIAL 等
|
|
14560
14116
|
* 跨领域自然通用词允许。
|
|
@@ -14567,7 +14123,7 @@ function registerBootstrap(ctx) {
|
|
|
14567
14123
|
* L6 结算)仅在本模块注释与内部文档出现,永不浮现在提示词文本。
|
|
14568
14124
|
*
|
|
14569
14125
|
* 结算(settlement)挂点:D6 用户拍板——若 CCC 有标准 SESSION 复盘仪式,
|
|
14570
|
-
* 结算视图归 trajectory-assistant。当前 close/archive 无复盘摘要 ⇒ 机制延后。
|
|
14126
|
+
* 结算视图归 cognitive-trajectory-assistant。当前 close/archive 无复盘摘要 ⇒ 机制延后。
|
|
14571
14127
|
* 🔴 2026-09-25(S142 分批清死代码·第 1 批):原先那个 `onSettlement(cb)` **空 seam 已删**
|
|
14572
14128
|
* (零调用者、无触发器、空实现)——仪式真落地时按当时的信号重新设计,**不再靠空占位**。
|
|
14573
14129
|
*/
|
|
@@ -14592,8 +14148,8 @@ const EVENT_LABEL = {
|
|
|
14592
14148
|
scratchPrune: "SCRATCH PRUNE"
|
|
14593
14149
|
};
|
|
14594
14150
|
/** 家族标识(所有动态注入统一前缀) */
|
|
14595
|
-
const ASSISTANT_PREFIX = "TRAJECTORY-ASSISTANT";
|
|
14596
|
-
/** 完整 token:`[TRAJECTORY-ASSISTANT · <LABEL>]` */
|
|
14151
|
+
const ASSISTANT_PREFIX = "COGNITIVE-TRAJECTORY-ASSISTANT";
|
|
14152
|
+
/** 完整 token:`[COGNITIVE-TRAJECTORY-ASSISTANT · <LABEL>]` */
|
|
14597
14153
|
function eventToken(event) {
|
|
14598
14154
|
return `[${ASSISTANT_PREFIX} · ${EVENT_LABEL[event]}]`;
|
|
14599
14155
|
}
|
|
@@ -14678,7 +14234,7 @@ function reminderText(code) {
|
|
|
14678
14234
|
* 简短留自由度(不提"加载 eap 工具",模型自知;只说修订 skill + 新建 skill 落 SESSION)。
|
|
14679
14235
|
*
|
|
14680
14236
|
* escalated=true(v1.23.3):连续多轮超阈值仍未 rebuild → 升级强制语气
|
|
14681
|
-
* (STOP and rebuild now,持续注入直到调用
|
|
14237
|
+
* (STOP and rebuild now,持续注入直到调用 cognitive_trajectory rebuild)。
|
|
14682
14238
|
*
|
|
14683
14239
|
* 需求①(S142 用户拍板,v1.28.0):百分比比例 → K 数值——**判定**改为绝对
|
|
14684
14240
|
* `projectedTokens ≥ thresholdK*1000`(触发语义,未变)。
|
|
@@ -14691,16 +14247,16 @@ function reminderText(code) {
|
|
|
14691
14247
|
*
|
|
14692
14248
|
* v1.31.1 交接协议(S142 用户需求"要求 LLM 将当前手头事项写在 SESSION.md 尾部,并要求
|
|
14693
14249
|
* rebuild 后去读并处理"):**写侧** = 这里;**读侧** = rebuild 锚点(rebuild.ts)。
|
|
14694
|
-
* 两侧共用 trajectory-assistant 的常量(单一真相源)。
|
|
14250
|
+
* 两侧共用 cognitive-trajectory-assistant 的常量(单一真相源)。
|
|
14695
14251
|
*
|
|
14696
14252
|
* 🔴 **v1.49.0(owner 2026-09-26 裁决:"砍掉 in-flight,这正是目的")**:写侧**不再**指向
|
|
14697
14253
|
* SESSION.md 末尾的段,改指**独立便签文件** `REBUILD-TODO_FILENAME`(轨迹目录下)——
|
|
14698
14254
|
* 理由见该常量的头注(持久文件 vs 只用一次的交界材料必须分家)。
|
|
14699
14255
|
*/
|
|
14700
14256
|
function rebuildReminderText(escalated = false) {
|
|
14701
|
-
const handover = ` Before rebuilding, write your current in-flight items (the exact step you are in the middle of, what is not finished yet, and what the next action is) into "${REBUILD_TODO_FILENAME}" in this trajectory's directory — create it if it does not exist. That file is a scratch note for the handover only: no format obligation, no archival duty, safe to discard at any time. The rebuilt conversation reads it first and continues from it.`;
|
|
14702
|
-
if (escalated) return `${eventToken("limitMandatory")} Context usage has reached the configured rebuild threshold — you have been reminded repeatedly and have NOT called the
|
|
14703
|
-
return `${eventToken("limit")} Context usage has reached the configured rebuild threshold. 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.${handover} ACT NOW: at the next natural pause (end of the current task step), call the
|
|
14257
|
+
const handover = ` Before rebuilding, write your current in-flight items (the exact step you are in the middle of, what is not finished yet, and what the next action is) into "${REBUILD_TODO_FILENAME}" in this cognitive trajectory's directory — create it if it does not exist. That file is a scratch note for the handover only: no format obligation, no archival duty, safe to discard at any time. The rebuilt conversation reads it first and continues from it.`;
|
|
14258
|
+
if (escalated) return `${eventToken("limitMandatory")} Context usage has reached the configured rebuild threshold — you have been reminded repeatedly and have NOT called the cognitive_trajectory rebuild action. 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 cognitive_trajectory rebuild immediately, passing --summary "<content summary ≤20 chars>" (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild).${handover} The conversation will be cleared and rebuilt in place; SESSION.md is the persistent cognitive trajectory and stays in place — identity continues from it. Do not continue working without rebuilding; this reminder persists until you call cognitive_trajectory rebuild.`;
|
|
14259
|
+
return `${eventToken("limit")} Context usage has reached the configured rebuild threshold. This session is the rebuildable carrier of the cognitive 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.${handover} ACT NOW: at the next natural pause (end of the current task step), call the cognitive_trajectory rebuild action — passing --summary "<content summary ≤20 chars>" describing the next work phase (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild) — 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.`;
|
|
14704
14260
|
}
|
|
14705
14261
|
/** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
|
|
14706
14262
|
function readContextPressure(ctx, session) {
|
|
@@ -14733,7 +14289,7 @@ function forgetLogbookCompactionState(scope) {
|
|
|
14733
14289
|
* 解析**活跃** SESSION.md 绝对路径(缓存 60s)。
|
|
14734
14290
|
*
|
|
14735
14291
|
* 候选链(刻意比 rebuild 的完整链短——这里每个工具调用都要跑,贵候选不划算):
|
|
14736
|
-
* ① `getActiveSessionInfo(scope).mdPath`(`
|
|
14292
|
+
* ① `getActiveSessionInfo(scope).mdPath`(`cognitive_trajectory use` / skiff 绑定 / 重启恢复都会写)
|
|
14737
14293
|
* ② `readLastBound(session).mdPath`(`.bindings.json` 权威绑定——重启后内存空时兜底)
|
|
14738
14294
|
* 两者都拿不到 → null(**不猜**:宁可不提醒,也不去猜别的轨迹——对齐 v1.30.13 D4 的教训)。
|
|
14739
14295
|
*/
|
|
@@ -14800,13 +14356,13 @@ function readSessionMdMaxKB(root, configPaths, now = Date.now) {
|
|
|
14800
14356
|
* ③ 允许整合不重要事项(被取代的决策 / 已解决问题 / 中间态)
|
|
14801
14357
|
* ④ 自主裁量权充分允许(唯一硬要求 = 骨架 + 未决项/决策理由/下一步仍可重建)
|
|
14802
14358
|
*/
|
|
14803
|
-
function
|
|
14359
|
+
function cognitiveTrajectoryCompactionReminderText(input) {
|
|
14804
14360
|
const { sizeKB, limitKB, mdPath, escalated = false } = input;
|
|
14805
14361
|
const facts = `SESSION.md is ${sizeKB} KB (limit ${limitKB} KB): ${mdPath}`;
|
|
14806
14362
|
const principles = "Load eap (praxis eap) first, then rewrite SESSION.md under these principles: (1) keep the EAP layered skeleton — a stable section structure, not a chronological dump; (2) move detail out — long evidence chains, logs and reference material belong in separate files (e.g. AGENT_SESSIONS/<session>/references/*.md) linked from SESSION.md; (3) merge or drop what no longer matters — superseded decisions, resolved issues, intermediate states; (4) you have full discretion on how far to go: the only hard requirements are the skeleton and that every still-open item, decision rationale and next step stays recoverable (reconstruction over preservation).";
|
|
14807
|
-
const frontmatterGuard = "Keep the YAML frontmatter at the top of the file exactly as it is — it carries this trajectory's skill declarations.";
|
|
14363
|
+
const frontmatterGuard = "Keep the YAML frontmatter at the top of the file exactly as it is — it carries this cognitive trajectory's skill declarations.";
|
|
14808
14364
|
if (escalated) return `${eventToken("compaction")} ${facts} — you have been reminded repeatedly and have NOT compacted SESSION.md. This is now mandatory: STOP the current task step, load eap (praxis eap), rewrite SESSION.md, then resume. ${principles} ${frontmatterGuard} This reminder persists until the file is under the limit.`;
|
|
14809
|
-
return `${eventToken("compaction")} ${facts}. Pause the current work and compact this trajectory's log (SESSION.md) before continuing — that log is the persistent body of this trajectory, and its size is operational entropy (H_op) the container pays on every read. ${principles} ${frontmatterGuard} After rewriting, resume the paused work; this reminder stops once the file is under the limit.`;
|
|
14365
|
+
return `${eventToken("compaction")} ${facts}. Pause the current work and compact this cognitive trajectory's log (SESSION.md) before continuing — that log is the persistent body of this cognitive trajectory, and its size is operational entropy (H_op) the container pays on every read. ${principles} ${frontmatterGuard} After rewriting, resume the paused work; this reminder stops once the file is under the limit.`;
|
|
14810
14366
|
}
|
|
14811
14367
|
/** 连续超限轮数达此值 → 语气升级(对齐 COMPACTION_ESCALATE_AFTER / REBUILD_ESCALATE_AFTER) */
|
|
14812
14368
|
const REBUILD_TODO_ESCALATE_AFTER = 3;
|
|
@@ -14816,7 +14372,7 @@ const rebuildTodoStates = /* @__PURE__ */ new Map();
|
|
|
14816
14372
|
*
|
|
14817
14373
|
* ## 与 SESSION.md 档的**语义差别**(刻意,不是疏漏)
|
|
14818
14374
|
*
|
|
14819
|
-
* | | SESSION.md 超限(`
|
|
14375
|
+
* | | SESSION.md 超限(`cognitiveTrajectoryCompactionReminderText`) | 便签超限(本条) |
|
|
14820
14376
|
* |---|---|---|
|
|
14821
14377
|
* | 动作 | **重写**(compaction) | **清理/丢弃**(prune) |
|
|
14822
14378
|
* | 结构义务 | 保 EAP 骨架 + 4 条重写原则 + frontmatter 护栏 | **无** |
|
|
@@ -14835,7 +14391,7 @@ const rebuildTodoStates = /* @__PURE__ */ new Map();
|
|
|
14835
14391
|
function rebuildTodoReminderText(input) {
|
|
14836
14392
|
const { sizeKB, limitKB, mdPath, escalated = false } = input;
|
|
14837
14393
|
const facts = `${REBUILD_TODO_FILENAME} is ${sizeKB} KB (limit ${limitKB} KB): ${mdPath}`;
|
|
14838
|
-
const action = "This file is a scratch note for the rebuild handover — it carries no archival duty and you may discard it wholesale at any time. Prune it now, writing in the spirit of eap (praxis eap): (1) delete what is already done; (2) delete what has been superseded; (3) delete what is already recorded in SESSION.md — that file is the trajectory's persistent body, this one only carries what the next self needs once. Do not restructure it into a formal document: the only mechanical constraint on this file is its size.";
|
|
14394
|
+
const action = "This file is a scratch note for the rebuild handover — it carries no archival duty and you may discard it wholesale at any time. Prune it now, writing in the spirit of eap (praxis eap): (1) delete what is already done; (2) delete what has been superseded; (3) delete what is already recorded in SESSION.md — that file is the cognitive trajectory's persistent body, this one only carries what the next self needs once. Do not restructure it into a formal document: the only mechanical constraint on this file is its size.";
|
|
14839
14395
|
if (escalated) return `${eventToken("scratchPrune")} ${facts} — you have been reminded repeatedly and have NOT pruned it. Prune it now. ${action} This reminder persists until the file is under the limit.`;
|
|
14840
14396
|
return `${eventToken("scratchPrune")} ${facts}. ${action} This reminder stops once the file is under the limit.`;
|
|
14841
14397
|
}
|
|
@@ -14862,8 +14418,8 @@ function registerKeeper(ctx, opts = {}) {
|
|
|
14862
14418
|
const root = cccRootForCwd(cwd);
|
|
14863
14419
|
if (!root) return next();
|
|
14864
14420
|
const sessionId = exec.agent?.session?.id;
|
|
14865
|
-
const skiffKeeper =
|
|
14866
|
-
const skiffRebuild =
|
|
14421
|
+
const skiffKeeper = skiffCognitiveTrajectoryEnabled(root, sessionId, "keeper");
|
|
14422
|
+
const skiffRebuild = skiffCognitiveTrajectoryEnabled(root, sessionId, "rebuild");
|
|
14867
14423
|
const tracker = trackerFor(exec);
|
|
14868
14424
|
const shouldRemind = skiffKeeper ? tracker.step(exec.name) : false;
|
|
14869
14425
|
const downstream = await next();
|
|
@@ -14898,7 +14454,7 @@ function registerKeeper(ctx, opts = {}) {
|
|
|
14898
14454
|
}
|
|
14899
14455
|
}
|
|
14900
14456
|
}
|
|
14901
|
-
if (
|
|
14457
|
+
if (skiffCognitiveTrajectoryEnabled(root, sessionId, "session")) {
|
|
14902
14458
|
const limitKB = readSessionMdMaxKB(root, opts.configPaths);
|
|
14903
14459
|
if (limitKB > 0) {
|
|
14904
14460
|
const scope = sessionId ?? "global";
|
|
@@ -14910,7 +14466,7 @@ function registerKeeper(ctx, opts = {}) {
|
|
|
14910
14466
|
st.consecutive += 1;
|
|
14911
14467
|
blocks.push({
|
|
14912
14468
|
type: "text",
|
|
14913
|
-
text:
|
|
14469
|
+
text: cognitiveTrajectoryCompactionReminderText({
|
|
14914
14470
|
sizeKB: Math.round(size / 1024),
|
|
14915
14471
|
limitKB,
|
|
14916
14472
|
mdPath,
|
|
@@ -14921,7 +14477,7 @@ function registerKeeper(ctx, opts = {}) {
|
|
|
14921
14477
|
} else compactionStates.delete(scope);
|
|
14922
14478
|
}
|
|
14923
14479
|
}
|
|
14924
|
-
if (
|
|
14480
|
+
if (skiffCognitiveTrajectoryEnabled(root, sessionId, "session")) {
|
|
14925
14481
|
const scope = sessionId ?? "global";
|
|
14926
14482
|
const session = exec.agent?.session;
|
|
14927
14483
|
const mdPath = resolveActiveSessionMdPath(root, scope, session);
|
|
@@ -15053,7 +14609,7 @@ function registerContext(ctx, opts = {}) {
|
|
|
15053
14609
|
const key = agentKey(agent);
|
|
15054
14610
|
if (isSkiffSessionId(key)) return;
|
|
15055
14611
|
registerEntrySkillSection(agent, root);
|
|
15056
|
-
|
|
14612
|
+
registerCognitiveTrajectorySkillSection(agent, root);
|
|
15057
14613
|
const scope = agentScope(agent);
|
|
15058
14614
|
if (shouldRestoreActive(agent) && getActiveSessionInfo(scope) === null) try {
|
|
15059
14615
|
if (loadSerenityConfig(root, configPaths).hooks?.autoRestoreSession ?? true) {
|
|
@@ -15152,7 +14708,7 @@ function registerContext(ctx, opts = {}) {
|
|
|
15152
14708
|
syncHostScheduleToolHiding(agent, root);
|
|
15153
14709
|
} catch {}
|
|
15154
14710
|
registerEntrySkillSection(agent, root);
|
|
15155
|
-
|
|
14711
|
+
registerCognitiveTrajectorySkillSection(agent, root);
|
|
15156
14712
|
}
|
|
15157
14713
|
if (!root || injected.has(key) || downstream.kind !== "enter") return downstream;
|
|
15158
14714
|
injected.add(key);
|
|
@@ -15630,11 +15186,11 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15630
15186
|
sendJson$3(res, 400, { error: chk.error });
|
|
15631
15187
|
return;
|
|
15632
15188
|
}
|
|
15633
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
15189
|
+
const { readWeixinSettings } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15634
15190
|
const { weixinBridgeStatus } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
15635
15191
|
const settings = readWeixinSettings(chk.root);
|
|
15636
15192
|
const bridge = weixinBridgeStatus().find((b) => b.ccc === chk.root);
|
|
15637
|
-
const { readWeixinCredential } = await import("./weixin-route-
|
|
15193
|
+
const { readWeixinCredential } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15638
15194
|
const accounts = (settings.accounts ?? []).map((a) => ({
|
|
15639
15195
|
accountId: a.accountId,
|
|
15640
15196
|
name: a.name ?? void 0,
|
|
@@ -15666,7 +15222,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15666
15222
|
const { fetchQRCode } = await import("./weixin-api-BrRcPiQs.js").then((n) => n.u);
|
|
15667
15223
|
const { randomUUID } = await import("node:crypto");
|
|
15668
15224
|
for (const [key, v] of weixinLogins) if (Date.now() - v.startedAt > WEIXIN_LOGIN_TTL_MS) weixinLogins.delete(key);
|
|
15669
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
15225
|
+
const { readWeixinSettings } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15670
15226
|
const qr = await fetchQRCode({ botType: readWeixinSettings(root).botType ?? void 0 });
|
|
15671
15227
|
const loginKey = randomUUID();
|
|
15672
15228
|
weixinLogins.set(loginKey, {
|
|
@@ -15687,7 +15243,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15687
15243
|
sendJson$3(res, 400, { error: "missing accountId" });
|
|
15688
15244
|
return;
|
|
15689
15245
|
}
|
|
15690
|
-
const { removeWeixinAccount } = await import("./weixin-route-
|
|
15246
|
+
const { removeWeixinAccount } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15691
15247
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
15692
15248
|
removeWeixinAccount(root, body.accountId);
|
|
15693
15249
|
syncCccBridge(ctx, root);
|
|
@@ -15700,8 +15256,8 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15700
15256
|
sendJson$3(res, 400, { error: "invalid routes (expected [{user, role}, ...])" });
|
|
15701
15257
|
return;
|
|
15702
15258
|
}
|
|
15703
|
-
const { saveWeixinRoutes } = await import("./weixin-route-
|
|
15704
|
-
const { readSkiffRoles } = await import("./skiff-role-
|
|
15259
|
+
const { saveWeixinRoutes } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15260
|
+
const { readSkiffRoles } = await import("./skiff-role-Boc4SKct.js").then((n) => n.d);
|
|
15705
15261
|
const roles = readSkiffRoles(root);
|
|
15706
15262
|
for (const r of routes) if (!roles.has(r.role)) {
|
|
15707
15263
|
sendJson$3(res, 400, { error: `unknown role: ${r.role} (not in ${root} skiff.roles)` });
|
|
@@ -15713,7 +15269,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15713
15269
|
}
|
|
15714
15270
|
if (body.action === "set-enabled") {
|
|
15715
15271
|
const enabled = body.enabled === true;
|
|
15716
|
-
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-
|
|
15272
|
+
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15717
15273
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
15718
15274
|
const settings = readWeixinSettings(root);
|
|
15719
15275
|
if (enabled && (settings.accounts ?? []).length === 0) {
|
|
@@ -15757,7 +15313,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15757
15313
|
return;
|
|
15758
15314
|
}
|
|
15759
15315
|
const { pollQRStatus } = await import("./weixin-api-BrRcPiQs.js").then((n) => n.u);
|
|
15760
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
15316
|
+
const { readWeixinSettings } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15761
15317
|
const settings = readWeixinSettings(login.root);
|
|
15762
15318
|
const status = await pollQRStatus({
|
|
15763
15319
|
baseUrl: void 0,
|
|
@@ -15772,7 +15328,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
15772
15328
|
});
|
|
15773
15329
|
return;
|
|
15774
15330
|
}
|
|
15775
|
-
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-
|
|
15331
|
+
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-BoSC-uPp.js").then((n) => n.u);
|
|
15776
15332
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
15777
15333
|
const accountId = nextWeixinAccountId(settings);
|
|
15778
15334
|
upsertWeixinAccount(login.root, {
|
|
@@ -15893,7 +15449,7 @@ function registerAcpAccountsApi(ctx) {
|
|
|
15893
15449
|
return;
|
|
15894
15450
|
}
|
|
15895
15451
|
const { acpChannelStatus, listAcpRoots } = await Promise.resolve().then(() => acp_auth_exports);
|
|
15896
|
-
const { listCccs } = await import("./ccc-roots-
|
|
15452
|
+
const { listCccs } = await import("./ccc-roots-RMzGU-CJ.js").then((n) => n.a);
|
|
15897
15453
|
const st = acpChannelStatus(chk.root);
|
|
15898
15454
|
const all = (await listCccs(ctx)).map((e) => e.root);
|
|
15899
15455
|
sendJson$3(res, 200, {
|
|
@@ -15923,7 +15479,7 @@ function registerAcpAccountsApi(ctx) {
|
|
|
15923
15479
|
sendJson$3(res, 400, { error: "invalid account (accountId 与 role 均必填)" });
|
|
15924
15480
|
return;
|
|
15925
15481
|
}
|
|
15926
|
-
const { readSkiffRoles } = await import("./skiff-role-
|
|
15482
|
+
const { readSkiffRoles } = await import("./skiff-role-Boc4SKct.js").then((n) => n.d);
|
|
15927
15483
|
const roles = readSkiffRoles(root);
|
|
15928
15484
|
if (!roles.has(account.role)) {
|
|
15929
15485
|
sendJson$3(res, 400, { error: `role "${account.role}" 不在该 CCC 的 skiff.roles 里(现有:${[...roles.keys()].join(", ") || "无"})` });
|
|
@@ -16383,7 +15939,7 @@ const HOST_SERVICES = [
|
|
|
16383
15939
|
name: "resolveAgent",
|
|
16384
15940
|
kind: "function"
|
|
16385
15941
|
}],
|
|
16386
|
-
impact: "trajectory 冷会话唤醒不可用(只能唤醒已加载的 live 会话)——唤醒注册表投递降级",
|
|
15942
|
+
impact: "cognitive trajectory 冷会话唤醒不可用(只能唤醒已加载的 live 会话)——唤醒注册表投递降级",
|
|
16387
15943
|
required: false
|
|
16388
15944
|
},
|
|
16389
15945
|
{
|
|
@@ -17776,13 +17332,13 @@ function buildRebuildAnchor(root, sessionName, activeMdPath, anchorMessages = DE
|
|
|
17776
17332
|
const sessionLine = sessionDir !== "AGENT_SESSIONS" ? `- Serenity session: ${sessionName !== "" ? `${sessionName} (${sessionDir})` : sessionDir}` : null;
|
|
17777
17333
|
const focusLine = sanitizeFocusLine(focus);
|
|
17778
17334
|
return [
|
|
17779
|
-
`${eventToken("rebuild")} The conversation has been cleared and rebuilt (Ship of Theseus: the carrier is replaced, the trajectory continues).`,
|
|
17335
|
+
`${eventToken("rebuild")} The conversation has been cleared and rebuilt (Ship of Theseus: the carrier is replaced, the cognitive trajectory continues).`,
|
|
17780
17336
|
"",
|
|
17781
17337
|
...anchorMessages.flatMap((text) => [stripAckSuffix(text), ""]),
|
|
17782
17338
|
sessionName !== "" ? `Continue the work of ${sessionName}.` : "Continue the current work.",
|
|
17783
17339
|
...sessionLine ? [sessionLine] : [],
|
|
17784
|
-
`- Persistent trajectory — SESSION.md path: ${rel}`,
|
|
17785
|
-
` (the trajectory's persistent body — stays in place through rebuilds)`,
|
|
17340
|
+
`- Persistent cognitive trajectory — SESSION.md path: ${rel}`,
|
|
17341
|
+
` (the cognitive trajectory's persistent body — stays in place through rebuilds)`,
|
|
17786
17342
|
`- Read that SESSION.md first (goal/decisions/progress/unresolved), then continue from the last checkpoint.`,
|
|
17787
17343
|
`- Then read the handover scratch note: ${todoRel} (path: ${todoAbs}) — the previous self paused mid-task and left its in-flight items there. That file carries no format obligation and may be discarded at any time; it is NOT the authoritative state. If it is missing, infer the in-flight items from SESSION.md's latest progress entries.`,
|
|
17788
17344
|
...focusLine ? [`- Task focus: ${focusLine}`] : []
|
|
@@ -17834,7 +17390,7 @@ function sessionNameFromMdPath(mdPath) {
|
|
|
17834
17390
|
* 它会把 rebuild 静默接到 AGENT_SESSIONS 里最新的另一个会话(临时 skiff 会话 persistent=false、
|
|
17835
17391
|
* 重启后首条消息前、新会话首条消息都命中该形态),而"接错轨迹"比重建失败危险得多
|
|
17836
17392
|
* (错误轨迹被继续写入 + 正确轨迹静默丢失)。现在全部候选失败 → 返回 null → 调用方
|
|
17837
|
-
* 报错引导用户显式 `
|
|
17393
|
+
* 报错引导用户显式 `cognitive_trajectory use`(响亮失败优于静默接错)。
|
|
17838
17394
|
* 备选:④ 仅在"同 scope 确无绑定"时兜底——同样会接错轨迹,故不采纳。
|
|
17839
17395
|
*
|
|
17840
17396
|
* 每候选 resolve 后 existsSync 校验(相对路径按 root 解析);全部失败返回 null → 调用方报错引导。
|
|
@@ -17894,7 +17450,7 @@ async function queueRebuild(ctx, opts) {
|
|
|
17894
17450
|
const session = ctx.sessions?.get?.(dshSessionId);
|
|
17895
17451
|
if (!session) throw new Error(`Unable to locate dsh session ${dshSessionId} (session may be closed)`);
|
|
17896
17452
|
const mdPath = resolveSessionMdPath(root, dshSessionId, session);
|
|
17897
|
-
if (!mdPath) throw new Error("Unable to determine the active SESSION.md for this session — checked (in order): in-memory activation (
|
|
17453
|
+
if (!mdPath) throw new Error("Unable to determine the active SESSION.md for this session — checked (in order): in-memory activation (cognitive_trajectory use in this process), [SESSION CONTEXT] events in this conversation, AGENT_SESSIONS/.bindings.json (authoritative binding), rebuild anchor in the surface. None matched an existing SESSION.md. Run \"cognitive_trajectory use <S###> --summary <内容概括 ≤20 字>\" to bind this conversation to a cognitive trajectory, then retry cognitive_trajectory rebuild. (No global fallback is applied on purpose — guessing could resume a different cognitive trajectory.)");
|
|
17898
17454
|
const sessionName = getActiveSessionInfo(dshSessionId)?.sessionId ?? sessionNameFromMdPath(mdPath);
|
|
17899
17455
|
const focus = note && note.trim() !== "" ? note : void 0;
|
|
17900
17456
|
const anchor = buildRebuildAnchor(root, sessionName, mdPath, DEFAULT_ANCHOR_MESSAGES, focus);
|
|
@@ -18064,7 +17620,7 @@ function registerRebuildTurnHook(ctx) {
|
|
|
18064
17620
|
agent.steer(createUserMessage({
|
|
18065
17621
|
content: [{
|
|
18066
17622
|
type: "text",
|
|
18067
|
-
text: `${eventToken("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.`
|
|
17623
|
+
text: `${eventToken("rebuild")} The conversation has been cleared and rebuilt. Follow the anchor instructions above now: read the persistent cognitive trajectory (SESSION.md) and continue the work automatically from the last checkpoint.`
|
|
18068
17624
|
}],
|
|
18069
17625
|
source: PLUGIN_SOURCE
|
|
18070
17626
|
}));
|
|
@@ -18072,7 +17628,7 @@ function registerRebuildTurnHook(ctx) {
|
|
|
18072
17628
|
sessionId: id,
|
|
18073
17629
|
event: "rebuilt"
|
|
18074
17630
|
});
|
|
18075
|
-
console.log(`[serenity-hooks]
|
|
17631
|
+
console.log(`[serenity-hooks] cognitive_trajectory rebuild executed with auto-continue (turn ${payload.turn ?? "?"} ended): ${id}`);
|
|
18076
17632
|
} else writeRebuildDiag(resolveSerenityRootFor(agent), {
|
|
18077
17633
|
sessionId: id,
|
|
18078
17634
|
event: "empty-surface"
|
|
@@ -18084,7 +17640,7 @@ function registerRebuildTurnHook(ctx) {
|
|
|
18084
17640
|
event: "failed",
|
|
18085
17641
|
detail: msg
|
|
18086
17642
|
});
|
|
18087
|
-
console.warn(`[serenity-hooks]
|
|
17643
|
+
console.warn(`[serenity-hooks] cognitive_trajectory rebuild failed: ${msg}`);
|
|
18088
17644
|
}
|
|
18089
17645
|
});
|
|
18090
17646
|
}
|
|
@@ -19828,7 +19384,7 @@ function skiffDebugPage(cccs, defaultRoot, webPort) {
|
|
|
19828
19384
|
details.think summary { cursor: pointer; padding: 6px 10px; font-size: 12px; color: #d29922; font-weight: 600; user-select: none; list-style-position: inside; }
|
|
19829
19385
|
details.think summary:hover { opacity: .8; }
|
|
19830
19386
|
details.think .think-body { padding: 2px 12px 10px; font-size: 13px; opacity: .78; white-space: normal; }
|
|
19831
|
-
#
|
|
19387
|
+
#cognitiveTrajectory { margin-top: 12px; font-size: 13px; }
|
|
19832
19388
|
.t-entry { border-left: 2px solid #d0d7de; padding: 6px 10px; margin: 6px 0; border-radius: 0 6px 6px 0; background: rgba(127,127,127,.06); white-space: pre-wrap; word-break: break-word; }
|
|
19833
19389
|
.t-user { border-left-color: #0ba875; }
|
|
19834
19390
|
.t-tool { border-left-color: #d29922; opacity: .85; font-family: ui-monospace, monospace; font-size: 12px; }
|
|
@@ -19855,7 +19411,7 @@ function skiffDebugPage(cccs, defaultRoot, webPort) {
|
|
|
19855
19411
|
<textarea id="q" placeholder="向该角色提问…(追问直接继续输入)"></textarea>
|
|
19856
19412
|
<button id="ask">提问</button>
|
|
19857
19413
|
<div id="answer" class="muted">等待提问…</div>
|
|
19858
|
-
<div id="
|
|
19414
|
+
<div id="cognitiveTrajectory"></div>
|
|
19859
19415
|
<p class="muted"><a href="${`http://127.0.0.1:${webPort}`}" target="_blank" rel="noopener">在 dsh WebUI 查看完整会话</a>(会话列表搜索 sessionId;WebUI 有完整交互)</p>
|
|
19860
19416
|
</main>
|
|
19861
19417
|
<script id="skiff-data" type="application/json" data-default="${escapeHtml(defaultRoot)}">${data}<\/script>
|
|
@@ -19869,7 +19425,7 @@ const sessionBadge = document.getElementById('sessionBadge')
|
|
|
19869
19425
|
const newChatBtn = document.getElementById('newChat')
|
|
19870
19426
|
const btn = document.getElementById('ask')
|
|
19871
19427
|
const answer = document.getElementById('answer')
|
|
19872
|
-
const traj = document.getElementById('
|
|
19428
|
+
const traj = document.getElementById('cognitiveTrajectory')
|
|
19873
19429
|
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))
|
|
19874
19430
|
let sessionId = ''
|
|
19875
19431
|
function shortId(id) { return id.length > 24 ? id.slice(0, 18) + '…' + id.slice(-6) : id }
|
|
@@ -19930,7 +19486,7 @@ btn.addEventListener('click', async () => {
|
|
|
19930
19486
|
answer.innerHTML = data.answer_html || esc(data.answer || '') || '(空回答)'
|
|
19931
19487
|
sessionId = data.sessionId || sessionId
|
|
19932
19488
|
renderSessionBadge()
|
|
19933
|
-
|
|
19489
|
+
renderCognitiveTrajectory(data.cognitiveTrajectory || [], data.sessionId || '')
|
|
19934
19490
|
} catch (err) {
|
|
19935
19491
|
answer.className = 'err'
|
|
19936
19492
|
answer.textContent = String(err.message || err)
|
|
@@ -19938,7 +19494,7 @@ btn.addEventListener('click', async () => {
|
|
|
19938
19494
|
btn.disabled = false
|
|
19939
19495
|
}
|
|
19940
19496
|
})
|
|
19941
|
-
function
|
|
19497
|
+
function renderCognitiveTrajectory(entries, sessionId) {
|
|
19942
19498
|
if (entries.length === 0) { traj.innerHTML = '<div class="muted">(本轮无轨迹)</div>'; return }
|
|
19943
19499
|
traj.innerHTML = '<div class="sub">本轮轨迹 · ' + esc(sessionId) + '</div>' + entries.map((e) => {
|
|
19944
19500
|
const cls = e.role === 'user' ? 't-user' : (e.role === 'tool' ? 't-tool' : '')
|
|
@@ -20180,7 +19736,7 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
20180
19736
|
answer_html: renderSkiffMarkdown(result.answer),
|
|
20181
19737
|
sessionId: result.sessionId,
|
|
20182
19738
|
continued,
|
|
20183
|
-
|
|
19739
|
+
cognitiveTrajectory: result.cognitiveTrajectory
|
|
20184
19740
|
});
|
|
20185
19741
|
return;
|
|
20186
19742
|
}
|
|
@@ -20223,9 +19779,9 @@ const MECHANISM_WORDS = [
|
|
|
20223
19779
|
"agent/turn-stopping",
|
|
20224
19780
|
"tools/pre-execute",
|
|
20225
19781
|
"tools/post-execute",
|
|
20226
|
-
"Trajectory Steward",
|
|
20227
|
-
"TRAJECTORY-STEWARD",
|
|
20228
|
-
"TRAJECTORY-ASSISTANT",
|
|
19782
|
+
"Cognitive Trajectory Steward",
|
|
19783
|
+
"COGNITIVE-TRAJECTORY-STEWARD",
|
|
19784
|
+
"COGNITIVE-TRAJECTORY-ASSISTANT",
|
|
20229
19785
|
"first-anchor",
|
|
20230
19786
|
"拦截缝",
|
|
20231
19787
|
"装配层",
|
|
@@ -20317,7 +19873,7 @@ const CATEGORY_GUIDE = {
|
|
|
20317
19873
|
* v1.26.10 只列裸词(如 "3080")模型仍不知道是什么/怎么规避;现在按类说明:
|
|
20318
19874
|
* "3080" → 内部服务端口,不要提及内部端口/地址/端点)。
|
|
20319
19875
|
* R↓ 安全性:命中词本就已在被拦截回复中进入会话(已暴露面),打回重复一次不扩大暴露;
|
|
20320
|
-
* steer 是 plugin source 消息不回显给用户(3100 对外响应已不含 trajectory → 打回文本不达外部用户)。
|
|
19876
|
+
* steer 是 plugin source 消息不回显给用户(3100 对外响应已不含 cognitive trajectory → 打回文本不达外部用户)。
|
|
20321
19877
|
*/
|
|
20322
19878
|
function buildRebuke(hits) {
|
|
20323
19879
|
const count = hits.length;
|
|
@@ -20338,8 +19894,10 @@ function isExternalFaceSession(sessionId) {
|
|
|
20338
19894
|
return isSkiffSessionId(sessionId) || sessionId.startsWith("acp-") || sessionId.startsWith("rebuild-");
|
|
20339
19895
|
}
|
|
20340
19896
|
/** 读会话最后一个 assistant/message 文本(skiff-core 同款)
|
|
20341
|
-
* **
|
|
20342
|
-
*
|
|
19897
|
+
* **v1.63.3 起唯一的导出理由消失**:原导出是给 `unattended-seam.ts` 复用的(D77),
|
|
19898
|
+
* 该文件已随无人值守功能整条退役 ⇒ 现只有本模块自己用(`isLeak` 的调用点)。
|
|
19899
|
+
* ⚠️ 保持 `export` 无害(它有测试覆盖,且将来"同一读法只留一份"的纪律仍成立),
|
|
19900
|
+
* 但**不要**再写成"为了 X 复用才导出"——那个 X 不存在了。
|
|
20343
19901
|
* **v1.27.7:剥离 think 块再检测**(用户:"不检查think内容(因为不输出)")——
|
|
20344
19902
|
* ` think…` 是模型思考过程,不进入用户可见输出;敏感词检测只针对**最终呈现文本**,
|
|
20345
19903
|
* 否则 think 内提及内部机制词(思考过程必然推演机制)会误打回。复用 stripThink
|
|
@@ -20398,235 +19956,6 @@ function registerOutputGuardHook(ctx) {
|
|
|
20398
19956
|
});
|
|
20399
19957
|
}
|
|
20400
19958
|
//#endregion
|
|
20401
|
-
//#region src/unattended-seam.ts
|
|
20402
|
-
/**
|
|
20403
|
-
* unattended-seam.ts — 无人值守代理回复的**拦截缝**(S142 **D77**,owner 2026-09-23 令「我需要的是一个机制」)
|
|
20404
|
-
*
|
|
20405
|
-
* ## 停在哪、怎么接(设计 §4 的裁决:**A 的通道 + B 的措辞**)
|
|
20406
|
-
*
|
|
20407
|
-
* 复用的是**已验证过的**通道——`agent/turn-stopping`(serial)里 `agent.steer()`:
|
|
20408
|
-
* 宿主语义 = "**listener 反对就打回**,机器重读 inbox:有新 steer ⇒ 再跑一步,没有 ⇒ 关轮"
|
|
20409
|
-
* (`host/contract.ts` 的 `HOST_EVENTS` 原文)。因此:
|
|
20410
|
-
* · **轮不结束**(语义正是"有人打回")⇒ **零新机械**;
|
|
20411
|
-
* · 文本**写成代理用户的话**(B 的措辞)+ **首句自报身份**(owner 已裁)。
|
|
20412
|
-
*
|
|
20413
|
-
* 模板 = `output-guard-seam.ts`(同一缝位、同一 `createUserMessage + PLUGIN_SOURCE` 形态)。
|
|
20414
|
-
*
|
|
20415
|
-
* ## 🔴「这轮是不是真实用户发起的」怎么判(本机制**唯一**的新机械;2026-09-23 取证)
|
|
20416
|
-
*
|
|
20417
|
-
* 交接块把它列为"动手前必须先查清的一件事",取证结论(读宿主类型面,非猜测):
|
|
20418
|
-
*
|
|
20419
|
-
* | 事实 | 出处 |
|
|
20420
|
-
* |---|---|
|
|
20421
|
-
* | 每条进入 inbox 的消息都发 `agent/inbox/inserted { agent, message }`;**在轮内离开 inbox 时**发 `agent/inbox/claimed { agent, message, turn }` | `@deepseek-ai/dsh-agent` README(Inbox 段)+ `runtime-types.d.ts` |
|
|
20422
|
-
* | `claimed.turn` = **拥有该消息的那一轮**;`turn-stopping.turn` = **即将关闭的那一轮** ⇒ 两者同编号可直接比对 | `runtime-types.d.ts` 同一文件两处 `turn` 声明 |
|
|
20423
|
-
* | 消息来源可辨:`MessageSourceMap` 里**真实用户** = `kind: 'user'`(`user` / `user-rpc` 两种形态**同 kind**);**ACC 自己注入**的一律 `kind: 'plugin:dsh-serenity-hooks'`(v1.47.0 改;旧值 = `'plugin'`) | `@deepseek-ai/dsh-llm` `src/message.ts` 的 `MessageSourceMap`(合并和类型)+ 本仓 `message-source.ts`(**唯一**声明处与常量处) |
|
|
20424
|
-
*
|
|
20425
|
-
* ⇒ **判据 = 「这一轮 claim 到的消息里,有没有 `source.kind === 'user'`」**。
|
|
20426
|
-
* 没有现成信号时本可退化为"记账法"(我们知道自己投过什么),**但不必**:宿主已给出一手信号。
|
|
20427
|
-
*
|
|
20428
|
-
* ## 🔴 为什么"用户发起的轮"要跳过(这是设计 §3-③ 的本意,别当成漏洞)
|
|
20429
|
-
*
|
|
20430
|
-
* 用户**刚说完话**⇒ 人就在跟前,代理插话既是冒充也是浪费。**"用户说完走开"那种情形**
|
|
20431
|
-
* 由**绳(长周期保险丝)/其它非用户发起的唤醒**覆盖:那条轮**不是**用户发起的 ⇒ 代理接手,
|
|
20432
|
-
* 一路推进到**合法收束**。⇒ 两个机制合起来才等于"用户走开后会话还能自己跑完"。
|
|
20433
|
-
*
|
|
20434
|
-
* ## 🔴 铁律(与 `unattended-ops.ts` 同族)
|
|
20435
|
-
*
|
|
20436
|
-
* **本模块的任何失败都不得影响 turn 收尾**:所有 handler 包 try/catch,异常只记一行 console。
|
|
20437
|
-
*/
|
|
20438
|
-
/** 取载体 id(payload 的 `agent` 面;取不到 ⇒ 空串) */
|
|
20439
|
-
function sessionIdOf$2(agent) {
|
|
20440
|
-
const id = agent?.session?.id;
|
|
20441
|
-
return typeof id === "string" ? id : "";
|
|
20442
|
-
}
|
|
20443
|
-
/** 取会话对象(payload 的 `agent.session`) */
|
|
20444
|
-
function sessionOf(agent) {
|
|
20445
|
-
return agent?.session;
|
|
20446
|
-
}
|
|
20447
|
-
/** 取会话 cwd(`header.cwd`;取不到回落进程 cwd——与 `output-guard-seam.ts` 同款读法) */
|
|
20448
|
-
function cwdOf(agent) {
|
|
20449
|
-
return sessionOf(agent)?.header?.cwd ?? process.cwd();
|
|
20450
|
-
}
|
|
20451
|
-
/** 是否 **CCC 内主会话**(`delegationDepth` 缺省 0;子代理 / 无头运行不算) */
|
|
20452
|
-
function isMainSession(agent) {
|
|
20453
|
-
return (sessionOf(agent)?.header?.delegationDepth ?? 0) === 0;
|
|
20454
|
-
}
|
|
20455
|
-
/**
|
|
20456
|
-
* 该会话所属轨迹**是否带 CRO 程序**(窄口径的唯一排除项)。
|
|
20457
|
-
* 判据 = 轨迹目录下存在 `continuous-re-occurrence.ts`(与 `cro.ts` 同源:**文件在 = 启用**)。
|
|
20458
|
-
* 无绑定(未挂在任何轨迹上)⇒ false(没有程序在驱动它)。
|
|
20459
|
-
*/
|
|
20460
|
-
function hasCroProgram(session, root) {
|
|
20461
|
-
const dirName = readLastBound(session)?.dirName;
|
|
20462
|
-
if (!dirName) return false;
|
|
20463
|
-
return existsSync(join(sessionsRoot(root), dirName, CRO_FILENAME));
|
|
20464
|
-
}
|
|
20465
|
-
/** 落一条流水(**永不抛**;无绑定 ⇒ 无落点,只回 false 由调用方记 console) */
|
|
20466
|
-
function logEvent(session, root, entry, nowMs) {
|
|
20467
|
-
const dirName = readLastBound(session)?.dirName;
|
|
20468
|
-
if (!dirName) return false;
|
|
20469
|
-
const res = appendUnattendedLog(root, dirName, entry, nowMs);
|
|
20470
|
-
if (!res.ok) console.log(`[serenity-hooks] unattended: 流水未落盘 — ${res.error ?? "未知原因"}`);
|
|
20471
|
-
return res.ok;
|
|
20472
|
-
}
|
|
20473
|
-
/**
|
|
20474
|
-
* 一轮收尾(`agent/turn-stopping`)的完整处置。
|
|
20475
|
-
*
|
|
20476
|
-
* 顺序:**取事实 → 纯函数判定 → 执行**(判定全在 `unattended-ops.ts`,本函数只做 I/O 与 steer)。
|
|
20477
|
-
* @returns 便于用例观察的判定结果文本(生产不使用返回值)
|
|
20478
|
-
*/
|
|
20479
|
-
function handleTurnStopping(agent, turn) {
|
|
20480
|
-
if (readSimpleSettings().unattendedEnabled !== true) return "skip:mode-off";
|
|
20481
|
-
const sessionId = sessionIdOf$2(agent);
|
|
20482
|
-
if (sessionId === "") return "skip:no-session-id";
|
|
20483
|
-
const root = cccRootForCwd(cwdOf(agent));
|
|
20484
|
-
if (root === null) return "skip:not-in-ccc";
|
|
20485
|
-
const session = sessionOf(agent);
|
|
20486
|
-
const nowMs = Date.now();
|
|
20487
|
-
const run = getProxyRun(sessionId);
|
|
20488
|
-
const text = lastAssistantText(agent);
|
|
20489
|
-
const nonces = parseDoneNonces(text);
|
|
20490
|
-
const sessionIdExcluded = isProxyExcludedSessionId(sessionId);
|
|
20491
|
-
const userInitiated = turn === null ? true : isUserTurn(sessionId, turn);
|
|
20492
|
-
const cro = !sessionIdExcluded && !userInitiated && isMainSession(agent) ? hasCroProgram(session, root) : false;
|
|
20493
|
-
const verdict = decideUnattendedAction({
|
|
20494
|
-
enabled: true,
|
|
20495
|
-
root,
|
|
20496
|
-
isMainSession: isMainSession(agent),
|
|
20497
|
-
sessionIdExcluded,
|
|
20498
|
-
userInitiated,
|
|
20499
|
-
hasCroProgram: cro,
|
|
20500
|
-
run,
|
|
20501
|
-
nowMs,
|
|
20502
|
-
doneNonce: nonces.length > 0 ? nonces[nonces.length - 1] ?? null : null,
|
|
20503
|
-
claimsNotify: claimsUserNotification(text),
|
|
20504
|
-
outboundSeen: run !== null && hasOutboundSendSince(root, run.startedAt, nowMs)
|
|
20505
|
-
});
|
|
20506
|
-
if (verdict.action === "skip") return `skip:${verdict.reason}`;
|
|
20507
|
-
if (verdict.action === "accept" || verdict.action === "cap") {
|
|
20508
|
-
clearProxyRun(sessionId);
|
|
20509
|
-
logEvent(session, root, {
|
|
20510
|
-
event: verdict.action,
|
|
20511
|
-
round: verdict.round,
|
|
20512
|
-
nonce: run?.nonce ?? null,
|
|
20513
|
-
reason: verdict.reason,
|
|
20514
|
-
sessionId
|
|
20515
|
-
}, nowMs);
|
|
20516
|
-
console.log(`[serenity-hooks] unattended: ${verdict.action} (${verdict.reason}, round=${verdict.round}, session=${sessionId})`);
|
|
20517
|
-
return `${verdict.action}:${verdict.reason}`;
|
|
20518
|
-
}
|
|
20519
|
-
const nonce = newNonce();
|
|
20520
|
-
const startedAt = run?.startedAt ?? nowMs;
|
|
20521
|
-
setProxyRun(sessionId, {
|
|
20522
|
-
round: verdict.round,
|
|
20523
|
-
nonce,
|
|
20524
|
-
startedAt
|
|
20525
|
-
});
|
|
20526
|
-
const message = verdict.reason === "false-notify" ? buildFalseNotifyRebuke({
|
|
20527
|
-
round: verdict.round,
|
|
20528
|
-
maxRounds: 15,
|
|
20529
|
-
nonce
|
|
20530
|
-
}) : buildProxyMessage({
|
|
20531
|
-
round: verdict.round,
|
|
20532
|
-
maxRounds: 15,
|
|
20533
|
-
nonce
|
|
20534
|
-
});
|
|
20535
|
-
try {
|
|
20536
|
-
agent.steer(createUserMessage({
|
|
20537
|
-
content: [{
|
|
20538
|
-
type: "text",
|
|
20539
|
-
text: message
|
|
20540
|
-
}],
|
|
20541
|
-
source: PLUGIN_SOURCE
|
|
20542
|
-
}));
|
|
20543
|
-
} catch (error) {
|
|
20544
|
-
clearProxyRun(sessionId);
|
|
20545
|
-
logEvent(session, root, {
|
|
20546
|
-
event: "cap",
|
|
20547
|
-
round: verdict.round,
|
|
20548
|
-
nonce,
|
|
20549
|
-
reason: "steer-failed",
|
|
20550
|
-
sessionId
|
|
20551
|
-
}, nowMs);
|
|
20552
|
-
console.warn(`[serenity-hooks] unattended: steer 失败(本轮放弃) — ${String(error?.message ?? error)}`);
|
|
20553
|
-
return "cap:steer-failed";
|
|
20554
|
-
}
|
|
20555
|
-
logEvent(session, root, {
|
|
20556
|
-
event: "inject",
|
|
20557
|
-
round: verdict.round,
|
|
20558
|
-
nonce,
|
|
20559
|
-
reason: verdict.reason,
|
|
20560
|
-
sessionId
|
|
20561
|
-
}, nowMs);
|
|
20562
|
-
console.log(`[serenity-hooks] unattended: inject round=${verdict.round}/15 (${verdict.reason}, session=${sessionId})`);
|
|
20563
|
-
return `inject:${verdict.reason}`;
|
|
20564
|
-
}
|
|
20565
|
-
/**
|
|
20566
|
-
* 装配无人值守代理缝(`index.ts` apply 调用)。
|
|
20567
|
-
*
|
|
20568
|
-
* 订阅三个事件(**都已在 `host/contract.ts` 白名单里**,新增的 `agent/inbox/claimed` 同批登记):
|
|
20569
|
-
* · `agent/inbox/claimed` —— 记「真实用户发起的轮次」;用户回来时**让位**(清代理段 + 记流水);
|
|
20570
|
-
* · `agent/turn-stopping` —— 判定 + 注入(本机制主体);
|
|
20571
|
-
* · `agent/disposed` / `session/disposed` —— 载体销毁清表(**本容器栽过 5 次"只增不清"**)。
|
|
20572
|
-
*/
|
|
20573
|
-
function registerUnattendedSeam(ctx) {
|
|
20574
|
-
const on = (event, handler) => {
|
|
20575
|
-
try {
|
|
20576
|
-
ctx.on(event, handler);
|
|
20577
|
-
} catch (err) {
|
|
20578
|
-
console.log(`[serenity-hooks] ✗ 无人值守代理未装配(${event} 不可用): ${String(err?.message ?? err)}`);
|
|
20579
|
-
}
|
|
20580
|
-
};
|
|
20581
|
-
on("agent/inbox/claimed", (payload) => {
|
|
20582
|
-
try {
|
|
20583
|
-
const p = payload;
|
|
20584
|
-
const sessionId = sessionIdOf$2(p?.agent);
|
|
20585
|
-
if (sessionId === "") return;
|
|
20586
|
-
if (p.message?.source?.kind !== "user") return;
|
|
20587
|
-
noteUserTurn(sessionId, (typeof p.turn === "number" ? p.turn : null) ?? -1);
|
|
20588
|
-
const run = getProxyRun(sessionId);
|
|
20589
|
-
if (!run) return;
|
|
20590
|
-
clearProxyRun(sessionId);
|
|
20591
|
-
const root = cccRootForCwd(cwdOf(p.agent));
|
|
20592
|
-
if (root) logEvent(sessionOf(p.agent), root, {
|
|
20593
|
-
event: "yield",
|
|
20594
|
-
round: run.round,
|
|
20595
|
-
nonce: run.nonce,
|
|
20596
|
-
reason: "user-returned",
|
|
20597
|
-
sessionId
|
|
20598
|
-
}, Date.now());
|
|
20599
|
-
console.log(`[serenity-hooks] unattended: yield — 用户回来了 (session=${sessionId}, round=${run.round})`);
|
|
20600
|
-
} catch (err) {
|
|
20601
|
-
console.log(`[serenity-hooks] unattended: claimed 观察失败 — ${String(err?.message ?? err)}`);
|
|
20602
|
-
}
|
|
20603
|
-
});
|
|
20604
|
-
on("agent/turn-stopping", (payload) => {
|
|
20605
|
-
try {
|
|
20606
|
-
const p = payload;
|
|
20607
|
-
if (!p?.agent) return;
|
|
20608
|
-
handleTurnStopping(p.agent, typeof p.turn === "number" ? p.turn : null);
|
|
20609
|
-
} catch (err) {
|
|
20610
|
-
console.log(`[serenity-hooks] unattended: turn-stopping 处置失败(已忽略) — ${String(err?.message ?? err)}`);
|
|
20611
|
-
}
|
|
20612
|
-
});
|
|
20613
|
-
on("agent/disposed", (payload) => {
|
|
20614
|
-
const sessionId = sessionIdOf$2(payload?.agent);
|
|
20615
|
-
if (sessionId === "") return;
|
|
20616
|
-
clearProxyRun(sessionId);
|
|
20617
|
-
clearUserTurn(sessionId);
|
|
20618
|
-
});
|
|
20619
|
-
on("session/disposed", (payload) => {
|
|
20620
|
-
const id = payload?.id;
|
|
20621
|
-
if (typeof id !== "string" || id === "") return;
|
|
20622
|
-
clearProxyRun(id);
|
|
20623
|
-
clearUserTurn(id);
|
|
20624
|
-
});
|
|
20625
|
-
registerDisposer(ctx, "unattended proxy", () => {
|
|
20626
|
-
clearUnattendedState();
|
|
20627
|
-
});
|
|
20628
|
-
}
|
|
20629
|
-
//#endregion
|
|
20630
19959
|
//#region src/human-channel.ts
|
|
20631
19960
|
/**
|
|
20632
19961
|
* human-channel.ts — 人机信道的**机制层**(机器级;2026-09-26 owner 定案 ⇒ 方案 §2.6)
|
|
@@ -21309,7 +20638,7 @@ function manualOutputFallbackNeeded(input) {
|
|
|
21309
20638
|
*/
|
|
21310
20639
|
async function resolveCccRootByAlias(ctx, alias) {
|
|
21311
20640
|
try {
|
|
21312
|
-
const { listCccs } = await import("./ccc-roots-
|
|
20641
|
+
const { listCccs } = await import("./ccc-roots-RMzGU-CJ.js").then((n) => n.a);
|
|
21313
20642
|
const entries = await listCccs(ctx);
|
|
21314
20643
|
const byName = entries.find((e) => e.name === alias);
|
|
21315
20644
|
if (byName) return byName.root;
|
|
@@ -21527,7 +20856,7 @@ async function handleIncoming(ctx, root, accountId, cred, msg, preset) {
|
|
|
21527
20856
|
const parts = [];
|
|
21528
20857
|
const activeWorkspace = getActiveSessionInfo(sessionId);
|
|
21529
20858
|
if (activeWorkspace?.mdPath) {
|
|
21530
|
-
if (!ensureWorkspacePromptSection(ref.agent, sessionId)) parts.push(
|
|
20859
|
+
if (!ensureWorkspacePromptSection(ref.agent, sessionId)) parts.push(workspaceCognitiveTrajectoryLine(activeWorkspace.mdPath));
|
|
21531
20860
|
}
|
|
21532
20861
|
const manualOutput = settings.autoReplyWithLastMessage === false;
|
|
21533
20862
|
if (text) parts.push(text);
|
|
@@ -21555,7 +20884,7 @@ async function handleIncoming(ctx, root, accountId, cred, msg, preset) {
|
|
|
21555
20884
|
})).catch((err) => {
|
|
21556
20885
|
console.log(`[serenity-hooks] weixin hook incoming error: ${err instanceof Error ? err.message : String(err)}`);
|
|
21557
20886
|
});
|
|
21558
|
-
const answer = (await askSkiff(ctx, ref.agent, question, void 0, {
|
|
20887
|
+
const answer = (await askSkiff(ctx, ref.agent, question, void 0, { includeCognitiveTrajectory: false })).answer ?? "";
|
|
21559
20888
|
if (manualOutput) {
|
|
21560
20889
|
if (!manualOutputFallbackNeeded({
|
|
21561
20890
|
fallbackOnNoSend: settings.fallbackOnNoSend,
|
|
@@ -21808,7 +21137,7 @@ function syncHumanChannel(ctx) {
|
|
|
21808
21137
|
(async () => {
|
|
21809
21138
|
try {
|
|
21810
21139
|
stopMachineLoops();
|
|
21811
|
-
const { listCccs } = await import("./ccc-roots-
|
|
21140
|
+
const { listCccs } = await import("./ccc-roots-RMzGU-CJ.js").then((n) => n.a);
|
|
21812
21141
|
for (const entry of await listCccs(ctx)) syncCccBridge(ctx, entry.root);
|
|
21813
21142
|
} catch {}
|
|
21814
21143
|
})();
|
|
@@ -22044,7 +21373,7 @@ function matchCcc(input, candidates) {
|
|
|
22044
21373
|
}
|
|
22045
21374
|
/** 组装候选列表(`ccc-roots.listCccs` 投影 + `.serenity` 名;动态 import 保持本模块静态依赖轻量) */
|
|
22046
21375
|
async function collectCandidates(ctx) {
|
|
22047
|
-
const { listCccs } = await import("./ccc-roots-
|
|
21376
|
+
const { listCccs } = await import("./ccc-roots-RMzGU-CJ.js").then((n) => n.a);
|
|
22048
21377
|
const entries = await listCccs(ctx, { defaultRoot: process.cwd() });
|
|
22049
21378
|
const seen = /* @__PURE__ */ new Set();
|
|
22050
21379
|
const out = [];
|
|
@@ -24901,7 +24230,6 @@ function renderClock(label, s) {
|
|
|
24901
24230
|
const parts = [
|
|
24902
24231
|
`armed=${s.armed}`,
|
|
24903
24232
|
`CRO 闸=${readSimpleSettings().croEnabled !== false}`,
|
|
24904
|
-
`无人值守闸=${readSimpleSettings().unattendedEnabled === true}`,
|
|
24905
24233
|
`tick 次数=${s.ticks}`,
|
|
24906
24234
|
`上次 tick=${ts(s.lastTickAt)}`
|
|
24907
24235
|
];
|
|
@@ -26022,7 +25350,6 @@ const Config = z.object({
|
|
|
26022
25350
|
skiffEnabled: z.boolean().volatile(),
|
|
26023
25351
|
skiffDebugPort: z.number().min(1024).max(65535).volatile(),
|
|
26024
25352
|
croEnabled: z.boolean().volatile(),
|
|
26025
|
-
unattendedEnabled: z.boolean().volatile(),
|
|
26026
25353
|
diagramEnabled: z.boolean().volatile(),
|
|
26027
25354
|
acpFaceEnabled: z.boolean().volatile(),
|
|
26028
25355
|
acpFacePort: z.number().min(1024).max(65535).volatile()
|
|
@@ -26044,7 +25371,7 @@ function apply(ctx, config) {
|
|
|
26044
25371
|
})();
|
|
26045
25372
|
if (config.tools) {
|
|
26046
25373
|
ctx.tools.register(ccFsTool);
|
|
26047
|
-
ctx.tools.register(
|
|
25374
|
+
ctx.tools.register(createCognitiveTrajectoryTool(ctx));
|
|
26048
25375
|
ctx.tools.register(createKitTool(ctx));
|
|
26049
25376
|
ctx.tools.register(gitTool);
|
|
26050
25377
|
ctx.tools.register(msmTool);
|
|
@@ -26080,7 +25407,6 @@ function apply(ctx, config) {
|
|
|
26080
25407
|
registerGateway(ctx);
|
|
26081
25408
|
registerRebuildTurnHook(ctx);
|
|
26082
25409
|
registerOutputGuardHook(ctx);
|
|
26083
|
-
registerUnattendedSeam(ctx);
|
|
26084
25410
|
if (config.env) registerEnv(ctx);
|
|
26085
25411
|
if (config.opencodeSkills) registerOpencodeSkills(ctx);
|
|
26086
25412
|
registerBootstrap(ctx);
|