@shgroup/dsh-serenity-hooks 1.76.0 → 1.77.1
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/cleanup-C-92qnDs.js +213 -0
- package/lib/index.js +174 -291
- package/lib/message-source-CYa8LbbM.js +488 -0
- package/lib/tools/trajectory-constraint-admin.d.ts +8 -1
- package/lib/tools/trajectory-self-docs.d.ts +35 -0
- package/lib/trajectory/bind-file.d.ts +228 -0
- package/lib/trajectory/bound.d.ts +117 -132
- package/package.json +1 -1
- package/lib/message-source-C_Zd2vJh.js +0 -435
package/lib/index.js
CHANGED
|
@@ -4,7 +4,8 @@ import { _ as resolveInside, a as findSerenityRoot$1, c as matchBlacklist, d as
|
|
|
4
4
|
import { A as sessionsRoot, B as localFileStamp, C as namingTitleFor, D as readTrajectoryUid, E as readActiveSessionMd, F as useSession, H as localIdStamp, I as TRAJECTORY_FILENAME, L as TRAJECTORY_LEGACY_FILENAMES, M as showSession, N as summarize, O as resolveSessionByTitle, P as trajectoryDescOf, R as readTrajectoryFile, S as listSessions, T as parseTrajectoryCode, V as localHuman, _ as extractSessionMdPathFromText, a as readTrajectoryConstraintAt, c as registerTrajectorySession$1, d as COGNITIVE_TRAJECTORY_ACTIONS, f as DEFAULT_SESSION_SCOPE, g as createSession, h as clearActiveSessionInfo, j as setActiveSessionInfo, k as sessionEvents$1, l as trajectoryDirNameForSession, m as archiveSession, n as isTrajectoryConstrained, o as readTrajectoryConstraintByCode, p as UID_FILENAME, r as listTrajectoryConstraints, s as trajectoryConstraintForSession, t as TRAJECTORY_CONSTRAINT_FILENAME, u as unregisterTrajectorySession$1, v as findSession, w as parseSessionContextFromEvents, x as listActiveTrajectories, y as getActiveSessionInfo, z as isoLocal } from "./profile-DplLGGqo.js";
|
|
5
5
|
import { _ as runLocalStore, a as readFeishuCredential, d as weixinSessionIdForTrajectory, f as LOCALSTORE_SCOPES, g as readStore, h as readGitTrack, i as matchWeixinRoute, l as sanitizeFileName$1, m as localstorePath, n as extractWeixinText, o as readWeixinCredential, p as checkLocalstoreGitCompliance, r as hasVoiceItem, s as readWeixinSettings, t as extractWeixinMedia, u as weixinInboundDir } from "./route-COOFLTpU.js";
|
|
6
6
|
import { a as buildTrajectoryBasePrompt, c as createProfilePromptReader, d as resolveConstraintSystemPrompt, f as ACC_VERSION, i as TRAJECTORY_CONSTRAINT_HINT, l as profileMsmWhitelist, n as readSimpleSettings, o as constraintCognitiveTrajectory, p as findPackageRoot, r as registerSettingsSection, s as constraintSystemPromptSource, u as profileToolWhitelist } from "./settings-f9WZXcgi.js";
|
|
7
|
-
import { a as
|
|
7
|
+
import { a as listBoundSessionIds, i as carrierState, n as PLUGIN_SOURCE, o as readLastBound, r as appendBound, s as resolveSessionCognitiveTrajectoryLabel, t as ACC_MESSAGE_KIND } from "./message-source-CYa8LbbM.js";
|
|
8
|
+
import { n as hasSessionLogById, r as sessionsRootDir } from "./cleanup-C-92qnDs.js";
|
|
8
9
|
import { REGISTERED_TOOLS } from "./invariant.js";
|
|
9
10
|
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-BMk2w2KY.js";
|
|
10
11
|
import { a as registerImAccountImpl, i as normalizeAccountChannel, n as getImAccountImpl, r as imAccountChannels } from "./accounts-BHQbakUh.js";
|
|
@@ -9595,215 +9596,6 @@ function createCheapWorkerTool(ctx) {
|
|
|
9595
9596
|
});
|
|
9596
9597
|
}
|
|
9597
9598
|
//#endregion
|
|
9598
|
-
//#region src/trajectory/cleanup.ts
|
|
9599
|
-
/**
|
|
9600
|
-
* trajectory/cleanup.ts — DSH 平台旧会话自动清理(v1.29,S142 需求③)
|
|
9601
|
-
*
|
|
9602
|
-
* 背景(实证):DSH 会话物理存储 = `$DSH_HOME/sessions/`(缺省 ~/.dsh/sessions;
|
|
9603
|
-
* base bundle cordis.patch.yml root: dshHomePath('sessions'))。磁盘布局:
|
|
9604
|
-
* <root>/--<project-slug>--/<encoded-session-id>/session[.vN].jsonl(.zstd)
|
|
9605
|
-
* **世代(v1.31.13 补)**:宿主自 DSH 0.1.5 起按世代写文件,且迁移**保留源文件**
|
|
9606
|
-
* ⇒ 一个会话目录常见 v0 与 v3 两个文件并存。本模块用 `sessionLogArtifacts()` 统一枚举世代。
|
|
9607
|
-
* DSH **无删除会话 API**(PersistenceCoordinator 仅 create/append/load/inspect/borrow,
|
|
9608
|
-
* 无 delete/purge;workspace archiveSession 只加归档集不删文件)——物理删除只能直接
|
|
9609
|
-
* 删文件。删除后一致性(利好):sessionPersistence.list() = readdir 扫描磁盘现存 →
|
|
9610
|
-
* 物理删后 list 不再返回;WorkspaceRegistry 下次启动重建 header index 自动消失。
|
|
9611
|
-
*
|
|
9612
|
-
* 方案(S142 用户拍板):lastActive 基准 + 手动触发 + 不过滤不归档直接物理删;
|
|
9613
|
-
* 安全底线 = live 会话保护跳过(删正在运行的会话 = 灾难)。
|
|
9614
|
-
*
|
|
9615
|
-
* 本模块纯逻辑(零 ctx 依赖):传入 sessionsRoot + liveIds,可单测。
|
|
9616
|
-
*/
|
|
9617
|
-
var cleanup_exports = /* @__PURE__ */ __exportAll({
|
|
9618
|
-
collectEligibleSessions: () => collectEligibleSessions,
|
|
9619
|
-
cutoffDaysAgo: () => cutoffDaysAgo,
|
|
9620
|
-
findSessionLog: () => findSessionLog,
|
|
9621
|
-
hasSessionLogById: () => hasSessionLogById,
|
|
9622
|
-
performCleanup: () => performCleanup,
|
|
9623
|
-
sessionLogArtifacts: () => sessionLogArtifacts,
|
|
9624
|
-
sessionLogBytesById: () => sessionLogBytesById,
|
|
9625
|
-
sessionsRootDir: () => sessionsRootDir
|
|
9626
|
-
});
|
|
9627
|
-
/** DSH 会话 root:env DSH_HOME → ~/.dsh(config-ops globalConfigPath 同款推导) */
|
|
9628
|
-
function sessionsRootDir() {
|
|
9629
|
-
const dshHome = process.env.DSH_HOME ?? join(process.env.HOME ?? "", ".dsh");
|
|
9630
|
-
return join(dshHome, "sessions");
|
|
9631
|
-
}
|
|
9632
|
-
/** 一天的毫秒数 */
|
|
9633
|
-
const DAY_MS = 864e5;
|
|
9634
|
-
/**
|
|
9635
|
-
* 扫描 sessions root,列出可清理候选:
|
|
9636
|
-
* - 递归 `<root>/<project>/<session-id>/session.jsonl(.zstd)`(任意深度下找 *session* 文件)
|
|
9637
|
-
* - 过滤 live(liveIds 集合)——安全底线
|
|
9638
|
-
* - 过滤 lastActive >= cutoffMs(最后活动未达阈值 → 保留)
|
|
9639
|
-
* root 不存在/为空 → 返回 [](非错误)。
|
|
9640
|
-
*/
|
|
9641
|
-
function collectEligibleSessions(root, cutoffMs, liveIds = /* @__PURE__ */ new Set()) {
|
|
9642
|
-
if (!existsSync(root)) return [];
|
|
9643
|
-
const out = [];
|
|
9644
|
-
for (const project of readdirSync(root, { withFileTypes: true })) {
|
|
9645
|
-
if (!project.isDirectory()) continue;
|
|
9646
|
-
const projectPath = join(root, project.name);
|
|
9647
|
-
for (const entry of readdirSync(projectPath, { withFileTypes: true })) {
|
|
9648
|
-
if (!entry.isDirectory()) continue;
|
|
9649
|
-
const dirPath = join(projectPath, entry.name);
|
|
9650
|
-
const artifacts = sessionLogArtifacts(dirPath);
|
|
9651
|
-
if (artifacts.length === 0) continue;
|
|
9652
|
-
const log = findSessionLog(dirPath);
|
|
9653
|
-
if (log === null) continue;
|
|
9654
|
-
if (liveIds.has(entry.name)) continue;
|
|
9655
|
-
let mtime = Number.NEGATIVE_INFINITY;
|
|
9656
|
-
for (const artifact of artifacts) try {
|
|
9657
|
-
mtime = Math.max(mtime, statSync(artifact.path).mtimeMs);
|
|
9658
|
-
} catch {}
|
|
9659
|
-
if (!Number.isFinite(mtime)) continue;
|
|
9660
|
-
if (mtime >= cutoffMs) continue;
|
|
9661
|
-
out.push({
|
|
9662
|
-
id: entry.name,
|
|
9663
|
-
project: project.name,
|
|
9664
|
-
logPath: log,
|
|
9665
|
-
lastActiveMs: mtime
|
|
9666
|
-
});
|
|
9667
|
-
}
|
|
9668
|
-
}
|
|
9669
|
-
return out;
|
|
9670
|
-
}
|
|
9671
|
-
/**
|
|
9672
|
-
* 会话日志文件名识别:`session.jsonl` / `session.jsonl.zstd`(= **v0 世代**)/
|
|
9673
|
-
* `session.v<N>.jsonl[.zstd]`(= 第 N 世代,DSH 0.1.5 起按世代写文件)。
|
|
9674
|
-
* 出处:`@deepseek-ai/dsh-session-persistence-jsonl` 的 `parseSessionFormatLogFilename` 同款形态。
|
|
9675
|
-
*/
|
|
9676
|
-
const SESSION_LOG_RE = /^session(?:\.v(\d+))?\.jsonl(\.zstd)?$/;
|
|
9677
|
-
/**
|
|
9678
|
-
* 列出会话目录下的**全部日志世代**,按代递增排序(同代内 `.jsonl` 在前、`.zstd` 在后 = 后者更代表"当前书写格式")。
|
|
9679
|
-
*
|
|
9680
|
-
* 为什么需要(R↓,v1.31.13 修复):宿主自 0.1.5 起改成**按世代写文件**,且迁移**保留源文件**
|
|
9681
|
-
* ⇒ 同一会话常见 `session.jsonl.zstd`(v0)与 `session.v3.jsonl.zstd`(v3)**并存**。
|
|
9682
|
-
* 旧实现只认前两个名字 → 新格式会话对本模块**完全不可见**(静默 no-op:清理永远不生效、磁盘只涨)。
|
|
9683
|
-
*/
|
|
9684
|
-
function sessionLogArtifacts(sessionDirPath) {
|
|
9685
|
-
let names;
|
|
9686
|
-
try {
|
|
9687
|
-
names = readdirSync(sessionDirPath);
|
|
9688
|
-
} catch {
|
|
9689
|
-
return [];
|
|
9690
|
-
}
|
|
9691
|
-
const out = [];
|
|
9692
|
-
for (const name of names) {
|
|
9693
|
-
const m = SESSION_LOG_RE.exec(name);
|
|
9694
|
-
if (m === null) continue;
|
|
9695
|
-
out.push({
|
|
9696
|
-
name,
|
|
9697
|
-
generation: m[1] === void 0 ? 0 : Number(m[1]),
|
|
9698
|
-
path: join(sessionDirPath, name)
|
|
9699
|
-
});
|
|
9700
|
-
}
|
|
9701
|
-
return out.sort((a, b) => a.generation - b.generation || (a.name.endsWith(".zstd") ? 1 : 0) - (b.name.endsWith(".zstd") ? 1 : 0));
|
|
9702
|
-
}
|
|
9703
|
-
/**
|
|
9704
|
-
* 在会话目录下找 session 日志文件;无 → null。
|
|
9705
|
-
* 多世代并存时返回**最高世代**(= 宿主 `findLog` 选中的当前世代;旧行为对单文件目录完全一致)。
|
|
9706
|
-
*/
|
|
9707
|
-
function findSessionLog(sessionDirPath) {
|
|
9708
|
-
const artifacts = sessionLogArtifacts(sessionDirPath);
|
|
9709
|
-
const latest = artifacts[artifacts.length - 1];
|
|
9710
|
-
return latest === void 0 ? null : latest.path;
|
|
9711
|
-
}
|
|
9712
|
-
/**
|
|
9713
|
-
* 执行清理(用户拍板:直接物理删,不过滤不归档;dryRun 预览不删)。
|
|
9714
|
-
* 删除 = rmSync 整个会话目录(recursive + force)。失败逐个记 errors 不中断。
|
|
9715
|
-
*/
|
|
9716
|
-
function performCleanup(root, cutoffMs, liveIds = /* @__PURE__ */ new Set(), opts = {}) {
|
|
9717
|
-
const candidates = collectEligibleSessions(root, cutoffMs, liveIds);
|
|
9718
|
-
if (opts.dryRun) return {
|
|
9719
|
-
candidates,
|
|
9720
|
-
result: null
|
|
9721
|
-
};
|
|
9722
|
-
const result = {
|
|
9723
|
-
deleted: [],
|
|
9724
|
-
errors: []
|
|
9725
|
-
};
|
|
9726
|
-
for (const c of candidates) {
|
|
9727
|
-
const dir = dirOf(c.logPath);
|
|
9728
|
-
try {
|
|
9729
|
-
rmSync(dir, {
|
|
9730
|
-
recursive: true,
|
|
9731
|
-
force: true
|
|
9732
|
-
});
|
|
9733
|
-
result.deleted.push(c.id);
|
|
9734
|
-
} catch (e) {
|
|
9735
|
-
result.errors.push({
|
|
9736
|
-
id: c.id,
|
|
9737
|
-
reason: e.message
|
|
9738
|
-
});
|
|
9739
|
-
}
|
|
9740
|
-
}
|
|
9741
|
-
return {
|
|
9742
|
-
candidates,
|
|
9743
|
-
result
|
|
9744
|
-
};
|
|
9745
|
-
}
|
|
9746
|
-
/** 从日志路径取会话目录(父目录) */
|
|
9747
|
-
function dirOf(logPath) {
|
|
9748
|
-
return logPath.slice(0, logPath.lastIndexOf("/")) || logPath;
|
|
9749
|
-
}
|
|
9750
|
-
/**
|
|
9751
|
-
* 某一 dsh 会话(目录名 / 会话 id)的**当前世代日志体积**(字节)。
|
|
9752
|
-
*
|
|
9753
|
-
* 为什么要它(S142 §0y「旧会话累积 ⇒ dsh 内存崩溃」,所有者令):
|
|
9754
|
-
* · 宿主恢复/继续一条会话必须**重放全部事件**(`scanLog` → `materializeAppendBatch`)
|
|
9755
|
-
* ⇒ **代价 ∝ 日志体积**;
|
|
9756
|
-
* · 实测(`_tmp/measure-session-load.mjs`,照宿主 `scanZstdFrames` 复刻):
|
|
9757
|
-
* 一条 **72.2 MB**(zstd)的日志 = 35 559 个 frame / 252.6 MB 文本 / 62 680 条事件,
|
|
9758
|
-
* **光是读一遍峰值 RSS +1 175 MB**(探针什么都不保留);
|
|
9759
|
-
* · 而宿主的解析缓存 `COLD_LOG_MEMO_MAX_ENTRIES = 2` 是**按条数限、不按字节限**的
|
|
9760
|
-
* (`dsh-session-persistence-jsonl/lib/index.js:2175`)⇒ 两条巨日志 = 上限内。
|
|
9761
|
-
* 🔴 **在本函数之前,ACC 侧没有任何地方知道"绑定的这条会话日志多大"——这个信号根本不存在。**
|
|
9762
|
-
* 这是"靠 agent 自觉 rebuild 不够"的机制层原因(keeper 的 K 量的是**模型上下文**,压缩后回落;
|
|
9763
|
-
* 而**日志文件只增不减** ⇒ rebuild 提醒永远不会因为"日志 72 MB"触发)。
|
|
9764
|
-
*
|
|
9765
|
-
* 只认会话目录(含任意世代日志);返回**最高世代**的日志(= 宿主 `findLog` 选中的当前世代)。
|
|
9766
|
-
* 未命中 / 不可读 → null(非错误,调用方按"未知"处理)。
|
|
9767
|
-
* @param root - DSH 会话 root({@link sessionsRootDir})
|
|
9768
|
-
* @param sessionId - 会话目录名(`.bindings.json` 的键,含 `session-` 前缀)或裸 id
|
|
9769
|
-
* @returns 字节数 + 日志绝对路径;未命中 → null
|
|
9770
|
-
*/
|
|
9771
|
-
function sessionLogBytesById(root, sessionId) {
|
|
9772
|
-
if (sessionId === "" || !existsSync(root)) return null;
|
|
9773
|
-
const dirName = sessionId.startsWith("session-") ? sessionId : `session-${sessionId}`;
|
|
9774
|
-
for (const project of readdirSync(root, { withFileTypes: true })) {
|
|
9775
|
-
if (!project.isDirectory()) continue;
|
|
9776
|
-
const log = findSessionLog(join(root, project.name, dirName));
|
|
9777
|
-
if (log === null) continue;
|
|
9778
|
-
try {
|
|
9779
|
-
return {
|
|
9780
|
-
bytes: statSync(log).size,
|
|
9781
|
-
path: log
|
|
9782
|
-
};
|
|
9783
|
-
} catch {
|
|
9784
|
-
return null;
|
|
9785
|
-
}
|
|
9786
|
-
}
|
|
9787
|
-
return null;
|
|
9788
|
-
}
|
|
9789
|
-
/**
|
|
9790
|
-
* **(c) 绑定一致性兜底**的判据:某会话的日志**是否仍在磁盘上**。
|
|
9791
|
-
*
|
|
9792
|
-
* 🔴 **fail-closed**:sessions root 本身不存在时**一律返回 true(当作"在")**——
|
|
9793
|
-
* 否则换 `DSH_HOME` / 换机器会让"全部绑定看起来都悬空",进而误清整张绑定表。
|
|
9794
|
-
* 只有"root 在、而这个会话目录确实不在"才算悬空。
|
|
9795
|
-
* @param sessionRoot - DSH 会话 root({@link sessionsRootDir})
|
|
9796
|
-
* @param sessionId - 会话目录名(`.bindings.json` 的键)或裸 id
|
|
9797
|
-
*/
|
|
9798
|
-
function hasSessionLogById(sessionRoot, sessionId) {
|
|
9799
|
-
if (!existsSync(sessionRoot)) return true;
|
|
9800
|
-
return sessionLogBytesById(sessionRoot, sessionId) !== null;
|
|
9801
|
-
}
|
|
9802
|
-
/** 便捷:默认 N 天前为 cutoff(供 API/调用方) */
|
|
9803
|
-
function cutoffDaysAgo(days, nowMs = Date.now()) {
|
|
9804
|
-
return nowMs - days * DAY_MS;
|
|
9805
|
-
}
|
|
9806
|
-
//#endregion
|
|
9807
9599
|
//#region src/wake/registry.ts
|
|
9808
9600
|
/**
|
|
9809
9601
|
* wake/registry.ts — 认知轨迹唤醒注册表(D58,S142 2026-09-13)
|
|
@@ -11912,6 +11704,21 @@ async function sendToCognitiveTrajectory(ctx, root, target, message, sender) {
|
|
|
11912
11704
|
let createdNote = "";
|
|
11913
11705
|
if ("error" in acquired) {
|
|
11914
11706
|
if (!acquired.notReady && acquired.noBinding === true) {
|
|
11707
|
+
const carrier = carrierState(root, t.dirName, (id) => {
|
|
11708
|
+
if (hostAgents(ctx)?.get?.(id)) return true;
|
|
11709
|
+
try {
|
|
11710
|
+
return hasSessionLogById(sessionsRootDir(), id);
|
|
11711
|
+
} catch {
|
|
11712
|
+
return true;
|
|
11713
|
+
}
|
|
11714
|
+
});
|
|
11715
|
+
if (carrier.state !== "never") {
|
|
11716
|
+
const why = carrier.state === "occupied" ? `该轨迹曾经由会话 ${carrier.sessionId} 承载,而它此刻不在了` : `该轨迹当前由会话 ${carrier.sessionId} 承载`;
|
|
11717
|
+
return {
|
|
11718
|
+
ok: false,
|
|
11719
|
+
detail: `${t.dirName}:${why} ⇒ **拒绝自动新建载体**(一条轨迹至多一个载体)。\n · 要让它重新跑起来:把那个会话恢复,或用 cognitive_trajectory rebuild 换代(显式 takeover)。\n · 不明所以就先 cognitive_trajectory show ${t.dirName} 看它的交代文件。`
|
|
11720
|
+
};
|
|
11721
|
+
}
|
|
11915
11722
|
acquired = await createAndBindTarget(ctx, root, t.dirName);
|
|
11916
11723
|
if ("error" in acquired) return {
|
|
11917
11724
|
ok: false,
|
|
@@ -12781,50 +12588,41 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12781
12588
|
return defineTool({
|
|
12782
12589
|
name: "cognitive_trajectory",
|
|
12783
12590
|
description: [
|
|
12784
|
-
"
|
|
12785
|
-
"
|
|
12786
|
-
"
|
|
12787
|
-
"
|
|
12788
|
-
"
|
|
12789
|
-
"
|
|
12790
|
-
"
|
|
12791
|
-
"
|
|
12792
|
-
"
|
|
12591
|
+
"A COGNITIVE TRAJECTORY is a durable body (SESSION.md) that outlives every conversation carrying it.",
|
|
12592
|
+
"AXIOM 1 — THE TRAJECTORY IS THE SUBJECT; THE CONVERSATION IS ITS CARRIER. Rebuilding the carrier replaces the conversation and leaves SESSION.md byte-identical.",
|
|
12593
|
+
"AXIOM 2 — A TRAJECTORY HAS NO LIFECYCLE. It never completes, closes, or expires, and age never counts against it. The only thing that changes is WHERE it is stored: archive moves it out of the active list without touching its directory.",
|
|
12594
|
+
"AXIOM 3 — IDENTITY IS THE CODE: the fixed-position segment of the directory name `YYYY-MM-DD--<code>[--<desc>]`. The code (not the summary, not the title) is the unique identifier, and create --id lets you choose it.",
|
|
12595
|
+
"DOES NOT: this tool never deletes a trajectory, never edits SESSION.md content, and never schedules recurring work (that is cro-guide).",
|
|
12596
|
+
"ENTITIES. TRAJECTORY = a directory under AGENT_SESSIONS/ holding SESSION.md. CARRIER = a dsh session bound to it. BINDING DECLARATION = the file `<trajectory>/current.dsh_session_bind` — ONE FILE PER TRAJECTORY, a single slot, never a table.",
|
|
12597
|
+
"IMMUTABLE 1 — ONE SESSION BELONGS TO AT MOST ONE TRAJECTORY, FOR LIFE. use refuses to move a session that already carries a different trajectory. There is NO --force override (retired 2026-10-20; passing it returns a refusal explaining why). To give another trajectory a carrier, open a new session.",
|
|
12598
|
+
"IMMUTABLE 2 — ONE TRAJECTORY HAS AT MOST ONE CURRENT CARRIER, and THE BINDING DECLARATION IS THE ONLY TRUTH. If several dsh sessions happen to be open, only the one named in the declaration carries the trajectory; the others are simply unbound. Never infer a binding from \"a session looks active\".",
|
|
12599
|
+
"IMMUTABLE 3 — A DECLARATION WHOSE SESSION NO LONGER EXISTS COUNTS AS UNBOUND. No cleanup is ever required, and a stale declaration is deliberately left in place: it records who carried this trajectory last.",
|
|
12600
|
+
"ACTIONS, GROUPED BY EFFECT — read the grouping before choosing, because it answers \"does this change what I am bound to?\":",
|
|
12793
12601
|
"(A) LIFECYCLE — changes which cognitive trajectory THIS conversation is bound to:",
|
|
12794
|
-
"list (inventory + stats
|
|
12795
|
-
"
|
|
12796
|
-
"
|
|
12797
|
-
"archive (move a trajectory into the archived area so it leaves the active list; one-way, never overwrites, and its content is left byte-identical).",
|
|
12798
|
-
"(B) DELIVERY — sends ONE message to ANOTHER cognitive trajectory; your own binding is unchanged:",
|
|
12799
|
-
"send-now (deliver RIGHT NOW) / send-later (deliver at ONE future instant).",
|
|
12800
|
-
"(C) REFERENCE — cro-guide (how to give THIS cognitive trajectory its own program that decides when it should be woken).",
|
|
12602
|
+
"list (inventory + stats) / show (read one SESSION.md) / create (make a NEW trajectory) / use (bind THIS conversation) / rebuild (clear-and-rebuild THIS conversation in place) / archive (move a trajectory out of the active list; one-way, never overwrites).",
|
|
12603
|
+
"(B) DELIVERY — sends ONE message to ANOTHER trajectory; your own binding is unchanged: send-now (right now) / send-later (one future instant).",
|
|
12604
|
+
"(C) REFERENCE — cro-guide: how to give a trajectory its own program that decides when it should be woken.",
|
|
12801
12605
|
"HOW TO CHOOSE (a rule, not a guess):",
|
|
12802
|
-
"not yet bound + starting new work ⇒ create
|
|
12803
|
-
"already bound + recording a
|
|
12804
|
-
"
|
|
12805
|
-
"want
|
|
12806
|
-
"
|
|
12807
|
-
"
|
|
12808
|
-
"RECEIPTS (read no more than is written): send-now returns a synchronous receipt meaning exactly \"injected / turn started\" —",
|
|
12809
|
-
"NEVER \"the target ran or replied\"; to prove the target actually moved, read its SESSION.md.",
|
|
12810
|
-
"It never queues behind the current turn: a busy target is steered mid-turn (read in the CURRENT turn), an idle target starts a turn at once.",
|
|
12606
|
+
"not yet bound + starting new work ⇒ create (with no prior binding it activates immediately, so the new trajectory has a carrier at once).",
|
|
12607
|
+
"already bound + recording a SEPARATE future line ⇒ create (it does NOT steal your binding; activate the new one later by send-now).",
|
|
12608
|
+
"want THIS conversation to move ⇒ use. It refuses if this session already carries another trajectory (IMMUTABLE 1) — that refusal is the design, not an error to work around.",
|
|
12609
|
+
"want to make another trajectory act ⇒ send-now. The target needs no prior setup: if it has no live session, one is created and bound automatically, and your message is injected.",
|
|
12610
|
+
"RECEIPTS (read no more than is written): send-now returns a synchronous receipt meaning exactly \"injected / turn started\" — NEVER \"the target ran or replied\". To prove the target actually moved, read its SESSION.md.",
|
|
12611
|
+
"send-now never queues behind the current turn: a busy target is steered mid-turn (read in the CURRENT turn), an idle target starts a turn at once.",
|
|
12811
12612
|
"send-later is fire-and-forget by design: no receipt, no recall, no panel.",
|
|
12812
|
-
"ASYMMETRY (deliberate
|
|
12813
|
-
"
|
|
12814
|
-
"
|
|
12815
|
-
"
|
|
12816
|
-
"
|
|
12817
|
-
"
|
|
12818
|
-
"
|
|
12819
|
-
"send-now
|
|
12820
|
-
"
|
|
12821
|
-
"
|
|
12822
|
-
"
|
|
12823
|
-
"
|
|
12824
|
-
"
|
|
12825
|
-
"⚠️ This is a behavioral rule, not a mechanical gate: nothing stops a careless call. Treat it as etiquette with a cost.",
|
|
12826
|
-
"uid: READS a legacy TRAJECTORY-UID only — setting one is retired (codes replaced it). Use create --id instead.",
|
|
12827
|
-
"The summary is appended to the dsh session title (S###-YYYY-MM-DD-<summary>); the code and date stay server-derived unless you pass --id."
|
|
12613
|
+
"ASYMMETRY (deliberate): send-now may create a missing carrier; send-later NEVER does. Scheduling for a trajectory that never existed is almost always a mistyped name, and auto-creating would hide that error.",
|
|
12614
|
+
"CARRIER GUARD: send-now auto-creates a carrier ONLY for a trajectory that has never had one. If a declaration exists but its session is gone, it refuses and says so — one trajectory keeps one carrier. Two independent gates can refuse: (a) the declared session cannot be resolved at all; (b) it existed and is now gone.",
|
|
12615
|
+
"PARAMETERS: create needs --desc <desc> [--id <code>] [--goal], OR --issue <ticket> (exactly one), PLUS --summary (≤20 chars).",
|
|
12616
|
+
"use needs --name + --summary (≤20 chars). --force is ACCEPTED BUT REFUSED (retired): it exists only to explain why.",
|
|
12617
|
+
"rebuild needs --summary (next-phase, ≤20 chars) + optional --note (≤200 chars, task focus for the rebuilt self).",
|
|
12618
|
+
"send-now needs target + message. send-later needs target + at (RFC3339 with timezone, or +30m / +2h) + message.",
|
|
12619
|
+
"target/name accept a code (S### or custom), a full AGENT_SESSIONS directory name, or a fuzzy keyword.",
|
|
12620
|
+
"AUTHORIZATION (read before creating anything): create and send-now both cost real work and must serve a human intent. Call create only when the user asked for a new line of work (or confirmed it) — never to \"tidy up\" or pre-provision what nobody requested. When in doubt, ask instead of creating. This is a behavioral rule, not a mechanical gate: treat it as etiquette with a cost.",
|
|
12621
|
+
"uid: READS a legacy TRAJECTORY-UID only. Setting one is retired — a trajectory's code is its identity now; use create --id <code>.",
|
|
12622
|
+
"summary is appended to the dsh session title as <code>-<date>-<summary>; the code and date stay server-derived unless you pass --id.",
|
|
12623
|
+
"SELF-DECLARATION MANUAL — how to write your own charter (PROMPT.md), declare yourself resident",
|
|
12624
|
+
"(trajectory.json `resident.serves`), or restrict yourself (tool/model whitelists):",
|
|
12625
|
+
"container_admin config docs trajectory-self. Read it before changing any file in your own directory."
|
|
12828
12626
|
].join(" "),
|
|
12829
12627
|
parameters: {
|
|
12830
12628
|
action: {
|
|
@@ -12832,11 +12630,11 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12832
12630
|
enum: [...COGNITIVE_TRAJECTORY_ACTIONS],
|
|
12833
12631
|
required: true,
|
|
12834
12632
|
description: [
|
|
12835
|
-
"Subcommand — grouped by effect.",
|
|
12633
|
+
"Subcommand — grouped by effect (see Tier 3 of the description).",
|
|
12836
12634
|
"LIFECYCLE (changes THIS conversation's binding):",
|
|
12837
|
-
"list (inventory + stats
|
|
12838
|
-
"create (new
|
|
12839
|
-
"use (activate context for THIS conversation;
|
|
12635
|
+
"list (inventory + stats) / show (a code, dir name, or fuzzy keyword) /",
|
|
12636
|
+
"create (new trajectory; --id sets its code. Activates itself when you have no binding, otherwise leaves your binding untouched and tells you how to activate it) /",
|
|
12637
|
+
"use (activate context for THIS conversation; refuses if this session already carries another trajectory — no override) /",
|
|
12840
12638
|
"rebuild (clear-and-rebuild current conversation — requires --summary + optional --note) /",
|
|
12841
12639
|
"archive (move a trajectory to the archived area; one-way, refuses to overwrite, content untouched — --dryRun previews).",
|
|
12842
12640
|
"DELIVERY (sends one message to ANOTHER cognitive trajectory; your binding is unchanged):",
|
|
@@ -12879,7 +12677,7 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12879
12677
|
},
|
|
12880
12678
|
force: {
|
|
12881
12679
|
type: "boolean",
|
|
12882
|
-
description: "use:
|
|
12680
|
+
description: "use: RETIRED (2026-10-20) — one session belongs to one cognitive trajectory for life, so switching is refused unconditionally. The flag is still ACCEPTED so that an old call gets an explanation instead of an \"unknown argument\" error; it never overrides the guard."
|
|
12883
12681
|
},
|
|
12884
12682
|
dryRun: {
|
|
12885
12683
|
type: "boolean",
|
|
@@ -12895,7 +12693,7 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12895
12693
|
},
|
|
12896
12694
|
message: {
|
|
12897
12695
|
type: "string",
|
|
12898
|
-
description: "send-later/send-now: the single message delivered to the target cognitive trajectory. send-now may create and bind a carrier when the target has
|
|
12696
|
+
description: "send-later/send-now: the single message delivered to the target cognitive trajectory. send-now may create and bind a carrier ONLY when the target has never had one — acceptable only for a delivery the user actually wants made (user-intent-driven), never for speculatively starting work."
|
|
12899
12697
|
}
|
|
12900
12698
|
},
|
|
12901
12699
|
output: {
|
|
@@ -12958,22 +12756,16 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12958
12756
|
if (!targetEntry) throw new Error(`Session not found: "${args.name}". Use "list" to see available sessions.`);
|
|
12959
12757
|
const targetDirName = targetEntry.dirName;
|
|
12960
12758
|
const currentDir = currentBoundDirName(exec, scope);
|
|
12961
|
-
|
|
12962
|
-
const force = args.force === true;
|
|
12963
|
-
if (switching && !force) throw new Error(`Session is bound to ${currentDir}. Switching to ${targetDirName} would orphan the current cognitive trajectory. Re-run with --force to switch (or close the current session first).`);
|
|
12759
|
+
if (currentDir !== null && targetDirName !== currentDir) throw new Error(`这个会话已经承载认知轨迹「${currentDir}」,不能改宗到「${targetDirName}」。\n一个会话终身只属于一条认知轨迹(2026-10-20 裁决,--force 已退役)。\n要继续用「${currentDir}」⇒ 不用任何操作;要给「${targetDirName}」开一个载体 ⇒ 新建一条会话再 use。`);
|
|
12964
12760
|
const active = useSession(root, args.name, scope);
|
|
12965
12761
|
const info = getActiveSessionInfo(scope);
|
|
12966
12762
|
if (info) renameDshSessionForActive(ctx, exec, info, args.summary);
|
|
12967
12763
|
const dsh = agentDshSession(exec);
|
|
12968
|
-
if (dsh && info) {
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
sessionId: info.sessionId,
|
|
12974
|
-
...switching ? { note: "forced switch" } : {}
|
|
12975
|
-
});
|
|
12976
|
-
}
|
|
12764
|
+
if (dsh && info) appendBound(dsh, "activate", {
|
|
12765
|
+
dirName: info.dirName,
|
|
12766
|
+
mdPath: info.mdPath,
|
|
12767
|
+
sessionId: info.sessionId
|
|
12768
|
+
});
|
|
12977
12769
|
const advisory = advisoryHint(targetEntry.dirName, join(targetEntry.path, "SESSION.md"));
|
|
12978
12770
|
const out = {
|
|
12979
12771
|
dir: active.dir,
|
|
@@ -12981,10 +12773,6 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12981
12773
|
context: active.context
|
|
12982
12774
|
};
|
|
12983
12775
|
if (advisory) out.advisory = advisory;
|
|
12984
|
-
if (dsh) {
|
|
12985
|
-
const pruned = pruneMissingBindings(root, (id) => !hasSessionLogById(sessionsRootDir(), id));
|
|
12986
|
-
if (pruned.length > 0) out.prunedBindings = pruned;
|
|
12987
|
-
}
|
|
12988
12776
|
return out;
|
|
12989
12777
|
}
|
|
12990
12778
|
case "rebuild": {
|
|
@@ -13170,36 +12958,52 @@ const localstoreTool = defineTool({
|
|
|
13170
12958
|
});
|
|
13171
12959
|
//#endregion
|
|
13172
12960
|
//#region src/tools/trajectory-constraint-admin.ts
|
|
13173
|
-
/**
|
|
13174
|
-
|
|
12961
|
+
/**
|
|
12962
|
+
* 轨迹约束 定义教程:**只讲「限制面」**(约束面 / 双白名单 / 纪律参与项 / 路由)。
|
|
12963
|
+
*
|
|
12964
|
+
* 🔴 2026-10-20 拆分(owner 裁 Q2):本章原先同时承载「描述自己」与「限制自己」。
|
|
12965
|
+
* 两者读者与前提不同 —— **描述自己是所有轨迹的常态,限制自己才是特例** ——
|
|
12966
|
+
* 故「章程 / 常驻 / 三个载体」已移入 `config docs trajectory-self`。
|
|
12967
|
+
* ⛔ **本文件不再重复那份内容**(一个内容一个正文入口,v1.75.0 指针纪律)。
|
|
12968
|
+
*/
|
|
12969
|
+
const TRAJECTORY_CONSTRAINT_GUIDE = `═══ 轨迹约束 Guide(限制面;2026-10-20 split)═══
|
|
13175
12970
|
|
|
13176
12971
|
**A 轨迹约束 IS a cognitive trajectory — the one whose directory contains \`${TRAJECTORY_CONSTRAINT_FILENAME}\`.**
|
|
13177
12972
|
That is the whole rule. No role table, no name registry: put the file there and the trajectory
|
|
13178
12973
|
becomes a 轨迹约束; delete it and it goes back to being an ordinary trajectory (takes effect at once).
|
|
13179
12974
|
|
|
12975
|
+
⚠️ **本章只讲"限制自己"。** 想写章程(PROMPT.md)或声明常驻(resident)?
|
|
12976
|
+
→ \`container_admin config docs trajectory-self\`(那是**所有轨迹**都用得上的自述手册)。
|
|
12977
|
+
|
|
13180
12978
|
AGENT_SESSIONS/<YYYY-MM-DD--<code>--<desc>>/
|
|
13181
12979
|
├─ SESSION.md ← its persistent body (this IS the workspace — nothing is auto-created)
|
|
13182
|
-
├─ PROMPT.md ← its charter (trajectory-
|
|
12980
|
+
├─ PROMPT.md ← its charter (see trajectory-self; NOT copied here)
|
|
13183
12981
|
├─ ${TRAJECTORY_CONSTRAINT_FILENAME} ← its declaration (this file is what makes it a 轨迹约束)
|
|
13184
12982
|
└─ continuous-re-occurrence.ts ← optional CRO program (spawned, never imported)
|
|
13185
12983
|
|
|
13186
12984
|
The older names ${TRAJECTORY_LEGACY_FILENAMES.map((n) => `\`${n}\``).join(" / ")} are still READ
|
|
13187
12985
|
(the family is checked in that order) — writing goes to \`${TRAJECTORY_FILENAME}\` only.
|
|
13188
12986
|
|
|
13189
|
-
── ${TRAJECTORY_CONSTRAINT_FILENAME} schema
|
|
12987
|
+
── ${TRAJECTORY_CONSTRAINT_FILENAME} schema(限制面部分)──
|
|
13190
12988
|
{
|
|
13191
12989
|
"model": "provider/model", // optional; falls back to cheap-worker.defaultModel
|
|
13192
12990
|
"tools": ["read","grep","msm",...], // non-MSM toolbar whitelist — anything not listed is REMOVED from the schema
|
|
13193
12991
|
"msms": ["msm-a","msm-b"], // MSM whitelist — msm exec may only run these; register/deregister always refused
|
|
13194
12992
|
"cognitiveTrajectory": { "session": true, "keeper": true, "rebuild": true }, // defaults TRUE (see below)
|
|
13195
|
-
"systemPromptFile": ".opencode/skiff/<role>.md", // relative to CCC root, hot-reloads on save
|
|
13196
|
-
"systemPrompt": "...", // inline fallback; systemPromptFile wins when both are set
|
|
13197
|
-
"uid": "my-handle", // optional legacy alias (read-only; the code is the identity now)
|
|
13198
12993
|
"cro": { "host": "<machine name>" } // optional: which machine may run this trajectory's CRO program
|
|
13199
12994
|
}
|
|
13200
12995
|
|
|
12996
|
+
⚠️ 提示词**不在这里**(新形态):写 \`PROMPT.md\` 即可,**所有轨迹**都适用。
|
|
12997
|
+
旧字段 \`systemPromptFile\` / \`systemPrompt\` 属**兼容期**形态,仍在读、但不再推荐;
|
|
12998
|
+
两者同时存在时 \`PROMPT.md\` 让位规则见 \`trajectory/prompt.ts\`(此处不重复)。
|
|
12999
|
+
|
|
13000
|
+
⚠️ **只想声明常驻、不想限制自己**:只写 \`"resident"\` 字段、**不写 tools/msms**。
|
|
13001
|
+
空白名单 = **不收窄任何工具面**(见下方"两个默认")—— 但请注意你仍因此"成为轨迹约束",
|
|
13002
|
+
只是没有实际限制。
|
|
13003
|
+
|
|
13201
13004
|
── Two whitelists, both default-deny ──
|
|
13202
13005
|
- \`tools: []\` + non-empty \`msms\` = pure-MSM 轨迹约束 (the usual knowledge-QA shape)
|
|
13006
|
+
- 空 \`tools\` + 空 \`msms\` = **无限制**(普通行为;常驻自述用的就是这一格)
|
|
13203
13007
|
- non-empty \`msms\` ⇒ the \`msm\` tool is added automatically (the only entry point for MSMs)
|
|
13204
13008
|
- anything outside both lists is invisible in the schema, not merely rejected at call time
|
|
13205
13009
|
|
|
@@ -13208,8 +13012,8 @@ becomes a 轨迹约束; delete it and it goes back to being an ordinary trajecto
|
|
|
13208
13012
|
its own body, so \`session\`/\`keeper\`/\`rebuild\` all default to true. Set false explicitly only
|
|
13209
13013
|
when you deliberately want that participation off.
|
|
13210
13014
|
|
|
13211
|
-
──
|
|
13212
|
-
\`
|
|
13015
|
+
── IM routing: user → trajectory code(微信 / 飞书同一张表)──
|
|
13016
|
+
\`im.routes\`: \`[{ "user": "<channel user id or *>", "trajectory": "<code>" }]\`
|
|
13213
13017
|
A route pointing at a code with no such trajectory is a hard error (the message is NOT delivered).
|
|
13214
13018
|
A trajectory WITHOUT \`${TRAJECTORY_CONSTRAINT_FILENAME}\` is still a legal target — it is just an ordinary trajectory.
|
|
13215
13019
|
|
|
@@ -13217,7 +13021,9 @@ becomes a 轨迹约束; delete it and it goes back to being an ordinary trajecto
|
|
|
13217
13021
|
Everything is read live: saving \`${TRAJECTORY_CONSTRAINT_FILENAME}\` takes effect on the next turn, no restart.
|
|
13218
13022
|
(The prompt FILE hot-reloads by mtime; the JSON itself is re-read every turn.)
|
|
13219
13023
|
After editing, run container_admin config apply for an explicit validation + confirmation pass.
|
|
13220
|
-
Skill loading is always available to 轨迹约束 sessions (skills are not whitelisted — they are read-only knowledge)
|
|
13024
|
+
Skill loading is always available to 轨迹约束 sessions (skills are not whitelisted — they are read-only knowledge).
|
|
13025
|
+
|
|
13026
|
+
写完之后怎么验 → container_admin config docs trajectory-self 的「写完之后怎么验」段(同一套判据,不复制)。`;
|
|
13221
13027
|
/** 校验当前 CCC 的 轨迹约束面:msms 均已注册 / model ∈ cheap-worker.models / 提示词非空 */
|
|
13222
13028
|
function validateTrajectoryConstraint(root) {
|
|
13223
13029
|
const profiles = listTrajectoryConstraints(root);
|
|
@@ -13297,6 +13103,81 @@ function applyTrajectoryConstraint(root) {
|
|
|
13297
13103
|
note: `constraints are read live from each trajectory's ${TRAJECTORY_CONSTRAINT_FILENAME} — they take effect on the next turn (no restart); ${TRAJECTORY_CONSTRAINT_HINT}`
|
|
13298
13104
|
};
|
|
13299
13105
|
}
|
|
13106
|
+
/**
|
|
13107
|
+
* 一章的全文。
|
|
13108
|
+
*
|
|
13109
|
+
* ⚠️ 本函数是**纯常量拼接**(无 IO、无 ctx):手册正文不随容器状态变化,
|
|
13110
|
+
* 唯一随状态变的东西("我现在是不是轨迹约束")由 `config list` 回答,不写进正文。
|
|
13111
|
+
*/
|
|
13112
|
+
const TRAJECTORY_SELF_GUIDE = [
|
|
13113
|
+
"═══ 一条认知轨迹如何描述自己(self-declaration guide)═══",
|
|
13114
|
+
"",
|
|
13115
|
+
"这份手册回答一件事:**我(某条认知轨迹)想给自己做设定时,该写进哪个文件、怎么写、怎么验。**",
|
|
13116
|
+
"",
|
|
13117
|
+
`── 三个载体:想改哪件事,就写进哪个文件 ──
|
|
13118
|
+
|
|
13119
|
+
<trajectory>/SESSION.md ← 我的**身体**(认知轨迹本体;持久、永不移动)
|
|
13120
|
+
<trajectory>/PROMPT.md ← 我的**章程**(我是谁、按什么工作;**所有轨迹都可以有**)
|
|
13121
|
+
<trajectory>/trajectory.json ← 我的**声明**(结构化字段;有它才成为"轨迹约束",见下)
|
|
13122
|
+
|
|
13123
|
+
⚠️ 三者主语相同(都是这条轨迹)、生命周期相同(随 git 走)、写入频率相同(人偶尔改)
|
|
13124
|
+
⇒ 这正是它们**该分开而不该合并**的理由:每一项都对应一种**不同的意图**。`,
|
|
13125
|
+
"",
|
|
13126
|
+
`── PROMPT.md:我的章程(推荐所有轨迹都写)──
|
|
13127
|
+
|
|
13128
|
+
写什么:我这名认知轨迹的**身份与工作方式** —— 我负责什么、我的判断依据是什么、
|
|
13129
|
+
我的边界在哪、我怎么跟其他轨迹协作。
|
|
13130
|
+
谁读它:**只有绑定在我身上的会话**读到它(作为系统提示词的一个独立段)。
|
|
13131
|
+
何时生效:**保存即生效**(按 mtime 热重载,无需重启、无需 rebuild)。
|
|
13132
|
+
没有它会怎样:不报错。我拿到的是 ACC 的通用身份 —— 普通轨迹的常态,不是缺陷。
|
|
13133
|
+
|
|
13134
|
+
⚠️ 与 SESSION.md 的分工(别写重了):
|
|
13135
|
+
SESSION.md = **发生了什么**(日志、决策、当前状态)
|
|
13136
|
+
PROMPT.md = **我是谁、怎么工作**(章程;很少改,改了就改变行为)`,
|
|
13137
|
+
"",
|
|
13138
|
+
`── 常驻:自己声明"欢迎别人来麻烦我"(trajectory.json 的 resident 字段)──
|
|
13139
|
+
|
|
13140
|
+
🔴 先排除一个误解:**常驻不是资源状态**。
|
|
13141
|
+
它不是"永不休眠"、不是"常驻内存"、不是"不许销毁我的会话"。
|
|
13142
|
+
一条轨迹**永远存在**(没有生命周期),常驻与否都不改变这一点。
|
|
13143
|
+
|
|
13144
|
+
那它是什么:**我对自己用途的公开声明** —— "我在这儿、我擅长什么、欢迎来麻烦我"。
|
|
13145
|
+
别的轨迹要找人干活时,先查这份名单,而不是靠猜谁可能懂。
|
|
13146
|
+
|
|
13147
|
+
写法:
|
|
13148
|
+
{ "resident": { "serves": "一句话说清我擅长什么、能帮上什么忙" } }
|
|
13149
|
+
|
|
13150
|
+
⚠️ 两个门槛,缺一不可:
|
|
13151
|
+
① 写了 \`resident\` ⇒ 我进得了别人的候选名单(**可被发现**)
|
|
13152
|
+
② \`serves\` 非空 ⇒ 别人读了这句话能判断"这事是不是该找你"(**可被判断**)
|
|
13153
|
+
只写半个(有字段、没 serves)等于把自己挂上去却不说能干什么 —— 那比不写更坏,
|
|
13154
|
+
因为别人会来问,然后发现问错了人。
|
|
13155
|
+
|
|
13156
|
+
谁能看见:通过 cognitive_trajectory 的常驻名单动作按需查询。
|
|
13157
|
+
**它不进系统提示词**(不占每个请求的 token)—— 只在有人真的要找人时才读。
|
|
13158
|
+
这是 owner 2026-10-20 的裁决:说明类信息按需取,不常驻在上下文里。`,
|
|
13159
|
+
"",
|
|
13160
|
+
`── 想限制自己(白名单 / 模型 / 纪律参与项)──
|
|
13161
|
+
|
|
13162
|
+
写 \`trajectory.json\` 即成为**轨迹约束**(判据是**文件存在**,不是某个字段存在)。
|
|
13163
|
+
删掉它 ⇒ 当场退回普通轨迹。
|
|
13164
|
+
|
|
13165
|
+
⚠️ 一个必须知道的连带后果:**写 trajectory.json 会把我变成受约束的轨迹**。
|
|
13166
|
+
所以如果你只想"描述自己"(章程、常驻),**不需要**建这个文件 ——
|
|
13167
|
+
章程走 PROMPT.md;常驻字段确实也在这个文件里,但**只写 resident、不写 tools/msms**
|
|
13168
|
+
时,白名单为空 = 不收窄任何工具面(见下一章"两个默认")。
|
|
13169
|
+
|
|
13170
|
+
schema、双白名单、纪律参与项的完整说明 → container_admin config docs trajectory-constraint`,
|
|
13171
|
+
"",
|
|
13172
|
+
`── 写完之后怎么验(别只看文档,要能机械判定)──
|
|
13173
|
+
|
|
13174
|
+
章程生效了吗 改 PROMPT.md 后**在同一会话里**看下一轮的系统提示词段是否变化(保存即生效)
|
|
13175
|
+
我是轨迹约束吗 container_admin config list —— 我的编码出现在列表里即成立
|
|
13176
|
+
配置合法吗 container_admin config validate —— msms 均已注册 / model 在可用表里 / 提示词非空
|
|
13177
|
+
常驻被看见了吗 cognitive_trajectory 的常驻名单动作 —— 我出现在名单里,且 serves 可读
|
|
13178
|
+
|
|
13179
|
+
⚠️ 以上四项都是**读取当前真实状态**,不是"保存成功"的回执。保存成功 ≠ 生效(本仓反复踩过)。`
|
|
13180
|
+
].join("\n");
|
|
13300
13181
|
//#endregion
|
|
13301
13182
|
//#region src/tools/container-admin.ts
|
|
13302
13183
|
/**
|
|
@@ -13439,9 +13320,14 @@ const DOCS_CHAPTERS = [
|
|
|
13439
13320
|
summary: "CCC 配置全表——9 段各是什么、怎么填、填错会怎样(.opencode/serenity.json)",
|
|
13440
13321
|
render: () => CCC_CONFIG_REFERENCE
|
|
13441
13322
|
},
|
|
13323
|
+
{
|
|
13324
|
+
id: "trajectory-self",
|
|
13325
|
+
summary: "一条轨迹怎么描述自己——章程 PROMPT.md / 常驻声明(resident.serves)/ 三个载体的分工 + 写完怎么验",
|
|
13326
|
+
render: () => TRAJECTORY_SELF_GUIDE
|
|
13327
|
+
},
|
|
13442
13328
|
{
|
|
13443
13329
|
id: "trajectory-constraint",
|
|
13444
|
-
summary: "
|
|
13330
|
+
summary: "轨迹约束(只讲限制面):trajectory.json 的双白名单 / 模型 / 纪律参与项 / IM 路由",
|
|
13445
13331
|
render: () => TRAJECTORY_CONSTRAINT_GUIDE
|
|
13446
13332
|
}
|
|
13447
13333
|
];
|
|
@@ -13453,7 +13339,7 @@ const DOCS_CHAPTERS = [
|
|
|
13453
13339
|
function createContainerAdminTool(ctx) {
|
|
13454
13340
|
return defineTool({
|
|
13455
13341
|
name: "container_admin",
|
|
13456
|
-
description: "Container administration (the ship's maintenance bay — one entry for managing the CCC): config — CCC configuration: docs (usage-manual index) / docs <chapter> (one chapter in full) / view (current values) + 轨迹约束 (guide/validate/apply/list); msm — MSM registry management (register/deregister/check quality DC-M1~M4) + manuals (guide dev manual / catalog ACC capability directory / ccc-config CCC configuration reference). Execution of registered MSMs belongs to the msm tool; this tool manages the container.",
|
|
13342
|
+
description: "Container administration (the ship's maintenance bay — one entry for managing the CCC): config — CCC configuration: docs (usage-manual index) / docs <chapter> (one chapter in full) / view (current values) + 轨迹约束 (guide/validate/apply/list); msm — MSM registry management (register/deregister/check quality DC-M1~M4) + manuals (guide dev manual / catalog ACC capability directory / ccc-config CCC configuration reference). TRAJECTORY SELF-DOCS: a cognitive trajectory describing ITSELF — charter (PROMPT.md) / resident declaration / self-restriction (whitelists) ⇒ config docs trajectory-self; the restriction side only ⇒ config docs trajectory-constraint. Execution of registered MSMs belongs to the msm tool; this tool manages the container.",
|
|
13457
13343
|
parameters: {
|
|
13458
13344
|
domain: {
|
|
13459
13345
|
type: "string",
|
|
@@ -16343,7 +16229,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
16343
16229
|
const id = s.id ?? s.header?.id;
|
|
16344
16230
|
if (id) liveIds.add(id);
|
|
16345
16231
|
}
|
|
16346
|
-
const { sessionsRootDir, collectEligibleSessions, performCleanup, cutoffDaysAgo } = await
|
|
16232
|
+
const { sessionsRootDir, collectEligibleSessions, performCleanup, cutoffDaysAgo } = await import("./cleanup-C-92qnDs.js").then((n) => n.t);
|
|
16347
16233
|
const root = sessionsRootDir();
|
|
16348
16234
|
if (req.method === "GET") {
|
|
16349
16235
|
const { candidates } = performCleanup(root, cutoffDaysAgo(days), liveIds, { dryRun: true });
|
|
@@ -18402,7 +18288,6 @@ async function queueRebuild(ctx, opts) {
|
|
|
18402
18288
|
const boundRec = {
|
|
18403
18289
|
dirName: basename(dirname(mdPath)),
|
|
18404
18290
|
mdPath,
|
|
18405
|
-
sessionId: sessionName.startsWith("S") ? sessionName : void 0,
|
|
18406
18291
|
note: "rebuild queued"
|
|
18407
18292
|
};
|
|
18408
18293
|
appendBound(session, "rebuild", boundRec);
|
|
@@ -25189,11 +25074,9 @@ function apply(ctx, config) {
|
|
|
25189
25074
|
(async () => {
|
|
25190
25075
|
try {
|
|
25191
25076
|
const domain = await openBindingDomain(ctx);
|
|
25192
|
-
|
|
25077
|
+
bindingStore(domain);
|
|
25193
25078
|
if (!domain) console.log("[serenity-hooks] §0L 存储域不可用 → 绑定继续走 CCC 内 .bindings.json(向前兼容回落)");
|
|
25194
|
-
} catch {
|
|
25195
|
-
setBindingStore(null);
|
|
25196
|
-
}
|
|
25079
|
+
} catch {}
|
|
25197
25080
|
})();
|
|
25198
25081
|
if (config.tools) {
|
|
25199
25082
|
ctx.tools.register(ccFsTool);
|