@shgroup/dsh-serenity-hooks 1.65.0 → 1.66.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/client.js +43 -1
- package/lib/cognitive-trajectory-ops.d.ts +92 -13
- package/lib/index.js +31 -16
- package/lib/{message-source--2veY0JU.js → message-source-DM9-2eUT.js} +128 -44
- package/lib/status.d.ts +8 -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.66.0",
|
|
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 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/官方 ACP 面/认知轨迹唤醒注册表。适配 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/client.js
CHANGED
|
@@ -8,7 +8,7 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
//#region \0sp-css:src/client/SafeModePanel
|
|
11
|
-
const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n width: min(680px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n max-height: calc(100vh - 120px);\n}\n/* 左栏:信息(正文)——固定 340px,与旧卡片同宽,保证原有排印不变 */\n.sp-popVoyage > .sp-popBody {\n position: relative;\n z-index: 1;\n order: 1;\n flex: none;\n width: 340px;\n min-width: 0;\n}\n/* 右栏:动画——吃掉剩余宽度,并吃满左栏高度 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n min-height: 260px; /* 动画的下限:信息很少时也不塌成一条 */\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(420px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-popBody {\n width: auto; /* 单栏下不再固定 340px,跟随卡片宽度 */\n flex: none; /* 按内容高(仍受自身 max-height 约束) */\n }\n .sp-voyage {\n flex: none;\n height: 200px; /* 单栏时给动画明确高度,避免被 flex 压扁 */\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n /* 🔴 曾写死 `width: 76px` ⇒ 「CheapWorker 模型」这类长标签会**溢出到数值上**\n (nowrap + 定宽 ⇒ 文字直接压过去,不换行也不报错)。\n 改 `min-width`:短标签仍在同一列对齐,长标签自己撑开,绝不压数值。 */\n flex: none;\n min-width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* 🔴 2026-10-14:`.sp-cheapWorkers*` / `.sp-cheapWorker*` 样式区块已随 background 模式删除\n —— 那是「运行中 cheap-worker」列表(进度文件驱动)的样式,而进度文件不再有生产者。 */\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
11
|
+
const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n /* 🔴 2026-10-18(owner:「可以到 1000px 宽,长度更长些,允许存在滚动条」):\n 680 → **1000px**。这是 CCC 状态面板的**容器**,未来更多 CCC 内容都装在这里,\n ⇒ 宽度按\"要承载什么\"给,不按\"当前装了多少\"给。\n 窄视口仍走 `calc(100vw - 24px)` 兜底(不因追求宽度而在小屏上溢出)。 */\n width: min(1000px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n /* 🔴 2026-10-18(owner:「长度更长些,允许存在滚动条」):120 → **80**。\n 上限贴着视口走,把能给的垂直空间尽量给出去;真正超长时由**内部两栏各自滚动**消化\n (左栏 `.sp-popBody` 有 overflow-y:auto,右栏动画吃满行高)——卡片本身仍不溢出视口。 */\n max-height: calc(100vh - 80px);\n}\n/* 左栏:信息(正文)——固定 340px,与旧卡片同宽,保证原有排印不变 */\n.sp-popVoyage > .sp-popBody {\n position: relative;\n z-index: 1;\n order: 1;\n flex: none;\n width: 340px;\n min-width: 0;\n}\n/* 右栏:动画——吃掉剩余宽度,并吃满左栏高度 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n /* 🔴 2026-10-18:下限 260 → **360**。卡片加高后动画也该跟着长起来,\n 否则\"加高\"只换来左栏更多留白、飞船还是原来那么大一块。 */\n min-height: 360px;\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(520px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-popBody {\n width: auto; /* 单栏下不再固定 340px,跟随卡片宽度 */\n flex: none; /* 按内容高(仍受自身 max-height 约束) */\n }\n .sp-voyage {\n flex: none;\n height: 200px; /* 单栏时给动画明确高度,避免被 flex 压扁 */\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* ── 活跃轨迹清单(owner 2026-10-18 需求二)─────────────────────────\n 形态:每行 = [S### 定宽] [目录名 伸缩] [天数 定宽右对齐] [空壳标记 条件]\n · 用 mono 字体:目录名与 S### 是**标识符**,等宽才扫得快\n · 名字用 `text-overflow: ellipsis` 单行截断 + `title` 全文 —— 目录名很长时\n 宁可行内截断,也不让一行折成三四行把整个清单撑得读不下去\n · 整个列表**有上限高度 + 自身滚动**:轨迹会越攒越多,而它的容器(左栏)不该跟着无限长 */\n.sp-count {\n font-size: 11px;\n line-height: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n padding: 0 6px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 99px;\n}\n\n.sp-trajList {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n /* 清单自身封顶:活跃轨迹会越攒越多,超出后本列表滚动,不把左栏顶长 */\n max-height: 240px;\n overflow-y: auto;\n}\n\n.sp-trajItem {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n padding: 5px 8px;\n border-radius: 8px;\n font-family: var(--ds-font-family-code, monospace);\n font-size: 11px;\n line-height: 16px;\n}\n.sp-trajItem:hover {\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-trajId {\n flex: none;\n min-width: 40px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-trajName {\n flex: 1;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sp-trajAge {\n flex: none;\n color: var(--dsw-alias-label-secondary);\n text-align: right;\n min-width: 32px;\n}\n\n/* 空壳标记:目录在但没有 SESSION.md —— 这是**真异常**(激活会失败),不是阶段。\n ⚠️ 因此用告警色,而不是任何\"状态\"色。 */\n.sp-trajWarn {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: #fff;\n background: #c2410c;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n /* 🔴 曾写死 `width: 76px` ⇒ 「CheapWorker 模型」这类长标签会**溢出到数值上**\n (nowrap + 定宽 ⇒ 文字直接压过去,不换行也不报错)。\n 改 `min-width`:短标签仍在同一列对齐,长标签自己撑开,绝不压数值。 */\n flex: none;\n min-width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* 🔴 2026-10-14:`.sp-cheapWorkers*` / `.sp-cheapWorker*` 样式区块已随 background 模式删除\n —— 那是「运行中 cheap-worker」列表(进度文件驱动)的样式,而进度文件不再有生产者。 */\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
12
12
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SafeModePanel\"]") === null) {
|
|
13
13
|
const tag = document.createElement("style");
|
|
14
14
|
tag.setAttribute("data-sp-css", "SafeModePanel");
|
|
@@ -57,6 +57,7 @@ window.__ModuleLoader__.load({
|
|
|
57
57
|
const popRef = (0, react.useRef)(null);
|
|
58
58
|
const [popPos, setPopPos] = (0, react.useState)(null);
|
|
59
59
|
const sessionId = props.sessionId;
|
|
60
|
+
const trajectories = status?.trajectories ?? [];
|
|
60
61
|
const refresh = (0, react.useCallback)(async () => {
|
|
61
62
|
const qs = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
|
|
62
63
|
try {
|
|
@@ -279,6 +280,47 @@ window.__ModuleLoader__.load({
|
|
|
279
280
|
})]
|
|
280
281
|
})]
|
|
281
282
|
}),
|
|
283
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
284
|
+
className: cx("sp-group"),
|
|
285
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
286
|
+
className: cx("sp-groupHead"),
|
|
287
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
288
|
+
className: cx("sp-groupTitle"),
|
|
289
|
+
children: "活跃轨迹"
|
|
290
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
291
|
+
className: cx("sp-count"),
|
|
292
|
+
children: trajectories.length
|
|
293
|
+
})]
|
|
294
|
+
}), trajectories.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
295
|
+
className: cx("sp-hint"),
|
|
296
|
+
children: "(暂无活跃轨迹)"
|
|
297
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
298
|
+
className: cx("sp-trajList"),
|
|
299
|
+
children: trajectories.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
300
|
+
className: cx("sp-trajItem"),
|
|
301
|
+
children: [
|
|
302
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
303
|
+
className: cx("sp-trajId"),
|
|
304
|
+
children: t.sessionId || "—"
|
|
305
|
+
}),
|
|
306
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
307
|
+
className: cx("sp-trajName"),
|
|
308
|
+
title: t.dirName,
|
|
309
|
+
children: t.dirName
|
|
310
|
+
}),
|
|
311
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
312
|
+
className: cx("sp-trajAge"),
|
|
313
|
+
children: [t.ageDays, "d"]
|
|
314
|
+
}),
|
|
315
|
+
!t.hasSessionMd && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
316
|
+
className: cx("sp-trajWarn"),
|
|
317
|
+
title: "目录存在但没有 SESSION.md(空壳)",
|
|
318
|
+
children: "空壳"
|
|
319
|
+
})
|
|
320
|
+
]
|
|
321
|
+
}, t.dirName))
|
|
322
|
+
})]
|
|
323
|
+
}),
|
|
282
324
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
283
325
|
className: cx("sp-hint"),
|
|
284
326
|
children: [
|
|
@@ -9,7 +9,30 @@
|
|
|
9
9
|
* 其函数(closeSession / archiveSessions / healthCheck / qaCheck)**同批删除**——
|
|
10
10
|
* 保留下来的只有:create(--desc / --issue 二选一、dry-run 预览)、
|
|
11
11
|
* list(含 summarize 仪表盘)、show、use(激活 + 绑定)、以及供 list 用的 summarize。
|
|
12
|
-
*
|
|
12
|
+
*
|
|
13
|
+
* ## 🔴 2026-10-18(owner 令):**认知轨迹没有生命周期**
|
|
14
|
+
*
|
|
15
|
+
* owner 原话要点:*"我需要允许永存的认知轨迹……我希望该生命周期足够简单,即没有生命周期,
|
|
16
|
+
* 但允许轨迹本身被操作为归档"*。据此**删除**了以下全部"生命周期"语义:
|
|
17
|
+
*
|
|
18
|
+
* | 被删的 | 为什么它属于生命周期 |
|
|
19
|
+
* |---|---|
|
|
20
|
+
* | `completed` 解析(SESSION.md 里的方括号小写 x) | "完成"是一个**终态**;没有生命周期就没有终态 |
|
|
21
|
+
* | `summarize` 的 Active / Completed / Stale / Ghost 四项计数 | 四项全部在给轨迹分"阶段" |
|
|
22
|
+
* | `advisoryHint` 的「N 天未更新」告警 | 老化告警的前提是"旧轨迹该退休了"——现在**旧不是问题** |
|
|
23
|
+
* | `findLatestActiveSessionMd`(**全仓零消费者**,纯死代码) | 它的语义是"最新的**未完成**轨迹",前提已不存在 |
|
|
24
|
+
*
|
|
25
|
+
* **保留**:SESSION.md 头部的状态行**照旧生成**(`- 状态: [ ] 进行中`),但**不再被任何代码解析**。
|
|
26
|
+
* 理由(owner 裁决):格式最小变动、老文件完全不用改。⇒ 它从"机器契约"降为**惰性文本**,
|
|
27
|
+
* 且"进行中"对永存轨迹**恒为真**,因此不构成谎言。
|
|
28
|
+
*
|
|
29
|
+
* **新增唯一的操作**:`archiveSession()` —— 单向移入 `_archived/`。
|
|
30
|
+
* 归档是**唯一的"变化"**,且它不属于轨迹自身:轨迹本体(目录 + SESSION.md)**永不被改**,
|
|
31
|
+
* 只是**换了个存放位置**、从清单里消失。恢复 = 手工 `container_fs mv` 移回(owner 裁决:不设 unarchive)。
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ 由此产生一条**必须成立**的配套:`readAllSessions` **跳过 `_` 前缀目录**。
|
|
34
|
+
* 否则归档后的轨迹会继续出现在清单里,"归档"就不是归档了(历史 bug:`_weixin-logs` /
|
|
35
|
+
* `_skiff-logs` / `_corpus` 曾被当成轨迹列出来)。
|
|
13
36
|
*/
|
|
14
37
|
/**
|
|
15
38
|
* `cognitive_trajectory` 工具的动作集合(v1.33,S142 §32 用户裁决后的收敛结果)。
|
|
@@ -37,7 +60,7 @@
|
|
|
37
60
|
* 而**管轨迹的人正是用本工具的人**;挂这儿 = 设计 §9-5 的"**既有 guide 位置、不新增域**"。
|
|
38
61
|
* ⚠️ 本动作**纯读**(输出指南正文,不碰 fs、不碰注册表)。
|
|
39
62
|
*/
|
|
40
|
-
type CognitiveTrajectoryAction = 'list' | 'show' | 'create' | 'use' | 'rebuild' | 'send-now' | 'send-later' | 'cro-guide';
|
|
63
|
+
type CognitiveTrajectoryAction = 'list' | 'show' | 'create' | 'use' | 'rebuild' | 'archive' | 'send-now' | 'send-later' | 'cro-guide';
|
|
41
64
|
export declare const COGNITIVE_TRAJECTORY_ACTIONS: readonly CognitiveTrajectoryAction[];
|
|
42
65
|
/**
|
|
43
66
|
* 读取 Session 事件序列(v1.28.1 适配 0.1.2-rc.1 补齐):rc.1 起官方 Session 类
|
|
@@ -50,15 +73,17 @@ export declare const COGNITIVE_TRAJECTORY_ACTIONS: readonly CognitiveTrajectoryA
|
|
|
50
73
|
* 泛型 T:调用方按需声明事件形状(如 `SessionEvent`),unknown 默认。
|
|
51
74
|
*/
|
|
52
75
|
export declare function sessionEvents<T = unknown>(session: unknown): readonly T[];
|
|
76
|
+
/** 归档区目录名(`_` 前缀 ⇒ 不是轨迹,见 readAllSessions 的跳过判据) */
|
|
77
|
+
export declare const ARCHIVED_DIR = "_archived";
|
|
53
78
|
export declare function sessionsRoot(root: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* 轨迹的**存在形态**(🔴 **2026-10-18 起不再是"生命周期"**)。
|
|
81
|
+
*
|
|
82
|
+
* `hasSessionMd` 是唯一的判据,而且它**不是阶段**:它只说"这个目录里有没有正文"。
|
|
83
|
+
* 旧字段 `completed` 已删除(见文件头「认知轨迹没有生命周期」)。
|
|
84
|
+
*/
|
|
54
85
|
interface SessionStatus {
|
|
55
86
|
hasSessionMd: boolean;
|
|
56
|
-
/**
|
|
57
|
-
* 「完成」的判据 = SESSION.md 里**出现 `[x]`**(v1.33 起不再由 close 动作写入)。
|
|
58
|
-
* 🔴 这是**全文匹配**、两代格式通用:新格式的状态行 `- 状态: [x] …` 与旧格式的
|
|
59
|
-
* `## 状态` 段内 `- [x] 已完成` 都能被识别 ⇒ **不可改成锚定某一行的正则**(会让旧会话永远"未完成")。
|
|
60
|
-
*/
|
|
61
|
-
completed: boolean;
|
|
62
87
|
}
|
|
63
88
|
interface SessionEntry {
|
|
64
89
|
dirName: string;
|
|
@@ -174,11 +199,65 @@ export declare function extractSessionMdPathFromText(text: string): string | nul
|
|
|
174
199
|
*/
|
|
175
200
|
export declare function resolveSessionByTitle(title: string, sessionsDir: string): string | null;
|
|
176
201
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
202
|
+
* summary 仪表盘(供 `list` 动作拼接;v1.33 起独立 `summary` 动作已撤,并入 list)。
|
|
203
|
+
*
|
|
204
|
+
* 🔴 **2026-10-18:四项生命周期计数已删**(Active / Completed / Stale / Ghost)。
|
|
205
|
+
* 它们全都在给轨迹**分阶段**,而 owner 裁决「没有生命周期」⇒ 唯一有意义的总数是 `Total`。
|
|
206
|
+
* ⚠️ **`Ghost` 也被删了**("目录在但没 SESSION.md")—— 它不是阶段而是**损坏**,但:
|
|
207
|
+
* · 真出现时 `use` 会在 `advisoryHint` 里**当场说清**(用到才发现,比长期挂一个计数更有效);
|
|
208
|
+
* · 挂一个恒 0 的计数只会钝化注意力。
|
|
209
|
+
* `Total` 现在 = **活跃轨迹数**(`_` 前缀的归档区与日志区已不在域内)。
|
|
180
210
|
*/
|
|
181
|
-
export declare function findLatestActiveSessionMd(root: string): string | null;
|
|
182
|
-
/** summary 仪表盘(供 `list` 动作拼接;v1.33 起独立 `summary` 动作已撤,并入 list) */
|
|
183
211
|
export declare function summarize(root: string): string;
|
|
212
|
+
/**
|
|
213
|
+
* 活跃轨迹的**结构化**清单(面板消费;owner 2026-10-18 需求二)。
|
|
214
|
+
*
|
|
215
|
+
* 🔴 为什么不在面板侧重新扫目录:**"哪些轨迹是活跃的"只有一个判据源** =
|
|
216
|
+
* `readAllSessions`(它已经实现 `_` 前缀跳过 = 归档区不在域内)。面板若自己扫一遍
|
|
217
|
+
* `AGENT_SESSIONS/`,就会长出**第二份会漂的实现** —— 归档之后面板仍会列出它,
|
|
218
|
+
* 而这种不一致**没有任何机械信号**(两边各自都"跑成功了")。
|
|
219
|
+
*
|
|
220
|
+
* 字段都取自**文件系统事实**,不做任何推断:
|
|
221
|
+
* `dirName` / `sessionId`(S### ,无则 '')/ `ageDays`(目录 mtime 距今天数)/ `hasSessionMd`。
|
|
222
|
+
* ⚠️ **没有 `completed`** —— 轨迹没有生命周期(见文件头)。
|
|
223
|
+
*
|
|
224
|
+
* @param root CCC 根
|
|
225
|
+
* @returns 按 mtime 降序的活跃轨迹(已排除 `_` 前缀目录)
|
|
226
|
+
*/
|
|
227
|
+
export interface TrajectorySummary {
|
|
228
|
+
dirName: string;
|
|
229
|
+
sessionId: string;
|
|
230
|
+
ageDays: number;
|
|
231
|
+
hasSessionMd: boolean;
|
|
232
|
+
}
|
|
233
|
+
export declare function listActiveTrajectories(root: string): TrajectorySummary[];
|
|
234
|
+
export interface ArchiveResult {
|
|
235
|
+
message: string;
|
|
236
|
+
dirName: string;
|
|
237
|
+
from: string;
|
|
238
|
+
to: string;
|
|
239
|
+
dryRun: boolean;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 把一条轨迹**单向**归档到 `AGENT_SESSIONS/_archived/`(owner 2026-10-18 裁决)。
|
|
243
|
+
*
|
|
244
|
+
* ## 为什么这是"唯一"的状态变化
|
|
245
|
+
* 轨迹没有生命周期 ⇒ 它**不完成、不关闭、不衰老**。它只会**换个存放位置**:
|
|
246
|
+
* 从活跃区移到归档区。目录与 SESSION.md **逐字节不动**(纯 rename)——
|
|
247
|
+
* 「归档」在此**不是删除,也不是收尾**,只是"不再列在活跃清单里"。
|
|
248
|
+
*
|
|
249
|
+
* ## 判据(R↓)
|
|
250
|
+
* ① **单向**:owner 裁决不设 unarchive。恢复 = 手工 `container_fs mv` 移回。
|
|
251
|
+
* ⇒ 不提供反向动作,避免"两个方向各自实现一遍、语义各自漂移"。
|
|
252
|
+
* ② **绝不覆盖**:目标已存在 ⇒ 报错,**不合并、不重命名、不删除**。归档是不可逆动作,
|
|
253
|
+
* 静默覆盖会**毁掉**先前那条轨迹(它的 SESSION.md 是唯一副本)。
|
|
254
|
+
* ③ **归档区不建在别处**:落点恒为 `<CCC>/AGENT_SESSIONS/_archived/`,
|
|
255
|
+
* 与 `readAllSessions` 的 `_` 跳过判据**成对**(换一处就要同时改两处 ⇒ 不换)。
|
|
256
|
+
* ④ `dryRun` 只报路径、不落盘。
|
|
257
|
+
*
|
|
258
|
+
* @param root CCC 根
|
|
259
|
+
* @param key 目录名 / S### / 唯一模糊串(同 findSession)
|
|
260
|
+
* @param dryRun 预览
|
|
261
|
+
*/
|
|
262
|
+
export declare function archiveSession(root: string, key: string, dryRun?: boolean): ArchiveResult;
|
|
184
263
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { c as hostInjected, d as hostSessions, f as hostSettings, h as hostWebServer, i as cccRootForExec, l as hostLlm, m as hostWeb, n as agentCwdFor, o as listCccs, p as hostSubagents, r as cccRootForCwd, s as hostAgents, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-RMzGU-CJ.js";
|
|
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, p as readCognitiveTrajectorySkills, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-C33QWoWZ.js";
|
|
4
|
-
import { A as
|
|
4
|
+
import { A as localFileStamp, C as sessionEvents$1, D as summarize, E as showSession, M as localIdStamp, O as useSession, S as resolveSessionByTitle, T as setActiveSessionInfo, _ as listActiveTrajectories, a as pruneMissingBindings, b as parseSessionContextFromEvents, c as setBindingStore, d as archiveSession, f as clearActiveSessionInfo, g as getActiveSessionInfo, h as findSession, i as listBoundSessionIds, j as localHuman, k as isoLocal, l as COGNITIVE_TRAJECTORY_ACTIONS, m as extractSessionMdPathFromText, n as PLUGIN_SOURCE, o as readLastBound, p as createSession, r as appendBound, s as resolveSessionCognitiveTrajectoryLabel, u as DEFAULT_SESSION_SCOPE, v as listSessions, w as sessionsRoot, x as readActiveSessionMd, y as namingTitleFor } from "./message-source-DM9-2eUT.js";
|
|
5
5
|
import { a as readGitTrack, c as unsetEntry, i as localstorePath, l as writeEntry, n as checkLocalstoreGitCompliance, o as readStore, r as getEntry, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./localstore-ops-ylQoPz1Q.js";
|
|
6
6
|
import { a as FACE_PORTS, c as MECHANISM_PORTS, d as registerSkiffSession$1, f as skiffRoleFor$1, h as ACC_VERSION, i as ACP_PORT, l as SKIFF_DEBUG_PORT, m as unregisterSkiffSession$1, n as readSimpleSettings, o as GATEWAY_PORT, p as skiffSessionInfo$1, r as registerSettingsSection, s as MAIN_WEB_PORT, u as WEIXIN_SEND_PORT } from "./settings-section-B1YzjFz6.js";
|
|
7
7
|
import { a as isSkiffSessionId, c as resolveSkiffKind, f as systemPromptSource, i as createRolePromptReader, l as roleMsmWhitelist, n as buildSkiffBasePrompt, o as readSkiffRoles, r as cognitiveTrajectorySubset, s as resolveRoleSystemPrompt, u as roleToolWhitelist } from "./skiff-role-Boc4SKct.js";
|
|
@@ -12330,11 +12330,15 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12330
12330
|
"cognitive_trajectory — one cognitive trajectory: its persistent body (SESSION.md) + its timeline.",
|
|
12331
12331
|
"A cognitive trajectory is NOT the conversation: SESSION.md is the durable body and never moves;",
|
|
12332
12332
|
"the dsh conversation is only its rebuildable carrier (Ship of Theseus — the carrier is replaced, the cognitive trajectory continues).",
|
|
12333
|
+
"A COGNITIVE TRAJECTORY HAS NO LIFECYCLE — it does not complete, close, or expire, and age never counts against it;",
|
|
12334
|
+
"it PERMANENTLY EXISTS. The only thing that ever changes is WHERE it is stored: archive moves it out of the active list",
|
|
12335
|
+
"without touching a single byte of its directory or SESSION.md. There is no reverse action — to restore one, use container_fs mv.",
|
|
12333
12336
|
"ACTIONS, grouped by EFFECT (read the grouping before picking one):",
|
|
12334
12337
|
"(A) LIFECYCLE — changes which cognitive trajectory THIS conversation is bound to:",
|
|
12335
12338
|
"list (inventory + stats + anomaly marks) / show (read one SESSION.md) / create (make a NEW cognitive trajectory) /",
|
|
12336
12339
|
"use (bind THIS conversation to an existing cognitive trajectory; inline integrity check — silent when fine, problems reported as hints, never errors) /",
|
|
12337
|
-
"rebuild (clear-and-rebuild THIS conversation in place)
|
|
12340
|
+
"rebuild (clear-and-rebuild THIS conversation in place) /",
|
|
12341
|
+
"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).",
|
|
12338
12342
|
"(B) DELIVERY — sends ONE message to ANOTHER cognitive trajectory; your own binding is unchanged:",
|
|
12339
12343
|
"send-now (deliver RIGHT NOW) / send-later (deliver at ONE future instant).",
|
|
12340
12344
|
"(C) REFERENCE — cro-guide (how to give THIS cognitive trajectory its own program that decides when it should be woken).",
|
|
@@ -12370,7 +12374,8 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12370
12374
|
"list (inventory + stats + anomaly marks) / show (S### or dir name or fuzzy keyword) /",
|
|
12371
12375
|
"create (new cognitive trajectory; activates itself when you have no binding, otherwise leaves your binding untouched and tells you how to activate it) /",
|
|
12372
12376
|
"use (activate context for THIS conversation; inline integrity check — pass silent, problems reported as hints, not errors) /",
|
|
12373
|
-
"rebuild (clear-and-rebuild current conversation — requires --summary + optional --note)
|
|
12377
|
+
"rebuild (clear-and-rebuild current conversation — requires --summary + optional --note) /",
|
|
12378
|
+
"archive (move a trajectory to the archived area; one-way, refuses to overwrite, content untouched — --dryRun previews).",
|
|
12374
12379
|
"DELIVERY (sends one message to ANOTHER cognitive trajectory; your binding is unchanged):",
|
|
12375
12380
|
"send-now (deliver RIGHT NOW; the target needs no prior setup — if it has no live session, one is created and bound automatically) /",
|
|
12376
12381
|
"send-later (one future instant + one message; fire-and-forget, and it does NOT auto-create a target).",
|
|
@@ -12530,6 +12535,18 @@ function createCognitiveTrajectoryTool(ctx) {
|
|
|
12530
12535
|
instruction: "Queued clear-and-rebuild: when this turn ends, the same conversation will be cleared and injected with the \"continue the work\" anchor (first-anchor protocol body included), then auto-continue — no manual input needed; resume from SESSION.md at that point."
|
|
12531
12536
|
};
|
|
12532
12537
|
}
|
|
12538
|
+
case "archive": {
|
|
12539
|
+
if (!args.name) throw new Error("archive requires name (S### or directory name)");
|
|
12540
|
+
const res = archiveSession(root, args.name, args.dryRun ?? false);
|
|
12541
|
+
return {
|
|
12542
|
+
ok: true,
|
|
12543
|
+
dirName: res.dirName,
|
|
12544
|
+
from: res.from,
|
|
12545
|
+
to: res.to,
|
|
12546
|
+
message: res.message,
|
|
12547
|
+
note: res.dryRun ? "预览:未落盘。" : "已从活跃清单移出(目录与 SESSION.md 逐字节未改)。恢复方式:container_fs mv 移回 AGENT_SESSIONS/。"
|
|
12548
|
+
};
|
|
12549
|
+
}
|
|
12533
12550
|
case "send-later": {
|
|
12534
12551
|
if (!args.target || !args.at || !args.message) throw new Error("send-later requires target (S### or dir name) + at (RFC3339 or +30m/+2h) + message");
|
|
12535
12552
|
const wakeScope = agentScope$1(exec);
|
|
@@ -12589,26 +12606,22 @@ function renderWakeEntry(e) {
|
|
|
12589
12606
|
].join("\n");
|
|
12590
12607
|
}
|
|
12591
12608
|
/**
|
|
12592
|
-
* `use` 的内联检查(v1.33
|
|
12609
|
+
* `use` 的内联检查(v1.33;🔴 **2026-10-18 起只剩一条**)。
|
|
12610
|
+
*
|
|
12611
|
+
* 为什么只剩"空壳"这一条(R↓):另一条是「SESSION.md 已 N 天未更新」的老化告警,
|
|
12612
|
+
* 而 **owner 2026-10-18 裁决「认知轨迹没有生命周期」** ⇒ "旧轨迹该退休了"这个前提消失:
|
|
12613
|
+
* **旧不是问题**,激活一条 300 天前的轨迹与激活昨天的是同一件事。
|
|
12614
|
+
* ⇒ 老化告警不是在提示风险,它是在**暗示一个已不存在的规矩**,故删除。
|
|
12615
|
+
*
|
|
12616
|
+
* 剩下的这条**与模板、与年龄都无关**,它是纯粹的**事实缺失**:
|
|
12617
|
+
* 目录在但没有 SESSION.md ⇒ 激活必然失败(`useSession` 会阻断),提前说清比让人撞墙好。
|
|
12593
12618
|
*
|
|
12594
|
-
* 为什么只有两条(R↓):原 `qa` 的六节名检查与 `health` 的 stalled/drift 都建立在"旧模板合规"假设上,
|
|
12595
|
-
* 而 ACC 层的编写标准已升级为 **EAP**(用户裁决:「这说明 qa 应该淘汰了」)⇒ 那类判据淘汰;
|
|
12596
|
-
* 剩下与模板无关、且"用到时才发现最划算"的只有:
|
|
12597
|
-
* ① **空壳**:目录在但没有 SESSION.md(无正文可载 ⇒ 由 useSession 阻断,此处只是提前说清)
|
|
12598
|
-
* ② **长期无活动**:SESSION.md 的 mtime 过旧(按 HEALTH_STALE_DAYS 同口径)
|
|
12599
12619
|
* @param dirName 目标轨迹目录名
|
|
12600
12620
|
* @param mdPath 目标 SESSION.md 绝对路径
|
|
12601
12621
|
* @returns 提示文本;无异常 → null(**通过就静默**)
|
|
12602
12622
|
*/
|
|
12603
12623
|
function advisoryHint(dirName, mdPath) {
|
|
12604
12624
|
if (!existsSync(mdPath)) return `⚠ ${dirName}: 目录存在但没有 SESSION.md(空壳)——本次激活会失败,先补正文或改用 create`;
|
|
12605
|
-
let ageDays = 0;
|
|
12606
|
-
try {
|
|
12607
|
-
ageDays = (Date.now() - statSync(mdPath).mtimeMs) / 864e5;
|
|
12608
|
-
} catch {
|
|
12609
|
-
return null;
|
|
12610
|
-
}
|
|
12611
|
-
if (ageDays >= 7) return `⚠ ${dirName}: SESSION.md 已 ${Math.floor(ageDays)} 天未更新——激活的是旧轨迹,确认是否接续的是它`;
|
|
12612
12625
|
return null;
|
|
12613
12626
|
}
|
|
12614
12627
|
//#endregion
|
|
@@ -14836,6 +14849,7 @@ function getStatus(cwd, configPaths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
14836
14849
|
blacklist: [],
|
|
14837
14850
|
threshold: null,
|
|
14838
14851
|
cheapWorkerModel: null,
|
|
14852
|
+
trajectories: [],
|
|
14839
14853
|
restrict
|
|
14840
14854
|
};
|
|
14841
14855
|
const cfg = loadSerenityConfig(root, configPaths);
|
|
@@ -14846,6 +14860,7 @@ function getStatus(cwd, configPaths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
14846
14860
|
blacklist: readBlacklist(root, configPaths),
|
|
14847
14861
|
threshold: cfg.sessionKeeper?.threshold ?? null,
|
|
14848
14862
|
cheapWorkerModel: readCheapWorkerConfig(root, configPaths)?.defaultModel ?? null,
|
|
14863
|
+
trajectories: listActiveTrajectories(root),
|
|
14849
14864
|
restrict
|
|
14850
14865
|
};
|
|
14851
14866
|
}
|
|
@@ -99,7 +99,30 @@ function localIdStamp(value = Date.now()) {
|
|
|
99
99
|
* 其函数(closeSession / archiveSessions / healthCheck / qaCheck)**同批删除**——
|
|
100
100
|
* 保留下来的只有:create(--desc / --issue 二选一、dry-run 预览)、
|
|
101
101
|
* list(含 summarize 仪表盘)、show、use(激活 + 绑定)、以及供 list 用的 summarize。
|
|
102
|
-
*
|
|
102
|
+
*
|
|
103
|
+
* ## 🔴 2026-10-18(owner 令):**认知轨迹没有生命周期**
|
|
104
|
+
*
|
|
105
|
+
* owner 原话要点:*"我需要允许永存的认知轨迹……我希望该生命周期足够简单,即没有生命周期,
|
|
106
|
+
* 但允许轨迹本身被操作为归档"*。据此**删除**了以下全部"生命周期"语义:
|
|
107
|
+
*
|
|
108
|
+
* | 被删的 | 为什么它属于生命周期 |
|
|
109
|
+
* |---|---|
|
|
110
|
+
* | `completed` 解析(SESSION.md 里的方括号小写 x) | "完成"是一个**终态**;没有生命周期就没有终态 |
|
|
111
|
+
* | `summarize` 的 Active / Completed / Stale / Ghost 四项计数 | 四项全部在给轨迹分"阶段" |
|
|
112
|
+
* | `advisoryHint` 的「N 天未更新」告警 | 老化告警的前提是"旧轨迹该退休了"——现在**旧不是问题** |
|
|
113
|
+
* | `findLatestActiveSessionMd`(**全仓零消费者**,纯死代码) | 它的语义是"最新的**未完成**轨迹",前提已不存在 |
|
|
114
|
+
*
|
|
115
|
+
* **保留**:SESSION.md 头部的状态行**照旧生成**(`- 状态: [ ] 进行中`),但**不再被任何代码解析**。
|
|
116
|
+
* 理由(owner 裁决):格式最小变动、老文件完全不用改。⇒ 它从"机器契约"降为**惰性文本**,
|
|
117
|
+
* 且"进行中"对永存轨迹**恒为真**,因此不构成谎言。
|
|
118
|
+
*
|
|
119
|
+
* **新增唯一的操作**:`archiveSession()` —— 单向移入 `_archived/`。
|
|
120
|
+
* 归档是**唯一的"变化"**,且它不属于轨迹自身:轨迹本体(目录 + SESSION.md)**永不被改**,
|
|
121
|
+
* 只是**换了个存放位置**、从清单里消失。恢复 = 手工 `container_fs mv` 移回(owner 裁决:不设 unarchive)。
|
|
122
|
+
*
|
|
123
|
+
* ⚠️ 由此产生一条**必须成立**的配套:`readAllSessions` **跳过 `_` 前缀目录**。
|
|
124
|
+
* 否则归档后的轨迹会继续出现在清单里,"归档"就不是归档了(历史 bug:`_weixin-logs` /
|
|
125
|
+
* `_skiff-logs` / `_corpus` 曾被当成轨迹列出来)。
|
|
103
126
|
*/
|
|
104
127
|
const COGNITIVE_TRAJECTORY_ACTIONS = [
|
|
105
128
|
"list",
|
|
@@ -107,6 +130,7 @@ const COGNITIVE_TRAJECTORY_ACTIONS = [
|
|
|
107
130
|
"create",
|
|
108
131
|
"use",
|
|
109
132
|
"rebuild",
|
|
133
|
+
"archive",
|
|
110
134
|
"send-now",
|
|
111
135
|
"send-later",
|
|
112
136
|
"cro-guide"
|
|
@@ -130,24 +154,26 @@ function sessionEvents(session) {
|
|
|
130
154
|
return s.events ?? [];
|
|
131
155
|
}
|
|
132
156
|
const SESSION_MD = "SESSION.md";
|
|
133
|
-
const HEALTH_STALE_DAYS = 7;
|
|
134
157
|
const DAY = 864e5;
|
|
158
|
+
/** 归档区目录名(`_` 前缀 ⇒ 不是轨迹,见 readAllSessions 的跳过判据) */
|
|
159
|
+
const ARCHIVED_DIR = "_archived";
|
|
135
160
|
function sessionsRoot(root) {
|
|
136
161
|
return join(root, "AGENT_SESSIONS");
|
|
137
162
|
}
|
|
138
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* 解析 SESSION.md 存在性。
|
|
165
|
+
*
|
|
166
|
+
* 🔴 **2026-10-18:不再解析"完成"**。旧实现 `completed: /\[\s*x\s*\]/i.test(content)` 已删——
|
|
167
|
+
* 轨迹永存 ⇒ 没有终态,而这个字段的所有消费者(排序 / 标记 / 计数)也同批删掉了。
|
|
168
|
+
* ⚠️ **副作用(有意接受)**:`[x]` 不再有机器含义 ⇒ 正文里出现它**不会再**把轨迹判成任何东西。
|
|
169
|
+
* 那条"正文绝不能用方括号小写 x"的老纪律随之解除(但它仍是无害的好习惯)。
|
|
170
|
+
*/
|
|
139
171
|
function parseSessionMd(filePath) {
|
|
140
172
|
try {
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
hasSessionMd: true,
|
|
144
|
-
completed: /\[\s*x\s*\]/i.test(content)
|
|
145
|
-
};
|
|
173
|
+
readFileSync(filePath, "utf-8");
|
|
174
|
+
return { hasSessionMd: true };
|
|
146
175
|
} catch {
|
|
147
|
-
return {
|
|
148
|
-
hasSessionMd: false,
|
|
149
|
-
completed: false
|
|
150
|
-
};
|
|
176
|
+
return { hasSessionMd: false };
|
|
151
177
|
}
|
|
152
178
|
}
|
|
153
179
|
function readSessionEntry(dirPath) {
|
|
@@ -156,10 +182,7 @@ function readSessionEntry(dirPath) {
|
|
|
156
182
|
if (!st.isDirectory()) return null;
|
|
157
183
|
const dirName = basename(dirPath);
|
|
158
184
|
const mdPath = join(dirPath, SESSION_MD);
|
|
159
|
-
const status = existsSync(mdPath) ? parseSessionMd(mdPath) : {
|
|
160
|
-
hasSessionMd: false,
|
|
161
|
-
completed: false
|
|
162
|
-
};
|
|
185
|
+
const status = existsSync(mdPath) ? parseSessionMd(mdPath) : { hasSessionMd: false };
|
|
163
186
|
return {
|
|
164
187
|
dirName,
|
|
165
188
|
path: dirPath,
|
|
@@ -170,14 +193,20 @@ function readSessionEntry(dirPath) {
|
|
|
170
193
|
return null;
|
|
171
194
|
}
|
|
172
195
|
}
|
|
173
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* 读取 AGENT_SESSIONS 中所有**活跃**轨迹,按 mtime 降序。
|
|
198
|
+
*
|
|
199
|
+
* 🔴 **跳过 `_` 前缀目录**(2026-10-18 新增,与归档配套):
|
|
200
|
+
* `_archived` 是归档区,`_skiff-logs` / `_weixin-logs` / `_corpus` 是**日志与数据区**——
|
|
201
|
+
* 它们**都不是轨迹**。旧实现会把它们当轨迹列出来(历史 bug)。
|
|
202
|
+
* 没有这条,归档后的轨迹仍会出现在清单里 ⇒ **"归档"名不副实**。
|
|
203
|
+
*
|
|
204
|
+
* ⚠️ 排序判据已变:旧实现是「未完成优先,再 mtime 降序」,现在**只有 mtime 降序**
|
|
205
|
+
* (没有"未完成"这个概念了)。
|
|
206
|
+
*/
|
|
174
207
|
function readAllSessions(sessionsDir) {
|
|
175
208
|
try {
|
|
176
|
-
return readdirSync(sessionsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => readSessionEntry(join(sessionsDir, e.name))).filter((s) => s !== null).sort((a, b) =>
|
|
177
|
-
if (!a.status.completed && b.status.completed) return -1;
|
|
178
|
-
if (a.status.completed && !b.status.completed) return 1;
|
|
179
|
-
return b.mtime.getTime() - a.mtime.getTime();
|
|
180
|
-
});
|
|
209
|
+
return readdirSync(sessionsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).filter((e) => !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => readSessionEntry(join(sessionsDir, e.name))).filter((s) => s !== null).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
181
210
|
} catch {
|
|
182
211
|
return [];
|
|
183
212
|
}
|
|
@@ -214,9 +243,9 @@ function listSessions(root, activeId) {
|
|
|
214
243
|
const lines = sessions.map((s) => {
|
|
215
244
|
const age = Math.floor((Date.now() - s.mtime.getTime()) / DAY);
|
|
216
245
|
const sessionId = extractSessionId(s.dirName);
|
|
217
|
-
return `${activeId !== void 0 && sessionId !== "" && sessionId === activeId ? "●" :
|
|
246
|
+
return `${activeId !== void 0 && sessionId !== "" && sessionId === activeId ? "●" : "○"} ${s.dirName} (${age}d ago)`;
|
|
218
247
|
});
|
|
219
|
-
return `AGENT_SESSIONS/ (${sessions.length}
|
|
248
|
+
return `AGENT_SESSIONS/ (${sessions.length} active)\n` + lines.join("\n");
|
|
220
249
|
}
|
|
221
250
|
/** show 子命令(对齐 osp showSession:`# dirName\n\n` + SESSION.md 内容) */
|
|
222
251
|
function showSession(root, key) {
|
|
@@ -230,10 +259,12 @@ function showSession(root, key) {
|
|
|
230
259
|
/**
|
|
231
260
|
* 生成 SESSION.md 模板(固定格式,owner 2026-09-25 裁决;时间戳 YYYY-MM-DD HH:mm)。
|
|
232
261
|
*
|
|
233
|
-
* 🔴
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
262
|
+
* 🔴 **2026-10-18:状态行不再是机器契约**(owner 裁决:轨迹永存,没有生命周期)。
|
|
263
|
+
* 它**照旧生成** `- 状态: [ ] 进行中`,但**没有任何代码解析它** —— 从"被解析的机器契约"
|
|
264
|
+
* 降为**惰性文本**。保留的两个理由:
|
|
265
|
+
* ① 格式最小变动、老文件完全不用改;
|
|
266
|
+
* ② 对永存轨迹来说"进行中"**恒为真**,所以它不是谎言,只是冗余。
|
|
267
|
+
* ⚠️ 仍然**不得**把任何一段固定 7 段的段名与顺序动掉(契约钉在 `tests/trajectory-ops.test.ts`)。
|
|
237
268
|
*/
|
|
238
269
|
function sessionMdTemplate(title, id, goal, now) {
|
|
239
270
|
const ts = localDateTimeMinutes(now);
|
|
@@ -508,33 +539,86 @@ function resolveSessionByTitle(title, sessionsDir) {
|
|
|
508
539
|
if (fuzzy.length === 1) return join(fuzzy[0].path, SESSION_MD);
|
|
509
540
|
return null;
|
|
510
541
|
}
|
|
511
|
-
/**
|
|
542
|
+
/**
|
|
543
|
+
* summary 仪表盘(供 `list` 动作拼接;v1.33 起独立 `summary` 动作已撤,并入 list)。
|
|
544
|
+
*
|
|
545
|
+
* 🔴 **2026-10-18:四项生命周期计数已删**(Active / Completed / Stale / Ghost)。
|
|
546
|
+
* 它们全都在给轨迹**分阶段**,而 owner 裁决「没有生命周期」⇒ 唯一有意义的总数是 `Total`。
|
|
547
|
+
* ⚠️ **`Ghost` 也被删了**("目录在但没 SESSION.md")—— 它不是阶段而是**损坏**,但:
|
|
548
|
+
* · 真出现时 `use` 会在 `advisoryHint` 里**当场说清**(用到才发现,比长期挂一个计数更有效);
|
|
549
|
+
* · 挂一个恒 0 的计数只会钝化注意力。
|
|
550
|
+
* `Total` 现在 = **活跃轨迹数**(`_` 前缀的归档区与日志区已不在域内)。
|
|
551
|
+
*/
|
|
512
552
|
function summarize(root) {
|
|
513
553
|
const sessions = readAllSessions(sessionsRoot(root));
|
|
514
554
|
if (sessions.length === 0) return "AGENT_SESSIONS/ is empty.";
|
|
515
555
|
const now = Date.now();
|
|
516
|
-
const completed = sessions.filter((s) => s.status.completed).length;
|
|
517
|
-
const active = sessions.length - completed;
|
|
518
|
-
const stale = sessions.filter((s) => !s.status.completed && (now - s.mtime.getTime()) / DAY > HEALTH_STALE_DAYS).length;
|
|
519
|
-
const ghost = sessions.filter((s) => !s.status.hasSessionMd).length;
|
|
520
556
|
const recent = sessions.slice(0, 5);
|
|
521
|
-
|
|
557
|
+
return [
|
|
522
558
|
`AGENT_SESSIONS Summary`,
|
|
523
559
|
`────────────────────────`,
|
|
524
|
-
`
|
|
525
|
-
`Active: ${active}`,
|
|
526
|
-
`Completed: ${completed}`,
|
|
527
|
-
`Stale: ${stale}`,
|
|
528
|
-
`Ghost: ${ghost}`,
|
|
560
|
+
`Active (not archived): ${sessions.length}`,
|
|
529
561
|
``,
|
|
530
562
|
`Recent activity (top 5):`,
|
|
531
563
|
...recent.map((s) => {
|
|
532
564
|
const age = Math.floor((now - s.mtime.getTime()) / DAY);
|
|
533
|
-
return `
|
|
565
|
+
return ` ○ ${s.dirName} (${age}d ago)`;
|
|
534
566
|
})
|
|
535
|
-
];
|
|
536
|
-
|
|
537
|
-
|
|
567
|
+
].join("\n");
|
|
568
|
+
}
|
|
569
|
+
function listActiveTrajectories(root) {
|
|
570
|
+
const now = Date.now();
|
|
571
|
+
return readAllSessions(sessionsRoot(root)).map((s) => ({
|
|
572
|
+
dirName: s.dirName,
|
|
573
|
+
sessionId: extractSessionId(s.dirName),
|
|
574
|
+
ageDays: Math.floor((now - s.mtime.getTime()) / DAY),
|
|
575
|
+
hasSessionMd: s.status.hasSessionMd
|
|
576
|
+
}));
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* 把一条轨迹**单向**归档到 `AGENT_SESSIONS/_archived/`(owner 2026-10-18 裁决)。
|
|
580
|
+
*
|
|
581
|
+
* ## 为什么这是"唯一"的状态变化
|
|
582
|
+
* 轨迹没有生命周期 ⇒ 它**不完成、不关闭、不衰老**。它只会**换个存放位置**:
|
|
583
|
+
* 从活跃区移到归档区。目录与 SESSION.md **逐字节不动**(纯 rename)——
|
|
584
|
+
* 「归档」在此**不是删除,也不是收尾**,只是"不再列在活跃清单里"。
|
|
585
|
+
*
|
|
586
|
+
* ## 判据(R↓)
|
|
587
|
+
* ① **单向**:owner 裁决不设 unarchive。恢复 = 手工 `container_fs mv` 移回。
|
|
588
|
+
* ⇒ 不提供反向动作,避免"两个方向各自实现一遍、语义各自漂移"。
|
|
589
|
+
* ② **绝不覆盖**:目标已存在 ⇒ 报错,**不合并、不重命名、不删除**。归档是不可逆动作,
|
|
590
|
+
* 静默覆盖会**毁掉**先前那条轨迹(它的 SESSION.md 是唯一副本)。
|
|
591
|
+
* ③ **归档区不建在别处**:落点恒为 `<CCC>/AGENT_SESSIONS/_archived/`,
|
|
592
|
+
* 与 `readAllSessions` 的 `_` 跳过判据**成对**(换一处就要同时改两处 ⇒ 不换)。
|
|
593
|
+
* ④ `dryRun` 只报路径、不落盘。
|
|
594
|
+
*
|
|
595
|
+
* @param root CCC 根
|
|
596
|
+
* @param key 目录名 / S### / 唯一模糊串(同 findSession)
|
|
597
|
+
* @param dryRun 预览
|
|
598
|
+
*/
|
|
599
|
+
function archiveSession(root, key, dryRun = false) {
|
|
600
|
+
const sessionsDir = sessionsRoot(root);
|
|
601
|
+
const target = findSession(sessionsDir, key);
|
|
602
|
+
if (!target) throw new Error(`Trajectory not found: "${key}". Use "list" to see available trajectories.`);
|
|
603
|
+
const archiveRoot = join(sessionsDir, ARCHIVED_DIR);
|
|
604
|
+
const dest = join(archiveRoot, target.dirName);
|
|
605
|
+
if (existsSync(dest)) throw new Error(`Cannot archive: "${target.dirName}" already exists in ${ARCHIVED_DIR}/. 归档不会覆盖既有轨迹(那是不可逆的丢失)。请先人工处理 ${dest}`);
|
|
606
|
+
if (dryRun) return {
|
|
607
|
+
message: `[dry-run] Would archive: ${target.dirName} → ${ARCHIVED_DIR}/`,
|
|
608
|
+
dirName: target.dirName,
|
|
609
|
+
from: target.path,
|
|
610
|
+
to: dest,
|
|
611
|
+
dryRun: true
|
|
612
|
+
};
|
|
613
|
+
mkdirSync(archiveRoot, { recursive: true });
|
|
614
|
+
renameSync(target.path, dest);
|
|
615
|
+
return {
|
|
616
|
+
message: `Archived: ${target.dirName} → ${ARCHIVED_DIR}/`,
|
|
617
|
+
dirName: target.dirName,
|
|
618
|
+
from: target.path,
|
|
619
|
+
to: dest,
|
|
620
|
+
dryRun: false
|
|
621
|
+
};
|
|
538
622
|
}
|
|
539
623
|
//#endregion
|
|
540
624
|
//#region src/cognitive-trajectory-bound.ts
|
|
@@ -965,4 +1049,4 @@ const ACC_MESSAGE_KIND = "plugin:dsh-serenity-hooks";
|
|
|
965
1049
|
/** ACC 注入消息的统一来源值(各缝共用;需要 `form` 时按 `ContextFormed` 另加字段)。 */
|
|
966
1050
|
const PLUGIN_SOURCE = { kind: ACC_MESSAGE_KIND };
|
|
967
1051
|
//#endregion
|
|
968
|
-
export {
|
|
1052
|
+
export { localFileStamp as A, sessionEvents as C, summarize as D, showSession as E, localIdStamp as M, useSession as O, resolveSessionByTitle as S, setActiveSessionInfo as T, listActiveTrajectories as _, pruneMissingBindings as a, parseSessionContextFromEvents as b, setBindingStore as c, archiveSession as d, clearActiveSessionInfo as f, getActiveSessionInfo as g, findSession as h, listBoundSessionIds as i, localHuman as j, isoLocal as k, COGNITIVE_TRAJECTORY_ACTIONS as l, extractSessionMdPathFromText as m, PLUGIN_SOURCE as n, readLastBound as o, createSession as p, appendBound as r, resolveSessionCognitiveTrajectoryLabel as s, ACC_MESSAGE_KIND as t, DEFAULT_SESSION_SCOPE as u, listSessions as v, sessionsRoot as w, readActiveSessionMd as x, namingTitleFor as y };
|
package/lib/status.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* WebUI 停靠栏的数据源:ACC 版本 / CCC 根 / safe-mode 状态 / 黑名单 / keeper 阈值 / cheap-worker 模型。
|
|
5
5
|
* setSafeMode 直接读写 .serenity-safe-on 标记(守卫实时读取,写即生效)。
|
|
6
6
|
*/
|
|
7
|
+
import { type TrajectorySummary } from './cognitive-trajectory-ops.js';
|
|
7
8
|
/**
|
|
8
9
|
* 🔴 **布局无关档**:从**正在跑的那个入口脚本**反推宿主包根,读它的 `version`。
|
|
9
10
|
*
|
|
@@ -50,6 +51,13 @@ interface SerenityStatus {
|
|
|
50
51
|
}[];
|
|
51
52
|
threshold: number | null;
|
|
52
53
|
cheapWorkerModel: string | null;
|
|
54
|
+
/**
|
|
55
|
+
* 活跃轨迹清单(owner 2026-10-18:CCC 状态面板要展示"所有没有归档的轨迹")。
|
|
56
|
+
* 🔴 判据源 = `listActiveTrajectories`(它内含 `_` 前缀跳过 ⇒ 归档区自动不在域内);
|
|
57
|
+
* ⛔ 面板侧**不得自己扫目录** —— 那会长出第二份会漂的实现。
|
|
58
|
+
* 无 CCC 根时为 `[]`。
|
|
59
|
+
*/
|
|
60
|
+
trajectories: TrajectorySummary[];
|
|
53
61
|
restrict: {
|
|
54
62
|
lastKey: string | null;
|
|
55
63
|
lastAttemptAt: string | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.66.0",
|
|
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 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/官方 ACP 面/认知轨迹唤醒注册表。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|