@shgroup/dsh-serenity-hooks 1.112.0 → 1.113.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/lib/{ccc-roots-DPMwBywc.js → ccc-roots-CsSSQjSS.js} +2 -2
- package/lib/{ccc-BmpE3cQz.js → ccc-x94FCzOv.js} +17 -1
- package/lib/{channels-Dty_NIl_.js → channels-DWZlcsE0.js} +1 -1
- package/lib/{contract-DtFUPbPg.js → contract-DJSTR5Up.js} +31 -5
- package/lib/index.js +184 -30
- package/lib/kernel/ccc.d.ts +29 -0
- package/lib/{message-source-BTIe2cVO.js → message-source-DLei3MqR.js} +7 -3
- package/lib/{observation-DzvyOI-A.js → observation-BKsS1Qke.js} +4 -4
- package/lib/{profile-BjBn_-be.js → profile-Djqz4Noy.js} +1 -1
- package/lib/{route-reyv8qcB.js → route-D-W3O2PF.js} +2 -2
- package/lib/{rules-BLkYTPUy.js → rules-DKlpuFN_.js} +1 -1
- package/lib/{scheduler-B-JpixMD.js → scheduler-CB06u0Qc.js} +5 -5
- package/lib/{service-CNzT-5Du.js → service-Cfv1byRk.js} +6 -6
- package/lib/{service-Z3aSR1cn.js → service-Dv-8E2bI.js} +1 -1
- package/lib/{session-DVvYS7z0.js → session-C7-l8Kzs.js} +6 -6
- package/lib/{settings-DY4_DYYY.js → settings-DibHCfEU.js} +1 -1
- package/lib/{subscriptions-BMWAwpCn.js → subscriptions-D05XffxT.js} +4 -4
- package/lib/tools/msm/service.d.ts +1 -1
- package/lib/trajectory/user-quotes.d.ts +183 -0
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.113.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——12 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/localstore/container_admin;另两个 im-bridge/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
|
|
2
|
-
import { a as findSerenityRoot } from "./ccc-
|
|
2
|
+
import { a as findSerenityRoot } from "./ccc-x94FCzOv.js";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
//#region src/host/access.ts
|
|
5
5
|
/**
|
|
@@ -235,7 +235,7 @@ async function listCccs(ctx, opts = {}) {
|
|
|
235
235
|
if (typeof defaultRoot === "string" && defaultRoot !== "" && !c.roots.includes(defaultRoot)) c.roots.unshift(defaultRoot);
|
|
236
236
|
let rolesOf = null;
|
|
237
237
|
if (opts.withRoles === true) {
|
|
238
|
-
const { listTrajectoryConstraints } = await import("./profile-
|
|
238
|
+
const { listTrajectoryConstraints } = await import("./profile-Djqz4Noy.js").then((n) => n.a);
|
|
239
239
|
rolesOf = (root) => {
|
|
240
240
|
try {
|
|
241
241
|
return listTrajectoryConstraints(root).map((p) => p.code);
|
|
@@ -220,6 +220,22 @@ function readTrajectorySubscriptions(root, paths = DEFAULT_SERENITY_CONFIG_PATHS
|
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
/**
|
|
223
|
+
* 用户语录台账是否开启(`userQuotes.enabled`,v1.113.0)。
|
|
224
|
+
*
|
|
225
|
+
* 🔴 **判据是 `=== true`,不是"键存在"**:写成 `"true"` / `1` / 缺 `enabled`
|
|
226
|
+
* 一律当**关**。理由见 `SerenityConfig.userQuotes` —— 这是"长期留存用户原话",
|
|
227
|
+
* 含糊的配置必须落到**不写**那一侧(fail-closed,与订阅表的 fail-open 方向相反,
|
|
228
|
+
* 因为这里的危险方向是**多写**,那里是**少通知**)。
|
|
229
|
+
*
|
|
230
|
+
* ⚠️ 配置损坏 ⇒ `loadSerenityConfig` 已响亮告警并返回 `{}` ⇒ 这里得到 `false`(关)。
|
|
231
|
+
* 即"配置坏了就不记",而不是"配置坏了就记到默认位置"。
|
|
232
|
+
*/
|
|
233
|
+
function readUserQuotesEnabled(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
234
|
+
const raw = loadSerenityConfig(root, paths).userQuotes;
|
|
235
|
+
if (typeof raw !== "object" || raw === null) return false;
|
|
236
|
+
return raw.enabled === true;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
223
239
|
* 读取 CCC 声明的「安全模式期间隐藏的工具」清单(`safeMode.hiddenTools`,v1.51.4)。
|
|
224
240
|
*
|
|
225
241
|
* 语义(R↓):**标记文件存在 ⇒ 才生效**。deny = `['bash', ...本清单]`(并集去重);
|
|
@@ -303,4 +319,4 @@ function matchBlacklist(relPath, rules) {
|
|
|
303
319
|
return null;
|
|
304
320
|
}
|
|
305
321
|
//#endregion
|
|
306
|
-
export { findSerenityRoot as a, matchBlacklist as c, readCccName as d, readCognitiveTrajectorySkills as f,
|
|
322
|
+
export { resolveInside as _, findSerenityRoot as a, matchBlacklist as c, readCccName as d, readCognitiveTrajectorySkills as f, readUtf8 as g, readUserQuotesEnabled as h, findGitRoot as i, pathInside as l, readTrajectorySubscriptions as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readSafeModeHiddenTools as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as readWeixinSettings, o as readImSettings } from "./route-
|
|
1
|
+
import { c as readWeixinSettings, o as readImSettings } from "./route-D-W3O2PF.js";
|
|
2
2
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
//#region src/im/log-scan.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { l as hostInjected, r as cccRootForCwd, u as hostService } from "./ccc-roots-
|
|
2
|
-
import { c as matchBlacklist, d as readCccName$1, l as pathInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readSafeModeHiddenTools, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-
|
|
1
|
+
import { l as hostInjected, r as cccRootForCwd, u as hostService } from "./ccc-roots-CsSSQjSS.js";
|
|
2
|
+
import { c as matchBlacklist, d as readCccName$1, l as pathInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readSafeModeHiddenTools, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-x94FCzOv.js";
|
|
3
3
|
import { F as isoLocal, g as listActiveTrajectories } from "./bind-file-B0TuOP89.js";
|
|
4
|
-
import { c as profileWriteWhitelist, s as profileToolWhitelist, u as ACC_VERSION } from "./rules-
|
|
5
|
-
import { l as trajectoryCodeForSession, u as trajectoryConstraintForSession } from "./profile-
|
|
6
|
-
import { n as isImWiredTrajectory } from "./channels-
|
|
4
|
+
import { c as profileWriteWhitelist, s as profileToolWhitelist, u as ACC_VERSION } from "./rules-DKlpuFN_.js";
|
|
5
|
+
import { l as trajectoryCodeForSession, u as trajectoryConstraintForSession } from "./profile-Djqz4Noy.js";
|
|
6
|
+
import { n as isImWiredTrajectory } from "./channels-DWZlcsE0.js";
|
|
7
7
|
import { REGISTERED_TOOLS } from "./invariant.js";
|
|
8
8
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -458,6 +458,32 @@ CCC 级微信个人号接入(iLink 协议):dsh 一进程多 CCC,每 CCC
|
|
|
458
458
|
▸ 旧配置里残留 exclusiveTools ⇒ **静默忽略**(schemastery 非 strict),不影响加载。
|
|
459
459
|
▸ ⚠️ 连带后果(owner 已知并确认):dashboard 是常驻工具 ⇒ diag 段对**所有轨迹**可见,
|
|
460
460
|
而它的 live 会话清单是**全部 CCC** 的(跨容器)。这是「方案 C」的既定代价,不是缺陷。
|
|
461
|
+
|
|
462
|
+
── 9. userQuotes.enabled(用户语录台账,v1.113.0)──
|
|
463
|
+
**把用户直接说的话按天、按轨迹存下来**,供之后反向分析用户。owner 原话:
|
|
464
|
+
「我需要一个可开启关闭的功能,就是记录用户直接对谁说过什么,对谁是指对具体的某个轨迹……
|
|
465
|
+
这个功能如果开启,则默认写到一个指定目录内,按天进行记录。这个功能是用于具体的宁静号CCC反向来分析用户的」。
|
|
466
|
+
|
|
467
|
+
Config:
|
|
468
|
+
{ "userQuotes": { "enabled": true } }
|
|
469
|
+
|
|
470
|
+
默认:**关**。🔴 判据是 \`enabled === true\`,⛔ 不是"键存在"——
|
|
471
|
+
写成 "true" / 1 / 只写 userQuotes: {} 一律当**关**。理由:这是把用户原话原文**长期留存**,
|
|
472
|
+
含糊的配置必须落到"不写"那一侧(fail-closed;与订阅表的 fail-open 方向相反,
|
|
473
|
+
因为这里的危险方向是**多写**)。
|
|
474
|
+
|
|
475
|
+
落点:\`AGENT_SESSIONS/_user-quotes/<YYYY-MM-DD>.jsonl\`(按天,一行一条):
|
|
476
|
+
{"ts":"…+08:00","trajectory":"<轨迹目录名 或 null>","sessionId":"…","messageId":"…","text":"(原话,一字不改)","chars":88}
|
|
477
|
+
|
|
478
|
+
▸ **采集点** = \`agent/pre-step\` 的 \`payload.messages\`(本 step 从 inbox 取出、即将进本步的消息)。
|
|
479
|
+
▸ **判别面 = \`source.kind === 'user'\`**(结构性,⛔ 不靠措辞黑名单)。
|
|
480
|
+
▸ **"对谁" = 按轨迹**(Q1),取绑定真相源(\`current.dsh_session_bind\`);未绑定 ⇒ \`null\` 但**照常记**。
|
|
481
|
+
▸ **全文**(Q3):一字不改,不做摘要;\`chars\` 是**码点数**不是 UTF-16 单元数。
|
|
482
|
+
▸ 两个**冒充 \`kind:'user'\`** 的生产者(载体开场白/rebuild 锚点)按各自标记排除。
|
|
483
|
+
▸ IM 入站(微信/飞书)**不会混进来**:它走 \`deliverToTrajectory\`,kind 是
|
|
484
|
+
\`plugin:dsh-serenity-hooks\`(已取证,非推演)。⇒ 台账 = **界面直话**。
|
|
485
|
+
▸ 采集失败只 warn、**不阻断 step**(台账是旁路观测,不是被观测对象的组成部分)。
|
|
486
|
+
判据全文(含取证与变异检验设计)⇒ \`src/trajectory/user-quotes.ts\` 头注释。
|
|
461
487
|
`;
|
|
462
488
|
function parseRegistry(raw) {
|
|
463
489
|
const data = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
package/lib/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { i as __toESM, n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
|
|
2
|
-
import { c as hostAgents, d as hostSessions, f as hostSettings, i as cccRootForExec, m as hostWebServer, n as agentCwdFor, o as listCccs, p as hostWeb, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-
|
|
3
|
-
import { a as findSerenityRoot$1, d as readCccName,
|
|
2
|
+
import { c as hostAgents, d as hostSessions, f as hostSettings, i as cccRootForExec, m as hostWebServer, n as agentCwdFor, o as listCccs, p as hostWeb, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-CsSSQjSS.js";
|
|
3
|
+
import { _ as resolveInside, a as findSerenityRoot$1, d as readCccName, h as readUserQuotesEnabled, l as pathInside, m as readTrajectorySubscriptions, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
|
|
4
4
|
import { A as useSession, C as resolveSessionByTitle, D as showSession, E as setActiveSessionInfo, F as isoLocal, I as localFileStamp, M as TRAJECTORY_LEGACY_FILENAMES, O as summarize, S as readTrajectoryUid, T as sessionsRoot, _ as listSessions, b as parseTrajectoryCode, c as UID_FILENAME, d as createSession, f as extractSessionMdPathFromText, j as TRAJECTORY_FILENAME, k as trajectoryDescOf, l as archiveSession, m as getActiveSessionInfo, o as COGNITIVE_TRAJECTORY_ACTIONS, p as findSession, u as clearActiveSessionInfo, v as namingTitleFor, w as sessionEvents, y as parseSessionContextFromEvents } from "./bind-file-B0TuOP89.js";
|
|
5
|
-
import { n as checkLocalstoreGitCompliance, o as readStore, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./service-
|
|
6
|
-
import { d as findPackageRoot, i as constraintSystemPromptSource, l as resolveConstraintSystemPrompt, r as constraintCognitiveTrajectory, t as TRAJECTORY_CONSTRAINT_HINT, u as ACC_VERSION } from "./rules-
|
|
7
|
-
import { C as docsPointer, E as setWeixinSendEndpoint, S as runMsmAsync, T as renderDocsIndex, _ as CCC_CONFIG_REFERENCE, a as setSafeMode, b as loadMsmEntries, d as forgetNonCccToolVisibility, g as ACC_CATALOG, h as forgetToolVisibility, i as readDshVersion, m as syncToolVisibility, n as summarizeHostContract, o as listOpencodeSkillDirs, p as registerGuards, r as getStatus, s as loadOpencodeSkill, t as hostContractReport, w as findDocsChapter, x as runMsm, y as findEntry } from "./contract-
|
|
8
|
-
import { c as replacesDefaultIdentity, i as listTrajectoryConstraints, l as trajectoryCodeForSession, n as findDirNameByCode, o as readTrajectoryConstraintAt, r as isTrajectoryConstrained, s as readTrajectoryConstraintByCode, t as TRAJECTORY_CONSTRAINT_FILENAME } from "./profile-
|
|
9
|
-
import { A as truncateContent, C as verifyTotpCode, D as buildResourceIndex, E as SKILL_ONTOLOGY_FILE, M as hasTrajectoryPrompt, O as buildResourceNameSuffix, S as MECHANISM_PORTS, T as REFERENCE_DIR, _ as projectKnownWorkspaces, a as trajectoryConstraintFor, b as verifyPassword, c as trajectoryWorkspaceMdPath, d as splitModel, f as registerCognitiveTrajectorySkillSection, g as migrateLegacyLocalstore, h as applyWirePatch, i as getTrajectoryAgent, j as TRAJECTORY_PROMPT_FILENAME, k as isSafeSkillName, l as unregisterTrajectorySession, m as registerEntrySkillSectionGlobal, n as deliverToTrajectory, o as trajectoryConstraintMsmGate, p as registerEntrySkillSection, r as ensureWorkspacePromptSection, s as trajectoryConstraintParticipates, t as createTrajectoryAgent, u as workspaceCognitiveTrajectoryLine, v as readAdvancedSettings, w as recordToolUsage, x as FACE_PORTS, y as toWire } from "./session-
|
|
10
|
-
import {
|
|
11
|
-
import { a as DEFAULT_LOOKBACK_MS, c as scanInterruptedSessions, d as registerDisposer, f as buildCroSnapshot,
|
|
5
|
+
import { n as checkLocalstoreGitCompliance, o as readStore, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./service-Dv-8E2bI.js";
|
|
6
|
+
import { d as findPackageRoot, i as constraintSystemPromptSource, l as resolveConstraintSystemPrompt, r as constraintCognitiveTrajectory, t as TRAJECTORY_CONSTRAINT_HINT, u as ACC_VERSION } from "./rules-DKlpuFN_.js";
|
|
7
|
+
import { C as docsPointer, E as setWeixinSendEndpoint, S as runMsmAsync, T as renderDocsIndex, _ as CCC_CONFIG_REFERENCE, a as setSafeMode, b as loadMsmEntries, d as forgetNonCccToolVisibility, g as ACC_CATALOG, h as forgetToolVisibility, i as readDshVersion, m as syncToolVisibility, n as summarizeHostContract, o as listOpencodeSkillDirs, p as registerGuards, r as getStatus, s as loadOpencodeSkill, t as hostContractReport, w as findDocsChapter, x as runMsm, y as findEntry } from "./contract-DJSTR5Up.js";
|
|
8
|
+
import { c as replacesDefaultIdentity, i as listTrajectoryConstraints, l as trajectoryCodeForSession, n as findDirNameByCode, o as readTrajectoryConstraintAt, r as isTrajectoryConstrained, s as readTrajectoryConstraintByCode, t as TRAJECTORY_CONSTRAINT_FILENAME } from "./profile-Djqz4Noy.js";
|
|
9
|
+
import { A as truncateContent, C as verifyTotpCode, D as buildResourceIndex, E as SKILL_ONTOLOGY_FILE, M as hasTrajectoryPrompt, O as buildResourceNameSuffix, S as MECHANISM_PORTS, T as REFERENCE_DIR, _ as projectKnownWorkspaces, a as trajectoryConstraintFor, b as verifyPassword, c as trajectoryWorkspaceMdPath, d as splitModel, f as registerCognitiveTrajectorySkillSection, g as migrateLegacyLocalstore, h as applyWirePatch, i as getTrajectoryAgent, j as TRAJECTORY_PROMPT_FILENAME, k as isSafeSkillName, l as unregisterTrajectorySession, m as registerEntrySkillSectionGlobal, n as deliverToTrajectory, o as trajectoryConstraintMsmGate, p as registerEntrySkillSection, r as ensureWorkspacePromptSection, s as trajectoryConstraintParticipates, t as createTrajectoryAgent, u as workspaceCognitiveTrajectoryLine, v as readAdvancedSettings, w as recordToolUsage, x as FACE_PORTS, y as toWire } from "./session-C7-l8Kzs.js";
|
|
10
|
+
import { a as findTrajectoryOfSession, c as resolveSessionCognitiveTrajectoryLabel, n as PLUGIN_SOURCE, r as appendBound, s as readLastBound, t as ACC_MESSAGE_KIND } from "./message-source-DLei3MqR.js";
|
|
11
|
+
import { a as DEFAULT_LOOKBACK_MS, c as scanInterruptedSessions, d as registerDisposer, f as buildCroSnapshot, g as addWake, h as WAKE_CATCH_UP_MS, l as listRunningSessionIds, m as CARRIER_OPENING_MARK, o as createResumeOne, r as sendToCognitiveTrajectory, s as projectDirNameForCwd, t as registerWakeScheduler, u as registerCroTurnTracking } from "./scheduler-CB06u0Qc.js";
|
|
12
12
|
import { i as sessionsRootDir } from "./cleanup-CPl1SSaK.js";
|
|
13
|
-
import { n as readSimpleSettings, r as registerSettingsSection } from "./settings-
|
|
14
|
-
import { a as readFeishuCredential, c as readWeixinSettings, d as weixinInboundDir, f as weixinSessionIdForTrajectory, i as matchWeixinRoute, n as extractWeixinText, r as hasVoiceItem, s as readWeixinCredential, t as extractWeixinMedia, u as sanitizeFileName$1 } from "./route-
|
|
15
|
-
import { i as runImBridge, n as isImWiredTrajectory, r as registerImChannel, t as imChannelIds } from "./channels-
|
|
16
|
-
import { i as subscriptionStats, n as registerTrajectorySubscriptions, r as subscriptionEval, t as defaultSubscriptionDeps } from "./subscriptions-
|
|
13
|
+
import { n as readSimpleSettings, r as registerSettingsSection } from "./settings-DibHCfEU.js";
|
|
14
|
+
import { a as readFeishuCredential, c as readWeixinSettings, d as weixinInboundDir, f as weixinSessionIdForTrajectory, i as matchWeixinRoute, n as extractWeixinText, r as hasVoiceItem, s as readWeixinCredential, t as extractWeixinMedia, u as sanitizeFileName$1 } from "./route-D-W3O2PF.js";
|
|
15
|
+
import { i as runImBridge, n as isImWiredTrajectory, r as registerImChannel, t as imChannelIds } from "./channels-DWZlcsE0.js";
|
|
16
|
+
import { i as subscriptionStats, n as registerTrajectorySubscriptions, r as subscriptionEval, t as defaultSubscriptionDeps } from "./subscriptions-D05XffxT.js";
|
|
17
17
|
import { c as sendTextMessage, i as getUpdates, l as sendTyping, n as downloadMedia, o as markdownToPlainText, r as getConfig, s as sendFileMessage, t as TypingStatus, u as sniffImageExt } from "./ilink-CsZ-f64q.js";
|
|
18
18
|
import { a as registerImAccountImpl, i as normalizeAccountChannel, n as getImAccountImpl, r as imAccountChannels } from "./accounts-BHQbakUh.js";
|
|
19
19
|
import { createRequire } from "node:module";
|
|
@@ -516,7 +516,7 @@ const KIT_ACTIONS = [
|
|
|
516
516
|
async function runKit(root, args, hostCtx) {
|
|
517
517
|
switch (args.action) {
|
|
518
518
|
case "health": {
|
|
519
|
-
const { containerStatus } = await import("./observation-
|
|
519
|
+
const { containerStatus } = await import("./observation-BKsS1Qke.js");
|
|
520
520
|
const s = containerStatus({
|
|
521
521
|
root,
|
|
522
522
|
hostCtx
|
|
@@ -562,7 +562,7 @@ async function runKit(root, args, hostCtx) {
|
|
|
562
562
|
}
|
|
563
563
|
case "diag": {
|
|
564
564
|
if (hostCtx === void 0) throw new Error("diag requires the plugin context (live session data unavailable without it)");
|
|
565
|
-
const { runDiag, renderDiag } = await import("./service-
|
|
565
|
+
const { runDiag, renderDiag } = await import("./service-Cfv1byRk.js");
|
|
566
566
|
return renderDiag(await runDiag(hostCtx, root ?? ""));
|
|
567
567
|
}
|
|
568
568
|
default: throw new Error(`Unknown action: ${args.action}`);
|
|
@@ -7655,7 +7655,7 @@ const DOCS_CHAPTERS = [
|
|
|
7655
7655
|
},
|
|
7656
7656
|
{
|
|
7657
7657
|
id: "config",
|
|
7658
|
-
summary: "CCC 配置全表——
|
|
7658
|
+
summary: "CCC 配置全表——10 段各是什么、怎么填、填错会怎样(.opencode/serenity.json)",
|
|
7659
7659
|
render: () => CCC_CONFIG_REFERENCE
|
|
7660
7660
|
},
|
|
7661
7661
|
{
|
|
@@ -8837,6 +8837,139 @@ function resolveInstallModelSelection() {
|
|
|
8837
8837
|
return resolveCache;
|
|
8838
8838
|
}
|
|
8839
8839
|
//#endregion
|
|
8840
|
+
//#region src/trajectory/user-quotes.ts
|
|
8841
|
+
/** 台账目录(CCC 根相对)—— 与 `_im-logs/` 同族:`_` 前缀 = 非内容目录。 */
|
|
8842
|
+
const USER_QUOTES_DIR = "AGENT_SESSIONS/_user-quotes";
|
|
8843
|
+
/**
|
|
8844
|
+
* 冒充 `kind:'user'` 的两个生产者所打的标记。
|
|
8845
|
+
*
|
|
8846
|
+
* 🔴 判据来自**生产者自己**(它们本就为了让读者能分辨才打这个标记),
|
|
8847
|
+
* ⛔ 不是"我猜这些词像注入" —— 后者是措辞黑名单,会随措辞漂移而静默失效。
|
|
8848
|
+
*/
|
|
8849
|
+
const USER_QUOTE_FAKE_MARKERS = [CARRIER_OPENING_MARK, ASSISTANT_PREFIX];
|
|
8850
|
+
/**
|
|
8851
|
+
* 当前进程内已入账的 message id(有界 LRU)。
|
|
8852
|
+
*
|
|
8853
|
+
* ⚠️ **只在进程内有效**:跨重启的去重靠"日志里已经有这一行"——但那需要读全文件,
|
|
8854
|
+
* 而 `pre-step` 是每步都跑的热路径 ⇒ 不为它加一次全文件扫描。
|
|
8855
|
+
* 跨重启的重复投递**实际不会发生**(重启后 inbox 是空的,宿主不会重放已落盘的 user/message)。
|
|
8856
|
+
*/
|
|
8857
|
+
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
8858
|
+
const SEEN_MAX = 4096;
|
|
8859
|
+
function rememberMessageId(id) {
|
|
8860
|
+
if (seenMessageIds.has(id)) return;
|
|
8861
|
+
seenMessageIds.add(id);
|
|
8862
|
+
if (seenMessageIds.size > SEEN_MAX) {
|
|
8863
|
+
const oldest = seenMessageIds.values().next().value;
|
|
8864
|
+
if (oldest !== void 0) seenMessageIds.delete(oldest);
|
|
8865
|
+
}
|
|
8866
|
+
}
|
|
8867
|
+
/**
|
|
8868
|
+
* 一条消息的正文文本(只取 `text` 块,按出现顺序以 `\n` 相连)。
|
|
8869
|
+
*
|
|
8870
|
+
* 判据与 `session.ts:496` / `guard/seam.ts:70` 同款 —— 不另造一套取文法(否则同一条消息
|
|
8871
|
+
* 在不同缝里"说了什么"会不一样)。图片等非文本块**不参与**:Q3 要的是"用户说过什么",
|
|
8872
|
+
* 图片没有可记录的文字面(媒体路径另有 `_im-logs/.../media/` 承载)。
|
|
8873
|
+
*/
|
|
8874
|
+
function messageText(content) {
|
|
8875
|
+
if (!Array.isArray(content)) return "";
|
|
8876
|
+
const parts = [];
|
|
8877
|
+
for (const b of content) if (b?.type === "text" && typeof b.text === "string") parts.push(b.text);
|
|
8878
|
+
return parts.join("\n");
|
|
8879
|
+
}
|
|
8880
|
+
/**
|
|
8881
|
+
* 这条消息**是不是用户直接说的话**(结构性判据 + 冒充标记排除)。
|
|
8882
|
+
*
|
|
8883
|
+
* @returns `'quote'` 收录 / `'not-user'` 不是用户发言(ACC 注入等)/ `'fake-user'` 冒充 user 的生产者
|
|
8884
|
+
*/
|
|
8885
|
+
function classifyUserMessage(source, text) {
|
|
8886
|
+
if (source?.kind !== "user") return "not-user";
|
|
8887
|
+
for (const marker of USER_QUOTE_FAKE_MARKERS) if (text.startsWith(marker)) return "fake-user";
|
|
8888
|
+
return "quote";
|
|
8889
|
+
}
|
|
8890
|
+
/** 本地时区 ISO8601(与 `cro/log.ts` 等处同款口径:不引第三方库,用 Date 的本地字段) */
|
|
8891
|
+
function localIso(ms) {
|
|
8892
|
+
const d = new Date(ms);
|
|
8893
|
+
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
8894
|
+
const offMin = -d.getTimezoneOffset();
|
|
8895
|
+
const sign = offMin >= 0 ? "+" : "-";
|
|
8896
|
+
const abs = Math.abs(offMin);
|
|
8897
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}${sign}${p(Math.floor(abs / 60))}:${p(abs % 60)}`;
|
|
8898
|
+
}
|
|
8899
|
+
/** 台账文件名(按天,本地时区) */
|
|
8900
|
+
function userQuoteFileName(ms) {
|
|
8901
|
+
const d = new Date(ms);
|
|
8902
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
8903
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}.jsonl`;
|
|
8904
|
+
}
|
|
8905
|
+
/**
|
|
8906
|
+
* 把本 step 的 `messages` 里的**用户真发言**追加进当日台账。
|
|
8907
|
+
*
|
|
8908
|
+
* @param root CCC 根
|
|
8909
|
+
* @param sessionId 当前会话 id
|
|
8910
|
+
* @param messages `agent/pre-step` 的 `payload.messages`
|
|
8911
|
+
* @param nowMs 时间戳(测试注入;缺省 `Date.now()`)
|
|
8912
|
+
* @returns 落盘结果(**永不抛** —— 采集失败只 warn)
|
|
8913
|
+
*/
|
|
8914
|
+
function recordUserQuotes(root, sessionId, messages, nowMs = Date.now()) {
|
|
8915
|
+
const file = join(root, USER_QUOTES_DIR, userQuoteFileName(nowMs));
|
|
8916
|
+
const out = {
|
|
8917
|
+
written: 0,
|
|
8918
|
+
excludedFake: 0,
|
|
8919
|
+
skippedDuplicate: 0,
|
|
8920
|
+
file
|
|
8921
|
+
};
|
|
8922
|
+
if (!Array.isArray(messages) || messages.length === 0) return out;
|
|
8923
|
+
const quotes = [];
|
|
8924
|
+
for (const m of messages) {
|
|
8925
|
+
const text = messageText(m?.content);
|
|
8926
|
+
if (text.trim() === "") continue;
|
|
8927
|
+
const cls = classifyUserMessage(m?.source, text);
|
|
8928
|
+
if (cls === "not-user") continue;
|
|
8929
|
+
if (cls === "fake-user") {
|
|
8930
|
+
out.excludedFake++;
|
|
8931
|
+
continue;
|
|
8932
|
+
}
|
|
8933
|
+
const id = typeof m?.id === "string" ? m.id : "";
|
|
8934
|
+
if (id !== "" && seenMessageIds.has(id)) {
|
|
8935
|
+
out.skippedDuplicate++;
|
|
8936
|
+
continue;
|
|
8937
|
+
}
|
|
8938
|
+
quotes.push({
|
|
8939
|
+
ts: localIso(nowMs),
|
|
8940
|
+
trajectory: safeFindTrajectory(root, sessionId),
|
|
8941
|
+
sessionId,
|
|
8942
|
+
messageId: id,
|
|
8943
|
+
text,
|
|
8944
|
+
chars: [...text].length
|
|
8945
|
+
});
|
|
8946
|
+
}
|
|
8947
|
+
if (quotes.length === 0) return out;
|
|
8948
|
+
try {
|
|
8949
|
+
mkdirSync(join(root, USER_QUOTES_DIR), { recursive: true });
|
|
8950
|
+
appendFileSync(file, quotes.map((q) => `${JSON.stringify(q)}\n`).join(""), {
|
|
8951
|
+
encoding: "utf8",
|
|
8952
|
+
flag: "a"
|
|
8953
|
+
});
|
|
8954
|
+
} catch (e) {
|
|
8955
|
+
out.error = String(e?.message ?? e);
|
|
8956
|
+
console.warn(`[serenity-hooks] user-quotes 写入失败(不影响本步):${file} — ${out.error}`);
|
|
8957
|
+
return out;
|
|
8958
|
+
}
|
|
8959
|
+
for (const q of quotes) if (q.messageId !== "") rememberMessageId(q.messageId);
|
|
8960
|
+
out.written = quotes.length;
|
|
8961
|
+
return out;
|
|
8962
|
+
}
|
|
8963
|
+
/** 查这次会话属于哪条轨迹;查不到/抛错 ⇒ `null`(⛔ 不让"查不到归属"毁掉整条录入) */
|
|
8964
|
+
function safeFindTrajectory(root, sessionId) {
|
|
8965
|
+
if (typeof sessionId !== "string" || sessionId === "") return null;
|
|
8966
|
+
try {
|
|
8967
|
+
return findTrajectoryOfSession(root, sessionId);
|
|
8968
|
+
} catch {
|
|
8969
|
+
return null;
|
|
8970
|
+
}
|
|
8971
|
+
}
|
|
8972
|
+
//#endregion
|
|
8840
8973
|
//#region src/seams/context.ts
|
|
8841
8974
|
/**
|
|
8842
8975
|
* 该会话的轨迹**是否自带章程**(= 该不该旁路 ACC 身份类默认行为)。D183。
|
|
@@ -9109,6 +9242,27 @@ function registerContext(ctx, opts = {}) {
|
|
|
9109
9242
|
const cwd = agent.session?.header?.cwd ?? process.cwd();
|
|
9110
9243
|
const root = cccRootForCwd(cwd);
|
|
9111
9244
|
const key = agentKey(agent);
|
|
9245
|
+
/**
|
|
9246
|
+
* 🔴 用户语录台账采集(v1.113.0,owner 2026-11-02 需求)。
|
|
9247
|
+
*
|
|
9248
|
+
* 放在 `next()` **之前**:`payload.messages` 是"本 step 从 inbox 取出、即将进入本步的消息"
|
|
9249
|
+
* (宿主 `runtime-types.d.ts` 逐字)。先记再放行 ⇒ **这一步真的会看见的话,就一定先记下了**;
|
|
9250
|
+
* 若下游 reject 掉这一步,台账上会多一条"它没看见"的记录 —— 那是**已知且可接受**的方向
|
|
9251
|
+
* (宁多记不漏记:owner 要的是"用户说过什么",不是"模型最终读到了什么")。
|
|
9252
|
+
*
|
|
9253
|
+
* ⚠️ **只旁路、不阻断**:`recordUserQuotes` 自己 catch 全部失败并 warn,
|
|
9254
|
+
* 这里再包一层 `try/catch` 是第二道保险 —— 台账坏了**绝不能让 step 跑不动**。
|
|
9255
|
+
* ⚠️ **必须排在 `next()` 之前且不改变 `messages`**:本函数是 waterfall,
|
|
9256
|
+
* 但我们**不修改** `payload.messages`(那是下游的输入,动它就是行为变更)。
|
|
9257
|
+
*/
|
|
9258
|
+
if (root) try {
|
|
9259
|
+
if (readUserQuotesEnabled(root, configPaths)) {
|
|
9260
|
+
const sessionId = agent.session?.id;
|
|
9261
|
+
if (typeof sessionId === "string" && sessionId !== "") recordUserQuotes(root, sessionId, messages);
|
|
9262
|
+
}
|
|
9263
|
+
} catch (e) {
|
|
9264
|
+
console.warn(`[serenity-hooks] user-quotes 采集失败(不影响本步):${String(e?.message ?? e)}`);
|
|
9265
|
+
}
|
|
9112
9266
|
const downstream = await next();
|
|
9113
9267
|
try {
|
|
9114
9268
|
syncToolVisibility(agent, root);
|
|
@@ -9578,11 +9732,11 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9578
9732
|
sendJson$2(res, 400, { error: chk.error });
|
|
9579
9733
|
return;
|
|
9580
9734
|
}
|
|
9581
|
-
const { readWeixinSettings } = await import("./route-
|
|
9735
|
+
const { readWeixinSettings } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9582
9736
|
const { weixinBridgeStatus } = await Promise.resolve().then(() => bridge_exports);
|
|
9583
9737
|
const settings = readWeixinSettings(chk.root);
|
|
9584
9738
|
const bridge = weixinBridgeStatus().find((b) => b.ccc === chk.root);
|
|
9585
|
-
const { imCredentialBound } = await import("./route-
|
|
9739
|
+
const { imCredentialBound } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9586
9740
|
const { normalizeAccountChannel } = await import("./accounts-BHQbakUh.js").then((n) => n.t);
|
|
9587
9741
|
const accounts = (settings.accounts ?? []).map((a) => {
|
|
9588
9742
|
const channel = normalizeAccountChannel(a.channel);
|
|
@@ -9619,7 +9773,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9619
9773
|
const { fetchQRCode } = await import("./ilink-CsZ-f64q.js").then((n) => n.a);
|
|
9620
9774
|
const { randomUUID } = await import("node:crypto");
|
|
9621
9775
|
for (const [key, v] of weixinLogins) if (Date.now() - v.startedAt > WEIXIN_LOGIN_TTL_MS) weixinLogins.delete(key);
|
|
9622
|
-
const { readWeixinSettings } = await import("./route-
|
|
9776
|
+
const { readWeixinSettings } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9623
9777
|
const qr = await fetchQRCode({ botType: readWeixinSettings(root).botType ?? void 0 });
|
|
9624
9778
|
const loginKey = randomUUID();
|
|
9625
9779
|
weixinLogins.set(loginKey, {
|
|
@@ -9640,7 +9794,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9640
9794
|
sendJson$2(res, 400, { error: "missing accountId" });
|
|
9641
9795
|
return;
|
|
9642
9796
|
}
|
|
9643
|
-
const { removeWeixinAccount } = await import("./route-
|
|
9797
|
+
const { removeWeixinAccount } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9644
9798
|
const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
|
|
9645
9799
|
removeWeixinAccount(root, body.accountId);
|
|
9646
9800
|
syncCccBridge(ctx, root);
|
|
@@ -9666,8 +9820,8 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9666
9820
|
trajectory: code
|
|
9667
9821
|
});
|
|
9668
9822
|
}
|
|
9669
|
-
const { saveWeixinRoutes } = await import("./route-
|
|
9670
|
-
const { findDirNameByCode } = await import("./profile-
|
|
9823
|
+
const { saveWeixinRoutes } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9824
|
+
const { findDirNameByCode } = await import("./profile-Djqz4Noy.js").then((n) => n.a);
|
|
9671
9825
|
for (const r of routes) if (findDirNameByCode(root, r.trajectory) === null) {
|
|
9672
9826
|
sendJson$2(res, 400, { error: `unknown trajectory: ${r.trajectory} (no such trajectory code in ${root})` });
|
|
9673
9827
|
return;
|
|
@@ -9678,7 +9832,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9678
9832
|
}
|
|
9679
9833
|
if (body.action === "set-enabled") {
|
|
9680
9834
|
const enabled = body.enabled === true;
|
|
9681
|
-
const { readWeixinSettings, setWeixinEnabled } = await import("./route-
|
|
9835
|
+
const { readWeixinSettings, setWeixinEnabled } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9682
9836
|
const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
|
|
9683
9837
|
const settings = readWeixinSettings(root);
|
|
9684
9838
|
if (enabled && (settings.accounts ?? []).length === 0) {
|
|
@@ -9701,7 +9855,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9701
9855
|
sendJson$2(res, 400, { error: "appId 与 appSecret 都必须填(只填一个等于没绑)" });
|
|
9702
9856
|
return;
|
|
9703
9857
|
}
|
|
9704
|
-
const { readWeixinSettings, upsertWeixinAccount, writeFeishuCredential, nextFeishuAccountId } = await import("./route-
|
|
9858
|
+
const { readWeixinSettings, upsertWeixinAccount, writeFeishuCredential, nextFeishuAccountId } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9705
9859
|
const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
|
|
9706
9860
|
const before = readWeixinSettings(root);
|
|
9707
9861
|
const accountId = (body.accountId ?? "").trim() !== "" ? body.accountId.trim() : nextFeishuAccountId(before);
|
|
@@ -9751,7 +9905,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9751
9905
|
return;
|
|
9752
9906
|
}
|
|
9753
9907
|
const { pollQRStatus } = await import("./ilink-CsZ-f64q.js").then((n) => n.a);
|
|
9754
|
-
const { readWeixinSettings } = await import("./route-
|
|
9908
|
+
const { readWeixinSettings } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9755
9909
|
const settings = readWeixinSettings(login.root);
|
|
9756
9910
|
const status = await pollQRStatus({
|
|
9757
9911
|
baseUrl: void 0,
|
|
@@ -9766,7 +9920,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
9766
9920
|
});
|
|
9767
9921
|
return;
|
|
9768
9922
|
}
|
|
9769
|
-
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./route-
|
|
9923
|
+
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./route-D-W3O2PF.js").then((n) => n.l);
|
|
9770
9924
|
const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
|
|
9771
9925
|
const accountId = nextWeixinAccountId(settings);
|
|
9772
9926
|
upsertWeixinAccount(login.root, {
|
|
@@ -14256,7 +14410,7 @@ const MEDIA_MAX_BYTES = 20971520;
|
|
|
14256
14410
|
*/
|
|
14257
14411
|
async function resolveCccRootByAlias(ctx, alias) {
|
|
14258
14412
|
try {
|
|
14259
|
-
const { listCccs } = await import("./ccc-roots-
|
|
14413
|
+
const { listCccs } = await import("./ccc-roots-CsSSQjSS.js").then((n) => n.a);
|
|
14260
14414
|
const entries = await listCccs(ctx);
|
|
14261
14415
|
const byName = entries.find((e) => e.name === alias);
|
|
14262
14416
|
if (byName) return byName.root;
|
|
@@ -14324,7 +14478,7 @@ async function resolveInboundTarget(ctx, input) {
|
|
|
14324
14478
|
});
|
|
14325
14479
|
return null;
|
|
14326
14480
|
}
|
|
14327
|
-
const { findDirNameByCode } = await import("./profile-
|
|
14481
|
+
const { findDirNameByCode } = await import("./profile-Djqz4Noy.js").then((n) => n.a);
|
|
14328
14482
|
if (findDirNameByCode(root, target.role) === null) {
|
|
14329
14483
|
const detail = `目标 CCC "${target.ccc}" 内没有编码为 "${target.role}" 的认知轨迹`;
|
|
14330
14484
|
console.error(`[serenity-hooks] ✗ human-channel: ${detail}(account=${input.account} user=${input.user})—— 该消息**未投递**,请改订阅表里的目标编码`);
|
|
@@ -15286,7 +15440,7 @@ function syncHumanChannel(ctx) {
|
|
|
15286
15440
|
(async () => {
|
|
15287
15441
|
try {
|
|
15288
15442
|
stopMachineLoops();
|
|
15289
|
-
const { listCccs } = await import("./ccc-roots-
|
|
15443
|
+
const { listCccs } = await import("./ccc-roots-CsSSQjSS.js").then((n) => n.a);
|
|
15290
15444
|
for (const entry of await listCccs(ctx)) syncCccBridge(ctx, entry.root);
|
|
15291
15445
|
} catch {}
|
|
15292
15446
|
})();
|
|
@@ -15579,7 +15733,7 @@ function matchCcc(input, candidates) {
|
|
|
15579
15733
|
}
|
|
15580
15734
|
/** 组装候选列表(`ccc-roots.listCccs` 投影 + `.serenity` 名;动态 import 保持本模块静态依赖轻量) */
|
|
15581
15735
|
async function collectCandidates(ctx) {
|
|
15582
|
-
const { listCccs } = await import("./ccc-roots-
|
|
15736
|
+
const { listCccs } = await import("./ccc-roots-CsSSQjSS.js").then((n) => n.a);
|
|
15583
15737
|
const entries = await listCccs(ctx, { defaultRoot: process.cwd() });
|
|
15584
15738
|
const seen = /* @__PURE__ */ new Set();
|
|
15585
15739
|
const out = [];
|
package/lib/kernel/ccc.d.ts
CHANGED
|
@@ -148,6 +148,23 @@ interface SerenityConfig {
|
|
|
148
148
|
* 本节是「**别人**干完活了,告诉我一声」(互观察)。两者都走唤醒链路,但**主语相反**。
|
|
149
149
|
*/
|
|
150
150
|
trajectorySubscriptions?: TrajectorySubscription[];
|
|
151
|
+
/**
|
|
152
|
+
* **用户语录台账**(v1.113.0,owner 2026-11-02 需求)。
|
|
153
|
+
*
|
|
154
|
+
* owner 原话:「我需要一个可开启关闭的功能,就是记录用户直接对谁说过什么……
|
|
155
|
+
* 这个功能如果开启,则默认写到一个指定目录内,按天进行记录。」
|
|
156
|
+
*
|
|
157
|
+
* **开关放 CCC 面(owner Q2 的答)**:改它不发版(D23)——机制在 ACC,声明在 CCC。
|
|
158
|
+
*
|
|
159
|
+
* **缺省 = 关**。这不只是保守:台账是**把用户原话原文长期留存**,
|
|
160
|
+
* 未经明示开启就去写它,等于替 owner 做了一个他没有同意的持久化决定。
|
|
161
|
+
* ⇒ `enabled` 必须**显式为 true** 才开;缺键/写错/写成字符串都算关。
|
|
162
|
+
*
|
|
163
|
+
* 采集点、判别面与落盘形态的判据 ⇒ `trajectory/user-quotes.ts` 头注释(单一真相源)。
|
|
164
|
+
*/
|
|
165
|
+
userQuotes?: {
|
|
166
|
+
enabled?: boolean;
|
|
167
|
+
};
|
|
151
168
|
}
|
|
152
169
|
/**
|
|
153
170
|
* 一条轨迹消息订阅:`watcher` 观察 `members`。
|
|
@@ -373,6 +390,18 @@ export declare function readTrajectorySubscriptions(root: string, paths?: string
|
|
|
373
390
|
subs: TrajectorySubscription[];
|
|
374
391
|
problems: string[];
|
|
375
392
|
};
|
|
393
|
+
/**
|
|
394
|
+
* 用户语录台账是否开启(`userQuotes.enabled`,v1.113.0)。
|
|
395
|
+
*
|
|
396
|
+
* 🔴 **判据是 `=== true`,不是"键存在"**:写成 `"true"` / `1` / 缺 `enabled`
|
|
397
|
+
* 一律当**关**。理由见 `SerenityConfig.userQuotes` —— 这是"长期留存用户原话",
|
|
398
|
+
* 含糊的配置必须落到**不写**那一侧(fail-closed,与订阅表的 fail-open 方向相反,
|
|
399
|
+
* 因为这里的危险方向是**多写**,那里是**少通知**)。
|
|
400
|
+
*
|
|
401
|
+
* ⚠️ 配置损坏 ⇒ `loadSerenityConfig` 已响亮告警并返回 `{}` ⇒ 这里得到 `false`(关)。
|
|
402
|
+
* 即"配置坏了就不记",而不是"配置坏了就记到默认位置"。
|
|
403
|
+
*/
|
|
404
|
+
export declare function readUserQuotesEnabled(root: string, paths?: string[]): boolean;
|
|
376
405
|
/**
|
|
377
406
|
* 读取 CCC 声明的「安全模式期间隐藏的工具」清单(`safeMode.hiddenTools`,v1.51.4)。
|
|
378
407
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as cccRootForCwd } from "./ccc-roots-
|
|
2
|
-
import { T as sessionsRoot, a as readBindDeclaration, b as parseTrajectoryCode, h as getLastActiveSessionInfo, i as listBindDeclarations, m as getActiveSessionInfo, r as isBindUsable, t as claimBinding, w as sessionEvents } from "./bind-file-B0TuOP89.js";
|
|
1
|
+
import { r as cccRootForCwd } from "./ccc-roots-CsSSQjSS.js";
|
|
2
|
+
import { T as sessionsRoot, a as readBindDeclaration, b as parseTrajectoryCode, h as getLastActiveSessionInfo, i as listBindDeclarations, m as getActiveSessionInfo, n as findTrajectoryHolding, r as isBindUsable, t as claimBinding, w as sessionEvents } from "./bind-file-B0TuOP89.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region src/trajectory/bound.ts
|
|
5
5
|
/**
|
|
@@ -244,6 +244,10 @@ function carrierState(root, dirName, alive) {
|
|
|
244
244
|
sessionId: decl.sessionId
|
|
245
245
|
};
|
|
246
246
|
}
|
|
247
|
+
/** 某会话 id 现在安家在哪个轨迹(唯一性查询的对外门面) */
|
|
248
|
+
function findTrajectoryOfSession(root, sessionId) {
|
|
249
|
+
return findTrajectoryHolding(sessionsRoot(root), sessionId);
|
|
250
|
+
}
|
|
247
251
|
//#endregion
|
|
248
252
|
//#region src/kernel/message-source.ts
|
|
249
253
|
/** ACC 注入消息的 kind —— 唤醒 / 打回 / keeper 提醒 / 锚定轮 / rebuild 交接等一律同值。 */
|
|
@@ -251,4 +255,4 @@ const ACC_MESSAGE_KIND = "plugin:dsh-serenity-hooks";
|
|
|
251
255
|
/** ACC 注入消息的统一来源值(各缝共用;需要 `form` 时按 `ContextFormed` 另加字段)。 */
|
|
252
256
|
const PLUGIN_SOURCE = { kind: ACC_MESSAGE_KIND };
|
|
253
257
|
//#endregion
|
|
254
|
-
export {
|
|
258
|
+
export { findTrajectoryOfSession as a, resolveSessionCognitiveTrajectoryLabel as c, carrierState as i, setBindingStore as l, PLUGIN_SOURCE as n, listBoundSessionIds as o, appendBound as r, readLastBound as s, ACC_MESSAGE_KIND as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { d as readCccName, i as findGitRoot, l as pathInside, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
-
import { u as ACC_VERSION } from "./rules-
|
|
3
|
-
import { i as readDshVersion, t as hostContractReport, v as checkRegistryQuality } from "./contract-
|
|
4
|
-
import {
|
|
1
|
+
import { d as readCccName, i as findGitRoot, l as pathInside, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
|
|
2
|
+
import { u as ACC_VERSION } from "./rules-DKlpuFN_.js";
|
|
3
|
+
import { i as readDshVersion, t as hostContractReport, v as checkRegistryQuality } from "./contract-DJSTR5Up.js";
|
|
4
|
+
import { _ as listWakes, i as wakeSchedulerState } from "./scheduler-CB06u0Qc.js";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
7
7
|
//#region src/surface/observation.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
|
|
2
|
-
import {
|
|
2
|
+
import { _ as resolveInside, g as readUtf8 } from "./ccc-x94FCzOv.js";
|
|
3
3
|
import { N as findTrajectoryFile, T as sessionsRoot, b as parseTrajectoryCode, i as listBindDeclarations, j as TRAJECTORY_FILENAME } from "./bind-file-B0TuOP89.js";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { readdirSync } from "node:fs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
|
|
2
|
-
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
3
|
-
import { c as unsetEntry, l as writeEntry, r as getEntry } from "./service-
|
|
2
|
+
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
|
|
3
|
+
import { c as unsetEntry, l as writeEntry, r as getEntry } from "./service-Dv-8E2bI.js";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as resolveInside, g as readUtf8 } from "./ccc-x94FCzOv.js";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
|
|
2
|
-
import { c as hostAgents, o as listCccs, u as hostService } from "./ccc-roots-
|
|
3
|
-
import {
|
|
2
|
+
import { c as hostAgents, o as listCccs, u as hostService } from "./ccc-roots-CsSSQjSS.js";
|
|
3
|
+
import { _ as resolveInside } from "./ccc-x94FCzOv.js";
|
|
4
4
|
import { F as isoLocal, L as localHuman, P as readTrajectoryFile, R as localIdStamp, T as sessionsRoot, b as parseTrajectoryCode, j as TRAJECTORY_FILENAME, k as trajectoryDescOf, p as findSession, v as namingTitleFor } from "./bind-file-B0TuOP89.js";
|
|
5
|
-
import {
|
|
5
|
+
import { i as carrierState, n as PLUGIN_SOURCE, o as listBoundSessionIds, r as appendBound } from "./message-source-DLei3MqR.js";
|
|
6
6
|
import { i as sessionsRootDir, n as findSessionLog, r as hasSessionLogById } from "./cleanup-CPl1SSaK.js";
|
|
7
|
-
import { n as readSimpleSettings } from "./settings-
|
|
7
|
+
import { n as readSimpleSettings } from "./settings-DibHCfEU.js";
|
|
8
8
|
import { basename, dirname, join } from "node:path";
|
|
9
9
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
@@ -3722,4 +3722,4 @@ function registerWakeScheduler(ctx) {
|
|
|
3722
3722
|
registerDisposer(ctx, "认知轨迹唤醒调度器", () => clock.dispose());
|
|
3723
3723
|
}
|
|
3724
3724
|
//#endregion
|
|
3725
|
-
export { DEFAULT_LOOKBACK_MS as a, scanInterruptedSessions as c, registerDisposer as d, buildCroSnapshot as f,
|
|
3725
|
+
export { listWakes as _, DEFAULT_LOOKBACK_MS as a, scanInterruptedSessions as c, registerDisposer as d, buildCroSnapshot as f, addWake as g, WAKE_CATCH_UP_MS as h, wakeSchedulerState as i, listRunningSessionIds as l, CARRIER_OPENING_MARK as m, scheduler_exports as n, createResumeOne as o, listCroCognitiveTrajectories as p, sendToCognitiveTrajectory as r, projectDirNameForCwd as s, registerWakeScheduler as t, registerCroTurnTracking as u };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { d as hostSessions, r as cccRootForCwd } from "./ccc-roots-
|
|
2
|
-
import { m as readTrajectorySubscriptions } from "./ccc-
|
|
1
|
+
import { d as hostSessions, r as cccRootForCwd } from "./ccc-roots-CsSSQjSS.js";
|
|
2
|
+
import { m as readTrajectorySubscriptions } from "./ccc-x94FCzOv.js";
|
|
3
3
|
import { F as isoLocal, w as sessionEvents } from "./bind-file-B0TuOP89.js";
|
|
4
|
-
import {
|
|
5
|
-
import { n as readSimpleSettings } from "./settings-
|
|
6
|
-
import { i as subscriptionStats, r as subscriptionEval } from "./subscriptions-
|
|
7
|
-
import { containerClocks, containerWakes } from "./observation-
|
|
4
|
+
import { h as WAKE_CATCH_UP_MS, p as listCroCognitiveTrajectories } from "./scheduler-CB06u0Qc.js";
|
|
5
|
+
import { n as readSimpleSettings } from "./settings-DibHCfEU.js";
|
|
6
|
+
import { i as subscriptionStats, r as subscriptionEval } from "./subscriptions-D05XffxT.js";
|
|
7
|
+
import { containerClocks, containerWakes } from "./observation-BKsS1Qke.js";
|
|
8
8
|
//#region src/trajectory/live.ts
|
|
9
9
|
/**
|
|
10
10
|
* trajectory/live.ts — 进程内 live 会话取数 + 精简诊断
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
1
|
+
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
//#region src/tools/localstore/service.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { c as hostAgents, n as agentCwdFor, r as cccRootForCwd, s as hostAgentDefaultModel } from "./ccc-roots-
|
|
2
|
-
import { f as readCognitiveTrajectorySkills,
|
|
1
|
+
import { c as hostAgents, n as agentCwdFor, r as cccRootForCwd, s as hostAgentDefaultModel } from "./ccc-roots-CsSSQjSS.js";
|
|
2
|
+
import { f as readCognitiveTrajectorySkills, g as readUtf8, o as isSafeModeOn, u as readBlacklist } from "./ccc-x94FCzOv.js";
|
|
3
3
|
import { F as isoLocal, T as sessionsRoot, s as DEFAULT_SESSION_SCOPE, x as readActiveSessionMd } from "./bind-file-B0TuOP89.js";
|
|
4
|
-
import { a as readGitTrack, i as localstorePath } from "./service-
|
|
5
|
-
import { a as createProfilePromptReader, n as buildTrajectoryBasePrompt, o as profileMsmWhitelist, r as constraintCognitiveTrajectory, u as ACC_VERSION } from "./rules-
|
|
6
|
-
import { d as registerTrajectorySession$1, f as trajectoryDirNameForSession, o as readTrajectoryConstraintAt, p as unregisterTrajectorySession$1, u as trajectoryConstraintForSession } from "./profile-
|
|
7
|
-
import { n as PLUGIN_SOURCE,
|
|
4
|
+
import { a as readGitTrack, i as localstorePath } from "./service-Dv-8E2bI.js";
|
|
5
|
+
import { a as createProfilePromptReader, n as buildTrajectoryBasePrompt, o as profileMsmWhitelist, r as constraintCognitiveTrajectory, u as ACC_VERSION } from "./rules-DKlpuFN_.js";
|
|
6
|
+
import { d as registerTrajectorySession$1, f as trajectoryDirNameForSession, o as readTrajectoryConstraintAt, p as unregisterTrajectorySession$1, u as trajectoryConstraintForSession } from "./profile-Djqz4Noy.js";
|
|
7
|
+
import { n as PLUGIN_SOURCE, s as readLastBound } from "./message-source-DLei3MqR.js";
|
|
8
8
|
import { basename, dirname, join, resolve } from "node:path";
|
|
9
9
|
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { r as cccRootForCwd } from "./ccc-roots-
|
|
2
|
-
import { m as readTrajectorySubscriptions } from "./ccc-
|
|
1
|
+
import { r as cccRootForCwd } from "./ccc-roots-CsSSQjSS.js";
|
|
2
|
+
import { m as readTrajectorySubscriptions } from "./ccc-x94FCzOv.js";
|
|
3
3
|
import { L as localHuman, n as findTrajectoryHolding } from "./bind-file-B0TuOP89.js";
|
|
4
|
-
import { d as registerDisposer } from "./scheduler-
|
|
4
|
+
import { d as registerDisposer } from "./scheduler-CB06u0Qc.js";
|
|
5
5
|
//#region src/trajectory/subscriptions.ts
|
|
6
6
|
/**
|
|
7
7
|
* trajectory/subscriptions.ts — 轨迹消息订阅(v1.107.0,owner 2026-11-01 令)
|
|
@@ -306,7 +306,7 @@ function defaultSubscriptionDeps(ctx) {
|
|
|
306
306
|
readSubs: (root) => readTrajectorySubscriptions(root),
|
|
307
307
|
dirForSession: (root, sessionId) => findTrajectoryHolding(`${root}/AGENT_SESSIONS`, sessionId),
|
|
308
308
|
deliver: async (root, target, message) => {
|
|
309
|
-
const { sendToCognitiveTrajectory } = await import("./scheduler-
|
|
309
|
+
const { sendToCognitiveTrajectory } = await import("./scheduler-CB06u0Qc.js").then((n) => n.n);
|
|
310
310
|
const res = await sendToCognitiveTrajectory(ctx, root, target, message, "trajectory-subscription");
|
|
311
311
|
return {
|
|
312
312
|
ok: res.ok,
|
|
@@ -25,7 +25,7 @@ export declare const MSM_GUIDE = "MSM Development Manual (Mech & Semi-Mech frame
|
|
|
25
25
|
* 🔴 **2026-10-19 起导出**:`container_admin config docs config` 的两层菜单把它作为
|
|
26
26
|
* 「config」章正文返回。**不另写一份**——同一段说明有第二份副本就会漂(本轨反复踩过)。
|
|
27
27
|
*/
|
|
28
|
-
export declare const CCC_CONFIG_REFERENCE = "\u2550\u2550\u2550 CCC Configuration Reference \u2550\u2550\u2550\n\nCCC-level features are configured in .opencode/serenity.json.\nBelow are all available configuration sections.\n\n\u2500\u2500 1. sessionKeeper.threshold / sessionKeeper.sessionMdMaxKB \u2500\u2500\ncognitive-trajectory-assistant\uFF08COGNITIVE-TRAJECTORY-ASSISTANT \u00B7 CHECKPOINT\uFF09\u63D0\u9192\u673A\u5236\u7684\u79EF\u5206\u9608\u503C\uFF08\u975E headless \u4E3B agent\uFF09\u3002\n\u6309\u5DE5\u5177\u8C03\u7528\u52A0\u6743 + \u8017\u65F6\u8BA1\u5206\uFF1B\u8FBE\u5230\u9608\u503C\u6CE8\u5165\u63D0\u9192\uFF0C\u8981\u6C42\u6A21\u578B\u56DE\u590D ACK \u7801\u3002\n\n Config:\n { \"sessionKeeper\": { \"threshold\": 150, \"sessionMdMaxKB\": 100 } }\n\n \u8BA1\u5206\uFF1Awrite/edit = 3\uFF0Ctask = 10\uFF0Cread/grep/glob/msm \u7B49 = 1\uFF0C\u65F6\u95F4 = 1/\u5206\u949F\n \u9ED8\u8BA4\uFF1A150\n\n \u25B8 sessionMdMaxKB\uFF08v1.31.1 \u65B0\u589E\uFF1Bv1.31.2 100\u2192200\uFF1B\uD83D\uDD34 v1.84.0 200\u2192100\uFF09\uFF1A\n **\u6D3B\u8DC3 SESSION.md \u4F53\u79EF\u4E0A\u9650\uFF08KB\uFF0C\u7F3A\u7701 100\uFF1B0 = \u5173\u95ED\uFF09**\u3002\n \u8D85\u8FC7 \u2192 \u6CE8\u5165 [COGNITIVE-TRAJECTORY-ASSISTANT \u00B7 LOGBOOK COMPACTION] \u63D0\u9192\uFF1A\u6682\u505C\u5F53\u524D\u5DE5\u4F5C \u2192\n \u52A0\u8F7D eap\uFF08praxis eap\uFF09\u2192 **\u6309 EAP \u91CD\u6784\u6574\u4E2A SESSION.md**\uFF08v1.84.0 \u8D77\uFF1B\u65E7\u7248\u662F\u56DB\u6761\u91CD\u5199\u539F\u5219\uFF09\u3002\n \u5224\u636E = EAP \u4E09\u4E2A\u53D8\u91CF\uFF1AE\u2191 \u53D8\u91CF/\u8FB9\u754C/\u6709\u5411\u5173\u7CFB\u5168\u663E\u5F0F \uFF5C R\u2193 \u5E26\u7406\u7531\u4E0E\u88AB\u5426\u9879\uFF0C\u957F\u8BC1\u636E\u94FE\u5916\u79FB\u5230\n references/*.md\uFF08SESSION.md \u7559\u94FE\u63A5\uFF09\uFF5C S\u2191 \u7ED3\u6784\u6BCF\u6B21\u540C\u6837\u91CD\u5EFA\uFF0C\u4E0D\u662F\u65F6\u95F4\u6D41\u6C34\u8D26\u3002\n \u4E24\u6761\u786C\u7EA6\u675F\u968F\u63D0\u9192\u7ED9\u51FA\uFF1A\u4FDD\u7559\u56FA\u5B9A\u6BB5\u843D\u7684**\u987A\u5E8F\u4E0E\u6807\u9898**\uFF08\u4E0E\u7CFB\u7EDF\u63D0\u793A\u8BCD\u5BF9\u9F50\uFF09+ \u4FDD\u7559\u9876\u90E8 frontmatter\u3002\n \u8D85\u9650\u540E\u6BCF\u6B21\u5DE5\u5177\u8C03\u7528\u90FD\u63D0\u9192\uFF1B\u8FDE\u7EED 3 \u6B21\u672A\u91CD\u5199 \u2192 \u5347\u7EA7\u5F3A\u5236\u8BED\u6C14\uFF1B\n \u6587\u4EF6\u56DE\u5230\u9650\u5185 \u2192 \u81EA\u52A8\u505C\u6B62\uFF08\u81EA\u6108\uFF09\u3002\u8F68\u8FF9\u63D0\u9192**\u4E0D\u673A\u68B0\u963B\u65AD\u5DE5\u4F5C**\uFF08\u4E0E rebuild \u63D0\u9192\u540C\u65CF\uFF09\u3002\n\n\u2500\u2500 2. localstore.gitTrack \u2500\u2500\nlocalstore.json \u7684 git \u7B56\u7565\uFF1Aallow \u53EF\u63D0\u4EA4 / deny \u7981\u63D0\u4EA4\uFF08\u9ED8\u8BA4 deny\uFF09\u3002\ndeny \u65F6\u5199\u5165\u81EA\u52A8\u786E\u4FDD .gitignore \u542B\u8BE5\u6587\u4EF6\uFF08\u7269\u7406\u4FDD\u8BC1\uFF09\uFF0Ccontainer_git commit \u4F1A\u68C0\u67E5\u62D2\u7EDD\u3002\n\n Config:\n { \"localstore\": { \"gitTrack\": \"allow\" } }\n\n\u2500\u2500 3. hooks.autoRestoreSession \u2500\u2500\n\u4F1A\u8BDD\u81EA\u52A8\u6062\u590D\uFF08\u9ED8\u8BA4 true\uFF1B\u53D7 events \u95E8\u63A7\u2014\u2014\u4EC5\u6839\u4F1A\u8BDD + \u5DF2\u6709\u5BF9\u8BDD\u5386\u53F2\u624D\u6062\u590D\uFF09\u3002\n\n Config:\n { \"hooks\": { \"autoRestoreSession\": false } }\n\n\u2500\u2500 4. safeMode\uFF08\u9ED1\u540D\u5355 \uFF0B \u5F00\u542F\u671F\u95F4\u9690\u85CF\u7684\u5DE5\u5177\uFF09\u2500\u2500\nsafe-mode \u7531 WebUI \u5F00\u5173\u63A7\u5236\uFF08\u5199 .serenity-safe-on \u6807\u8BB0\uFF09\uFF1B\u9ED1\u540D\u5355\u8DEF\u5F84\u53D7 guards seam \u62E6\u622A\u3002\n\n \u25B8 blacklist\uFF1A\u5199\u8DEF\u5F84\u9ED1\u540D\u5355\uFF08\u540C\u4E0B Config\uFF09\u3002\n \u25B8 hiddenTools\uFF08v1.51.4\uFF0Cowner 2026-10-09 \u4EE4\uFF09\uFF1A**\u4EC5\u5728\u5B89\u5168\u6A21\u5F0F\u5F00\u542F\u671F\u95F4**\u989D\u5916\u4ECE\u6A21\u578B\u5DE5\u5177\n \u6E05\u5355\u9690\u85CF\u7684\u5DE5\u5177\uFF08ACC \u57FA\u7840\u8868\u6052\u9690 bash\uFF09\u3002\u6253\u5F00 safe-mode \u751F\u6548\u3001**\u5173\u6389\u5373\u5168\u90E8\u6062\u590D**\uFF1B\n \u6539\u672C\u952E**\u70ED\u751F\u6548**\uFF08\u4E0B\u4E00\u4E2A pre-step \u91CD\u7B97\uFF0C\u65E0\u9700\u91CD\u542F\uFF09\u3002\u58F0\u660E\u5728 CCC\uFF08D23\uFF09\u2014\u2014ACC \u4EE3\u7801\u91CC\n \u4E0D\u51FA\u73B0\u4EFB\u4F55\u5177\u4F53\u5DE5\u5177\u540D\u3002\n \u26A0\uFE0F \u5199\u4E00\u4E2A**\u672C CCC \u672A\u6CE8\u518C**\u7684\u540D\u5B57\u4F1A\u8BA9**\u6574\u6B21 restrict \u5931\u8D25**\uFF08DSH \u5951\u7EA6\u9010\u5B57\uFF1A\"unknown\n names \u2026 fail\"\uFF09\u21D2 \u6B64\u65F6**\u9000\u5316\u4E3A\u53EA\u9690\u85CF\u57FA\u7840\u8868**\u5E76\u5728\u65E5\u5FD7\u54CD\u4EAE\u62A5\u9519\uFF08\u7EDD\u4E0D\u56E0\u4E00\u4E2A\u9519\u540D\u4E22\u6389\n safe-mode \u7684\u6838\u5FC3\u9690\u85CF\uFF09\u3002\u8BCA\u65AD\uFF1A`AGENT_SESSIONS/.restrict-diag.json` \u7684 `denyByKey`\u3002\n \u26A0\uFE0F \u9690\u85CF = **\u4ECE schema \u79FB\u9664**\uFF08agent \u6839\u672C\u770B\u4E0D\u5230\u8BE5\u5DE5\u5177\uFF09\u21D2 \u522B\u628A skill/\u6587\u6863\u8FD8\u5728\u6559\u7684\u5DE5\u5177\u5199\u8FDB\u6765\u3002\n\n Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"], \"hiddenTools\": [\"workflow\", \"job_kill\"] } }\n\n\u2500\u2500 5. \u8F68\u8FF9\u7EA6\u675F.roles\uFF08F4 \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF09\u2500\u2500\n\u8F68\u8FF9\u7EA6\u675F\u89D2\u8272 = \u5168\u77E5\u5168\u80FD cognitive trajectory \u7684\u4EFB\u610F\u5B50\u96C6\uFF08CCC \u5B9A\u4E49\uFF09\u3002\u6BCF\u89D2\u8272\u72EC\u7ACB\u6A21\u578B + \u53CC\u767D\u540D\u5355\n\uFF08MSM \u767D\u540D\u5355 msms[] \u4E0E\u975E MSM \u5DE5\u5177\u767D\u540D\u5355 tools[]\uFF0C\u767D\u540D\u5355\u5916\u5168\u9690\u85CF\uFF1Bskill \u52A0\u8F7D\u6052\u53EF\u7528\uFF09\u3002\ncognitive trajectory \u7EAA\u5F8B\u5B50\u96C6\uFF08session/keeper/rebuild \u53C2\u4E0E\u9879\uFF09\u9ED8\u8BA4\u5168\u5173 = \u5B8C\u5168\u72EC\u7ACB\u3002\nsystemPrompt \u5185\u8054 \u6216 systemPromptFile\uFF08.md \u5F15\u7528\uFF0C\u63A8\u8350\uFF09\u2014\u2014\u89D2\u8272\u4F1A\u8BDD\u7684\u5B8C\u6574\u4EBA\u683C/\u8FB9\u754C\u63D0\u793A\u8BCD\u3002\nvalidate \u6821\u9A8C / apply \u751F\u6548 / list \u67E5\u770B\uFF1Acontainer_admin config <guide|validate|apply|list>\uFF08\u539F role \u57DF\u5DF2\u5E76\u5165 config\uFF0C\u65E7\u540D\u4F5C\u8FC7\u6E21\u522B\u540D\u4ECD\u53EF\u7528\uFF09\u3002\n\n Config:\n { \"\u8F68\u8FF9\u7EA6\u675F\": { \"roles\": {\n \"qa\": {\n \"model\": \"provider/model\",\n \"msms\": [\"web-search\", \"vlm-describe\"],\n \"tools\": [\"read\", \"grep\", \"glob\"],\n \"systemPromptFile\": \".opencode/skiff/qa.md\"\n } } } }\n\n\u2500\u2500 6. cognitive_trajectory\uFF08\u8F68\u8FF9 + \u5524\u9192\u6CE8\u518C\u8868\uFF0CD58\uFF09\u2500\u2500\nCCC \u7684\u4E00\u6761 cognitive trajectory = \u6301\u4E45\u8EAB\u4F53\uFF08SESSION.md\uFF09+ \u65F6\u95F4\u8F74\uFF08AGENT_SESSIONS/ \u76EE\u5F55 + \u5524\u9192\u6CE8\u518C\u8868\uFF09\u3002\n\u4E00\u4E2A CCC \u5185\u53EF\u591A\u6761\u5E76\u884C\uFF1B\u6761\u76EE\u5168\u90E8\u7531\u4EBA\u7C7B/agent \u663E\u5F0F\u767B\u8BB0\u3002\n\u26A0\uFE0F ACC \u4E0D\u518D\u63D0\u4F9B**\u5468\u671F\u81EA\u5524\u9192**\uFF08\u539F autopilot\uFF0C2026-09-15 \u6574\u6BB5\u9000\u573A\uFF1A\u673A\u5236/\u65F6\u949F/container_admin autopilot\n\u57DF/\u9762\u677F\u533A\u5757\u4E00\u5E76\u5220\u9664\uFF09\u3002\u81EA\u4E3B\u5DE1\u822A\u6539\u7531 CCC \u81EA\u5DF1\u9A71\u52A8\uFF08home-serenity \u4FA7 msm autopilot-round \u6BCF\u8F6E\u81EA\u6392\u4E0B\u4E00\u8F6E\uFF0C\n\u771F\u76F8\u6E90 = S151 SESSION.md \u00A71/\u00A71b/\u00A71c\uFF09\u3002**\u672C\u8F6E\u7684 cognitiveTrajectory.autopilot \u914D\u7F6E\u6BB5\u4FDD\u7559**\uFF08\u89C1\u4E0B\u8868\uFF09\u2014\u2014\n\u5B83\u73B0\u7531 **CCC \u4FA7\u811A\u672C**\u8BFB\u53D6\uFF0CACC \u4E0D\u518D\u6D88\u8D39\u8BE5\u6BB5\u3002\n\u5524\u9192\u6CE8\u518C\u8868\uFF08cognitive_trajectory send-later\uFF09\uFF1A\u5524\u9192 = \u672A\u6765\u65F6\u523B + \u4E00\u6761 message\uFF0C\u53EF\u5524\u9192\u81EA\u5DF1\u6216\u522B\u7684 cognitive trajectory\uFF1B\n\u843D\u70B9 AGENT_SESSIONS/wake-registry.json\uFF0C\u4E2D\u5FC3\u8C03\u5EA6\u5668\u5230\u70B9\u6295\u9012\uFF08fire-and-forget\uFF1B\u65E0\u56DE\u6267\u3001\u65E0\u56DE\u6536\uFF09\u3002\n\u8FD0\u884C\u6001\u8BCA\u65AD\uFF08ACC \u8D1F\u8D23\u4EBA\u4E13\u7528\uFF09\uFF1Aacc-diag\uFF08\u2460 live \u8FD0\u884C\u6001 / \u2460b \u5524\u9192\u65F6\u949F / \u2462 \u5524\u9192\u6CE8\u518C\u8868\uFF09\u3002\n\n\u25B8 cognitiveTrajectory.skills\uFF08v1.44.0\uFF0C**CCC \u7EA7 skill \u4F9B\u7ED9**\uFF1Bowner \u4EE4 2026-09-20\u300C\u8981\u6C42\u5B9E\u73B0\u6211\u7684\u8BC9\u6C42\u2026\u2026\n \u6CE8\u5165\u662F\u52A8\u6001\u6CE8\u5165\u5C31\u884C\uFF1Bcreate \u5148\u4E0D\u89E6\u53D1\u5427\uFF1B**\u8F68\u8FF9\u7EA6\u675F\u4E5F\u652F\u6301**\u300D\uFF09\uFF1A\n **\u672C CCC \u7684\u6BCF\u6761\u8F68\u8FF9\u90FD\u5E26\u8FD9\u4E9B skill**\u2014\u2014\u58F0\u660E\u7684 skill \u5168\u6587\u5728\u8BE5\u8F68\u8FF9**\u88AB\u7ED1\u5B9A\u671F\u95F4**\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\n \uFF08**\u52A8\u6001\u6CE8\u5165**\uFF1A\u6BCF\u8BF7\u6C42\u91CD\u65B0\u6C42\u503C\u3001\u4E0D\u7F13\u5B58 \u21D2 \u6539\u914D\u7F6E\u6216\u6539 frontmatter **\u5373\u65F6\u751F\u6548**\uFF0C\u65E0\u9700\u91CD\u65B0 use\u3001\u65E0\u9700\u91CD\u542F\uFF09\u3002\n \u4E24\u6761\u58F0\u660E\u6765\u6E90**\u5E76\u96C6**\uFF08\u4E0D\u662F\u4E8C\u9009\u4E00\uFF09\uFF1A\u672C\u952E **cognitiveTrajectory.skills**\uFF08\u5BB9\u5668\u7ED9\u7684\u5E95\u5EA7\uFF0C**\u5728\u524D**\uFF09\n \uFF0B \u8F68\u8FF9\u81EA\u5DF1 SESSION.md \u9876\u90E8 frontmatter \u7684 skills:\uFF08\u8FD9\u6761\u8F68\u8FF9\u989D\u5916\u7684\uFF0C**\u8FFD\u52A0**\uFF1B\u540C\u540D\u53BB\u91CD\uFF09\u3002\n \u2705 **\u542B \u8F68\u8FF9\u7EA6\u675F\u89D2\u8272\u4F1A\u8BDD**\uFF08\u65C1\u8DEF\u7684\u662F ACC \u8EAB\u4EFD\u4E0E\u7EAA\u5F8B\uFF1Bskill \u662F\"\u5DE5\u4F5C\u8D44\u6599\"\uFF0C\u7167\u7ED9\uFF09\u3002\n \u5B88\u536B\uFF1A\u540D\u5B57\u5148\u8FC7\u5B89\u5168\u6821\u9A8C\u624D\u7528\u4E8E\u62FC\u8DEF\u5F84\uFF08\u9632\u7A7F\u8D8A\uFF09\uFF5C**\u5408\u5E76\u540E**\u7EDF\u4E00 32 KB \u4E0A\u9650\u622A\u65AD\uFF5C\u627E\u4E0D\u5230\u54CD\u4EAE\u5199 [\u7F3A\u5931]\u3002\n \u26A0\uFE0F \u4E0E create **\u65E0\u5173**\uFF08create \u523B\u610F\u4E0D\u593A\u7ED1\u5B9A\uFF0CU6\uFF09\u21D2 \u6CE8\u5165\u7684\u95E8\u662F\"**\u5B58\u5728\u7ED1\u5B9A**\"\uFF08cognitive_trajectory use\uFF09\u3002\n \u5931\u8D25\u8BED\u4E49\uFF1A\u672A\u914D\u7F6E / \u5B57\u6BB5\u7F3A\u5931 / \u914D\u7F6E\u635F\u574F \u21D2 **\u7A7A\u6570\u7EC4**\uFF08= \u65E0 CCC \u7EA7\u4F9B\u7ED9\uFF1B\u8F68\u8FF9\u7EA7\u58F0\u660E\u7167\u65E7\u751F\u6548\uFF09\u3002\n \u5F52\u5C5E\uFF08D23\uFF09\uFF1A**\u673A\u5236\u5728 ACC**\uFF08\u8BFB\u53D6 + \u5408\u5E76 + \u6CE8\u5165 + \u5B88\u536B\uFF09\uFF0C**\u58F0\u660E\u5728 CCC**\uFF08\u672C\u952E\uFF09\u3002\n\n Config:\n { \"cognitiveTrajectory\": {\n \"skills\": [\"acc-session\", \"cce\"], // v1.44.0\uFF1A\u672C CCC \u7684\u6BCF\u6761\u8F68\u8FF9\u90FD\u5E26\uFF08\u542B \u8F68\u8FF9\u7EA6\u675F\u4F1A\u8BDD\uFF09\n \"autopilot\": { // \u539F autopilotTrajectory \u6BB5\uFF0C\u5B57\u6BB5\u4E0D\u53D8\uFF08\u65E7\u952E\u4ECD\u56DE\u9000\u8BFB\uFF09\n \"enabled\": true,\n \"intervalHours\": 2,\n \"session\": \"S151\",\n \"biasProvider\": \"autopilot-bias.ts\",\n \"topPrompt\": \"\u672C\u8F68\u8FF9\u6838\u5FC3\u76EE\u6807/\u7EAA\u5F8B/\u8D28\u91CF\u8981\u6C42\uFF08CCC \u81EA\u586B\uFF0C\u9632\u6F02\u79FB\uFF09\",\n \"avoidWakeHours\": { \"start\": 8, \"end\": 18 }\n } } }\n\n\u2500\u2500 7. weixin\uFF08\u5FAE\u4FE1\u6865 F4c-3\uFF0C\u542B\u6D88\u606F\u8BB0\u5F55 hook\uFF09\u2500\u2500\nCCC \u7EA7\u5FAE\u4FE1\u4E2A\u4EBA\u53F7\u63A5\u5165\uFF08iLink \u534F\u8BAE\uFF09\uFF1Adsh \u4E00\u8FDB\u7A0B\u591A CCC\uFF0C\u6BCF CCC \u72EC\u7ACB\u5BF9\u63A5\u5FAE\u4FE1\u6865\u3002\n\u8D26\u53F7/\u8DEF\u7531/\u5F00\u5173\u5728\u6B64\u6587\u4EF6\uFF1B**bot_token \u51ED\u636E\u5728 CCC localstore credential scope**\n\uFF08\u626B\u7801\u7ED1\u5B9A\u540E\u81EA\u52A8\u5199\u5165\uFF0C\u6C38\u4E0D\u8FDB git \u660E\u6587\u9762\uFF09\u3002\n\u8DEF\u7531 user \u2192 role\uFF1Aexact \u4F18\u5148\uFF0C* \u901A\u914D\u515C\u5E95\uFF1Brole \u5FC5\u987B \u2208 \u8BE5 CCC \u8F68\u8FF9\u7EA6\u675F.roles\u3002\n\u9762\u677F\uFF08WebUI \u8BBE\u7F6E \u2192 \u5FAE\u4FE1\u6865\uFF09\u53EF\u626B\u7801\u7ED1\u5B9A/\u79FB\u9664\u8D26\u53F7/\u7F16\u8F91\u8DEF\u7531\uFF1Bmsm(\"weixin-doctor\", [\"status\"|\"diag\"|\"verify\"|\"guide\"]) \u6392\u67E5 + \u5927\u800C\u5168\u6307\u5357\u3002\u51ED\u636E\u4E3B\u52A8\u67E5\u770B\uFF1Alocalstore get WEIXIN_<ACCOUNT>_TOKEN\u3002\n \u25B8 \u4E3B\u52A8\u53D1\u9001\uFF08v1.31.0\uFF1A**ACC \u5DE5\u5177 im-bridge**\uFF09\uFF1A\u8C03\n im-bridge({channel:\"weixin\", action:\"send\", user:\"<\u522B\u540D|id>\", text:\"<\u6587\u672C>\"}) \u7ED9\u7528\u6237\u53D1\u6D88\u606F\u2014\u2014\n **\u5DE5\u5177\u53EA\u80FD\u64CD\u4F5C\u672C\u4F1A\u8BDD CCC**\uFF08\u65E0 ccc \u53C2\u6570\uFF0C\u9632\u8BEF\u53D1\u4ED6\u5BB9\u5668\uFF09\u2192 weixin-bridge.sendProactiveText \u2192\n \u6210\u529F\u540E\u89E6\u53D1 outgoing hook\uFF08source=\"proactive\"\uFF09\u3002\u6587\u4EF6\u7528 action:\"send-file\" + file:\"<CCC \u5185\u8DEF\u5F84>\"\n \uFF08\u9003\u9038/\u7F3A\u5931/\u8D85 20MB \u62D2\u7EDD\uFF09+ \u53EF\u9009 caption\u3002**\u53EF\u89C1\u6027**\uFF1A\u672C CCC \u914D\u7F6E\u4E86 IM \u901A\u9053\n \uFF08weixin.enabled=true\uFF09\u624D\u51FA\u73B0\u5728\u6A21\u578B\u5DE5\u5177\u6E05\u5355\u91CC\uFF1B\u672A\u914D\u7F6E \u2192 \u7531 guards \u9010 agent\n tools.restrict({deny:['im-bridge']}) \u79FB\u9664\uFF08\u4E0D\u662F\"\u770B\u5F97\u5230\u4F46\u88AB\u62D2\"\uFF09\u3002\u8F68\u8FF9\u7EA6\u675F\u89D2\u8272\u53E6\u9700\u5728\n roles.<role>.tools \u767D\u540D\u5355\u5217\u51FA im-bridge\u3002\n \u5386\u53F2\uFF08v1.30.9~v1.30.x\uFF09\uFF1ACCC \u4FA7 MSM weixin-send\uFF08\u8D70 loopback HTTP \u5165\u53E3 3082\uFF09\u2014\u2014\n v1.31.0 \u5DF2\u9000\u5F79\uFF08\u811A\u672C\u5220\u9664 + \u6CE8\u518C\u8868\u6CE8\u9500\uFF09\uFF1B3082 \u5165\u53E3\u4FDD\u7559\u7ED9**\u975E agent \u8C03\u7528\u8005**\u3002\n\n \u25B8 \uD83D\uDD34\uD83D\uDD34 **\u51FA\u7AD9\u53EA\u6709\u4E00\u6761\u8DEF\uFF1Aagent \u81EA\u5DF1\u8C03 im-bridge**\uFF082026-10-24 \u8D77\uFF0Cowner \u4EE4\uFF09\u3002\n \u6865**\u6C38\u4E0D**\u66FF\u4F60\u56DE\u53D1\u6700\u7EC8\u6587\u672C \u2014\u2014 \u4F60\u8BF4\u4EC0\u4E48\u3001\u8BF4\u51E0\u6761\u3001\u4EC0\u4E48\u65F6\u5019\u8BF4\u3001\u8981\u4E0D\u8981\u4E0D\u8BF4\uFF0C\u5168\u7531\u4F60\u51B3\u5B9A\u3002\n \u26D4 \u4E0D\u8981\u6307\u671B\"\u6211\u628A\u8BDD\u5199\u8FDB\u56DE\u7B54\u91CC\u5C31\u4F1A\u81EA\u52A8\u53D1\u51FA\u53BB\"\uFF1A**\u4E0D\u4F1A**\u3002\u5FC5\u987B\u771F\u7684\u8C03\u5DE5\u5177\u3002\n \u21D2 \u6BCF\u8F6E\u7528\u6237\u6D88\u606F\u672B\u5C3E\u90FD\u4F1A\u9644\u4E00\u884C**\u5DF2\u586B\u597D\u53C2\u6570**\u7684 im-bridge \u8C03\u7528\u6A21\u677F\uFF08\u673A\u5236\u6807\u8BB0\n [serenity:weixin-manual-output]\uFF09\uFF0C\u7167\u5B83\u6539 text \u5373\u53EF\u3002\n \u21D2 \u672C\u8F6E\u7ED3\u675F\u4ECD\u672A\u6210\u529F\u8C03\u7528\uFF08send / send-file\uFF09\u2192 \u4F60\u4F1A\u88AB\u6253\u56DE\uFF08\u22642 \u6B21\uFF0C\u63D0\u793A = \u6807\u8BB0 + \u4E8B\u5B9E\uFF09\uFF1B\n \u8FBE\u4E0A\u9650\u540E**\u7528\u6237\u5C06\u6536\u4E0D\u5230\u4EFB\u4F55\u6D88\u606F**\uFF08\u6CA1\u6709\u4EFB\u4F55\u515C\u5E95\u8F6C\u53D1\uFF09\u3002\n \u21D2 \u6210\u529F\u8C03\u7528\u624D\u7B97\u9001\u8FBE\uFF1B\u5931\u8D25\u8C03\u7528\u4E0D\u7B97\uFF08\u4E0D\u4F1A\u88AB\u5F53\u6210\u672C\u8F6E\u5DF2\u53D1\u9001\uFF09\u3002\n \u26A0\uFE0F **\u524D\u63D0**\uFF1A\u672C\u8F68\u8FF9\u5FC5\u987B\u5728 `.opencode/serenity.json` \u7684 `im.routes` \u91CC\u88AB\u70B9\u540D\uFF0C\u5426\u5219 `im-bridge`\n \u5BF9\u4F60\u4E0D**\u53EF\u89C1**\u2014\u2014\u90A3\u79CD\u60C5\u51B5\u4E0B\u95F8\u95E8\u4E0D\u4F1A\u6253\u56DE\u4F60\uFF08\u6539\u6210\u4E00\u6761\u54CD\u4EAE\u544A\u8B66\uFF0C\u6307\u660E\"\u8BE5\u8F68\u8FF9\u65E0\u53D1\u9001\u6743\"\uFF09\uFF0C\n \u56E0\u4E3A\u6253\u56DE\u4E00\u4E2A\u4F60\u6839\u672C\u6EE1\u8DB3\u4E0D\u4E86\u7684\u8981\u6C42\u53EA\u4F1A\u7A7A\u8F6C\u3002\n\n \u25B8 **\u5DF2\u9000\u5F79\uFF08\u4E0D\u8981\u518D\u914D\uFF0C\u914D\u4E86\u4E5F\u4E0D\u751F\u6548\uFF09**\uFF1A\n \u00B7 `weixin.autoReplyWithLastMessage`\uFF08v1.30.10~2026-10-24\uFF09= \u6865\u66FF agent \u56DE\u53D1\u6700\u7EC8\u6587\u672C\u3002\n **\u88AB\u5426\u7406\u7531**\uFF08owner \u539F\u8BDD\uFF09\uFF1A\u300CautoReply \u5E76\u4E0D\u5408\u9002\uFF0C\u4E3B\u52A8\u6027\u7684 imBridge \u4ECD\u7136\u662F\u66F4\u597D\u7684\u300D\u2014\u2014\n \u5B83\u8BA9 agent \u5931\u53BB\"\u8BF4\u51E0\u6761/\u4F55\u65F6\u8BF4/\u8981\u4E0D\u8981\u4E0D\u8BF4\"\u7684\u51B3\u5B9A\u6743\uFF0C\u800C\u90A3\u6B63\u662F\u4E3B\u52A8\u53D1\u9001\u7684\u4EF7\u503C\u3002\n \u00B7 `weixin.fallbackOnNoSend`\uFF08v1.30.17~2026-10-24\uFF09= \u624B\u52A8\u6A21\u5F0F\u4E0B agent \u6CA1\u53D1\u6210\u529F\u65F6\u6865\u515C\u5E95\u8F6C\u53D1\u3002\n \u5B83\u53EA\u5728\u4E0A\u9762\u90A3\u4E2A\u5F00\u5173 =false \u65F6\u624D\u6709\u610F\u4E49 \u21D2 \u4E00\u8D77\u9000\u5F79\u3002\n \u26A0\uFE0F \u65E7\u914D\u7F6E\u91CC\u6B8B\u7559\u8FD9\u4E24\u4E2A\u952E**\u9759\u9ED8\u5FFD\u7565**\uFF08\u4E0D\u4F1A\u62A5\u9519\uFF0C\u4E5F\u4E0D\u4F1A\u751F\u6548\uFF09\u3002\n\n Config:\n { \"weixin\": {\n \"enabled\": true,\n \"hook\": \"scripts/weixin-message-hook.ts\",\n \"accounts\": [{ \"accountId\": \"wechat-1\", \"name\": \"\u5BB6\u5EAD\u52A9\u624B\", \"enabled\": true }],\n \"routes\": [{ \"user\": \"*\", \"role\": \"my-role\" }]\n } }\n\n \u25B8 weixin.hook\uFF08\u6D88\u606F\u8BB0\u5F55 hook\uFF0Cv1.27.13\uFF09\uFF1A\n \u5FAE\u4FE1\u6865\u6BCF\u6536/\u53D1\u4E00\u6761\u6D88\u606F\u89E6\u53D1\u4E00\u6B21 CCC \u81EA\u5199\u811A\u672C\uFF0C\u7531 CCC \u81EA\u884C\u6301\u4E45\u5316\u4FDD\u5B58\uFF08\u5B58\u54EA/\u5B58\u6210\u4EC0\u4E48\n /\u662F\u5426\u5165\u5E93\u5168\u5F52 CCC\u2014\u2014ACC \u4E0D\u7ED1\u5B9A\u5B58\u50A8\uFF09\u3002\u672A\u914D\u7F6E hook \u2192 \u96F6\u53D8\u5316\uFF08\u4E0D\u89E6\u53D1\uFF09\u3002\n\n \u89E6\u53D1\uFF08\u53CC\u5411\uFF09\uFF1A\n incoming = \u7528\u6237 \u2192 bot\uFF1A\u8DEF\u7531\u547D\u4E2D\u540E\u3001\u5A92\u4F53\u843D\u76D8\u540E\u89E6\u53D1\uFF08\u6587\u672C\u542B\u8BED\u97F3\u8F6C\u5199\uFF1B\u5A92\u4F53\u5E26\u843D\u76D8 relPath\uFF09\n outgoing = bot \u2192 \u7528\u6237\uFF1A\u53D1\u9001\u6210\u529F\u540E\u89E6\u53D1\uFF08reply = \u7528\u6237\u5B9E\u9645\u6536\u5230\u7684\u7EAF\u6587\u672C\uFF0C\u5DF2\u5265\u79BB think\uFF09\n \u00B7 source=\"proactive\" = agent \u7ECF im-bridge \u4E3B\u52A8\u53D1\u8D77\uFF08**2026-10-24 \u8D77\u8FD9\u662F\u552F\u4E00\u503C**\uFF09\n \u00B7 \u5386\u53F2\u503C reply / reply-fallback\uFF08\u6865\u81EA\u52A8\u56DE\u53D1 / \u6865\u515C\u5E95\u8F6C\u53D1\uFF09\u5DF2\u968F\u4E24\u4E2A\u5F00\u5173\u4E00\u8D77\u9000\u5F79\uFF1B\n \u5B58\u91CF\u65E5\u5FD7\u91CC\u4ECD\u80FD\u770B\u5230\u5B83\u4EEC\uFF0C\u90A3\u662F\u5386\u53F2\u4E8B\u5B9E\uFF0C\u4E0D\u662F\u5F53\u524D\u884C\u4E3A\n \u00B7 file\uFF08\u4EC5 send-file \u8865\u8BB0\uFF09\uFF1A{ \"name\": \"\u62A5\u544A.pdf\", \"size\": 12345, \"caption\": \"\u53EF\u9009\" }\n\n \u811A\u672C\u7EA6\u5B9A\uFF08\u4E8B\u4EF6 JSON \u5355\u884C\u7ECF stdin \u4F20\u5165\uFF1Bbun \u4F18\u5148 node \u515C\u5E95\uFF09\uFF1A\n #!/usr/bin/env bun \u6216 node script.js \u2014\u2014 \u8BFB process.stdin \u6574\u6BB5 JSON.parse\n const ev = JSON.parse(await new Response(process.stdin).text())\n ev.event === 'incoming' | 'outgoing'\n\n \u4E8B\u4EF6 schema\uFF08**\u4E0D\u542B\u4EFB\u4F55\u4F1A\u8BDD\u51ED\u636E**\u2014\u2014context_token/bot_token/token/aes_key \u5747\u4E0D\u51FA\u73B0\uFF09\uFF1A\n { \"event\": \"incoming\", \"ts\": 1788359941488, \"cccRoot\": \"/path/ccc\",\n \"accountId\": \"wechat-1\", \"userId\": \"u1@im.wechat\",\n \"sessionId\": \"skiff-weixin-xxx\", \"role\": \"my-role\",\n \"message\": { \"text\": \"\u4F60\u597D\", \"media\": [{ \"kind\": \"image\", \"relPath\": \"_tmp/weixin-inbound/<hash>/img_x.jpg\" }] } }\n { \"event\": \"outgoing\", \"ts\": ..., \"cccRoot\": ..., \"accountId\": ...,\n \"userId\": ..., \"sessionId\": ..., \"role\": ...,\n \"reply\": \"\u5DF2\u8BB0\u5F55\uFF08\u7EAF\u6587\u672C\uFF09\", \"source\": \"proactive\" }\n\n \u793A\u4F8B\u811A\u672C\uFF08\u8FFD\u52A0\u5230\u6309\u65E5\u6587\u4EF6\u2014\u2014\u6301\u4E45\u5316\u5F52 CCC \u81EA\u9009\uFF1A\u6587\u4EF6/DB/\u8FDC\u7AEF\u5747\u53EF\uFF09\uFF1A\n const fs = require('node:fs'); const p = '/path/ccc/AGENT_SESSIONS/_weixin-log.jsonl';\n fs.appendFileSync(p, JSON.stringify(ev) + '\\n');\n\n \u6267\u884C\u8BED\u4E49\uFF08\u65C1\u8DEF\u5BB9\u5FCD\uFF09\uFF1A\u5F02\u6B65 fire-and-forget + 15s \u8D85\u65F6 kill + \u5931\u8D25\u4EC5\u65E5\u5FD7\u2014\u2014\n \u5FAE\u4FE1\u6865\u6D88\u606F\u5904\u7406/\u56DE\u590D\u4E0D\u53D7 hook \u5F71\u54CD\uFF1B\u811A\u672C\u987B\u5728 CCC \u6839\u5185\uFF08\u8DEF\u5F84\u9003\u9038\u62D2\u7EDD\uFF09\u3002\n \u5A92\u4F53 relPath \u6307\u5411 _tmp/weixin-inbound/\uFF08\u4E34\u65F6\u76EE\u5F55\uFF09\u2014\u2014\u9700\u6301\u4E45\u4FDD\u5B58\u5A92\u4F53\u6587\u4EF6\u8BF7\u81EA\u884C copy\u3002\n\n\u2500\u2500 8. \uFF08\u5DF2\u5220\u9664\uFF09exclusiveTools\uFF08\u4E13\u5C5E\u5DE5\u5177\u58F0\u660E\uFF0Cv1.33 \u2192 v1.106.0 \u5220\u9664\uFF09\u2500\u2500\n\uD83D\uDD34 **\u672C\u952E\u5DF2\u4E0D\u5B58\u5728**\uFF08owner 2026-11-01 \u88C1\u300C\u673A\u5236\u6574\u4F53\u5220\u300D\uFF09\u3002\n\u539F\u8BED\u4E49\uFF1AACC \u6CE8\u518C\u4F46\u9ED8\u8BA4\u5BF9\u6240\u6709 CCC \u9690\u85CF\u7684\u5DE5\u5177\uFF0C\u53EA\u6709\u5728 CCC \u914D\u7F6E\u91CC\u70B9\u540D\u624D\u53EF\u89C1\uFF08fail-closed\uFF09\u3002\n**\u5220\u9664\u7406\u7531**\uFF1A\u90A3\u5F20\u8868\u6C38\u8FDC\u53EA\u6709\u4E00\u4E2A\u6210\u5458 acc-diag\uFF0C\u800C\u5B83\u5DF2\u5E76\u5165**\u5E38\u9A7B**\u7684 dashboard \u7684 diag \u52A8\u4F5C\u3002\n\u8868\u7A7A\u4E4B\u540E\u90A3\u4E2A filter \u6052\u56DE\u7A7A\u6570\u7EC4 \u21D2 \u673A\u5236**\u9759\u9ED8\u7A7A\u8F6C\u3001\u4E14\u4EFB\u4F55\u6D4B\u8BD5\u90FD\u8BC1\u4F2A\u4E0D\u4E86\u5B83**\n\uFF08\u4E00\u4E2A\u9A8C\u4E0D\u51FA\u6B7B\u6D3B\u7684\u673A\u5236\u6BD4\u6CA1\u6709\u5B83\u66F4\u574F\uFF1A\u5B83\u63D0\u4F9B\"\u8FD9\u91CC\u8FD8\u6709\u4E00\u5C42\u4FDD\u62A4\"\u7684\u9519\u89C9\uFF09\u3002\n\n \u25B8 \u8981\u8FD0\u884C\u6001\u8BCA\u65AD \u21D2 \u76F4\u63A5\u7528 dashboard \u7684 diag \u52A8\u4F5C\uFF08**\u65E0\u9700\u4EFB\u4F55\u58F0\u660E\uFF0C\u6240\u6709\u8F68\u8FF9\u53EF\u89C1**\uFF09\u3002\n \u25B8 \u65E7\u914D\u7F6E\u91CC\u6B8B\u7559 exclusiveTools \u21D2 **\u9759\u9ED8\u5FFD\u7565**\uFF08schemastery \u975E strict\uFF09\uFF0C\u4E0D\u5F71\u54CD\u52A0\u8F7D\u3002\n \u25B8 \u26A0\uFE0F \u8FDE\u5E26\u540E\u679C\uFF08owner \u5DF2\u77E5\u5E76\u786E\u8BA4\uFF09\uFF1Adashboard \u662F\u5E38\u9A7B\u5DE5\u5177 \u21D2 diag \u6BB5\u5BF9**\u6240\u6709\u8F68\u8FF9**\u53EF\u89C1\uFF0C\n \u800C\u5B83\u7684 live \u4F1A\u8BDD\u6E05\u5355\u662F**\u5168\u90E8 CCC** \u7684\uFF08\u8DE8\u5BB9\u5668\uFF09\u3002\u8FD9\u662F\u300C\u65B9\u6848 C\u300D\u7684\u65E2\u5B9A\u4EE3\u4EF7\uFF0C\u4E0D\u662F\u7F3A\u9677\u3002\n";
|
|
28
|
+
export declare const CCC_CONFIG_REFERENCE = "\u2550\u2550\u2550 CCC Configuration Reference \u2550\u2550\u2550\n\nCCC-level features are configured in .opencode/serenity.json.\nBelow are all available configuration sections.\n\n\u2500\u2500 1. sessionKeeper.threshold / sessionKeeper.sessionMdMaxKB \u2500\u2500\ncognitive-trajectory-assistant\uFF08COGNITIVE-TRAJECTORY-ASSISTANT \u00B7 CHECKPOINT\uFF09\u63D0\u9192\u673A\u5236\u7684\u79EF\u5206\u9608\u503C\uFF08\u975E headless \u4E3B agent\uFF09\u3002\n\u6309\u5DE5\u5177\u8C03\u7528\u52A0\u6743 + \u8017\u65F6\u8BA1\u5206\uFF1B\u8FBE\u5230\u9608\u503C\u6CE8\u5165\u63D0\u9192\uFF0C\u8981\u6C42\u6A21\u578B\u56DE\u590D ACK \u7801\u3002\n\n Config:\n { \"sessionKeeper\": { \"threshold\": 150, \"sessionMdMaxKB\": 100 } }\n\n \u8BA1\u5206\uFF1Awrite/edit = 3\uFF0Ctask = 10\uFF0Cread/grep/glob/msm \u7B49 = 1\uFF0C\u65F6\u95F4 = 1/\u5206\u949F\n \u9ED8\u8BA4\uFF1A150\n\n \u25B8 sessionMdMaxKB\uFF08v1.31.1 \u65B0\u589E\uFF1Bv1.31.2 100\u2192200\uFF1B\uD83D\uDD34 v1.84.0 200\u2192100\uFF09\uFF1A\n **\u6D3B\u8DC3 SESSION.md \u4F53\u79EF\u4E0A\u9650\uFF08KB\uFF0C\u7F3A\u7701 100\uFF1B0 = \u5173\u95ED\uFF09**\u3002\n \u8D85\u8FC7 \u2192 \u6CE8\u5165 [COGNITIVE-TRAJECTORY-ASSISTANT \u00B7 LOGBOOK COMPACTION] \u63D0\u9192\uFF1A\u6682\u505C\u5F53\u524D\u5DE5\u4F5C \u2192\n \u52A0\u8F7D eap\uFF08praxis eap\uFF09\u2192 **\u6309 EAP \u91CD\u6784\u6574\u4E2A SESSION.md**\uFF08v1.84.0 \u8D77\uFF1B\u65E7\u7248\u662F\u56DB\u6761\u91CD\u5199\u539F\u5219\uFF09\u3002\n \u5224\u636E = EAP \u4E09\u4E2A\u53D8\u91CF\uFF1AE\u2191 \u53D8\u91CF/\u8FB9\u754C/\u6709\u5411\u5173\u7CFB\u5168\u663E\u5F0F \uFF5C R\u2193 \u5E26\u7406\u7531\u4E0E\u88AB\u5426\u9879\uFF0C\u957F\u8BC1\u636E\u94FE\u5916\u79FB\u5230\n references/*.md\uFF08SESSION.md \u7559\u94FE\u63A5\uFF09\uFF5C S\u2191 \u7ED3\u6784\u6BCF\u6B21\u540C\u6837\u91CD\u5EFA\uFF0C\u4E0D\u662F\u65F6\u95F4\u6D41\u6C34\u8D26\u3002\n \u4E24\u6761\u786C\u7EA6\u675F\u968F\u63D0\u9192\u7ED9\u51FA\uFF1A\u4FDD\u7559\u56FA\u5B9A\u6BB5\u843D\u7684**\u987A\u5E8F\u4E0E\u6807\u9898**\uFF08\u4E0E\u7CFB\u7EDF\u63D0\u793A\u8BCD\u5BF9\u9F50\uFF09+ \u4FDD\u7559\u9876\u90E8 frontmatter\u3002\n \u8D85\u9650\u540E\u6BCF\u6B21\u5DE5\u5177\u8C03\u7528\u90FD\u63D0\u9192\uFF1B\u8FDE\u7EED 3 \u6B21\u672A\u91CD\u5199 \u2192 \u5347\u7EA7\u5F3A\u5236\u8BED\u6C14\uFF1B\n \u6587\u4EF6\u56DE\u5230\u9650\u5185 \u2192 \u81EA\u52A8\u505C\u6B62\uFF08\u81EA\u6108\uFF09\u3002\u8F68\u8FF9\u63D0\u9192**\u4E0D\u673A\u68B0\u963B\u65AD\u5DE5\u4F5C**\uFF08\u4E0E rebuild \u63D0\u9192\u540C\u65CF\uFF09\u3002\n\n\u2500\u2500 2. localstore.gitTrack \u2500\u2500\nlocalstore.json \u7684 git \u7B56\u7565\uFF1Aallow \u53EF\u63D0\u4EA4 / deny \u7981\u63D0\u4EA4\uFF08\u9ED8\u8BA4 deny\uFF09\u3002\ndeny \u65F6\u5199\u5165\u81EA\u52A8\u786E\u4FDD .gitignore \u542B\u8BE5\u6587\u4EF6\uFF08\u7269\u7406\u4FDD\u8BC1\uFF09\uFF0Ccontainer_git commit \u4F1A\u68C0\u67E5\u62D2\u7EDD\u3002\n\n Config:\n { \"localstore\": { \"gitTrack\": \"allow\" } }\n\n\u2500\u2500 3. hooks.autoRestoreSession \u2500\u2500\n\u4F1A\u8BDD\u81EA\u52A8\u6062\u590D\uFF08\u9ED8\u8BA4 true\uFF1B\u53D7 events \u95E8\u63A7\u2014\u2014\u4EC5\u6839\u4F1A\u8BDD + \u5DF2\u6709\u5BF9\u8BDD\u5386\u53F2\u624D\u6062\u590D\uFF09\u3002\n\n Config:\n { \"hooks\": { \"autoRestoreSession\": false } }\n\n\u2500\u2500 4. safeMode\uFF08\u9ED1\u540D\u5355 \uFF0B \u5F00\u542F\u671F\u95F4\u9690\u85CF\u7684\u5DE5\u5177\uFF09\u2500\u2500\nsafe-mode \u7531 WebUI \u5F00\u5173\u63A7\u5236\uFF08\u5199 .serenity-safe-on \u6807\u8BB0\uFF09\uFF1B\u9ED1\u540D\u5355\u8DEF\u5F84\u53D7 guards seam \u62E6\u622A\u3002\n\n \u25B8 blacklist\uFF1A\u5199\u8DEF\u5F84\u9ED1\u540D\u5355\uFF08\u540C\u4E0B Config\uFF09\u3002\n \u25B8 hiddenTools\uFF08v1.51.4\uFF0Cowner 2026-10-09 \u4EE4\uFF09\uFF1A**\u4EC5\u5728\u5B89\u5168\u6A21\u5F0F\u5F00\u542F\u671F\u95F4**\u989D\u5916\u4ECE\u6A21\u578B\u5DE5\u5177\n \u6E05\u5355\u9690\u85CF\u7684\u5DE5\u5177\uFF08ACC \u57FA\u7840\u8868\u6052\u9690 bash\uFF09\u3002\u6253\u5F00 safe-mode \u751F\u6548\u3001**\u5173\u6389\u5373\u5168\u90E8\u6062\u590D**\uFF1B\n \u6539\u672C\u952E**\u70ED\u751F\u6548**\uFF08\u4E0B\u4E00\u4E2A pre-step \u91CD\u7B97\uFF0C\u65E0\u9700\u91CD\u542F\uFF09\u3002\u58F0\u660E\u5728 CCC\uFF08D23\uFF09\u2014\u2014ACC \u4EE3\u7801\u91CC\n \u4E0D\u51FA\u73B0\u4EFB\u4F55\u5177\u4F53\u5DE5\u5177\u540D\u3002\n \u26A0\uFE0F \u5199\u4E00\u4E2A**\u672C CCC \u672A\u6CE8\u518C**\u7684\u540D\u5B57\u4F1A\u8BA9**\u6574\u6B21 restrict \u5931\u8D25**\uFF08DSH \u5951\u7EA6\u9010\u5B57\uFF1A\"unknown\n names \u2026 fail\"\uFF09\u21D2 \u6B64\u65F6**\u9000\u5316\u4E3A\u53EA\u9690\u85CF\u57FA\u7840\u8868**\u5E76\u5728\u65E5\u5FD7\u54CD\u4EAE\u62A5\u9519\uFF08\u7EDD\u4E0D\u56E0\u4E00\u4E2A\u9519\u540D\u4E22\u6389\n safe-mode \u7684\u6838\u5FC3\u9690\u85CF\uFF09\u3002\u8BCA\u65AD\uFF1A`AGENT_SESSIONS/.restrict-diag.json` \u7684 `denyByKey`\u3002\n \u26A0\uFE0F \u9690\u85CF = **\u4ECE schema \u79FB\u9664**\uFF08agent \u6839\u672C\u770B\u4E0D\u5230\u8BE5\u5DE5\u5177\uFF09\u21D2 \u522B\u628A skill/\u6587\u6863\u8FD8\u5728\u6559\u7684\u5DE5\u5177\u5199\u8FDB\u6765\u3002\n\n Config:\n { \"safeMode\": { \"blacklist\": [\".secrets/\"], \"hiddenTools\": [\"workflow\", \"job_kill\"] } }\n\n\u2500\u2500 5. \u8F68\u8FF9\u7EA6\u675F.roles\uFF08F4 \u8BA4\u77E5\u5B50\u96C6\u89D2\u8272\uFF09\u2500\u2500\n\u8F68\u8FF9\u7EA6\u675F\u89D2\u8272 = \u5168\u77E5\u5168\u80FD cognitive trajectory \u7684\u4EFB\u610F\u5B50\u96C6\uFF08CCC \u5B9A\u4E49\uFF09\u3002\u6BCF\u89D2\u8272\u72EC\u7ACB\u6A21\u578B + \u53CC\u767D\u540D\u5355\n\uFF08MSM \u767D\u540D\u5355 msms[] \u4E0E\u975E MSM \u5DE5\u5177\u767D\u540D\u5355 tools[]\uFF0C\u767D\u540D\u5355\u5916\u5168\u9690\u85CF\uFF1Bskill \u52A0\u8F7D\u6052\u53EF\u7528\uFF09\u3002\ncognitive trajectory \u7EAA\u5F8B\u5B50\u96C6\uFF08session/keeper/rebuild \u53C2\u4E0E\u9879\uFF09\u9ED8\u8BA4\u5168\u5173 = \u5B8C\u5168\u72EC\u7ACB\u3002\nsystemPrompt \u5185\u8054 \u6216 systemPromptFile\uFF08.md \u5F15\u7528\uFF0C\u63A8\u8350\uFF09\u2014\u2014\u89D2\u8272\u4F1A\u8BDD\u7684\u5B8C\u6574\u4EBA\u683C/\u8FB9\u754C\u63D0\u793A\u8BCD\u3002\nvalidate \u6821\u9A8C / apply \u751F\u6548 / list \u67E5\u770B\uFF1Acontainer_admin config <guide|validate|apply|list>\uFF08\u539F role \u57DF\u5DF2\u5E76\u5165 config\uFF0C\u65E7\u540D\u4F5C\u8FC7\u6E21\u522B\u540D\u4ECD\u53EF\u7528\uFF09\u3002\n\n Config:\n { \"\u8F68\u8FF9\u7EA6\u675F\": { \"roles\": {\n \"qa\": {\n \"model\": \"provider/model\",\n \"msms\": [\"web-search\", \"vlm-describe\"],\n \"tools\": [\"read\", \"grep\", \"glob\"],\n \"systemPromptFile\": \".opencode/skiff/qa.md\"\n } } } }\n\n\u2500\u2500 6. cognitive_trajectory\uFF08\u8F68\u8FF9 + \u5524\u9192\u6CE8\u518C\u8868\uFF0CD58\uFF09\u2500\u2500\nCCC \u7684\u4E00\u6761 cognitive trajectory = \u6301\u4E45\u8EAB\u4F53\uFF08SESSION.md\uFF09+ \u65F6\u95F4\u8F74\uFF08AGENT_SESSIONS/ \u76EE\u5F55 + \u5524\u9192\u6CE8\u518C\u8868\uFF09\u3002\n\u4E00\u4E2A CCC \u5185\u53EF\u591A\u6761\u5E76\u884C\uFF1B\u6761\u76EE\u5168\u90E8\u7531\u4EBA\u7C7B/agent \u663E\u5F0F\u767B\u8BB0\u3002\n\u26A0\uFE0F ACC \u4E0D\u518D\u63D0\u4F9B**\u5468\u671F\u81EA\u5524\u9192**\uFF08\u539F autopilot\uFF0C2026-09-15 \u6574\u6BB5\u9000\u573A\uFF1A\u673A\u5236/\u65F6\u949F/container_admin autopilot\n\u57DF/\u9762\u677F\u533A\u5757\u4E00\u5E76\u5220\u9664\uFF09\u3002\u81EA\u4E3B\u5DE1\u822A\u6539\u7531 CCC \u81EA\u5DF1\u9A71\u52A8\uFF08home-serenity \u4FA7 msm autopilot-round \u6BCF\u8F6E\u81EA\u6392\u4E0B\u4E00\u8F6E\uFF0C\n\u771F\u76F8\u6E90 = S151 SESSION.md \u00A71/\u00A71b/\u00A71c\uFF09\u3002**\u672C\u8F6E\u7684 cognitiveTrajectory.autopilot \u914D\u7F6E\u6BB5\u4FDD\u7559**\uFF08\u89C1\u4E0B\u8868\uFF09\u2014\u2014\n\u5B83\u73B0\u7531 **CCC \u4FA7\u811A\u672C**\u8BFB\u53D6\uFF0CACC \u4E0D\u518D\u6D88\u8D39\u8BE5\u6BB5\u3002\n\u5524\u9192\u6CE8\u518C\u8868\uFF08cognitive_trajectory send-later\uFF09\uFF1A\u5524\u9192 = \u672A\u6765\u65F6\u523B + \u4E00\u6761 message\uFF0C\u53EF\u5524\u9192\u81EA\u5DF1\u6216\u522B\u7684 cognitive trajectory\uFF1B\n\u843D\u70B9 AGENT_SESSIONS/wake-registry.json\uFF0C\u4E2D\u5FC3\u8C03\u5EA6\u5668\u5230\u70B9\u6295\u9012\uFF08fire-and-forget\uFF1B\u65E0\u56DE\u6267\u3001\u65E0\u56DE\u6536\uFF09\u3002\n\u8FD0\u884C\u6001\u8BCA\u65AD\uFF08ACC \u8D1F\u8D23\u4EBA\u4E13\u7528\uFF09\uFF1Aacc-diag\uFF08\u2460 live \u8FD0\u884C\u6001 / \u2460b \u5524\u9192\u65F6\u949F / \u2462 \u5524\u9192\u6CE8\u518C\u8868\uFF09\u3002\n\n\u25B8 cognitiveTrajectory.skills\uFF08v1.44.0\uFF0C**CCC \u7EA7 skill \u4F9B\u7ED9**\uFF1Bowner \u4EE4 2026-09-20\u300C\u8981\u6C42\u5B9E\u73B0\u6211\u7684\u8BC9\u6C42\u2026\u2026\n \u6CE8\u5165\u662F\u52A8\u6001\u6CE8\u5165\u5C31\u884C\uFF1Bcreate \u5148\u4E0D\u89E6\u53D1\u5427\uFF1B**\u8F68\u8FF9\u7EA6\u675F\u4E5F\u652F\u6301**\u300D\uFF09\uFF1A\n **\u672C CCC \u7684\u6BCF\u6761\u8F68\u8FF9\u90FD\u5E26\u8FD9\u4E9B skill**\u2014\u2014\u58F0\u660E\u7684 skill \u5168\u6587\u5728\u8BE5\u8F68\u8FF9**\u88AB\u7ED1\u5B9A\u671F\u95F4**\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\n \uFF08**\u52A8\u6001\u6CE8\u5165**\uFF1A\u6BCF\u8BF7\u6C42\u91CD\u65B0\u6C42\u503C\u3001\u4E0D\u7F13\u5B58 \u21D2 \u6539\u914D\u7F6E\u6216\u6539 frontmatter **\u5373\u65F6\u751F\u6548**\uFF0C\u65E0\u9700\u91CD\u65B0 use\u3001\u65E0\u9700\u91CD\u542F\uFF09\u3002\n \u4E24\u6761\u58F0\u660E\u6765\u6E90**\u5E76\u96C6**\uFF08\u4E0D\u662F\u4E8C\u9009\u4E00\uFF09\uFF1A\u672C\u952E **cognitiveTrajectory.skills**\uFF08\u5BB9\u5668\u7ED9\u7684\u5E95\u5EA7\uFF0C**\u5728\u524D**\uFF09\n \uFF0B \u8F68\u8FF9\u81EA\u5DF1 SESSION.md \u9876\u90E8 frontmatter \u7684 skills:\uFF08\u8FD9\u6761\u8F68\u8FF9\u989D\u5916\u7684\uFF0C**\u8FFD\u52A0**\uFF1B\u540C\u540D\u53BB\u91CD\uFF09\u3002\n \u2705 **\u542B \u8F68\u8FF9\u7EA6\u675F\u89D2\u8272\u4F1A\u8BDD**\uFF08\u65C1\u8DEF\u7684\u662F ACC \u8EAB\u4EFD\u4E0E\u7EAA\u5F8B\uFF1Bskill \u662F\"\u5DE5\u4F5C\u8D44\u6599\"\uFF0C\u7167\u7ED9\uFF09\u3002\n \u5B88\u536B\uFF1A\u540D\u5B57\u5148\u8FC7\u5B89\u5168\u6821\u9A8C\u624D\u7528\u4E8E\u62FC\u8DEF\u5F84\uFF08\u9632\u7A7F\u8D8A\uFF09\uFF5C**\u5408\u5E76\u540E**\u7EDF\u4E00 32 KB \u4E0A\u9650\u622A\u65AD\uFF5C\u627E\u4E0D\u5230\u54CD\u4EAE\u5199 [\u7F3A\u5931]\u3002\n \u26A0\uFE0F \u4E0E create **\u65E0\u5173**\uFF08create \u523B\u610F\u4E0D\u593A\u7ED1\u5B9A\uFF0CU6\uFF09\u21D2 \u6CE8\u5165\u7684\u95E8\u662F\"**\u5B58\u5728\u7ED1\u5B9A**\"\uFF08cognitive_trajectory use\uFF09\u3002\n \u5931\u8D25\u8BED\u4E49\uFF1A\u672A\u914D\u7F6E / \u5B57\u6BB5\u7F3A\u5931 / \u914D\u7F6E\u635F\u574F \u21D2 **\u7A7A\u6570\u7EC4**\uFF08= \u65E0 CCC \u7EA7\u4F9B\u7ED9\uFF1B\u8F68\u8FF9\u7EA7\u58F0\u660E\u7167\u65E7\u751F\u6548\uFF09\u3002\n \u5F52\u5C5E\uFF08D23\uFF09\uFF1A**\u673A\u5236\u5728 ACC**\uFF08\u8BFB\u53D6 + \u5408\u5E76 + \u6CE8\u5165 + \u5B88\u536B\uFF09\uFF0C**\u58F0\u660E\u5728 CCC**\uFF08\u672C\u952E\uFF09\u3002\n\n Config:\n { \"cognitiveTrajectory\": {\n \"skills\": [\"acc-session\", \"cce\"], // v1.44.0\uFF1A\u672C CCC \u7684\u6BCF\u6761\u8F68\u8FF9\u90FD\u5E26\uFF08\u542B \u8F68\u8FF9\u7EA6\u675F\u4F1A\u8BDD\uFF09\n \"autopilot\": { // \u539F autopilotTrajectory \u6BB5\uFF0C\u5B57\u6BB5\u4E0D\u53D8\uFF08\u65E7\u952E\u4ECD\u56DE\u9000\u8BFB\uFF09\n \"enabled\": true,\n \"intervalHours\": 2,\n \"session\": \"S151\",\n \"biasProvider\": \"autopilot-bias.ts\",\n \"topPrompt\": \"\u672C\u8F68\u8FF9\u6838\u5FC3\u76EE\u6807/\u7EAA\u5F8B/\u8D28\u91CF\u8981\u6C42\uFF08CCC \u81EA\u586B\uFF0C\u9632\u6F02\u79FB\uFF09\",\n \"avoidWakeHours\": { \"start\": 8, \"end\": 18 }\n } } }\n\n\u2500\u2500 7. weixin\uFF08\u5FAE\u4FE1\u6865 F4c-3\uFF0C\u542B\u6D88\u606F\u8BB0\u5F55 hook\uFF09\u2500\u2500\nCCC \u7EA7\u5FAE\u4FE1\u4E2A\u4EBA\u53F7\u63A5\u5165\uFF08iLink \u534F\u8BAE\uFF09\uFF1Adsh \u4E00\u8FDB\u7A0B\u591A CCC\uFF0C\u6BCF CCC \u72EC\u7ACB\u5BF9\u63A5\u5FAE\u4FE1\u6865\u3002\n\u8D26\u53F7/\u8DEF\u7531/\u5F00\u5173\u5728\u6B64\u6587\u4EF6\uFF1B**bot_token \u51ED\u636E\u5728 CCC localstore credential scope**\n\uFF08\u626B\u7801\u7ED1\u5B9A\u540E\u81EA\u52A8\u5199\u5165\uFF0C\u6C38\u4E0D\u8FDB git \u660E\u6587\u9762\uFF09\u3002\n\u8DEF\u7531 user \u2192 role\uFF1Aexact \u4F18\u5148\uFF0C* \u901A\u914D\u515C\u5E95\uFF1Brole \u5FC5\u987B \u2208 \u8BE5 CCC \u8F68\u8FF9\u7EA6\u675F.roles\u3002\n\u9762\u677F\uFF08WebUI \u8BBE\u7F6E \u2192 \u5FAE\u4FE1\u6865\uFF09\u53EF\u626B\u7801\u7ED1\u5B9A/\u79FB\u9664\u8D26\u53F7/\u7F16\u8F91\u8DEF\u7531\uFF1Bmsm(\"weixin-doctor\", [\"status\"|\"diag\"|\"verify\"|\"guide\"]) \u6392\u67E5 + \u5927\u800C\u5168\u6307\u5357\u3002\u51ED\u636E\u4E3B\u52A8\u67E5\u770B\uFF1Alocalstore get WEIXIN_<ACCOUNT>_TOKEN\u3002\n \u25B8 \u4E3B\u52A8\u53D1\u9001\uFF08v1.31.0\uFF1A**ACC \u5DE5\u5177 im-bridge**\uFF09\uFF1A\u8C03\n im-bridge({channel:\"weixin\", action:\"send\", user:\"<\u522B\u540D|id>\", text:\"<\u6587\u672C>\"}) \u7ED9\u7528\u6237\u53D1\u6D88\u606F\u2014\u2014\n **\u5DE5\u5177\u53EA\u80FD\u64CD\u4F5C\u672C\u4F1A\u8BDD CCC**\uFF08\u65E0 ccc \u53C2\u6570\uFF0C\u9632\u8BEF\u53D1\u4ED6\u5BB9\u5668\uFF09\u2192 weixin-bridge.sendProactiveText \u2192\n \u6210\u529F\u540E\u89E6\u53D1 outgoing hook\uFF08source=\"proactive\"\uFF09\u3002\u6587\u4EF6\u7528 action:\"send-file\" + file:\"<CCC \u5185\u8DEF\u5F84>\"\n \uFF08\u9003\u9038/\u7F3A\u5931/\u8D85 20MB \u62D2\u7EDD\uFF09+ \u53EF\u9009 caption\u3002**\u53EF\u89C1\u6027**\uFF1A\u672C CCC \u914D\u7F6E\u4E86 IM \u901A\u9053\n \uFF08weixin.enabled=true\uFF09\u624D\u51FA\u73B0\u5728\u6A21\u578B\u5DE5\u5177\u6E05\u5355\u91CC\uFF1B\u672A\u914D\u7F6E \u2192 \u7531 guards \u9010 agent\n tools.restrict({deny:['im-bridge']}) \u79FB\u9664\uFF08\u4E0D\u662F\"\u770B\u5F97\u5230\u4F46\u88AB\u62D2\"\uFF09\u3002\u8F68\u8FF9\u7EA6\u675F\u89D2\u8272\u53E6\u9700\u5728\n roles.<role>.tools \u767D\u540D\u5355\u5217\u51FA im-bridge\u3002\n \u5386\u53F2\uFF08v1.30.9~v1.30.x\uFF09\uFF1ACCC \u4FA7 MSM weixin-send\uFF08\u8D70 loopback HTTP \u5165\u53E3 3082\uFF09\u2014\u2014\n v1.31.0 \u5DF2\u9000\u5F79\uFF08\u811A\u672C\u5220\u9664 + \u6CE8\u518C\u8868\u6CE8\u9500\uFF09\uFF1B3082 \u5165\u53E3\u4FDD\u7559\u7ED9**\u975E agent \u8C03\u7528\u8005**\u3002\n\n \u25B8 \uD83D\uDD34\uD83D\uDD34 **\u51FA\u7AD9\u53EA\u6709\u4E00\u6761\u8DEF\uFF1Aagent \u81EA\u5DF1\u8C03 im-bridge**\uFF082026-10-24 \u8D77\uFF0Cowner \u4EE4\uFF09\u3002\n \u6865**\u6C38\u4E0D**\u66FF\u4F60\u56DE\u53D1\u6700\u7EC8\u6587\u672C \u2014\u2014 \u4F60\u8BF4\u4EC0\u4E48\u3001\u8BF4\u51E0\u6761\u3001\u4EC0\u4E48\u65F6\u5019\u8BF4\u3001\u8981\u4E0D\u8981\u4E0D\u8BF4\uFF0C\u5168\u7531\u4F60\u51B3\u5B9A\u3002\n \u26D4 \u4E0D\u8981\u6307\u671B\"\u6211\u628A\u8BDD\u5199\u8FDB\u56DE\u7B54\u91CC\u5C31\u4F1A\u81EA\u52A8\u53D1\u51FA\u53BB\"\uFF1A**\u4E0D\u4F1A**\u3002\u5FC5\u987B\u771F\u7684\u8C03\u5DE5\u5177\u3002\n \u21D2 \u6BCF\u8F6E\u7528\u6237\u6D88\u606F\u672B\u5C3E\u90FD\u4F1A\u9644\u4E00\u884C**\u5DF2\u586B\u597D\u53C2\u6570**\u7684 im-bridge \u8C03\u7528\u6A21\u677F\uFF08\u673A\u5236\u6807\u8BB0\n [serenity:weixin-manual-output]\uFF09\uFF0C\u7167\u5B83\u6539 text \u5373\u53EF\u3002\n \u21D2 \u672C\u8F6E\u7ED3\u675F\u4ECD\u672A\u6210\u529F\u8C03\u7528\uFF08send / send-file\uFF09\u2192 \u4F60\u4F1A\u88AB\u6253\u56DE\uFF08\u22642 \u6B21\uFF0C\u63D0\u793A = \u6807\u8BB0 + \u4E8B\u5B9E\uFF09\uFF1B\n \u8FBE\u4E0A\u9650\u540E**\u7528\u6237\u5C06\u6536\u4E0D\u5230\u4EFB\u4F55\u6D88\u606F**\uFF08\u6CA1\u6709\u4EFB\u4F55\u515C\u5E95\u8F6C\u53D1\uFF09\u3002\n \u21D2 \u6210\u529F\u8C03\u7528\u624D\u7B97\u9001\u8FBE\uFF1B\u5931\u8D25\u8C03\u7528\u4E0D\u7B97\uFF08\u4E0D\u4F1A\u88AB\u5F53\u6210\u672C\u8F6E\u5DF2\u53D1\u9001\uFF09\u3002\n \u26A0\uFE0F **\u524D\u63D0**\uFF1A\u672C\u8F68\u8FF9\u5FC5\u987B\u5728 `.opencode/serenity.json` \u7684 `im.routes` \u91CC\u88AB\u70B9\u540D\uFF0C\u5426\u5219 `im-bridge`\n \u5BF9\u4F60\u4E0D**\u53EF\u89C1**\u2014\u2014\u90A3\u79CD\u60C5\u51B5\u4E0B\u95F8\u95E8\u4E0D\u4F1A\u6253\u56DE\u4F60\uFF08\u6539\u6210\u4E00\u6761\u54CD\u4EAE\u544A\u8B66\uFF0C\u6307\u660E\"\u8BE5\u8F68\u8FF9\u65E0\u53D1\u9001\u6743\"\uFF09\uFF0C\n \u56E0\u4E3A\u6253\u56DE\u4E00\u4E2A\u4F60\u6839\u672C\u6EE1\u8DB3\u4E0D\u4E86\u7684\u8981\u6C42\u53EA\u4F1A\u7A7A\u8F6C\u3002\n\n \u25B8 **\u5DF2\u9000\u5F79\uFF08\u4E0D\u8981\u518D\u914D\uFF0C\u914D\u4E86\u4E5F\u4E0D\u751F\u6548\uFF09**\uFF1A\n \u00B7 `weixin.autoReplyWithLastMessage`\uFF08v1.30.10~2026-10-24\uFF09= \u6865\u66FF agent \u56DE\u53D1\u6700\u7EC8\u6587\u672C\u3002\n **\u88AB\u5426\u7406\u7531**\uFF08owner \u539F\u8BDD\uFF09\uFF1A\u300CautoReply \u5E76\u4E0D\u5408\u9002\uFF0C\u4E3B\u52A8\u6027\u7684 imBridge \u4ECD\u7136\u662F\u66F4\u597D\u7684\u300D\u2014\u2014\n \u5B83\u8BA9 agent \u5931\u53BB\"\u8BF4\u51E0\u6761/\u4F55\u65F6\u8BF4/\u8981\u4E0D\u8981\u4E0D\u8BF4\"\u7684\u51B3\u5B9A\u6743\uFF0C\u800C\u90A3\u6B63\u662F\u4E3B\u52A8\u53D1\u9001\u7684\u4EF7\u503C\u3002\n \u00B7 `weixin.fallbackOnNoSend`\uFF08v1.30.17~2026-10-24\uFF09= \u624B\u52A8\u6A21\u5F0F\u4E0B agent \u6CA1\u53D1\u6210\u529F\u65F6\u6865\u515C\u5E95\u8F6C\u53D1\u3002\n \u5B83\u53EA\u5728\u4E0A\u9762\u90A3\u4E2A\u5F00\u5173 =false \u65F6\u624D\u6709\u610F\u4E49 \u21D2 \u4E00\u8D77\u9000\u5F79\u3002\n \u26A0\uFE0F \u65E7\u914D\u7F6E\u91CC\u6B8B\u7559\u8FD9\u4E24\u4E2A\u952E**\u9759\u9ED8\u5FFD\u7565**\uFF08\u4E0D\u4F1A\u62A5\u9519\uFF0C\u4E5F\u4E0D\u4F1A\u751F\u6548\uFF09\u3002\n\n Config:\n { \"weixin\": {\n \"enabled\": true,\n \"hook\": \"scripts/weixin-message-hook.ts\",\n \"accounts\": [{ \"accountId\": \"wechat-1\", \"name\": \"\u5BB6\u5EAD\u52A9\u624B\", \"enabled\": true }],\n \"routes\": [{ \"user\": \"*\", \"role\": \"my-role\" }]\n } }\n\n \u25B8 weixin.hook\uFF08\u6D88\u606F\u8BB0\u5F55 hook\uFF0Cv1.27.13\uFF09\uFF1A\n \u5FAE\u4FE1\u6865\u6BCF\u6536/\u53D1\u4E00\u6761\u6D88\u606F\u89E6\u53D1\u4E00\u6B21 CCC \u81EA\u5199\u811A\u672C\uFF0C\u7531 CCC \u81EA\u884C\u6301\u4E45\u5316\u4FDD\u5B58\uFF08\u5B58\u54EA/\u5B58\u6210\u4EC0\u4E48\n /\u662F\u5426\u5165\u5E93\u5168\u5F52 CCC\u2014\u2014ACC \u4E0D\u7ED1\u5B9A\u5B58\u50A8\uFF09\u3002\u672A\u914D\u7F6E hook \u2192 \u96F6\u53D8\u5316\uFF08\u4E0D\u89E6\u53D1\uFF09\u3002\n\n \u89E6\u53D1\uFF08\u53CC\u5411\uFF09\uFF1A\n incoming = \u7528\u6237 \u2192 bot\uFF1A\u8DEF\u7531\u547D\u4E2D\u540E\u3001\u5A92\u4F53\u843D\u76D8\u540E\u89E6\u53D1\uFF08\u6587\u672C\u542B\u8BED\u97F3\u8F6C\u5199\uFF1B\u5A92\u4F53\u5E26\u843D\u76D8 relPath\uFF09\n outgoing = bot \u2192 \u7528\u6237\uFF1A\u53D1\u9001\u6210\u529F\u540E\u89E6\u53D1\uFF08reply = \u7528\u6237\u5B9E\u9645\u6536\u5230\u7684\u7EAF\u6587\u672C\uFF0C\u5DF2\u5265\u79BB think\uFF09\n \u00B7 source=\"proactive\" = agent \u7ECF im-bridge \u4E3B\u52A8\u53D1\u8D77\uFF08**2026-10-24 \u8D77\u8FD9\u662F\u552F\u4E00\u503C**\uFF09\n \u00B7 \u5386\u53F2\u503C reply / reply-fallback\uFF08\u6865\u81EA\u52A8\u56DE\u53D1 / \u6865\u515C\u5E95\u8F6C\u53D1\uFF09\u5DF2\u968F\u4E24\u4E2A\u5F00\u5173\u4E00\u8D77\u9000\u5F79\uFF1B\n \u5B58\u91CF\u65E5\u5FD7\u91CC\u4ECD\u80FD\u770B\u5230\u5B83\u4EEC\uFF0C\u90A3\u662F\u5386\u53F2\u4E8B\u5B9E\uFF0C\u4E0D\u662F\u5F53\u524D\u884C\u4E3A\n \u00B7 file\uFF08\u4EC5 send-file \u8865\u8BB0\uFF09\uFF1A{ \"name\": \"\u62A5\u544A.pdf\", \"size\": 12345, \"caption\": \"\u53EF\u9009\" }\n\n \u811A\u672C\u7EA6\u5B9A\uFF08\u4E8B\u4EF6 JSON \u5355\u884C\u7ECF stdin \u4F20\u5165\uFF1Bbun \u4F18\u5148 node \u515C\u5E95\uFF09\uFF1A\n #!/usr/bin/env bun \u6216 node script.js \u2014\u2014 \u8BFB process.stdin \u6574\u6BB5 JSON.parse\n const ev = JSON.parse(await new Response(process.stdin).text())\n ev.event === 'incoming' | 'outgoing'\n\n \u4E8B\u4EF6 schema\uFF08**\u4E0D\u542B\u4EFB\u4F55\u4F1A\u8BDD\u51ED\u636E**\u2014\u2014context_token/bot_token/token/aes_key \u5747\u4E0D\u51FA\u73B0\uFF09\uFF1A\n { \"event\": \"incoming\", \"ts\": 1788359941488, \"cccRoot\": \"/path/ccc\",\n \"accountId\": \"wechat-1\", \"userId\": \"u1@im.wechat\",\n \"sessionId\": \"skiff-weixin-xxx\", \"role\": \"my-role\",\n \"message\": { \"text\": \"\u4F60\u597D\", \"media\": [{ \"kind\": \"image\", \"relPath\": \"_tmp/weixin-inbound/<hash>/img_x.jpg\" }] } }\n { \"event\": \"outgoing\", \"ts\": ..., \"cccRoot\": ..., \"accountId\": ...,\n \"userId\": ..., \"sessionId\": ..., \"role\": ...,\n \"reply\": \"\u5DF2\u8BB0\u5F55\uFF08\u7EAF\u6587\u672C\uFF09\", \"source\": \"proactive\" }\n\n \u793A\u4F8B\u811A\u672C\uFF08\u8FFD\u52A0\u5230\u6309\u65E5\u6587\u4EF6\u2014\u2014\u6301\u4E45\u5316\u5F52 CCC \u81EA\u9009\uFF1A\u6587\u4EF6/DB/\u8FDC\u7AEF\u5747\u53EF\uFF09\uFF1A\n const fs = require('node:fs'); const p = '/path/ccc/AGENT_SESSIONS/_weixin-log.jsonl';\n fs.appendFileSync(p, JSON.stringify(ev) + '\\n');\n\n \u6267\u884C\u8BED\u4E49\uFF08\u65C1\u8DEF\u5BB9\u5FCD\uFF09\uFF1A\u5F02\u6B65 fire-and-forget + 15s \u8D85\u65F6 kill + \u5931\u8D25\u4EC5\u65E5\u5FD7\u2014\u2014\n \u5FAE\u4FE1\u6865\u6D88\u606F\u5904\u7406/\u56DE\u590D\u4E0D\u53D7 hook \u5F71\u54CD\uFF1B\u811A\u672C\u987B\u5728 CCC \u6839\u5185\uFF08\u8DEF\u5F84\u9003\u9038\u62D2\u7EDD\uFF09\u3002\n \u5A92\u4F53 relPath \u6307\u5411 _tmp/weixin-inbound/\uFF08\u4E34\u65F6\u76EE\u5F55\uFF09\u2014\u2014\u9700\u6301\u4E45\u4FDD\u5B58\u5A92\u4F53\u6587\u4EF6\u8BF7\u81EA\u884C copy\u3002\n\n\u2500\u2500 8. \uFF08\u5DF2\u5220\u9664\uFF09exclusiveTools\uFF08\u4E13\u5C5E\u5DE5\u5177\u58F0\u660E\uFF0Cv1.33 \u2192 v1.106.0 \u5220\u9664\uFF09\u2500\u2500\n\uD83D\uDD34 **\u672C\u952E\u5DF2\u4E0D\u5B58\u5728**\uFF08owner 2026-11-01 \u88C1\u300C\u673A\u5236\u6574\u4F53\u5220\u300D\uFF09\u3002\n\u539F\u8BED\u4E49\uFF1AACC \u6CE8\u518C\u4F46\u9ED8\u8BA4\u5BF9\u6240\u6709 CCC \u9690\u85CF\u7684\u5DE5\u5177\uFF0C\u53EA\u6709\u5728 CCC \u914D\u7F6E\u91CC\u70B9\u540D\u624D\u53EF\u89C1\uFF08fail-closed\uFF09\u3002\n**\u5220\u9664\u7406\u7531**\uFF1A\u90A3\u5F20\u8868\u6C38\u8FDC\u53EA\u6709\u4E00\u4E2A\u6210\u5458 acc-diag\uFF0C\u800C\u5B83\u5DF2\u5E76\u5165**\u5E38\u9A7B**\u7684 dashboard \u7684 diag \u52A8\u4F5C\u3002\n\u8868\u7A7A\u4E4B\u540E\u90A3\u4E2A filter \u6052\u56DE\u7A7A\u6570\u7EC4 \u21D2 \u673A\u5236**\u9759\u9ED8\u7A7A\u8F6C\u3001\u4E14\u4EFB\u4F55\u6D4B\u8BD5\u90FD\u8BC1\u4F2A\u4E0D\u4E86\u5B83**\n\uFF08\u4E00\u4E2A\u9A8C\u4E0D\u51FA\u6B7B\u6D3B\u7684\u673A\u5236\u6BD4\u6CA1\u6709\u5B83\u66F4\u574F\uFF1A\u5B83\u63D0\u4F9B\"\u8FD9\u91CC\u8FD8\u6709\u4E00\u5C42\u4FDD\u62A4\"\u7684\u9519\u89C9\uFF09\u3002\n\n \u25B8 \u8981\u8FD0\u884C\u6001\u8BCA\u65AD \u21D2 \u76F4\u63A5\u7528 dashboard \u7684 diag \u52A8\u4F5C\uFF08**\u65E0\u9700\u4EFB\u4F55\u58F0\u660E\uFF0C\u6240\u6709\u8F68\u8FF9\u53EF\u89C1**\uFF09\u3002\n \u25B8 \u65E7\u914D\u7F6E\u91CC\u6B8B\u7559 exclusiveTools \u21D2 **\u9759\u9ED8\u5FFD\u7565**\uFF08schemastery \u975E strict\uFF09\uFF0C\u4E0D\u5F71\u54CD\u52A0\u8F7D\u3002\n \u25B8 \u26A0\uFE0F \u8FDE\u5E26\u540E\u679C\uFF08owner \u5DF2\u77E5\u5E76\u786E\u8BA4\uFF09\uFF1Adashboard \u662F\u5E38\u9A7B\u5DE5\u5177 \u21D2 diag \u6BB5\u5BF9**\u6240\u6709\u8F68\u8FF9**\u53EF\u89C1\uFF0C\n \u800C\u5B83\u7684 live \u4F1A\u8BDD\u6E05\u5355\u662F**\u5168\u90E8 CCC** \u7684\uFF08\u8DE8\u5BB9\u5668\uFF09\u3002\u8FD9\u662F\u300C\u65B9\u6848 C\u300D\u7684\u65E2\u5B9A\u4EE3\u4EF7\uFF0C\u4E0D\u662F\u7F3A\u9677\u3002\n\n\u2500\u2500 9. userQuotes.enabled\uFF08\u7528\u6237\u8BED\u5F55\u53F0\u8D26\uFF0Cv1.113.0\uFF09\u2500\u2500\n**\u628A\u7528\u6237\u76F4\u63A5\u8BF4\u7684\u8BDD\u6309\u5929\u3001\u6309\u8F68\u8FF9\u5B58\u4E0B\u6765**\uFF0C\u4F9B\u4E4B\u540E\u53CD\u5411\u5206\u6790\u7528\u6237\u3002owner \u539F\u8BDD\uFF1A\n\u300C\u6211\u9700\u8981\u4E00\u4E2A\u53EF\u5F00\u542F\u5173\u95ED\u7684\u529F\u80FD\uFF0C\u5C31\u662F\u8BB0\u5F55\u7528\u6237\u76F4\u63A5\u5BF9\u8C01\u8BF4\u8FC7\u4EC0\u4E48\uFF0C\u5BF9\u8C01\u662F\u6307\u5BF9\u5177\u4F53\u7684\u67D0\u4E2A\u8F68\u8FF9\u2026\u2026\n\u8FD9\u4E2A\u529F\u80FD\u5982\u679C\u5F00\u542F\uFF0C\u5219\u9ED8\u8BA4\u5199\u5230\u4E00\u4E2A\u6307\u5B9A\u76EE\u5F55\u5185\uFF0C\u6309\u5929\u8FDB\u884C\u8BB0\u5F55\u3002\u8FD9\u4E2A\u529F\u80FD\u662F\u7528\u4E8E\u5177\u4F53\u7684\u5B81\u9759\u53F7CCC\u53CD\u5411\u6765\u5206\u6790\u7528\u6237\u7684\u300D\u3002\n\n Config:\n { \"userQuotes\": { \"enabled\": true } }\n\n \u9ED8\u8BA4\uFF1A**\u5173**\u3002\uD83D\uDD34 \u5224\u636E\u662F `enabled === true`\uFF0C\u26D4 \u4E0D\u662F\"\u952E\u5B58\u5728\"\u2014\u2014\n \u5199\u6210 \"true\" / 1 / \u53EA\u5199 userQuotes: {} \u4E00\u5F8B\u5F53**\u5173**\u3002\u7406\u7531\uFF1A\u8FD9\u662F\u628A\u7528\u6237\u539F\u8BDD\u539F\u6587**\u957F\u671F\u7559\u5B58**\uFF0C\n \u542B\u7CCA\u7684\u914D\u7F6E\u5FC5\u987B\u843D\u5230\"\u4E0D\u5199\"\u90A3\u4E00\u4FA7\uFF08fail-closed\uFF1B\u4E0E\u8BA2\u9605\u8868\u7684 fail-open \u65B9\u5411\u76F8\u53CD\uFF0C\n \u56E0\u4E3A\u8FD9\u91CC\u7684\u5371\u9669\u65B9\u5411\u662F**\u591A\u5199**\uFF09\u3002\n\n \u843D\u70B9\uFF1A`AGENT_SESSIONS/_user-quotes/<YYYY-MM-DD>.jsonl`\uFF08\u6309\u5929\uFF0C\u4E00\u884C\u4E00\u6761\uFF09\uFF1A\n {\"ts\":\"\u2026+08:00\",\"trajectory\":\"<\u8F68\u8FF9\u76EE\u5F55\u540D \u6216 null>\",\"sessionId\":\"\u2026\",\"messageId\":\"\u2026\",\"text\":\"\uFF08\u539F\u8BDD\uFF0C\u4E00\u5B57\u4E0D\u6539\uFF09\",\"chars\":88}\n\n \u25B8 **\u91C7\u96C6\u70B9** = `agent/pre-step` \u7684 `payload.messages`\uFF08\u672C step \u4ECE inbox \u53D6\u51FA\u3001\u5373\u5C06\u8FDB\u672C\u6B65\u7684\u6D88\u606F\uFF09\u3002\n \u25B8 **\u5224\u522B\u9762 = `source.kind === 'user'`**\uFF08\u7ED3\u6784\u6027\uFF0C\u26D4 \u4E0D\u9760\u63AA\u8F9E\u9ED1\u540D\u5355\uFF09\u3002\n \u25B8 **\"\u5BF9\u8C01\" = \u6309\u8F68\u8FF9**\uFF08Q1\uFF09\uFF0C\u53D6\u7ED1\u5B9A\u771F\u76F8\u6E90\uFF08`current.dsh_session_bind`\uFF09\uFF1B\u672A\u7ED1\u5B9A \u21D2 `null` \u4F46**\u7167\u5E38\u8BB0**\u3002\n \u25B8 **\u5168\u6587**\uFF08Q3\uFF09\uFF1A\u4E00\u5B57\u4E0D\u6539\uFF0C\u4E0D\u505A\u6458\u8981\uFF1B`chars` \u662F**\u7801\u70B9\u6570**\u4E0D\u662F UTF-16 \u5355\u5143\u6570\u3002\n \u25B8 \u4E24\u4E2A**\u5192\u5145 `kind:'user'`** \u7684\u751F\u4EA7\u8005\uFF08\u8F7D\u4F53\u5F00\u573A\u767D\uFF0Frebuild \u951A\u70B9\uFF09\u6309\u5404\u81EA\u6807\u8BB0\u6392\u9664\u3002\n \u25B8 IM \u5165\u7AD9\uFF08\u5FAE\u4FE1/\u98DE\u4E66\uFF09**\u4E0D\u4F1A\u6DF7\u8FDB\u6765**\uFF1A\u5B83\u8D70 `deliverToTrajectory`\uFF0Ckind \u662F\n `plugin:dsh-serenity-hooks`\uFF08\u5DF2\u53D6\u8BC1\uFF0C\u975E\u63A8\u6F14\uFF09\u3002\u21D2 \u53F0\u8D26 = **\u754C\u9762\u76F4\u8BDD**\u3002\n \u25B8 \u91C7\u96C6\u5931\u8D25\u53EA warn\u3001**\u4E0D\u963B\u65AD step**\uFF08\u53F0\u8D26\u662F\u65C1\u8DEF\u89C2\u6D4B\uFF0C\u4E0D\u662F\u88AB\u89C2\u6D4B\u5BF9\u8C61\u7684\u7EC4\u6210\u90E8\u5206\uFF09\u3002\n \u5224\u636E\u5168\u6587\uFF08\u542B\u53D6\u8BC1\u4E0E\u53D8\u5F02\u68C0\u9A8C\u8BBE\u8BA1\uFF09\u21D2 `src/trajectory/user-quotes.ts` \u5934\u6CE8\u91CA\u3002\n";
|
|
29
29
|
interface MsmFlag {
|
|
30
30
|
name: string;
|
|
31
31
|
type?: string;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trajectory/user-quotes.ts — 用户语录台账(owner 2026-11-02 需求)
|
|
3
|
+
*
|
|
4
|
+
* ## owner 原话(逐字,来源 = 2026-11-02 会话内用户输入)
|
|
5
|
+
*
|
|
6
|
+
* 「我需要一个可开启关闭的功能,就是记录用户直接对谁说过什么,对谁是指对具体的某个轨迹,
|
|
7
|
+
* 本质上这是个用户语录功能,这个功能如果开启,则默认写到一个指定目录内,按天进行记录。
|
|
8
|
+
* 这个功能是用于 具体的宁静号CCC反向来分析用户的」
|
|
9
|
+
*
|
|
10
|
+
* ## owner 三答(已锁定;⛔ 不要再问)
|
|
11
|
+
*
|
|
12
|
+
* | # | 问题 | 答 |
|
|
13
|
+
* |---|---|---|
|
|
14
|
+
* | Q1 | 「对谁」怎么算 | **按轨迹**(不是按会话) |
|
|
15
|
+
* | Q2 | 开关放哪层 | **CCC 配置面**(`.opencode/serenity.json`,改它不发版 —— D23) |
|
|
16
|
+
* | Q3 | 记录粒度 | **全文**(一字不改,不做摘要) |
|
|
17
|
+
*
|
|
18
|
+
* ## 本模块是什么 / 不是什么
|
|
19
|
+
*
|
|
20
|
+
* - **是**:`agent/pre-step` 那一刻「用户直接说的话」的**实时**落盘(按天一行一条 JSONL)。
|
|
21
|
+
* - **不是**:`dsh-session corpus` 的替代品。那个是**事后扫描**,判据是 20+ 条**措辞黑名单**
|
|
22
|
+
* (`INJECTION_MARKERS`/`INJECTION_PATTERNS`),它的 `heads` 子命令本身就是"量漏网家族"的取证工具
|
|
23
|
+
* ⇒ **判据天生不完备**。本模块改用**结构性判据**(`source.kind`),不猜措辞。
|
|
24
|
+
* - **不是**:`_im-logs/` 的替代品。那份记的是**IM 渠道双向原文**;本模块记的是**所有渠道**的用户发言。
|
|
25
|
+
*
|
|
26
|
+
* ## 采集点为什么是 `agent/pre-step`
|
|
27
|
+
*
|
|
28
|
+
* 宿主 `dsh-agent/lib/types/runtime-types.d.ts` 逐字:
|
|
29
|
+
* `'agent/pre-step'(payload: { agent; messages: UserMessage[]; turn; step; signal }, next)`,
|
|
30
|
+
* 其中 `messages` = **本 step 从 inbox 取出、即将进入本步的消息**,每条都是**完整 `UserMessage`,自带 `source`**。
|
|
31
|
+
* ⇒ 这是"用户直接说的话"**最接近源头**的位置(再往下就混进模型输出与工具结果了)。
|
|
32
|
+
*
|
|
33
|
+
* ## 判别面 = `source.kind`(结构性,⛔ 不靠措辞)
|
|
34
|
+
*
|
|
35
|
+
* 宿主 `dsh-llm/lib/types/message.d.ts` 逐字:*"user messages carry **any** producer's kind,
|
|
36
|
+
* and consumers fall through unknown kinds."* ⇒ 同一个 `role:'user'` 位置上,**不同生产者各有自己的 kind**。
|
|
37
|
+
*
|
|
38
|
+
* 实测三条(`dsh-session corpus raw` 真读出来的,⛔ 不是推演):
|
|
39
|
+
*
|
|
40
|
+
* | 来源 | `source` 实际值 |
|
|
41
|
+
* |---|---|
|
|
42
|
+
* | **界面真发言** | `{"kind":"user","rpcId":"…","clientTimeZone":"Asia/Shanghai"}` |
|
|
43
|
+
* | ACC 注入(唤醒/开场白/提醒/打回) | `{"kind":"plugin:dsh-serenity-hooks", form?, summary?}` |
|
|
44
|
+
* | **IM 入站(微信/飞书)** | `{"kind":"plugin:dsh-serenity-hooks"}` ← 🔴 **见下** |
|
|
45
|
+
*
|
|
46
|
+
* ⇒ **主判据 = `source.kind === 'user'`**(`ACC_MESSAGE_KIND` 是 ACC 侧的单一真相源)。
|
|
47
|
+
*
|
|
48
|
+
* ## 🔴🔴 IM 渠道那一格(2026-11-02 取证结案,⛔ 别再当成未决项)
|
|
49
|
+
*
|
|
50
|
+
* 本模块立项时留过一个开放项:「IM 消息是否也走 `agent/pre-step`、是否同为 `kind:'user'`,
|
|
51
|
+
* 会与界面直话混在一起」。**已取证,答案是不会混** —— 因为 IM 入站**根本不是** `kind:'user'`:
|
|
52
|
+
*
|
|
53
|
+
* - 代码面:`im/inbound-event.ts` 的入站走 `deliverToTrajectory`(`trajectory/session.ts:655`),
|
|
54
|
+
* 它构造消息用的是 `createUserMessage({ …, source: PLUGIN_SOURCE })` ⇒ **`kind:'plugin:dsh-serenity-hooks'`**。
|
|
55
|
+
* - 日志面(`dsh-session corpus raw --session skiff-weixin-…`,同一会话两条并存):
|
|
56
|
+
* 微信那条 = `{"kind":"plugin:dsh-serenity-hooks",…}`,正文含 `[serenity:weixin-manual-output]` 标记;
|
|
57
|
+
* 界面那条 = `{"kind":"user","rpcId":…,"clientTimeZone":…}`。
|
|
58
|
+
*
|
|
59
|
+
* ⇒ **按 `kind === 'user'` 过滤,天然只收界面直话,IM 入站一条都进不来。**
|
|
60
|
+
* ⚠️ 这是**当前实现的属性**,不是协议保证:若哪天 IM 改回 `kind:'user'`,本模块会**开始**收录它。
|
|
61
|
+
* 那时 `channel` 判别需要重新裁决 —— 不是靠本函数自己猜,而是要 owner 拍板(Q1 的范围问题)。
|
|
62
|
+
*
|
|
63
|
+
* ## 🔴 两个「冒充 user」的生产者(结构性分不开,只能靠标记排除)
|
|
64
|
+
*
|
|
65
|
+
* | 生产者 | 位置 | 标记 | 为什么冒充 |
|
|
66
|
+
* |---|---|---|---|
|
|
67
|
+
* | 载体开场白 | `trajectory/user-carrier.ts:117` | `CARRIER_OPENING_MARK` | 让宿主自动命名生效 |
|
|
68
|
+
* | rebuild 锚点 | `trajectory/rebuild.ts:419` | 正文以 `[COGNITIVE-TRAJECTORY-ASSISTANT` 开头 | 宿主校验强制要 `kind:'user'` |
|
|
69
|
+
*
|
|
70
|
+
* ⚠️ 这两条是**本方案唯一的不完美点**,如实登记于此。判据不是"看起来像注入"(那是措辞黑名单,
|
|
71
|
+
* 正是 corpus 那条不完备的路),而是**这两个生产者自己声明的标记**——它们本来就是为了
|
|
72
|
+
* "让将来翻日志的人一眼看出这不是真主说的"而存在的(见 `CARRIER_OPENING_MARK` 注释的用途 ②)。
|
|
73
|
+
*
|
|
74
|
+
* ## 为什么按轨迹、以及"对谁"怎么取
|
|
75
|
+
*
|
|
76
|
+
* Q1 = **按轨迹**。唯一权威入口 = `findTrajectoryOfSession`(`trajectory/bound.ts`,
|
|
77
|
+
* 内部即 `findTrajectoryHolding`)—— 给会话 id,返回持有它的轨迹目录名。
|
|
78
|
+
* ⛔ **不要自己扫目录**:绑定真相源只有一个(`current.dsh_session_bind`),扫描会造出第二份判据。
|
|
79
|
+
*
|
|
80
|
+
* 未绑定的会话(纯探索/临时问答)⇒ `trajectory` 字段写 `null`,**照常入账**。
|
|
81
|
+
* 理由:owner 要的是"用户说过什么";丢掉未绑定的会话会让台账**有系统性空洞**(且空洞不可见)。
|
|
82
|
+
* 读侧按 `trajectory === null` 就能把它们分出来。
|
|
83
|
+
*
|
|
84
|
+
* ## 落盘形态
|
|
85
|
+
*
|
|
86
|
+
* ```
|
|
87
|
+
* AGENT_SESSIONS/_user-quotes/<YYYY-MM-DD>.jsonl ← 按天,一行一条
|
|
88
|
+
* ```
|
|
89
|
+
* ```json
|
|
90
|
+
* {"ts":"2026-11-02T21:42:37+08:00","trajectory":"2026-08-24--S142--…","sessionId":"session-…","messageId":"…","text":"(原话,一字不改)","chars":88}
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* **为什么 JSONL 而不是每日 md**:① 与 `_im-logs/` 同族形态 ② **追加原子**、写不坏
|
|
94
|
+
* ③ 按字段过滤(`trajectory` / 日期)比正则快。
|
|
95
|
+
*
|
|
96
|
+
* ## 失败语义:**响亮降级,绝不阻断 step**(与其他缝同规格)
|
|
97
|
+
*
|
|
98
|
+
* 采集失败(目录只读 / 磁盘满 / 配置坏)⇒ `console.warn` 一次并**放行**。
|
|
99
|
+
* 🔴 判据:**台账是旁路观测,不是被观测对象的组成部分** —— 它坏了不该让宿主的一步跑不动
|
|
100
|
+
* (对照:`output-guard` 打回是**阻断式**的,因为它守的是"别把敏感词说出去",性质不同)。
|
|
101
|
+
* ⚠️ 但**不许静默**:warn 里带路径与原因,否则"没记上"和"没人说过话"在读数上长得一样(D253 同族)。
|
|
102
|
+
*
|
|
103
|
+
* ## 去重(同一句被重复投递)
|
|
104
|
+
*
|
|
105
|
+
* inbox splice / 重试 / 冷载入都可能让**同一条消息**(同一个 `message.id`)在同一进程里
|
|
106
|
+
* 被 `pre-step` 看到多遍。⇒ 进程内按 `messageId` 去重(有界 LRU,避免无限增长)。
|
|
107
|
+
* ⚠️ **只去重"同一条消息"**,不按文本去重 —— 用户真的重复说同一句话是**合法事实**,
|
|
108
|
+
* 按文本去重会把它吞掉(那是"用去重制造假数据")。
|
|
109
|
+
*/
|
|
110
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
111
|
+
/** 台账目录(CCC 根相对)—— 与 `_im-logs/` 同族:`_` 前缀 = 非内容目录。 */
|
|
112
|
+
export declare const USER_QUOTES_DIR = "AGENT_SESSIONS/_user-quotes";
|
|
113
|
+
/**
|
|
114
|
+
* 冒充 `kind:'user'` 的两个生产者所打的标记。
|
|
115
|
+
*
|
|
116
|
+
* 🔴 判据来自**生产者自己**(它们本就为了让读者能分辨才打这个标记),
|
|
117
|
+
* ⛔ 不是"我猜这些词像注入" —— 后者是措辞黑名单,会随措辞漂移而静默失效。
|
|
118
|
+
*/
|
|
119
|
+
export declare const USER_QUOTE_FAKE_MARKERS: readonly string[];
|
|
120
|
+
/** 一条语录(落盘即此形状,字段顺序即 JSON 输出顺序) */
|
|
121
|
+
export interface UserQuote {
|
|
122
|
+
/** 本地时区 ISO8601(全 ACC 统一当地时区,D66) */
|
|
123
|
+
ts: string;
|
|
124
|
+
/** 持有该会话的轨迹目录名;未绑定 ⇒ `null` */
|
|
125
|
+
trajectory: string | null;
|
|
126
|
+
/** dsh 会话 id */
|
|
127
|
+
sessionId: string;
|
|
128
|
+
/** 宿主消息 id(去重键) */
|
|
129
|
+
messageId: string;
|
|
130
|
+
/** 原话,**一字不改**(Q3) */
|
|
131
|
+
text: string;
|
|
132
|
+
/** text 的码点数(读侧免 repeat 计算;⛔ 不是字节数) */
|
|
133
|
+
chars: number;
|
|
134
|
+
}
|
|
135
|
+
/** 落盘结果(供调用方与测试判定;退出码无关) */
|
|
136
|
+
export interface UserQuoteWriteResult {
|
|
137
|
+
/** 实际写了几条(0 = 没有可写的真发言) */
|
|
138
|
+
written: number;
|
|
139
|
+
/** 因冒充标记被排除几条 */
|
|
140
|
+
excludedFake: number;
|
|
141
|
+
/** 因重复 id 被跳过几条 */
|
|
142
|
+
skippedDuplicate: number;
|
|
143
|
+
/** 台账日期文件绝对路径(written > 0 时才有意义) */
|
|
144
|
+
file: string;
|
|
145
|
+
/** 写失败原因(成功为 undefined) */
|
|
146
|
+
error?: string;
|
|
147
|
+
}
|
|
148
|
+
/** 仅测试用:清空去重记忆(⛔ 生产代码不许调) */
|
|
149
|
+
export declare function __resetUserQuoteDedupeForTest(): void;
|
|
150
|
+
/**
|
|
151
|
+
* 一条消息的正文文本(只取 `text` 块,按出现顺序以 `\n` 相连)。
|
|
152
|
+
*
|
|
153
|
+
* 判据与 `session.ts:496` / `guard/seam.ts:70` 同款 —— 不另造一套取文法(否则同一条消息
|
|
154
|
+
* 在不同缝里"说了什么"会不一样)。图片等非文本块**不参与**:Q3 要的是"用户说过什么",
|
|
155
|
+
* 图片没有可记录的文字面(媒体路径另有 `_im-logs/.../media/` 承载)。
|
|
156
|
+
*/
|
|
157
|
+
export declare function messageText(content: readonly ContentBlock[] | undefined): string;
|
|
158
|
+
/**
|
|
159
|
+
* 这条消息**是不是用户直接说的话**(结构性判据 + 冒充标记排除)。
|
|
160
|
+
*
|
|
161
|
+
* @returns `'quote'` 收录 / `'not-user'` 不是用户发言(ACC 注入等)/ `'fake-user'` 冒充 user 的生产者
|
|
162
|
+
*/
|
|
163
|
+
export declare function classifyUserMessage(source: unknown, text: string): 'quote' | 'not-user' | 'fake-user';
|
|
164
|
+
/** 本地时区 ISO8601(与 `cro/log.ts` 等处同款口径:不引第三方库,用 Date 的本地字段) */
|
|
165
|
+
export declare function localIso(ms: number): string;
|
|
166
|
+
/** 台账文件名(按天,本地时区) */
|
|
167
|
+
export declare function userQuoteFileName(ms: number): string;
|
|
168
|
+
/**
|
|
169
|
+
* 把本 step 的 `messages` 里的**用户真发言**追加进当日台账。
|
|
170
|
+
*
|
|
171
|
+
* @param root CCC 根
|
|
172
|
+
* @param sessionId 当前会话 id
|
|
173
|
+
* @param messages `agent/pre-step` 的 `payload.messages`
|
|
174
|
+
* @param nowMs 时间戳(测试注入;缺省 `Date.now()`)
|
|
175
|
+
* @returns 落盘结果(**永不抛** —— 采集失败只 warn)
|
|
176
|
+
*/
|
|
177
|
+
export declare function recordUserQuotes(root: string, sessionId: string, messages: readonly {
|
|
178
|
+
id?: string;
|
|
179
|
+
source?: unknown;
|
|
180
|
+
content?: readonly ContentBlock[];
|
|
181
|
+
}[] | undefined, nowMs?: number): UserQuoteWriteResult;
|
|
182
|
+
/** 台账目录是否已存在(诊断面用:区分"功能关着"与"开着但还没人说过话") */
|
|
183
|
+
export declare function userQuotesDirExists(root: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.113.0",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:12 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/localstore/container_admin;另两个 im-bridge/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|