@shgroup/dsh-serenity-hooks 1.79.1 → 1.79.2
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/index.js +114 -15
- package/lib/{message-source-BOG0piL8.js → message-source-BJO5q-rD.js} +68 -17
- package/lib/{session-BxDVrxPH.js → session-B4eZD3Hv.js} +1 -1
- package/lib/trajectory/alive.d.ts +95 -0
- package/lib/trajectory/bind-file.d.ts +28 -0
- package/lib/trajectory/bound.d.ts +15 -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.79.
|
|
3
|
+
"version": "1.79.2",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/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": {
|
package/lib/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { c as hostInjected, d as hostSessions, f as hostSettings, h as hostWebSe
|
|
|
3
3
|
import { _ as resolveInside, a as findSerenityRoot$1, c as matchBlacklist, d as readCccName$1, f as readCheapWorkerConfig, h as readSafeModeHiddenTools, i as findGitRoot, l as pathInside, m as readExclusiveTools, n as SAFE_MODE_MARKER, o as isSafeModeOn, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-CyYJL4F2.js";
|
|
4
4
|
import { A as sessionEvents, B as isoLocal, C as listSessions, E as parseTrajectoryCode, F as trajectoryDescOf, H as localHuman, I as useSession, L as TRAJECTORY_FILENAME, M as setActiveSessionInfo, N as showSession, O as readTrajectoryUid, P as summarize, R as TRAJECTORY_LEGACY_FILENAMES, S as listActiveTrajectories, T as parseSessionContextFromEvents, U as localIdStamp, V as localFileStamp, _ as createSession, a as readTrajectoryConstraintAt, b as getActiveSessionInfo, c as trajectoryConstraintForSession, f as COGNITIVE_TRAJECTORY_ACTIONS, g as clearActiveSessionInfo, h as archiveSession, j as sessionsRoot, k as resolveSessionByTitle, m as UID_FILENAME, n as isTrajectoryConstrained, o as readTrajectoryConstraintByCode, r as listTrajectoryConstraints, s as replacesDefaultIdentity, t as TRAJECTORY_CONSTRAINT_FILENAME, v as extractSessionMdPathFromText, w as namingTitleFor, y as findSession, z as readTrajectoryFile } from "./profile-C7BuyZK7.js";
|
|
5
5
|
import { n as checkLocalstoreGitCompliance, o as readStore, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./service-DDmHSglV.js";
|
|
6
|
-
import { _ as ACC_VERSION, a as listBoundSessionIds, d as constraintCognitiveTrajectory, f as constraintSystemPromptSource, g as resolveConstraintSystemPrompt, h as profileToolWhitelist, i as carrierState, l as TRAJECTORY_CONSTRAINT_HINT, n as PLUGIN_SOURCE, o as readLastBound, r as appendBound, s as resolveSessionCognitiveTrajectoryLabel, t as ACC_MESSAGE_KIND, v as findPackageRoot } from "./message-source-
|
|
7
|
-
import { A as buildResourceIndex, C as verifyPassword, D as recordToolUsage, E as verifyTotpCode, F as hasTrajectoryPrompt, M as isSafeSkillName, N as truncateContent, O as REFERENCE_DIR, P as TRAJECTORY_PROMPT_FILENAME, S as toWire, T as MECHANISM_PORTS, _ as registerEntrySkillSectionGlobal, a as trajectoryConstraintFor, b as projectKnownWorkspaces, c as trajectoryWorkspaceMdPath, d as CHEAP_WORKER_GUIDE, f as providerAvailabilityError, g as registerEntrySkillSection, h as registerCognitiveTrajectorySkillSection, i as getTrajectoryAgent, j as buildResourceNameSuffix, k as SKILL_ONTOLOGY_FILE, l as unregisterTrajectorySession, m as splitModel, n as createTrajectoryAgent, o as trajectoryConstraintMsmGate, p as requireWhitelistedModel, r as ensureWorkspacePromptSection, s as trajectoryConstraintParticipates, t as askTrajectory, u as workspaceCognitiveTrajectoryLine, v as applyWirePatch, w as FACE_PORTS, x as readAdvancedSettings, y as migrateLegacyLocalstore } from "./session-
|
|
6
|
+
import { _ as ACC_VERSION, a as listBoundSessionIds, d as constraintCognitiveTrajectory, f as constraintSystemPromptSource, g as resolveConstraintSystemPrompt, h as profileToolWhitelist, i as carrierState, l as TRAJECTORY_CONSTRAINT_HINT, n as PLUGIN_SOURCE, o as readLastBound, r as appendBound, s as resolveSessionCognitiveTrajectoryLabel, t as ACC_MESSAGE_KIND, v as findPackageRoot } from "./message-source-BJO5q-rD.js";
|
|
7
|
+
import { A as buildResourceIndex, C as verifyPassword, D as recordToolUsage, E as verifyTotpCode, F as hasTrajectoryPrompt, M as isSafeSkillName, N as truncateContent, O as REFERENCE_DIR, P as TRAJECTORY_PROMPT_FILENAME, S as toWire, T as MECHANISM_PORTS, _ as registerEntrySkillSectionGlobal, a as trajectoryConstraintFor, b as projectKnownWorkspaces, c as trajectoryWorkspaceMdPath, d as CHEAP_WORKER_GUIDE, f as providerAvailabilityError, g as registerEntrySkillSection, h as registerCognitiveTrajectorySkillSection, i as getTrajectoryAgent, j as buildResourceNameSuffix, k as SKILL_ONTOLOGY_FILE, l as unregisterTrajectorySession, m as splitModel, n as createTrajectoryAgent, o as trajectoryConstraintMsmGate, p as requireWhitelistedModel, r as ensureWorkspacePromptSection, s as trajectoryConstraintParticipates, t as askTrajectory, u as workspaceCognitiveTrajectoryLine, v as applyWirePatch, w as FACE_PORTS, x as readAdvancedSettings, y as migrateLegacyLocalstore } from "./session-B4eZD3Hv.js";
|
|
8
8
|
import { n as hasSessionLogById, r as sessionsRootDir } from "./cleanup-C-92qnDs.js";
|
|
9
9
|
import { n as readSimpleSettings, r as registerSettingsSection } from "./settings-Bb4OnEdI.js";
|
|
10
10
|
import { REGISTERED_TOOLS } from "./invariant.js";
|
|
@@ -7113,6 +7113,102 @@ function createCheapWorkerTool(ctx) {
|
|
|
7113
7113
|
});
|
|
7114
7114
|
}
|
|
7115
7115
|
//#endregion
|
|
7116
|
+
//#region src/trajectory/alive.ts
|
|
7117
|
+
/**
|
|
7118
|
+
* trajectory/alive.ts — **"这个 dsh 会话还活着吗"的唯一判据**(owner 2026-10-20 裁决)
|
|
7119
|
+
*
|
|
7120
|
+
* ## owner 的原话(本模块的全部设计依据)
|
|
7121
|
+
*
|
|
7122
|
+
* > 「死应该包含归档判断;只有 ① 或者 ②且不在归档集 才算活」
|
|
7123
|
+
* > 「use 只要被调用了,可以无条件覆盖绑定到当前会话(这说明当前会话肯定活着啊)」
|
|
7124
|
+
*
|
|
7125
|
+
* ## 为什么必须收成一个判据(在此之前有三个互不一致的判据)
|
|
7126
|
+
*
|
|
7127
|
+
* | # | 判据 | 旧实现位置 | 单独使用时的漏洞 |
|
|
7128
|
+
* |---|---|---|---|
|
|
7129
|
+
* | ① | 进程内 loaded agent | `hostAgents(ctx).get(id)` | 冷会话全判死 |
|
|
7130
|
+
* | ② | 磁盘上有会话日志 | `hasSessionLogById()` | **归档后日志逐字节不动** ⇒ 归档会话判活 |
|
|
7131
|
+
* | ③ | 宿主归档集 | `workspaceRegistry.archivedSessionIds` | 旧 dsp **零引用**(整个仓库 0 处) |
|
|
7132
|
+
*
|
|
7133
|
+
* 宿主的归档语义是**只往集合里加一个 id、不动日志**(`dsh-workspace` 的
|
|
7134
|
+
* `archiveSession` 注释:「Archiving never touches workspace accounting」)。
|
|
7135
|
+
* ⇒ 只问 ② 的实现在归档上**必然判错**,而那个错误没有任何机械信号。
|
|
7136
|
+
*
|
|
7137
|
+
* ## 判据(owner 给定,逐字实现)
|
|
7138
|
+
*
|
|
7139
|
+
* ```text
|
|
7140
|
+
* 活 ⟺ ① (进程内 loaded ⇒ 一定在)
|
|
7141
|
+
* ∨ (② ∧ 不在归档集) (日志在、且没人把它归档掉)
|
|
7142
|
+
* ```
|
|
7143
|
+
*
|
|
7144
|
+
* 🔴 **一处对原话的收紧(必须说出来,不许默默改)**:owner 的 ① 未附加"不在归档集",
|
|
7145
|
+
* 而本模块把**归档判定排在最前** —— 即 `归档 ∧ live ⇒ 判死`。理由:归档是**人对这条会话
|
|
7146
|
+
* 的处置声明**(宿主会把它从所有分组面隐藏),若仍被当成载体,唤醒调度器会去驱动一条
|
|
7147
|
+
* **使用者以为已经收起来**的会话。⚠️ 与字面规则的差异仅此一格;`use` 不受影响
|
|
7148
|
+
* (见下方 §use 逃生门),因此真需要复活一条已归档会话时**仍然有受支持的路**。
|
|
7149
|
+
*
|
|
7150
|
+
* ## 🔴 `use` 的逃生门(owner 第二条,写在调用点)
|
|
7151
|
+
*
|
|
7152
|
+
* **`use` 不查存活**:它被调用这件事本身证明**当前会话活着**(本模块的被测对象是
|
|
7153
|
+
* "别人写的那个 id",而 `use` 手里拿的是**自己**)。⇒ `use` 走无条件覆盖
|
|
7154
|
+
* (`claimBinding` 的 `takeover`),因此**判据再严也不会把一条轨迹锁死**。
|
|
7155
|
+
* 这条逃生门是判据可以取严的前提,两者必须一起存在。
|
|
7156
|
+
*
|
|
7157
|
+
* ## 依赖纪律
|
|
7158
|
+
*
|
|
7159
|
+
* 纯判据 `isSessionAlive` 零依赖、可单测;宿主面只在 `hostAliveProbe` 里,
|
|
7160
|
+
* 且**不抛错**(服务缺失 ⇒ 该维度作废,而不是让 ACC 工具整块失败)。
|
|
7161
|
+
*/
|
|
7162
|
+
/** 宿主侧的归档集读取(不可用 ⇒ 空集 = 该维度作废,**不是**把所有会话判死) */
|
|
7163
|
+
function archivedIdsOf(ctx) {
|
|
7164
|
+
try {
|
|
7165
|
+
const ids = hostService(ctx, "workspaceRegistry")?.archivedSessionIds;
|
|
7166
|
+
return new Set(Array.isArray(ids) ? ids : []);
|
|
7167
|
+
} catch {
|
|
7168
|
+
return /* @__PURE__ */ new Set();
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
/**
|
|
7172
|
+
* **宿主面的判活探针**(`(sessionId) => boolean`)——所有需要"这条会话还活着吗"的
|
|
7173
|
+
* 生产代码都应当用这一个,不要自己拼 `agents.get` + `hasSessionLogById`。
|
|
7174
|
+
*
|
|
7175
|
+
* 语义 = {@link isSessionAlive},其中:
|
|
7176
|
+
* · `liveIds` 因为宿主只给 `get(id)` 而**按 id 逐次问**(不是集合);
|
|
7177
|
+
* · `archivedIds` 在**首次调用时**取一次快照并在闭包内复用(每 tick 一个探针,
|
|
7178
|
+
* 归档集在一个 tick 内不变)—— 避免为每条轨迹重复读存储域。
|
|
7179
|
+
*
|
|
7180
|
+
* ⚠️ **快照的代价**:同一探针内若有人中途归档了某会话,本探针直到下次新建才看见。
|
|
7181
|
+
* 调度器是每 tick 新建一个探针(见 `wake/scheduler.ts`),故实际窗口 ≤1 tick。
|
|
7182
|
+
*
|
|
7183
|
+
* @param ctx 插件上下文(取 `agents` 与 `workspaceRegistry`)
|
|
7184
|
+
* @returns 探针;**永不抛**
|
|
7185
|
+
*/
|
|
7186
|
+
function hostAliveProbe(ctx) {
|
|
7187
|
+
let archived;
|
|
7188
|
+
const archivedOnce = () => archived ??= archivedIdsOf(ctx);
|
|
7189
|
+
return (sessionId) => {
|
|
7190
|
+
if (typeof sessionId !== "string" || sessionId === "") return false;
|
|
7191
|
+
if (archivedOnce().has(sessionId)) return false;
|
|
7192
|
+
try {
|
|
7193
|
+
if (hostAgents(ctx)?.get?.(sessionId)) return true;
|
|
7194
|
+
} catch {}
|
|
7195
|
+
try {
|
|
7196
|
+
return hasSessionLogById(sessionsRootDir(), sessionId);
|
|
7197
|
+
} catch {
|
|
7198
|
+
return true;
|
|
7199
|
+
}
|
|
7200
|
+
};
|
|
7201
|
+
}
|
|
7202
|
+
/**
|
|
7203
|
+
* 判据只有**两个**出口,别再开第三个:
|
|
7204
|
+
* · {@link isSessionAlive} —— 纯判据(给定事实),单测的靶子;
|
|
7205
|
+
* · {@link hostAliveProbe} —— 生产入口(自己去宿主与磁盘取事实)。
|
|
7206
|
+
*
|
|
7207
|
+
* ⚠️ 曾有一个 `aliveFactsFor`(把探针摊平成 `SessionAliveFacts`),**全仓零调用点**,
|
|
7208
|
+
* 已删除 —— 留一个"看起来有用、实际无人调用"的导出,会让下一个人以为存在第二条判活路径
|
|
7209
|
+
* (本仓同族教训:`cheapWorkerPresetInheritance` 全仓零调用点 ⇒ 接线断了却看不出来)。
|
|
7210
|
+
*/
|
|
7211
|
+
//#endregion
|
|
7116
7212
|
//#region src/wake/registry.ts
|
|
7117
7213
|
/**
|
|
7118
7214
|
* wake/registry.ts — 认知轨迹唤醒注册表(D58,S142 2026-09-13)
|
|
@@ -9065,7 +9161,7 @@ function wakeSchedulerState() {
|
|
|
9065
9161
|
*/
|
|
9066
9162
|
async function acquireWakeAgent(ctx, root, dirName) {
|
|
9067
9163
|
const agents = hostAgents(ctx);
|
|
9068
|
-
const ids = listBoundSessionIds(root, dirName);
|
|
9164
|
+
const ids = listBoundSessionIds(root, dirName, hostAliveProbe(ctx));
|
|
9069
9165
|
for (const id of ids) {
|
|
9070
9166
|
const live = agents?.get?.(id);
|
|
9071
9167
|
if (live) return {
|
|
@@ -9221,14 +9317,7 @@ async function sendToCognitiveTrajectory(ctx, root, target, message, sender) {
|
|
|
9221
9317
|
let createdNote = "";
|
|
9222
9318
|
if ("error" in acquired) {
|
|
9223
9319
|
if (!acquired.notReady && acquired.noBinding === true) {
|
|
9224
|
-
const carrier = carrierState(root, t.dirName, (
|
|
9225
|
-
if (hostAgents(ctx)?.get?.(id)) return true;
|
|
9226
|
-
try {
|
|
9227
|
-
return hasSessionLogById(sessionsRootDir(), id);
|
|
9228
|
-
} catch {
|
|
9229
|
-
return true;
|
|
9230
|
-
}
|
|
9231
|
-
});
|
|
9320
|
+
const carrier = carrierState(root, t.dirName, hostAliveProbe(ctx));
|
|
9232
9321
|
if (carrier.state !== "never") {
|
|
9233
9322
|
const why = carrier.state === "occupied" ? `该轨迹曾经由会话 ${carrier.sessionId} 承载,而它此刻不在了` : `该轨迹当前由会话 ${carrier.sessionId} 承载`;
|
|
9234
9323
|
return {
|
|
@@ -9613,7 +9702,7 @@ async function runCroPhase(ctx, root, registry, log) {
|
|
|
9613
9702
|
log.push(`· ${root}: ${dirName}: CRO 机器绑定未通过(${verdict.code}):${verdict.detail}`);
|
|
9614
9703
|
continue;
|
|
9615
9704
|
}
|
|
9616
|
-
const bound = listBoundSessionIds(root, dirName);
|
|
9705
|
+
const bound = listBoundSessionIds(root, dirName, hostAliveProbe(ctx));
|
|
9617
9706
|
const running = runningCarriersOf(bound, nowMs);
|
|
9618
9707
|
const outcome = await evaluateCro(root, dirName, readCroSnapshotInput(root, dirName, nowMs, {
|
|
9619
9708
|
boundSessionIds: bound,
|
|
@@ -10380,10 +10469,12 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
10380
10469
|
const info = getActiveSessionInfo(scope);
|
|
10381
10470
|
if (info) renameDshSessionForActive(ctx, exec, info, args.summary);
|
|
10382
10471
|
const dsh = agentDshSession(exec);
|
|
10383
|
-
|
|
10472
|
+
let bound = null;
|
|
10473
|
+
if (dsh && info) bound = appendBound(dsh, "activate", {
|
|
10384
10474
|
dirName: info.dirName,
|
|
10385
10475
|
mdPath: info.mdPath,
|
|
10386
|
-
sessionId: info.sessionId
|
|
10476
|
+
sessionId: info.sessionId,
|
|
10477
|
+
alive: hostAliveProbe(ctx)
|
|
10387
10478
|
});
|
|
10388
10479
|
const advisory = advisoryHint(targetEntry.dirName, join(targetEntry.path, "SESSION.md"));
|
|
10389
10480
|
const out = {
|
|
@@ -10392,6 +10483,11 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
10392
10483
|
context: active.context
|
|
10393
10484
|
};
|
|
10394
10485
|
if (advisory) out.advisory = advisory;
|
|
10486
|
+
if (bound === true) out.binding = "written";
|
|
10487
|
+
else if (bound === false) {
|
|
10488
|
+
out.binding = "not-written";
|
|
10489
|
+
out.bindingHint = `绑定未写入:本会话取不到可用的 header(id / cwd 缺一不可)⇒ 按契约「绑定声明是唯一真相」,「${info?.dirName ?? targetEntry.dirName}」此刻仍是**未绑定**。 会话标题变了**不能**当作绑定成功的证据(改名走的是另一条通路)。 详见宿主日志里的 \`✗ 绑定未写入(SESSION_HEADER_UNRESOLVED)\`。`;
|
|
10490
|
+
}
|
|
10395
10491
|
return out;
|
|
10396
10492
|
}
|
|
10397
10493
|
case "rebuild": {
|
|
@@ -14652,8 +14748,11 @@ const HOST_SERVICES = [
|
|
|
14652
14748
|
members: [{
|
|
14653
14749
|
name: "list",
|
|
14654
14750
|
kind: "function"
|
|
14751
|
+
}, {
|
|
14752
|
+
name: "archivedSessionIds",
|
|
14753
|
+
kind: "value"
|
|
14655
14754
|
}],
|
|
14656
|
-
impact: "
|
|
14755
|
+
impact: "CCC 自动发现回落失效 + **归档状态不可知**(已归档会话会被误判成活载体)",
|
|
14657
14756
|
required: false
|
|
14658
14757
|
},
|
|
14659
14758
|
{
|
|
@@ -326,6 +326,8 @@ const BIND_FILENAME = "current.dsh_session_bind";
|
|
|
326
326
|
* (上游改名、下游写死旧名 ⇒ 症状与真因在读数上完全不像)。
|
|
327
327
|
*/
|
|
328
328
|
const BIND_FILENAME_FAMILY = [BIND_FILENAME];
|
|
329
|
+
/** 轨迹归档区的目录名(**自持一份,不从 `core.ts` import**)。 */
|
|
330
|
+
const ARCHIVED_DIR_NAME = "_archived";
|
|
329
331
|
/** 校验一条声明(容忍手改 / 部分字段缺失) */
|
|
330
332
|
function asDeclaration(value) {
|
|
331
333
|
const d = value;
|
|
@@ -397,24 +399,44 @@ function clearBindDeclaration(trajectoryDir) {
|
|
|
397
399
|
* 这是**反向查询**(会话 → 轨迹 / 轨迹 → 会话)的唯一入口,取代旧的旁路表扫描。
|
|
398
400
|
* 跳过 `_` / `.` 前缀目录(归档区与运行时区不是轨迹,判据同 `activeTrajectoryDirNames`)。
|
|
399
401
|
*
|
|
402
|
+
* 🔴 **2026-10-20(owner 裁决「死包含归档判断」配套):`_archived/` 必须**再扫一层**。**
|
|
403
|
+
*
|
|
404
|
+
* 修之前:归档目录整体被跳过 ⇒ **已归档轨迹的绑定声明对唯一性检查完全隐形**。
|
|
405
|
+
* 后果:承载过它的 dsh 会话看起来"无归属",可以被 `use` 到另一条轨迹上 ——
|
|
406
|
+
* 这正是 IMMUTABLE 1「一个会话终身只属于一条轨迹」要禁的事,而且是**静默**发生的。
|
|
407
|
+
*
|
|
408
|
+
* 为什么只扫归档区、不扫别的 `_` 目录:归档区里的**确实是轨迹**(只是不再活跃),
|
|
409
|
+
* 而 `_轨迹约束-logs` / `_weixin-logs` / `_corpus` 是日志与数据区,它们里面不会有
|
|
410
|
+
* `current.dsh_session_bind`(真有一个也只是垃圾,读到会被 `asDeclaration` 挡掉)。
|
|
411
|
+
* ⇒ 只多付一次 `_archived` 的 readdir,不改变"活跃清单"的语义(那是 `readAllSessions` 的事)。
|
|
412
|
+
*
|
|
400
413
|
* @param sessionsRoot `<CCC>/AGENT_SESSIONS`
|
|
401
414
|
* @returns 有交代的轨迹;**永不抛**(目录读不到 ⇒ 空表)
|
|
402
415
|
*/
|
|
403
416
|
function listBindDeclarations(sessionsRoot) {
|
|
404
417
|
const out = [];
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
418
|
+
const collect = (dir, into) => {
|
|
419
|
+
let names;
|
|
420
|
+
try {
|
|
421
|
+
names = readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name);
|
|
422
|
+
} catch {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
for (const dirName of names) {
|
|
426
|
+
const decl = readBindDeclaration(join(dir, dirName));
|
|
427
|
+
if (decl) into(dirName, decl);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
collect(sessionsRoot, (dirName, decl) => out.push({
|
|
431
|
+
dirName,
|
|
432
|
+
decl
|
|
433
|
+
}));
|
|
434
|
+
collect(join(sessionsRoot, ARCHIVED_DIR_NAME), (dirName, decl) => {
|
|
435
|
+
if (!out.some((r) => r.dirName === dirName)) out.push({
|
|
414
436
|
dirName,
|
|
415
437
|
decl
|
|
416
438
|
});
|
|
417
|
-
}
|
|
439
|
+
});
|
|
418
440
|
return out;
|
|
419
441
|
}
|
|
420
442
|
/**
|
|
@@ -481,10 +503,24 @@ function findTrajectoryHolding(sessionsRoot, sessionId) {
|
|
|
481
503
|
* `takeover` = 载体换代(`rebuild` / 崩溃后新建),由调用方**显式**声明;
|
|
482
504
|
* 绝不默认允许 —— 静默换代正是"一条轨迹长出两个载体"的成因。
|
|
483
505
|
*
|
|
506
|
+
* 🔴 **2026-10-20(owner 裁决,S196 实测逼出):第 ② 条只在"旧载体还活着"时成立。**
|
|
507
|
+
*
|
|
508
|
+
* 修之前:`claimBinding` **完全不问存活**,只要文件里有别的 `sessionId` 就拒。
|
|
509
|
+
* 而读侧(`listBoundSessionIds(…, alive)`)却按 IMMUTABLE 3 把过期声明当"没绑定"
|
|
510
|
+
* ⇒ **同一条轨迹,两个函数给出相反的答案**。S196 就是撞上这一格:
|
|
511
|
+
* 它的绑定文件指着 `session-52d680ae`(**已在宿主归档集里** = 死),
|
|
512
|
+
* 而 `use` 被 `TRAJECTORY_OCCUPIED` 拒掉,回执还说"成功"(另有一条 D186 的静默问题)。
|
|
513
|
+
*
|
|
514
|
+
* ⇒ 判据(第三条不变量的**写侧形态**):**旧载体会死 ⇒ 本条轨迹算空槽,直接写**。
|
|
515
|
+
* `alive` 不传时**沿用"文件在就算占用"**的旧语义(保守、向后兼容)——
|
|
516
|
+
* ⚠️ 那是"读不到宿主事实"的降级,**不是**"它一定活着";
|
|
517
|
+
* 生产调用点必须显式传 {@link hostAliveProbe}。
|
|
518
|
+
*
|
|
484
519
|
* @param sessionsRoot `<CCC>/AGENT_SESSIONS`
|
|
485
520
|
* @param dirName 目标轨迹目录名
|
|
486
521
|
* @param decl 要写入的声明
|
|
487
522
|
* @param opts.takeover 允许顶掉本条轨迹现有的另一个载体
|
|
523
|
+
* @param opts.alive 探针:**旧载体**是否还活着(缺省 ⇒ 一律当活 = 旧语义)
|
|
488
524
|
* @returns `{ ok: true }` 或 `{ ok: false, code, detail }`(**不抛**,由调用方决定如何呈现)
|
|
489
525
|
*/
|
|
490
526
|
function claimBinding(sessionsRoot, dirName, decl, opts = {}) {
|
|
@@ -495,11 +531,13 @@ function claimBinding(sessionsRoot, dirName, decl, opts = {}) {
|
|
|
495
531
|
detail: `会话 ${decl.sessionId} 已经承载轨迹「${holder}」,不能同时再承载「${dirName}」。一个会话只能属于一条认知轨迹 —— 要给「${dirName}」开一个载体,请新建一条会话。`
|
|
496
532
|
};
|
|
497
533
|
const cur = readBindDeclaration(join(sessionsRoot, dirName));
|
|
498
|
-
if (cur !== null && cur.sessionId !== decl.sessionId && opts.takeover !== true)
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
534
|
+
if (cur !== null && cur.sessionId !== decl.sessionId && opts.takeover !== true) {
|
|
535
|
+
if (opts.alive === void 0 ? true : opts.alive(cur.sessionId)) return {
|
|
536
|
+
ok: false,
|
|
537
|
+
code: "TRAJECTORY_OCCUPIED",
|
|
538
|
+
detail: `轨迹「${dirName}」当前已由会话 ${cur.sessionId} 承载(${cur.action},${new Date(cur.at).toISOString()})。如需换代(rebuild / 重建载体),调用方必须显式声明 takeover —— 静默换代会让一条轨迹出现两个载体。`
|
|
539
|
+
};
|
|
540
|
+
}
|
|
503
541
|
let released;
|
|
504
542
|
if (holder !== null && holder !== dirName) {
|
|
505
543
|
if (clearBindDeclaration(join(sessionsRoot, holder))) released = holder;
|
|
@@ -657,6 +695,13 @@ function resolveSessionCognitiveTrajectoryLabel(session, scope) {
|
|
|
657
695
|
* 那是"读不到宿主事实"的降级,**不是**"会话一定在"。需要严格判的一方请显式传探针
|
|
658
696
|
* (唤醒选人是这种:见 `wake/scheduler.ts`)。
|
|
659
697
|
*
|
|
698
|
+
* 🔴 **2026-10-20(owner 裁决):生产调用点必须传探针,两个调用点都已接上。**
|
|
699
|
+
* 缺省分支**保留**只为两件事:① 既有单测的向后兼容;② 诊断面"只读文件"的显式用法。
|
|
700
|
+
* ⛔ 不要新增不传探针的生产调用点 —— 那等于把"归档会话算活"的漏洞再开一次
|
|
701
|
+
* (本仓同族已踩:`carrierState` + CRO 快照两处都漏传过)。
|
|
702
|
+
* 生产一律用 `trajectory/alive.ts` 的 `hostAliveProbe(ctx)`(判据 = owner 定的
|
|
703
|
+
* 「① 进程内 loaded ∨ ② 日志在且未被归档」)。
|
|
704
|
+
*
|
|
660
705
|
* @param root CCC 根
|
|
661
706
|
* @param dirName 轨迹目录名
|
|
662
707
|
* @param alive 探针:该 dsh 会话 id 此刻是否真实存在
|
|
@@ -678,14 +723,20 @@ function listBoundSessionIds(root, dirName, alive) {
|
|
|
678
723
|
function appendBound(session, action, rec) {
|
|
679
724
|
const id = sessionHeader(session)?.id;
|
|
680
725
|
const root = rootOf(session);
|
|
681
|
-
if (typeof id !== "string" || id === "" || root === null)
|
|
726
|
+
if (typeof id !== "string" || id === "" || root === null) {
|
|
727
|
+
console.warn(`[serenity-hooks] ✗ 绑定未写入(SESSION_HEADER_UNRESOLVED):会话对象取不到可用的 header(id=${typeof id === "string" && id !== "" ? id : "(空/非字符串)"},root=${root ?? "null(header.cwd 缺失或上溯不到 .serenity)"}) ⇒ 轨迹「${rec.dirName}」的 current.dsh_session_bind **未被改写**。 排查:该会话的 header.cwd 是否指向某个含 .serenity 的目录; 无 cwd 的会话(如某些程序性创建的载体)**不会**写绑定。`);
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
682
730
|
const result = claimBinding(sessionsRoot(root), rec.dirName, {
|
|
683
731
|
sessionId: id,
|
|
684
732
|
at: Date.now(),
|
|
685
733
|
action,
|
|
686
734
|
...rec.note !== void 0 ? { note: rec.note } : {},
|
|
687
735
|
...rec.createdAsUser === true ? { createdAsUser: true } : {}
|
|
688
|
-
}, {
|
|
736
|
+
}, {
|
|
737
|
+
takeover: rec.takeover === true,
|
|
738
|
+
...rec.alive !== void 0 ? { alive: rec.alive } : {}
|
|
739
|
+
});
|
|
689
740
|
if (!result.ok) {
|
|
690
741
|
console.warn(`[serenity-hooks] ✗ 绑定未写入(${result.code}):${result.detail}`);
|
|
691
742
|
return false;
|
|
@@ -2,7 +2,7 @@ import { n as agentCwdFor, r as cccRootForCwd, s as hostAgents } from "./ccc-roo
|
|
|
2
2
|
import { g as readUtf8, o as isSafeModeOn, p as readCognitiveTrajectorySkills, u as readBlacklist } from "./ccc-CyYJL4F2.js";
|
|
3
3
|
import { B as isoLocal, D as readActiveSessionMd, a as readTrajectoryConstraintAt, c as trajectoryConstraintForSession, d as unregisterTrajectorySession$1, j as sessionsRoot, l as registerTrajectorySession$1, p as DEFAULT_SESSION_SCOPE, u as trajectoryDirNameForSession } from "./profile-C7BuyZK7.js";
|
|
4
4
|
import { a as readGitTrack, i as localstorePath } from "./service-DDmHSglV.js";
|
|
5
|
-
import { _ as ACC_VERSION, d as constraintCognitiveTrajectory, m as profileMsmWhitelist, n as PLUGIN_SOURCE, o as readLastBound, p as createProfilePromptReader, u as buildTrajectoryBasePrompt } from "./message-source-
|
|
5
|
+
import { _ as ACC_VERSION, d as constraintCognitiveTrajectory, m as profileMsmWhitelist, n as PLUGIN_SOURCE, o as readLastBound, p as createProfilePromptReader, u as buildTrajectoryBasePrompt } from "./message-source-BJO5q-rD.js";
|
|
6
6
|
import { basename, dirname, join, resolve } from "node:path";
|
|
7
7
|
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trajectory/alive.ts — **"这个 dsh 会话还活着吗"的唯一判据**(owner 2026-10-20 裁决)
|
|
3
|
+
*
|
|
4
|
+
* ## owner 的原话(本模块的全部设计依据)
|
|
5
|
+
*
|
|
6
|
+
* > 「死应该包含归档判断;只有 ① 或者 ②且不在归档集 才算活」
|
|
7
|
+
* > 「use 只要被调用了,可以无条件覆盖绑定到当前会话(这说明当前会话肯定活着啊)」
|
|
8
|
+
*
|
|
9
|
+
* ## 为什么必须收成一个判据(在此之前有三个互不一致的判据)
|
|
10
|
+
*
|
|
11
|
+
* | # | 判据 | 旧实现位置 | 单独使用时的漏洞 |
|
|
12
|
+
* |---|---|---|---|
|
|
13
|
+
* | ① | 进程内 loaded agent | `hostAgents(ctx).get(id)` | 冷会话全判死 |
|
|
14
|
+
* | ② | 磁盘上有会话日志 | `hasSessionLogById()` | **归档后日志逐字节不动** ⇒ 归档会话判活 |
|
|
15
|
+
* | ③ | 宿主归档集 | `workspaceRegistry.archivedSessionIds` | 旧 dsp **零引用**(整个仓库 0 处) |
|
|
16
|
+
*
|
|
17
|
+
* 宿主的归档语义是**只往集合里加一个 id、不动日志**(`dsh-workspace` 的
|
|
18
|
+
* `archiveSession` 注释:「Archiving never touches workspace accounting」)。
|
|
19
|
+
* ⇒ 只问 ② 的实现在归档上**必然判错**,而那个错误没有任何机械信号。
|
|
20
|
+
*
|
|
21
|
+
* ## 判据(owner 给定,逐字实现)
|
|
22
|
+
*
|
|
23
|
+
* ```text
|
|
24
|
+
* 活 ⟺ ① (进程内 loaded ⇒ 一定在)
|
|
25
|
+
* ∨ (② ∧ 不在归档集) (日志在、且没人把它归档掉)
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* 🔴 **一处对原话的收紧(必须说出来,不许默默改)**:owner 的 ① 未附加"不在归档集",
|
|
29
|
+
* 而本模块把**归档判定排在最前** —— 即 `归档 ∧ live ⇒ 判死`。理由:归档是**人对这条会话
|
|
30
|
+
* 的处置声明**(宿主会把它从所有分组面隐藏),若仍被当成载体,唤醒调度器会去驱动一条
|
|
31
|
+
* **使用者以为已经收起来**的会话。⚠️ 与字面规则的差异仅此一格;`use` 不受影响
|
|
32
|
+
* (见下方 §use 逃生门),因此真需要复活一条已归档会话时**仍然有受支持的路**。
|
|
33
|
+
*
|
|
34
|
+
* ## 🔴 `use` 的逃生门(owner 第二条,写在调用点)
|
|
35
|
+
*
|
|
36
|
+
* **`use` 不查存活**:它被调用这件事本身证明**当前会话活着**(本模块的被测对象是
|
|
37
|
+
* "别人写的那个 id",而 `use` 手里拿的是**自己**)。⇒ `use` 走无条件覆盖
|
|
38
|
+
* (`claimBinding` 的 `takeover`),因此**判据再严也不会把一条轨迹锁死**。
|
|
39
|
+
* 这条逃生门是判据可以取严的前提,两者必须一起存在。
|
|
40
|
+
*
|
|
41
|
+
* ## 依赖纪律
|
|
42
|
+
*
|
|
43
|
+
* 纯判据 `isSessionAlive` 零依赖、可单测;宿主面只在 `hostAliveProbe` 里,
|
|
44
|
+
* 且**不抛错**(服务缺失 ⇒ 该维度作废,而不是让 ACC 工具整块失败)。
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* 判活所需的三个事实(**纯数据**,由调用方去宿主/磁盘取)。
|
|
48
|
+
*
|
|
49
|
+
* 分开的理由与 `bind-file.ts` 的 `isBindUsable` 同款:把"判断"与"取数"拆开,
|
|
50
|
+
* 判据可单测、不依赖 cordis。
|
|
51
|
+
*/
|
|
52
|
+
export interface SessionAliveFacts {
|
|
53
|
+
/** ① 进程内 loaded(`hostAgents.get` 命中即算) */
|
|
54
|
+
readonly liveIds: ReadonlySet<string>;
|
|
55
|
+
/** ③ 宿主归档集(`workspaceRegistry.archivedSessionIds`) */
|
|
56
|
+
readonly archivedIds: ReadonlySet<string>;
|
|
57
|
+
/** ② 磁盘上还有这条会话的日志(`hasSessionLogById`) */
|
|
58
|
+
readonly hasLog: (sessionId: string) => boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* **唯一的存活判据**(owner 2026-10-20)。
|
|
62
|
+
*
|
|
63
|
+
* @param sessionId dsh 会话 id(`session-<uuid>`,或 `skiff-*` 这类无前缀的)
|
|
64
|
+
* @param facts 三个事实
|
|
65
|
+
* @returns 活 ⇒ true
|
|
66
|
+
*
|
|
67
|
+
* 🔴 求值顺序是**判据的一部分**(不是优化):归档先于 live,
|
|
68
|
+
* 因为"归档"是对会话的**处置声明**,而 live 只说明字节还在内存里。
|
|
69
|
+
*/
|
|
70
|
+
export declare function isSessionAlive(sessionId: string, facts: SessionAliveFacts): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* **宿主面的判活探针**(`(sessionId) => boolean`)——所有需要"这条会话还活着吗"的
|
|
73
|
+
* 生产代码都应当用这一个,不要自己拼 `agents.get` + `hasSessionLogById`。
|
|
74
|
+
*
|
|
75
|
+
* 语义 = {@link isSessionAlive},其中:
|
|
76
|
+
* · `liveIds` 因为宿主只给 `get(id)` 而**按 id 逐次问**(不是集合);
|
|
77
|
+
* · `archivedIds` 在**首次调用时**取一次快照并在闭包内复用(每 tick 一个探针,
|
|
78
|
+
* 归档集在一个 tick 内不变)—— 避免为每条轨迹重复读存储域。
|
|
79
|
+
*
|
|
80
|
+
* ⚠️ **快照的代价**:同一探针内若有人中途归档了某会话,本探针直到下次新建才看见。
|
|
81
|
+
* 调度器是每 tick 新建一个探针(见 `wake/scheduler.ts`),故实际窗口 ≤1 tick。
|
|
82
|
+
*
|
|
83
|
+
* @param ctx 插件上下文(取 `agents` 与 `workspaceRegistry`)
|
|
84
|
+
* @returns 探针;**永不抛**
|
|
85
|
+
*/
|
|
86
|
+
export declare function hostAliveProbe(ctx: unknown): (sessionId: string) => boolean;
|
|
87
|
+
/**
|
|
88
|
+
* 判据只有**两个**出口,别再开第三个:
|
|
89
|
+
* · {@link isSessionAlive} —— 纯判据(给定事实),单测的靶子;
|
|
90
|
+
* · {@link hostAliveProbe} —— 生产入口(自己去宿主与磁盘取事实)。
|
|
91
|
+
*
|
|
92
|
+
* ⚠️ 曾有一个 `aliveFactsFor`(把探针摊平成 `SessionAliveFacts`),**全仓零调用点**,
|
|
93
|
+
* 已删除 —— 留一个"看起来有用、实际无人调用"的导出,会让下一个人以为存在第二条判活路径
|
|
94
|
+
* (本仓同族教训:`cheapWorkerPresetInheritance` 全仓零调用点 ⇒ 接线断了却看不出来)。
|
|
95
|
+
*/
|
|
@@ -85,6 +85,8 @@ export declare const BIND_FILENAME = "current.dsh_session_bind";
|
|
|
85
85
|
* (上游改名、下游写死旧名 ⇒ 症状与真因在读数上完全不像)。
|
|
86
86
|
*/
|
|
87
87
|
export declare const BIND_FILENAME_FAMILY: readonly ["current.dsh_session_bind"];
|
|
88
|
+
/** 轨迹归档区的目录名(**自持一份,不从 `core.ts` import**)。 */
|
|
89
|
+
export declare const ARCHIVED_DIR_NAME = "_archived";
|
|
88
90
|
/**
|
|
89
91
|
* 绑定文件的内容(**自我交代**:不只是"谁",还有"何时、为何、谁建的")。
|
|
90
92
|
*
|
|
@@ -129,6 +131,17 @@ export declare function clearBindDeclaration(trajectoryDir: string): boolean;
|
|
|
129
131
|
* 这是**反向查询**(会话 → 轨迹 / 轨迹 → 会话)的唯一入口,取代旧的旁路表扫描。
|
|
130
132
|
* 跳过 `_` / `.` 前缀目录(归档区与运行时区不是轨迹,判据同 `activeTrajectoryDirNames`)。
|
|
131
133
|
*
|
|
134
|
+
* 🔴 **2026-10-20(owner 裁决「死包含归档判断」配套):`_archived/` 必须**再扫一层**。**
|
|
135
|
+
*
|
|
136
|
+
* 修之前:归档目录整体被跳过 ⇒ **已归档轨迹的绑定声明对唯一性检查完全隐形**。
|
|
137
|
+
* 后果:承载过它的 dsh 会话看起来"无归属",可以被 `use` 到另一条轨迹上 ——
|
|
138
|
+
* 这正是 IMMUTABLE 1「一个会话终身只属于一条轨迹」要禁的事,而且是**静默**发生的。
|
|
139
|
+
*
|
|
140
|
+
* 为什么只扫归档区、不扫别的 `_` 目录:归档区里的**确实是轨迹**(只是不再活跃),
|
|
141
|
+
* 而 `_轨迹约束-logs` / `_weixin-logs` / `_corpus` 是日志与数据区,它们里面不会有
|
|
142
|
+
* `current.dsh_session_bind`(真有一个也只是垃圾,读到会被 `asDeclaration` 挡掉)。
|
|
143
|
+
* ⇒ 只多付一次 `_archived` 的 readdir,不改变"活跃清单"的语义(那是 `readAllSessions` 的事)。
|
|
144
|
+
*
|
|
132
145
|
* @param sessionsRoot `<CCC>/AGENT_SESSIONS`
|
|
133
146
|
* @returns 有交代的轨迹;**永不抛**(目录读不到 ⇒ 空表)
|
|
134
147
|
*/
|
|
@@ -210,14 +223,29 @@ export declare function findTrajectoryHolding(sessionsRoot: string, sessionId: s
|
|
|
210
223
|
* `takeover` = 载体换代(`rebuild` / 崩溃后新建),由调用方**显式**声明;
|
|
211
224
|
* 绝不默认允许 —— 静默换代正是"一条轨迹长出两个载体"的成因。
|
|
212
225
|
*
|
|
226
|
+
* 🔴 **2026-10-20(owner 裁决,S196 实测逼出):第 ② 条只在"旧载体还活着"时成立。**
|
|
227
|
+
*
|
|
228
|
+
* 修之前:`claimBinding` **完全不问存活**,只要文件里有别的 `sessionId` 就拒。
|
|
229
|
+
* 而读侧(`listBoundSessionIds(…, alive)`)却按 IMMUTABLE 3 把过期声明当"没绑定"
|
|
230
|
+
* ⇒ **同一条轨迹,两个函数给出相反的答案**。S196 就是撞上这一格:
|
|
231
|
+
* 它的绑定文件指着 `session-52d680ae`(**已在宿主归档集里** = 死),
|
|
232
|
+
* 而 `use` 被 `TRAJECTORY_OCCUPIED` 拒掉,回执还说"成功"(另有一条 D186 的静默问题)。
|
|
233
|
+
*
|
|
234
|
+
* ⇒ 判据(第三条不变量的**写侧形态**):**旧载体会死 ⇒ 本条轨迹算空槽,直接写**。
|
|
235
|
+
* `alive` 不传时**沿用"文件在就算占用"**的旧语义(保守、向后兼容)——
|
|
236
|
+
* ⚠️ 那是"读不到宿主事实"的降级,**不是**"它一定活着";
|
|
237
|
+
* 生产调用点必须显式传 {@link hostAliveProbe}。
|
|
238
|
+
*
|
|
213
239
|
* @param sessionsRoot `<CCC>/AGENT_SESSIONS`
|
|
214
240
|
* @param dirName 目标轨迹目录名
|
|
215
241
|
* @param decl 要写入的声明
|
|
216
242
|
* @param opts.takeover 允许顶掉本条轨迹现有的另一个载体
|
|
243
|
+
* @param opts.alive 探针:**旧载体**是否还活着(缺省 ⇒ 一律当活 = 旧语义)
|
|
217
244
|
* @returns `{ ok: true }` 或 `{ ok: false, code, detail }`(**不抛**,由调用方决定如何呈现)
|
|
218
245
|
*/
|
|
219
246
|
export declare function claimBinding(sessionsRoot: string, dirName: string, decl: BindDeclaration, opts?: {
|
|
220
247
|
takeover?: boolean;
|
|
248
|
+
alive?: (sessionId: string) => boolean;
|
|
221
249
|
}): {
|
|
222
250
|
ok: true;
|
|
223
251
|
released?: string;
|
|
@@ -91,6 +91,13 @@ export declare function resolveSessionCognitiveTrajectoryLabel(session: unknown,
|
|
|
91
91
|
* 那是"读不到宿主事实"的降级,**不是**"会话一定在"。需要严格判的一方请显式传探针
|
|
92
92
|
* (唤醒选人是这种:见 `wake/scheduler.ts`)。
|
|
93
93
|
*
|
|
94
|
+
* 🔴 **2026-10-20(owner 裁决):生产调用点必须传探针,两个调用点都已接上。**
|
|
95
|
+
* 缺省分支**保留**只为两件事:① 既有单测的向后兼容;② 诊断面"只读文件"的显式用法。
|
|
96
|
+
* ⛔ 不要新增不传探针的生产调用点 —— 那等于把"归档会话算活"的漏洞再开一次
|
|
97
|
+
* (本仓同族已踩:`carrierState` + CRO 快照两处都漏传过)。
|
|
98
|
+
* 生产一律用 `trajectory/alive.ts` 的 `hostAliveProbe(ctx)`(判据 = owner 定的
|
|
99
|
+
* 「① 进程内 loaded ∨ ② 日志在且未被归档」)。
|
|
100
|
+
*
|
|
94
101
|
* @param root CCC 根
|
|
95
102
|
* @param dirName 轨迹目录名
|
|
96
103
|
* @param alive 探针:该 dsh 会话 id 此刻是否真实存在
|
|
@@ -129,6 +136,14 @@ export declare function appendBound(session: unknown, action: SessionBoundAction
|
|
|
129
136
|
createdAsUser?: boolean;
|
|
130
137
|
/** 允许顶掉该轨迹现有的另一个载体(rebuild / 崩溃后重建载体时**必须显式**声明) */
|
|
131
138
|
takeover?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* 探针:该轨迹**现有载体**是否还活着(决定"算不算占用")。
|
|
141
|
+
*
|
|
142
|
+
* 🔴 2026-10-20(owner 裁决):**死载体不构成占用**。
|
|
143
|
+
* 不传 ⇒ 同一条轨迹现有的任何声明都算占用(旧语义,保守拒绝)。
|
|
144
|
+
* 生产调用点应当传 {@link hostAliveProbe}(`trajectory/alive.ts`)。
|
|
145
|
+
*/
|
|
146
|
+
alive?: (sessionId: string) => boolean;
|
|
132
147
|
}): boolean;
|
|
133
148
|
/**
|
|
134
149
|
* 这条轨迹**是否曾经有过载体**(判"要不要允许自建")。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.79.
|
|
3
|
+
"version": "1.79.2",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/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": {
|