@a9i5k4/dsh-auto-memory 2.4.2 → 2.5.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/docs/internal/GROUP-DIGEST-SETUP.md +62 -0
- package/docs/internal/GROUP-LISTENER-SETUP.md +49 -0
- package/docs/internal/GROUP-WEBHOOK-SETUP.md +60 -0
- package/docs/internal/HANDOFF-TO-ZCODE.md +168 -0
- package/docs/internal/NEXT-VERSION-TODO.md +29 -1
- package/docs/internal/RELEASE-PROCESS.md +16 -0
- package/docs/internal/TODO-BACKLOG.md +137 -0
- package/docs/prompts/DOCS-AUDIT-AGENT.md +32 -0
- package/docs/prompts/README.md +4 -0
- package/docs/prompts/REGRESSION-AGENT.md +42 -0
- package/docs/prompts/RELEASE-AGENT.md +119 -0
- package/docs/prompts/TRACE-PATROL-AGENT.md +29 -0
- package/lib/client.js +23 -8
- package/lib/index.js +138 -23
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# 群同步日报(GROUP-DIGEST)部署说明
|
|
2
|
+
|
|
3
|
+
> 2026-09-12 搭建。每天北京时间 **12:00 / 21:00** 由 **GitHub Actions** 自动统计仓库
|
|
4
|
+
> issues / PRs / commits / release,生成中文摘要投递到 QQ 群「dsh-auto-memory交流群」。
|
|
5
|
+
> 跑在 GitHub 云端,**本机电脑关机也照常执行**;本机从未登录任何 QQ 协议端。
|
|
6
|
+
|
|
7
|
+
## 文件清单
|
|
8
|
+
|
|
9
|
+
| 文件 | 作用 |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `.github/workflows/group-digest.yml` | 定时(UTC 4:00/13:00)+ 手动触发入口;凭据走 secrets/vars |
|
|
12
|
+
| `.github/scripts/group-digest.mjs` | 统计 + 生成 + 多通道投递,零依赖;`--print` 本机试跑 |
|
|
13
|
+
| `.github/scripts/qq-capture-openid.mjs` | 一次性工具:连官方 WS 网关抓群 `group_openid`(仅本机跑) |
|
|
14
|
+
| `.github/digest/NOTES.md` | 人工备注区:写了什么,群消息「备注」栏就带什么(改完要上 main 才生效) |
|
|
15
|
+
| `.github/digest/PREVIEW.md` | 「下版本前瞻」板块文案:网页编辑,写几行就整块出现在日报里,留空隐藏 |
|
|
16
|
+
| `tools/release.mjs` | 复制白名单已加 `.github` → 以后每次发版自动带到 REL 树,不会被发版清掉 |
|
|
17
|
+
|
|
18
|
+
**窗口口径**=上一次「成功」的 workflow run → 现在;错过一次自动并进下一次,封顶 7 天。
|
|
19
|
+
**发送失败**的 run 记为失败(不重置窗口);**未配置通道**时 run 成功,消息只归档到 run 页面。
|
|
20
|
+
|
|
21
|
+
## 通道配置(仓库 Settings → Secrets and variables → Actions)
|
|
22
|
+
|
|
23
|
+
先设仓库**变量** `DIGEST_CHANNEL`(variables 区,非 secret),再按下表配 secrets:
|
|
24
|
+
|
|
25
|
+
| 通道 | DIGEST_CHANNEL 值 | 需要的 secrets | 说明 |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| QQ 官方机器人(推荐) | `qq_official` | `QQ_APP_ID` / `QQ_APP_SECRET` / `QQ_GROUP_OPENID` | 零服务器、零封号风险;QQ 群消息**禁止 URL**,脚本自动剥链接 |
|
|
28
|
+
| NapCat(OneBot11) | `napcat` | `NAPCAT_HTTP_URL` / `NAPCAT_GROUP_ID` /(`NAPCAT_TOKEN`) | 功能最全,可加「群内反馈」采集(见下);需一台常开设备 |
|
|
29
|
+
| Telegram | `telegram` | `TG_BOT_TOKEN` / `TG_CHAT_ID` | 最简单,2 分钟配完 |
|
|
30
|
+
| Discord / 飞书 / 钉钉 / 自定义 | `discord` / `feishu` / `dingtalk` / `generic` | 对应 `*_WEBHOOK_URL` | 备用/转发用 |
|
|
31
|
+
|
|
32
|
+
### A. QQ 官方机器人(推荐路径)
|
|
33
|
+
|
|
34
|
+
1. [q.qq.com](https://q.qq.com) 注册开发者 → **个人身份证认证**(主动消息频控:认证后 60 条/分钟、单群 1000 条/天,每天 2 条绰绰有余;未认证 30/分钟也够)。
|
|
35
|
+
2. 创建机器人(名字建议 `auto-memory 助手`),拿到 AppID / AppSecret。
|
|
36
|
+
3. 群主把机器人添加进群(机器人管理页 → 添加到群聊)。
|
|
37
|
+
4. 本机抓 `group_openid`(开放平台不直接展示,只随「@机器人」事件下发):
|
|
38
|
+
```
|
|
39
|
+
QQ_APP_ID=xxx QQ_APP_SECRET=xxx node .github/scripts/qq-capture-openid.mjs
|
|
40
|
+
# 然后在群里发一条「@机器人 你好」,脚本会打印 group_openid
|
|
41
|
+
```
|
|
42
|
+
5. 配 secrets + 变量 → Actions 页手动触发 `group-digest` 验证。
|
|
43
|
+
|
|
44
|
+
### B. NapCat(顺带把"群内问题反馈"带进日报)
|
|
45
|
+
|
|
46
|
+
在任一常开设备(旧安卓手机 Termux / 学生机 / NAS)Docker 跑 [NapCat](https://napneko.github.io),
|
|
47
|
+
开 OneBot11 HTTP 服务(如 `http://IP:3000`,建议配 access_token)。
|
|
48
|
+
之后把采集地址填到 secret `FEEDBACK_URL`(relay 提供 `GET /feedback?hours=N` 返回
|
|
49
|
+
`{"items":[{"user","text"}]}`),日报自动多一栏「群内反馈」。relay 可后补,不阻塞日报上线。
|
|
50
|
+
|
|
51
|
+
### C. 其他平台 webhook
|
|
52
|
+
|
|
53
|
+
TG / Discord / 飞书 / 钉钉按表配即可,适合"先进别的群/推给自己,再转发 QQ 群"的过渡期。
|
|
54
|
+
|
|
55
|
+
## 测试与运维
|
|
56
|
+
|
|
57
|
+
- **手动试跑**:GitHub 仓库 → Actions → group-digest → Run workflow(可填 `since_hours=24`)。
|
|
58
|
+
消息全文写在 run 的 Summary 里,发送结果看日志最后一行。
|
|
59
|
+
- **临时改备注**:直接在 GitHub 网页编辑 main 的 `.github/digest/NOTES.md`,下次统计生效。
|
|
60
|
+
- **已知限制**:GitHub schedule 可能有 5~30 分钟调度延迟(照常按窗口统计,不丢数据);
|
|
61
|
+
首次运行窗口=过去 12 小时;secret 没配齐时该通道报错,run 页面能直接看到原因。
|
|
62
|
+
- **改文案/格式**:`group-digest.mjs` 的 `compose()`;改动要走 pre 树 + 发版流程,或直接网页改 main(下次发版前会被 pre 版本覆盖,注意同步)。
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 群反馈闭环(GROUP-REPORT)部署说明
|
|
2
|
+
|
|
3
|
+
> 2026-09-13 搭建。日报(见 `GROUP-DIGEST-SETUP.md`)之外的第二条链路:**群员在 QQ 群里汇报问题
|
|
4
|
+
> → 自动建 GitHub issue → 状态回执「收到 ✅ / 正在处理 🔧 / 处理完毕 ✅」双端同步(QQ 群一句话 + issue 结构化评论)**。
|
|
5
|
+
> 修理工可以是 Copilot(把 issue 分配给 @copilot),也可以是人。
|
|
6
|
+
|
|
7
|
+
## 状态词汇表(和群主口头回复一致)
|
|
8
|
+
|
|
9
|
+
| 状态 | 触发点 | QQ 群播报 | GitHub issue |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| 收到 ✅ | 群反馈建单 | `收到 ✅ 群反馈已建单 #N「…」,处理进度会同步` | 「收到 ✅」评论 |
|
|
12
|
+
| 正在处理 🔧 | PR 开出且引用该 issue | `正在处理 🔧 群反馈 #N → PR #M「…」` | 「正在处理 🔧」评论 |
|
|
13
|
+
| 处理完毕 ✅ | issue 关闭(合并自动关单也算) | `处理完毕 ✅ 群反馈 #N「…」已解决` | 「处理完毕 ✅」评论 |
|
|
14
|
+
|
|
15
|
+
## 组成
|
|
16
|
+
|
|
17
|
+
| 文件 | 作用 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `.github/workflows/group-report-status.yml` | 状态机:issues opened/labeled/closed + PR opened/reopened → 双端同步 |
|
|
20
|
+
| `.github/scripts/report-sync.mjs` | 状态判定 + issue 评论 + QQ 播报(云端) |
|
|
21
|
+
| `.github/scripts/qq-send.mjs` | QQ 发送共享模块 |
|
|
22
|
+
| `.github/scripts/group-listener.mjs` | 「耳朵」:常开设备上连官方网关收群消息 → 建单 → 群里回收到 |
|
|
23
|
+
|
|
24
|
+
## 群员怎么报问题(两种都通)
|
|
25
|
+
|
|
26
|
+
1. **在群里说**(需要耳朵在跑):@机器人 并带触发词,如「@automemory 反馈助手 反馈 导出按钮点了没反应」→ 自动建单;
|
|
27
|
+
2. **直接提 GitHub issue**:标题随意,打上 `group-report` 标签 → 同样进入状态闭环(适合贴日志/截图)。
|
|
28
|
+
|
|
29
|
+
## 耳朵部署(旧安卓手机 Termux)
|
|
30
|
+
|
|
31
|
+
1. Termux(F-Droid 版)里:`pkg install nodejs-lts git`;
|
|
32
|
+
2. `git clone https://github.com/Aik358/dsh-auto-memory && cd dsh-auto-memory/.github/scripts`;
|
|
33
|
+
3. 建 `.env`(模板见 `group-listener.mjs` 头注释;GH_TOKEN 用**仅 issues:write 的细粒度 PAT**,别复用推送 PAT);
|
|
34
|
+
4. `node group-listener.mjs` → 看到「已上线,监听触发词」即可;
|
|
35
|
+
5. 常驻:关闭 Termux 电池优化 + `termux-wake-lock`;进阶用 Termux:Boot 开机自启。
|
|
36
|
+
|
|
37
|
+
**约束**:同一机器人同时只允许一个网关连接——耳朵在跑时,勿再跑 `qq-capture-openid.mjs` 或绑定 WorkBuddy(二者互踢)。
|
|
38
|
+
|
|
39
|
+
## Copilot 当修理工
|
|
40
|
+
|
|
41
|
+
1. 前提:仓库 Settings → Copilot 开启 coding agent;账号需 Copilot 付费档(GitHub 学生包通常自带 Copilot Pro);
|
|
42
|
+
2. 在群反馈 issue 里 assign `@copilot` → 它开工并开出草稿 PR(正文引用本 issue)→ 状态机自动播「正在处理 🔧」;
|
|
43
|
+
3. 人审合并 → issue 自动关闭 → 自动播「处理完毕 ✅」。
|
|
44
|
+
|
|
45
|
+
## 测试方法(需先 push 到 main)
|
|
46
|
+
|
|
47
|
+
1. 建一个测试 issue 打上 `group-report` → 群里应出现「收到 ✅」+ issue 出现评论;
|
|
48
|
+
2. 开个 PR 正文写 `fixes #<测试issue号>` → 群里「正在处理 🔧」;
|
|
49
|
+
3. 关闭测试 issue → 群里「处理完毕 ✅」。
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 群反馈云端耳朵(Webhook)部署说明
|
|
2
|
+
|
|
3
|
+
> 2026-09-13 搭建(同日改为 AI 归纳路线):**不再自动建 GitHub issue**。云端耳朵把群里命中
|
|
4
|
+
> 反馈词/问题关键词的消息静默收进一个**秘密 Gist**;每天 12:00/21:00 的日报(group-digest)读取
|
|
5
|
+
> gist、调用大模型归纳成带标题的「群内反馈(AI 归纳)」清单随日报发群,并清空 gist 防重复。
|
|
6
|
+
> 前置阅读:状态闭环词汇表见 `GROUP-LISTENER-SETUP.md`(issue 状态机保留但处于休眠,当前无建单方)。
|
|
7
|
+
|
|
8
|
+
## 组成
|
|
9
|
+
|
|
10
|
+
| 文件 | 作用 |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| `.github/cloud/qq-webhook/index.js` | Webhook 接收端(Web 函数,零依赖,任何 Node>=18 环境可跑) |
|
|
13
|
+
| `.github/scripts/group-listener.mjs` | WS 版耳朵(备用:有常开设备时的等价实现,二选一) |
|
|
14
|
+
|
|
15
|
+
## 部署步骤(腾讯云函数 · Web 函数)
|
|
16
|
+
|
|
17
|
+
1. 注册腾讯云并完成实名认证 → 控制台搜「函数服务 SCF」;
|
|
18
|
+
2. 新建函数:函数类型选 **Web 函数**,运行环境 **Node.js 18 或 20**,地域就近(如广州/上海);
|
|
19
|
+
3. 函数代码:把 `.github/cloud/qq-webhook/index.js` 打成 zip(就一个文件)上传,执行方法保持默认(`index.main_handler` 不适用 Web 函数,Web 函数看监听端口);
|
|
20
|
+
4. 环境变量(函数配置里加):
|
|
21
|
+
```
|
|
22
|
+
QQ_APP_ID=1905260114
|
|
23
|
+
QQ_APP_SECRET=<机器人secret>
|
|
24
|
+
QQ_GROUP_OPENID=D4D52BA3A7412F88E9192011CD4B935A
|
|
25
|
+
GH_TOKEN=<细粒度PAT,Account permissions → Gists: Read and write(收集用,不碰仓库)>
|
|
26
|
+
GIST_ID=<秘密 gist 的 32 位 id,文件名 group-feedback.jsonl>
|
|
27
|
+
REPO=Aik358/dsh-auto-memory
|
|
28
|
+
ROUTE_TOKEN=<自造一段随机字符串,防扫描>
|
|
29
|
+
FEEDBACK_KEYWORDS=问题,bug,报错,error,异常,失效,崩溃,闪退,不能用,出错了,坏了,修复 <可选,收集关键词>
|
|
30
|
+
LLM_API_KEY=<可选;配了才启用「@ 消息大模型应答」(DeepSeek key 或任意 OpenAI 兼容端点)>
|
|
31
|
+
LLM_MODEL=deepseek-chat <可选,默认 deepseek-chat>
|
|
32
|
+
LLM_BASE_URL=https://api.deepseek.com <可选,换其他 OpenAI 兼容服务时改>
|
|
33
|
+
```
|
|
34
|
+
5. 部署后,函数详情页拿「**访问服务 URL**」(默认公网域名,HTTPS),在末尾拼上 `/<ROUTE_TOKEN>/`;
|
|
35
|
+
6. QQ 新版控制台 → 开发设置 → 「事件订阅与回调地址」→ 接收方式切换 **Webhook** → 粘贴上面的 URL;
|
|
36
|
+
平台立刻发 op=13 验证请求,我们的服务会自动应答(用 AppSecret 派生 Ed25519 密钥签名),通过即绑定;
|
|
37
|
+
7. 真实验证:群里发「反馈 这是一条测试」→ 应出现:新建 issue(带 group-report 标签)+ 群里「收到 ✅」。
|
|
38
|
+
|
|
39
|
+
## 关键事实(来自官方文档,2026-09 核对)
|
|
40
|
+
|
|
41
|
+
- 回调只要求 **HTTPS + 端口 80/443/8080/8443**;文档未要求 ICP 备案(腾讯云函数默认域名可直用;若校验被拒,改用 API 网关触发器或 CloudBase 云接入的默认域名,再不行换 VPS 跑同一份代码);
|
|
42
|
+
- **签名**:seed = AppSecret 自我拼接补足 32 字节 → 派生 Ed25519 密钥对;op=13 用私钥签 `event_ts+plain_token` 回 `{plain_token, signature}`;事件推送用公钥验 `X-Signature-Ed25519`(原文 = 时间戳+原始 body);
|
|
43
|
+
- **切换即生效**:Webhook 与 WebSocket 互斥——切过去后,`qq-capture-openid.mjs`/`group-listener.mjs` 这类 WS 工具就收不到事件了(切回来同理);
|
|
44
|
+
- 建议在函数配置里把 `STRICT_VERIFY=1`(验签严格模式)等真实验证跑通后再开。
|
|
45
|
+
|
|
46
|
+
## 与 Copilot/修理工的关系
|
|
47
|
+
|
|
48
|
+
建出的 issue 分配给 @copilot(需 Copilot 付费档,学生包免费)或自行认领;PR 引用 issue 自动播「正在处理 🔧」,关闭自动播「处理完毕 ✅」——由 `group-report-status.yml` 负责,与本耳朵解耦。
|
|
49
|
+
|
|
50
|
+
## 按需报告接口(给 DeepSeek Harness / 人工用)
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
GET https://<函数URL>/<ROUTE_TOKEN>/?report=N # 最近 N 小时(1-48)群反馈
|
|
54
|
+
GET ...?report=12&raw=1 # 只要原文不要 AI 总结
|
|
55
|
+
```
|
|
56
|
+
返回 JSON:`{ window_hours, total, items:[{t,u,w,m}], summary?, v }`。
|
|
57
|
+
- items=带时间戳的原始反馈;配了云函数 LLM_API_KEY 时附 summary(AI 分诊:问题清单+修复优先级),没配则只有原文——harness 的模型可直接读原文自己分析。
|
|
58
|
+
- 注意:每次日报(11:40/20:40)读完后会清空收集区,所以可查范围≈「自上次日报以来收集的反馈」(与 12h 窗口天然对齐)。
|
|
59
|
+
- DeepSeek Harness 用法:直接 GET 该 URL(浏览器/curl/任意 HTTP 工具),把返回 JSON 交给模型出修复方案;或固化成 auto-memory 的 procedure。
|
|
60
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# 交接说明 · 给 ZCode(dsh-auto-memory)
|
|
2
|
+
|
|
3
|
+
> 写于 2026-09-11 21:4x(UTC+8)· 交接方:DSH 主对话(DeepSeek Harness)
|
|
4
|
+
> **本文件自包含**:ZCode 看不到写这份文档的那个会话的上下文与记忆,读完这一份 + 文末「关键路径表」里的文件即可开工。
|
|
5
|
+
> 一句话项目:**DeepSeek Harness Web GUI 的「主动联想记忆 + 上下文管理」插件**(npm 公开包,1 万+ 下载),当前 **v2.4.2 已发布**。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. 现在的状态(先对账,别信旧文档)
|
|
10
|
+
|
|
11
|
+
| 事项 | 值 |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| npm 包 | `@a9i5k4/dsh-auto-memory` · latest = **2.4.2**(发布包 218 files / 10.9 MB) |
|
|
14
|
+
| pre 线(开发) | `D:\dsh-auto-memory`,HEAD = `f83e144`(docs 收编)· 已跟踪文件**无未提交改动** |
|
|
15
|
+
| REL 线(发布基座) | `D:\dsh_debug\_publish_dsh-auto-memory`,HEAD = `243dee1`(= tag `v2.4.2` = GitHub main) |
|
|
16
|
+
| GitHub | https://github.com/Aik358/dsh-auto-memory(main = 243dee1) |
|
|
17
|
+
| 本机运行形态 | 插件以**符号链接**挂载:`~/.dsh/profiles/web/node_modules/@a9i5k4/dsh-auto-memory` → `D:\dsh-auto-memory` |
|
|
18
|
+
| 宿主 | 官方 `dsh` **0.1.5-rc.1**,Web GUI 在 `http://127.0.0.1:3080` |
|
|
19
|
+
|
|
20
|
+
**两条线是平行历史**:pre 线有 `-pre` 后缀(`lib/*-pre.js`、配置 `dsh-auto-memory-pre.json`、路由前缀 `/api/dsh-auto-memory-pre/`),`tools/release.mjs` 会在发布时把它们**裸名化**并重建 REL 树。
|
|
21
|
+
👉 **严禁把 pre 直接 push 到远端 main**;发布只能走下面的固定流程。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. 代码结构(没有构建步骤)
|
|
26
|
+
|
|
27
|
+
| 文件 | 作用 | 规模 |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `lib/index.js` | **宿主半边**:记忆引擎 + 配置表 + 全部 HTTP 路由 + 工具注册 | ~8,200 行 |
|
|
30
|
+
| `lib/client.js` | **界面半边**:浏览器 bundle(手写 `__ModuleLoader__`,React + 原生 DOM,零依赖) | ~4,600 行 |
|
|
31
|
+
| `lib/*-pre.js` | 其余宿主模块(检索/水位/接续/语义/Python 侧车…),多数有对应的裸名孪生文件 | — |
|
|
32
|
+
| `python/` | 可选 Python 语义引擎(BGE-M3 int8)+ worker,随包发布 | — |
|
|
33
|
+
| `tests/smoke/*.mjs` | **69 个**冒烟套件(全量回归就是逐个 `node` 跑) | — |
|
|
34
|
+
| `tools/release.mjs` | 发布构建器(pre→正式转换 + 校验闸门) | — |
|
|
35
|
+
| `docs/` | 会被打进 npm 包(**注意**:内部文档目前也在里面) | — |
|
|
36
|
+
|
|
37
|
+
**关键结构约束**:两个半边是**独立 bundle**,`client.js` 不能 `import` 宿主模块 → 「同一份事实」只能靠**测试锁**保证(已有 `tests/smoke/smoke-test-api-paths-pre.mjs`)。
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. 怎么验证改动(发版前置门)
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
cd D:\dsh-auto-memory
|
|
45
|
+
# 全量回归(约 2-3 分钟,必须 0 失败)
|
|
46
|
+
Get-ChildItem tests\smoke -File -Filter *.mjs | ForEach-Object { node $_.FullName > $null 2>&1; if ($LASTEXITCODE -ne 0) { "FAIL: " + $_.Name } }
|
|
47
|
+
# 语法 + 编码
|
|
48
|
+
node --check lib\index.js; node --check lib\client.js
|
|
49
|
+
# 写文件必须 UTF-8 无 BOM(用户硬性规则,BOM 会让 dsh web 起不来)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **路由数(46)与工具数(14)被多个用例硬锁**;加减路由/工具必须同步改测试。
|
|
53
|
+
- `smoke-test-autocont-host-pre.mjs` 用「方法白名单」式夹具:给被抽方法新增 `this.xxx()` 调用要同步加进白名单,否则 TypeError 被外层 `try/catch` 吞成"没反应"。
|
|
54
|
+
- 单跑原则:个别套件(如 `m53`)在批量连跑时会受机器负载影响,历史上是**单跑**确认。
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. 发版固定流程(10 步,唯一检查表 `docs/internal/RELEASE-PROCESS.md`)
|
|
59
|
+
|
|
60
|
+
1. **前置门**:全量回归 0 失败 + **脏树范围核实**(`release.mjs` 的源就是工作区,**脏树会整体进包且不可回溯**)。
|
|
61
|
+
2. 定版本号(纯修复 patch / 有新行为 minor)。
|
|
62
|
+
3. **必改 `CHANGELOG.md`**:新增 `## [<ver>] — <日期> · <主题>` 小节。
|
|
63
|
+
4. **必同步软件内版本标识**:①应用内更新说明字典 `lib/client.js` 的 `var CHANGELOG = { '<ver>': { zh: [...], en: [...] } }` ②界面指纹行 `console.log('[dsh-auto-memory] client v<ver> fingerprint: ...')` ③`package.json.version`(由 release.mjs 自动回写开发树)。
|
|
64
|
+
5. pre 线提交:`git add lib tests CHANGELOG.md tools/release.mjs`(+ 视情况 docs)→ commit(身份 `Aik358 <aik358@users.noreply.github.com>`)。
|
|
65
|
+
6. `node tools\release.mjs <ver> --dry-run` —— 验闸门(应输出 `版本标识一致性: OK(CHANGELOG / 应用内更新说明 / 界面指纹行)`)。
|
|
66
|
+
7. `node tools\release.mjs <ver>` —— 真构建(写 REL 树 + 回写开发树版本)。
|
|
67
|
+
8. REL:`git add -A` → commit → `git tag -f v<ver>`。
|
|
68
|
+
9. push(**必须带 PAT 行内 URL**,见 §5)→ `npm publish`。
|
|
69
|
+
10. **三处复核**:registry `/latest`、`git ls-remote ... refs/heads/main`、`refs/tags/v<ver>` 三者一致。
|
|
70
|
+
|
|
71
|
+
**第 3、4 步已做成机制**:`tools/release.mjs` §5.05「版本标识一致性闸门」——三项任一不符即 `process.exit(1)` **拒绝构建**(防止"检测更新"一直拿旧版本号比对)。
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 4. 环境、凭据与两个坑
|
|
76
|
+
|
|
77
|
+
- **本机 `~/.npmrc` 指向 npmmirror** → 查询与发布都必须显式 `--registry=https://registry.npmjs.org`,否则会查到旧版本、误判发布失败。
|
|
78
|
+
- **`npm view` 会命中本地缓存**:发布成功后可能仍回读上一版(v2.4.2 发布后回读 2.4.1)。判定用权威接口:
|
|
79
|
+
`Invoke-RestMethod 'https://registry.npmjs.org/@a9i5k4%2Fdsh-auto-memory/latest' | Select-Object -ExpandProperty version`
|
|
80
|
+
- **凭据不在本仓库**:GitHub PAT 与 npm token 存在另一个工作区的记忆文件里 ——
|
|
81
|
+
`~/.dsh/memory/workspaces/--D--dsh_debug--/MEMORY.md`(该文件明确标注"只存本地,**严禁写入任何会上传 GitHub/npm 的文件**")。
|
|
82
|
+
npm 发布需带 bypass-2FA 的那个 token;推送用 `git -c credential.helper= push https://x-access-token:<PAT>@github.com/Aik358/dsh-auto-memory.git main --tags --force`。
|
|
83
|
+
- **发布后生效**:宿主半边(`lib/index.js`)改动需**用户自己重启 dsh web**;界面半边刷新页面即可。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 5. 不可碰的契约 & 用户硬性规则
|
|
88
|
+
|
|
89
|
+
- **后端冻结(用户明确要求,大排期期间生效)**:记忆引擎逻辑、**46 条路由**契约、**85 个配置键**语义与默认值、prompt 层、Python worker、14 个工具名 —— 改外观/文档时**一律不动**。
|
|
90
|
+
- **未经明确同意,严禁停止/重启 dsh web 宿主进程**(会截断工具调用);host 改完只改文件并提示用户重启。
|
|
91
|
+
- **未经明确要求,不要 `npm publish` / push GitHub / 打 tag**。
|
|
92
|
+
- **写任何文件严禁 BOM**(保持 UTF-8 无 BOM;写完可用前三字节校验)。
|
|
93
|
+
- 界面文案与文档的**文风**遵循 `docs/PROMO-STYLE-GUIDE.md`(产品拟人称"她"、厂商腔黑名单、母比喻=一本书)。
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 6. 下一版要做的三件事(已记录,未开工)
|
|
98
|
+
|
|
99
|
+
完整清单(含行号、改法、验收)在 **`docs/internal/NEXT-VERSION-TODO.md`**。摘要:
|
|
100
|
+
|
|
101
|
+
1. **水位判据口径**:现在触发用的是「可用额度」分母 `effectiveWin = win − reserve`(`lib/index.js:1900`,本机 1,048,576 − 384,000 = 664,576),导致**上下文刚过半就触发接续**,比官方压缩点(≈80% ≈ 83.9 万)**早约 45%**,用户判定太浪费。
|
|
102
|
+
**改法**:正常触发线改用官方声明窗口为分母(或 `min(win, hardWin)`),阈值 0.75–0.78;`reserve` **不再参与分母**,只保留"距硬墙余量"展示 + 硬判据(`estTokens + reserve > win` 才硬触发)。补反向锁:断言"不得把 reserve 计入分母"。
|
|
103
|
+
2. **接续序号**:`contSeq = handoff 目录里 prev-session-*.md 文件数 + 1`(`lib/index.js:2646-2648`),在落盘失败 / 跨工作区(handoffDir 按工作区解析)/ carry 复用缓存时会**不递增、重复或为空**(为空即不 rename → 标题退回自动生成,用户已两次观察到"新窗口序号不对")。
|
|
104
|
+
**改法**:改**持久计数器** `handoff/cont-seq.json`(键 = workspaceId),缺失时从现有 `接续 #N` 标题/包名解析最大值 +1 兼容老数据;序号分配与包落盘做成同序事务;三条入口(面板一键 / 宿主兜底 / 重启后)全覆盖。
|
|
105
|
+
3. **固定流程外包给子代理**(用户提出的想法):发版这类已固化的流程**不由主对话逐步执行**(主对话上下文最贵)→ 交给子代理(思考强度不必高),它做完/出错后只回结构化结论,主对话只做三件事:开闸前确认前置门 / 放行或中止 / 失败时处置。
|
|
106
|
+
落地物:`docs/prompts/RELEASE-AGENT.md`(自包含任务书)+ `RELEASE-PROCESS.md` 顶部「角色分工」段 + 同类流程(全量回归 / 双语对账 / 痕迹巡检)各配任务书。回报格式 `{ ok, version, pre_sha, rel_sha, tag, npm_latest, failed_step, error_tail }`。
|
|
107
|
+
|
|
108
|
+
> 现状补充:**v2.4.2 已把 `autoContinueEnabled` 出厂默认改为 `false`**(并加反向锁 `smoke-test-autocont-host-pre.mjs` 断言默认必须为 false)。用户本机也已关闭。所以上述 ① 是"改好再考虑翻回默认开"的前置。
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 7. 待开工的大排期(界面 × 文档 × 首页)
|
|
113
|
+
|
|
114
|
+
用户已拍板方向,**设计优先、功能说明最后核对**,**后端冻结**:
|
|
115
|
+
|
|
116
|
+
- 排期与现状硬数据:`docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md`(含诊断、P0-P6 排期、5 条验收判据、6 个待用户拍板点)
|
|
117
|
+
- 美术方向与动效规格:`docs/internal/ART-DIRECTION-WIREFRAME.md`(线框稿 + EVA 式克制线描色板 + 线宽四档 + 工程图元素 + **24fps 动效时序表** + 性能预算 + **可直接转发给 Astra 的交付契约**)
|
|
118
|
+
- 三条线:①界面与设置页大改(12 页签塞进 440×560 浮层 = 容器错配;DSH 有 ~58 个原生插槽可用,`slots.inject/register` 是通用 API,挂原生位**不需要改后端**)②README/用户说明书/项目文档大改 ③`docs/landing/index.html` 首页大改(现为 1,745 行自包含单文件,靠 `preview` 分支 + `htmlpreview` 第三方代理发布;无 `.github/`、无 Pages)
|
|
119
|
+
- 设计主张:面板向 **DSH 原生 `--dsw-*` 令牌**靠(DSH 自己的 UI 包是打包后 CSS Modules,类名私有**不可复用**,只有公开 CSS 变量 + 插槽位可用);首页保留现代主义品牌色(暖纸 `#F4F1EB` / 墨 `#17171A` / 信号橙 `#E9470C`)。
|
|
120
|
+
- 角色动画(24 帧逐帧)**交给 Astra**:`ART-DIRECTION-WIREFRAME.md` §5 是完整交付契约(画布 1200×1200@1x、锚点 `(600,1080)`、PNG-24 + alpha、命名 `hero_0000.png`、分层导出、7 条验收)。
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 8. 血泪坑清单(照着躲)
|
|
125
|
+
|
|
126
|
+
1. **脏树进包**:`release.mjs` 的 DEV 源就是 `D:\dsh-auto-memory` 工作区 —— 发版前必须核实 `git status` 范围(本机曾两次发现工作区混有其他会话的未发布改动)。
|
|
127
|
+
2. **两半边独立 bundle**:`client.js` 是手写 `__ModuleLoader__`,不能用相对 `import`;跨半边一致性只能靠测试锁。
|
|
128
|
+
3. **同一文件一次消息发两个 edit 会丢前者**(实测过)→ 同文件改动串行,或写「替换清单 JSON + 计数断言脚本」一次做(不符即整体不写盘)。
|
|
129
|
+
4. **`edit` 工具会被全角引号/缩进差异绊住** → 大文件批量改推荐脚本 + 动态识别缩进(别写死空格数)。
|
|
130
|
+
5. **JS 正则不支持 `(?m)` 内联标志**(要用 `/.../m`;写进测试会直接 SyntaxError)。
|
|
131
|
+
6. **水位/接续相关**:判据优先级 = provider 错误文本 > 官方 `request/context.contextWindow` > 本地 tokenMeter > 启发式估算;本地估算在"中文+代码+大工具输出"会话里**偏乐观约 2×**。撞墙报错原文含 `CONTEXT_WINDOW_EXCEEDED`,是本机校准的唯一权威来源。
|
|
132
|
+
7. **接续有两条路径且行为不同**:宿主 `hostAutoContinue()`(无刷新仪式)与面板 `runContinueFlow()`(注入刷新仪式)。改任一侧要同步评估另一侧。排查"接续没反应"先看 `~/.dsh/dsh-auto-memory-pre-diagnose.log`。
|
|
133
|
+
8. **`docs/` 会被打进 npm 包**,而 README/USER-GUIDE 的图片走相对路径 `docs/screenshots/…` → 想收窄 `files` 必须**分层**(保留 screenshots + 论文 + 架构图),不能整目录删。
|
|
134
|
+
9. **`npm view` 命中本地缓存** + **本机 .npmrc 是 npmmirror**(见 §4)。
|
|
135
|
+
10. **凭据只走行内 URL / 环境变量**,绝不写进任何会上传 GitHub 或 npm 的文件。
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 9. 建议 ZCode 的第一件事
|
|
140
|
+
|
|
141
|
+
```powershell
|
|
142
|
+
cd D:\dsh-auto-memory
|
|
143
|
+
git log --oneline -5 # 确认在 pre 线
|
|
144
|
+
git status --short # 确认没有别人的未提交改动
|
|
145
|
+
Get-ChildItem tests\smoke -File -Filter *.mjs | ForEach-Object { node $_.FullName > $null 2>&1; if ($LASTEXITCODE -ne 0) { "FAIL: " + $_.Name } } # 回归基线
|
|
146
|
+
```
|
|
147
|
+
然后二选一:**A. 深改**(照 `docs/internal/NEXT-VERSION-TODO.md` 从改点 ① 开始)或 **B. 大排期**(照 `docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md`,但需先向用户取三样:官方 DSH 网页 URL/截图、角色基准图、风格探针许可)。
|
|
148
|
+
|
|
149
|
+
**别做**:不要 `npm publish`/push/tag(除非用户明确要求);不要重启 dsh web;不要改 §5 冻结清单里的任何契约。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 10. 关键路径表
|
|
154
|
+
|
|
155
|
+
| 路径 | 用途 |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `docs/internal/RELEASE-PROCESS.md` | 发版 10 步唯一检查表(含凭据纪律、npm 缓存坑) |
|
|
158
|
+
| `docs/internal/NEXT-VERSION-TODO.md` | 下一版三点待改(水位口径 / 接续序号 / 流程外包)+ 验收 |
|
|
159
|
+
| `docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md` | 界面×文档×首页大排期预研(诊断 + P0-P6 + 判据) |
|
|
160
|
+
| `docs/internal/ART-DIRECTION-WIREFRAME.md` | 美术方向 + 24fps 规格 + Astra 交付契约 |
|
|
161
|
+
| `docs/PROMO-STYLE-GUIDE.md` | 文风守则(README/landing/公告必须遵循) |
|
|
162
|
+
| `docs/UI-INVENTORY-RAW.md` | 界面逐条盘点(12 页签 / 85 键 / 46 端点,带行号) |
|
|
163
|
+
| `docs/USER-GUIDE.{en,zh-CN}.md` · `README{,.zh-CN}.md` | 面向用户的四份文档(待大改) |
|
|
164
|
+
| `docs/landing/index.html` | 首页(单文件、双语) |
|
|
165
|
+
| `tools/release.mjs` | 发布构建器 + 版本标识闸门(§5.05) |
|
|
166
|
+
| `tests/smoke/smoke-test-api-paths-pre.mjs` | 路径表一致性锁(客户端 ⊆ 宿主) |
|
|
167
|
+
| `~/.dsh/dsh-auto-memory-pre-diagnose.log` | 插件诊断日志(接续/水位/降级线索都在这) |
|
|
168
|
+
| `~/.dsh/dsh-auto-memory-pre.json` | **pre 线**实际配置文件(别读成非 `-pre` 的那个) |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 下一版待改(用户 2026-09-10 19:0x 指定)
|
|
2
2
|
|
|
3
3
|
> 来源:用户实机观察 —— 「自动接续又触发了,而且确实才刚过半,太浪费;你现在依靠那个 max output 来算,但官方压缩也是等到上下文真正占到 80% 才开始;新窗口依旧没有按正确序号排序。接续流程我已经关掉了,只要记着下一版怎么改就行。」
|
|
4
|
-
>
|
|
4
|
+
> 状态:**✅ 三项已于 v2.5.0(2026-09-13)落地**:①分母=官方声明窗口(reserve 退出分母,新增预测性硬墙 estTokens+reserve>判定窗)②cont-seq.json 持久计数器(全局单调/失败回滚/标题扫描兜底,smoke-test-contseq-pre.mjs)③docs/prompts/RELEASE-AGENT.md 等四份任务书 + RELEASE-PROCESS.md 角色分工。`autoContinueEnabled` 出厂默认仍为 false,是否翻回待用户实机验证后定夺。
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -65,3 +65,31 @@
|
|
|
65
65
|
- 用户**已手动关闭自动接续**(`autoContinueEnabled = false`)以免浪费 token;改完需用户自行开启并重启 dsh web 验证。
|
|
66
66
|
- `v2.4.1` 已含「已接续闩锁」(`~/.dsh/memory/auto-continue-done.json`),本次"又触发"是在**该闩锁之前就已 arm 的会话**上发生的,不代表闩锁失效;下版验证时要区分"闩锁没拦住"与"口径太早"。
|
|
67
67
|
- 相关 diag:`~/.dsh/dsh-auto-memory-pre-diagnose.log`(`auto-continue armed / deferred / deadline reached / rejected at edge / host-executed` 全在这条线上)。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 改点 3 · 固定流程外包给子代理(主对话只做决策)
|
|
72
|
+
|
|
73
|
+
> 用户 2026-09-10 提出:「这种固定流程(尤其是已经多次固化成 skill 的),比如发版本,不应该由主对话来处理,主对话太耗上下文了,应该丢给一个 sub agent,思考强度不用特别高。他做完了或者出错了就扔回主对话,让主对话决定怎么解决。」
|
|
74
|
+
|
|
75
|
+
### 目标形态
|
|
76
|
+
- **主对话只做三件事**:①开闸前确认前置门(脏树范围核实 + 全量回归结果)②收到回报后判定放行/中止 ③失败时决定处置方向。**不逐步执行流程**。
|
|
77
|
+
- **子代理执行**:按检查表全跑,**出错即停**,回报一个结构化结论:
|
|
78
|
+
`{ ok, version, pre_sha, rel_sha, tag, npm_latest, failed_step, error_tail(≤20 行) }`
|
|
79
|
+
- **凭据不进提示词**:子代理自行从 `--D--dsh_debug--/MEMORY.md` 读(该处明确「只存本地,严禁写入任何会上传 GitHub/npm 的文件」)。
|
|
80
|
+
|
|
81
|
+
### 落地形态(下版做)
|
|
82
|
+
1. **`docs/prompts/RELEASE-AGENT.md`** —— 给子代理的完整任务书:照 `docs/internal/RELEASE-PROCESS.md` 逐条展开 + 回报格式 + 出错即停规则 + 禁止事项(无 PAT 不得 push、未过闸门不得发布、除版本标识与 CHANGELOG 外不得改文件)。
|
|
83
|
+
2. **`RELEASE-PROCESS.md` 顶部加「角色分工」段**:主对话=决策者 / 子代理=执行者,并写明「主对话不得逐步执行本清单」。
|
|
84
|
+
3. **同类流程一并外包**:全量回归、docs 双语对账、子代理痕迹巡检,各写一份任务书(`docs/prompts/*-AGENT.md`)。
|
|
85
|
+
|
|
86
|
+
### 已知约束(先记下来,免得下版踩)
|
|
87
|
+
- **当前工具面无法给 `subagent` 指定思考强度**:`subagent` 只接受 `description/prompt/run_in_background`,`workflow` 的 `agent()` 会**显式拒绝** `effort`/`agentType`。要真压到 low/off 只有两条路:①在 DSH 侧给该路由/预设配默认推理强度;②**由插件自己 spawn** —— 插件已有 `subagentReasoningEffort: off|low|high|max`,经 `ctx.subagents.start({ agentOptions })` 下发。
|
|
88
|
+
- **子代理看不到主对话**:任务书必须自包含(路径、命令、判据、回报格式全写死)。
|
|
89
|
+
- **不得并发**:同一工作区的写盘流程(尤其发版)必须串行。
|
|
90
|
+
- 子代理同样受「不重启宿主」约束:需要重启才生效的事只能回报给用户,不能自己动手。
|
|
91
|
+
|
|
92
|
+
### 验收
|
|
93
|
+
- 主对话跑一次发版:其上下文增量只含「开闸判断 + 子代理回报 + 三处复核」三块;
|
|
94
|
+
- 故意造一次失败(如抽掉 CHANGELOG 的 `## [<ver>]` 小节):子代理回报 `failed_step=5.05` 且 `error_tail` 含闸门原文,主对话据此给处置;
|
|
95
|
+
- 子代理输出里不出现凭据明文(除命令行本身;不落盘、不入 git)。
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
> 2026-09-10 固化。本文件是发版的唯一检查表;`tools/release.mjs` 已内置**版本标识一致性闸门**(缺任一项即拒绝构建),所以下面第 2、3 步不是"记得做",而是"不做就发不出去"。
|
|
4
4
|
|
|
5
|
+
## 角色分工(2026-09-13 固化,改点3)
|
|
6
|
+
|
|
7
|
+
- **任务书流向**:主对话读本检查表 → 角色分工指向任务书 → **主对话填入版本号后原文派发给执行子代理**。用户不必、也不要绕过主对话直接投喂子代理(两条入口并存=并发冲突之源)。
|
|
8
|
+
- **主对话 = 决策者,只做三件事**:①开闸前确认前置门(第 0 节:全量回归结果 + 脏树范围核实)②收到子代理回报后**放行或中止** ③失败时决定处置方向。**主对话不得逐步执行本清单**——发版这类固化流程交给子代理执行(主对话上下文最贵)。
|
|
9
|
+
- **子代理 = 执行者**:投喂 [`docs/prompts/RELEASE-AGENT.md`](../prompts/RELEASE-AGENT.md)(自包含任务书,填入版本号),按本清单全跑、**出错即停**,只回结构化结论 `{ ok, version, pre_sha, rel_sha, tag, npm_latest, failed_step, error_tail }`。
|
|
10
|
+
- **不得并发(冲突防线)**:主对话**派活后等待回报**,期间对同一工作区**只读**(看日志/读状态可以;改文件、git 写操作、跑发版命令、再派第二个执行子代理都不行);只读类任务书(回归/对账/巡检)之间可并行,但**不与发版执行并发**(回归占满 CPU 会污染计时敏感套件)。
|
|
11
|
+
- 凭据不经主对话转手:子代理按任务书自行从 `--D--dsh_debug--` 记忆文件读取,回报中一律 `<redacted>`。
|
|
12
|
+
- 同类固化流程的任务书:全量回归=[`REGRESSION-AGENT.md`](../prompts/REGRESSION-AGENT.md) · 双语对账=[`DOCS-AUDIT-AGENT.md`](../prompts/DOCS-AUDIT-AGENT.md) · 痕迹巡检=[`TRACE-PATROL-AGENT.md`](../prompts/TRACE-PATROL-AGENT.md)。
|
|
13
|
+
|
|
5
14
|
## 0. 前置门(不满足不许开工)
|
|
6
15
|
|
|
7
16
|
- [ ] 全量回归:`cd D:\dsh-auto-memory; Get-ChildItem tests\smoke -File -Filter *.mjs | ForEach-Object { node $_.FullName }` → **0 失败**
|
|
@@ -76,6 +85,13 @@ git ls-remote https://github.com/Aik358/dsh-auto-memory.git refs/tags/v<ver>
|
|
|
76
85
|
```
|
|
77
86
|
三处 sha/版本必须一致。
|
|
78
87
|
|
|
88
|
+
**坑(2026-09-10 实测)**:`npm view` 会命中本地 npm 缓存 —— 刚发布后可能仍回读到**上一个版本**(本次 v2.4.2 发布成功后仍回读 2.4.1)。判定以 **registry 权威接口**为准:
|
|
89
|
+
|
|
90
|
+
```powershell
|
|
91
|
+
Invoke-RestMethod 'https://registry.npmjs.org/@a9i5k4%2Fdsh-auto-memory/latest' | Select-Object -ExpandProperty version
|
|
92
|
+
npm view @a9i5k4/dsh-auto-memory version --registry=https://registry.npmjs.org --prefer-online
|
|
93
|
+
```
|
|
94
|
+
|
|
79
95
|
## 纪律
|
|
80
96
|
|
|
81
97
|
- **凭据只走行内 URL / 环境变量,严禁写进任何会上传 GitHub 或 npm 的文件**(本文件亦不写)。
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# 待办总表 · dsh-auto-memory(2026-09-13 汇总)
|
|
2
|
+
|
|
3
|
+
> 用途:**唯一待办入口**。跨会话/跨工具(DSH、ZCode)通用 —— 不依赖任何一方的工作记忆,全部写路径与判据。
|
|
4
|
+
> 状态约定:🔴 阻塞或高优先 / 🟡 应做但可排期 / ⚪ 可选或长期 / ✅ 已关闭(留在 §J 备查,别重复劳动)
|
|
5
|
+
> 事实基线(2026-09-13 实测):npm latest = **2.4.2**;pre 线 `D:\dsh-auto-memory` HEAD = `e55a0ec`,已跟踪文件无未提交改动;REL = tag `v2.4.2` = GitHub main = `243dee1`。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. 一分钟看板
|
|
10
|
+
|
|
11
|
+
| 优先级 | 事项 | 归属 | 参考 |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| 🔴 | 大排期三件套(界面 / 文档 / 首页)**等用户拍板 6 点 + 给 3 样输入** | 用户 → 执行方 | `DESIGN-OVERHAUL-PRE-RESEARCH.md` §8 |
|
|
14
|
+
| 🟡 | **v2.5.0 待发版**:B 区三项深改已全部落地(2026-09-13),等用户令按 RELEASE-PROCESS 角色分工派子代理执行 | 执行方 | `NEXT-VERSION-TODO.md` 状态行 |
|
|
15
|
+
| 🟡 | **procedure 记忆机制整体重构**(沿用 Hermes、社区褒贬不一;等用户统一拍板整体逻辑,期间不动引擎) | 用户 → 执行方 | 本表 §A 末条 |
|
|
16
|
+
| 🟡 | 群反馈·日报 CI 的**安全与运维**收口(密钥/签名/成本/告警) | ZCode | `.github/`、`docs/internal/GROUP-*.md` |
|
|
17
|
+
| 🟡 | 冷启动闭环(公开用户"装上没反应") | 执行方 | 本表 §C |
|
|
18
|
+
| 🟡 | 分发瘦身 + 对外元数据(npm 包 11 MB 里 86% 是 docs) | 执行方 | 本表 §D |
|
|
19
|
+
| ⚪ | 界面技术债(令牌层/组件层/i18n)——属大排期主体 | 执行方 | `DESIGN-OVERHAUL-PRE-RESEARCH.md` §2.6 |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## A. 阻塞在用户决策上(不点头无法开工)
|
|
24
|
+
|
|
25
|
+
- [ ] 🔴 **大排期的 6 个拍板点**(`docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md` §8):①首页托管(htmlpreview vs GitHub Pages)②设计主张(面板靠 DSH 原生 / 首页留品牌色,接受"两种语境"?)③浮层面板是否降级为"状态+快捷入口"④`associativeMemoryEnabled` 是否改出厂默认(**建议保持 false + 向导明说并一键开启**)⑤docs 是否分层出包 ⑥排期节奏(P0-P6 顺序 vs 先集中做界面)。
|
|
26
|
+
- [ ] 🔴 **大排期开工需要的 3 样输入**(`docs/internal/ART-DIRECTION-WIREFRAME.md` §8):①官方 DSH 网页的 URL/截图(全网只搜到第三方介绍文)②角色基准(默认用 `docs/banner.jpg` 的水彩版做线稿化,或用户另给设定图)③风格探针许可(需用户在 Ark9 生图面板点批准)。
|
|
27
|
+
- [ ] 🟡 **是否解除 `docs/PROJECT-FREEZE-AND-ROADMAP.md:8`「大版本完成前禁止改 README」**的冻结(本次排期就是那个大版本)。
|
|
28
|
+
- [ ] 🟡 **procedure 记忆机制整体逻辑重构(等用户统一拍板,期间不动引擎)**:现状 = 插件的 procedure store(`lib/procedure-store-pre.js`:observed→candidate→validated→active 晋升状态机、M6 六级渐进激活、`renderChecklist` 渲染进动态快照)整体沿用 **Hermes 的记忆方式**;而 Hermes 的记忆工具在社区里**褒贬不一**,用户对这套机制整体不满,届时**统一重构整体逻辑**,不做零敲碎打。审计补充(2026-09-13):①数据层从未产出过任何 active procedure(全机 7 个工作区均无 `procedures.json`)②`renderChecklist` 语义是"主对话自己照步骤执行",无"执行类流程派子代理"出口 ③`memory_recall` 无 procedures scope(只能被动注入)。此前提议的两个小改点(delegate 出口 + procedures scope)**并入**这次统一重构,不单独做。过渡期执行类流程的固化走**仓库任务书通道**(`docs/prompts/*-AGENT.md` + 角色分工,主对话派子代理)。用户更详细的不满与改进建议见 **QQ 群反馈与 GitHub issues**(发版后的反馈核查任务要专门检索 procedure/技能/记忆/skill 相关条目)。
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## B. 下一版代码改动(3 项,均已定位到行,详见 `docs/internal/NEXT-VERSION-TODO.md`)
|
|
33
|
+
|
|
34
|
+
> **✅ 2026-09-13 状态:三项已全部落地 v2.5.0(未发版,等用户令)**。全量回归 70/70 绿;细节与验收见 `NEXT-VERSION-TODO.md` 顶部状态行与 §J 存档。以下原文保留备查(文中行号为落地前观测值)。
|
|
35
|
+
|
|
36
|
+
- [ ] 🔴 **水位判据口径**:现触发用 `effectiveWin = win − reserve` 当分母(`lib/index.js:1900`,本机 1,048,576 − 384,000 = 664,576)→ 上下文**刚过半就触发接续**,比官方压缩点(≈80% ≈ 83.9 万)早约 45%。
|
|
37
|
+
**改法**:正常触发线改用官方声明窗口(或 `min(win, hardWin)`)为分母,阈值 0.75–0.78;`reserve` 只留"距硬墙余量"展示 + 硬判据(`estTokens + reserve > win` 才硬触发)。**加反向锁**:断言"不得把 reserve 计入分母"。
|
|
38
|
+
验收:阈值 0.75 时触发点 ≈ 78.6 万(而非 ≈46 万);`water-window` / `water-hard-trigger` / `autocont-host` / `water-step` 全绿。
|
|
39
|
+
- [ ] 🔴 **接续序号**:`contSeq = handoff 目录里 prev-session-*.md 文件数 + 1`(`lib/index.js:2646-2648`)→ 落盘失败 / 跨工作区 / carry 复用缓存时会**不递增、重复或为空**(为空即不 rename,标题退回自动生成)。
|
|
40
|
+
**改法**:持久计数器 `handoff/cont-seq.json`(键 = workspaceId),缺失时从现有 `接续 #N` 标题/包名解析最大值 +1 兼容老数据;序号分配与包落盘同序事务;三条入口(面板一键 / 宿主兜底 / 重启后)全覆盖。
|
|
41
|
+
验收:连做 3 次接续得到 `#N → #N+1 → #N+2`,跨工作区不重复;新增 smoke 覆盖计数器持久化与"落盘失败不跳号"。
|
|
42
|
+
- [ ] 🟡 **固定流程外包给子代理**:主对话只做三件事(开闸前确认前置门 / 放行或中止 / 失败时处置),子代理按检查表全跑、出错即停、回报 `{ ok, version, pre_sha, rel_sha, tag, npm_latest, failed_step, error_tail }`。
|
|
43
|
+
落地物:①`docs/prompts/RELEASE-AGENT.md`(自包含任务书,**尚未创建**)②`docs/internal/RELEASE-PROCESS.md` 顶部加「角色分工」段 ③同类流程(全量回归 / 双语对账 / 痕迹巡检)各配任务书。
|
|
44
|
+
已知约束:当前工具面**不能给 subagent 指定思考强度**(`subagent` 只收 description/prompt/run_in_background;`workflow.agent()` 显式拒绝 effort)→ 要压到 low/off 得靠 DSH 侧配路由默认强度,或由插件自身 spawn(插件已有 `subagentReasoningEffort: off|low|high|max`)。
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## C. 冷启动与上手(公开用户"装上没反应"的根治)
|
|
49
|
+
|
|
50
|
+
来自冷启动审计(`docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md` §2.5)。**除最后一条外均未修**:
|
|
51
|
+
|
|
52
|
+
- [ ] 🔴 **首启向导无兜底触发**:只有 `update-check` 成功返回 `current` 才分发首启弹窗(`lib/client.js:4439`)→ **断网/代理环境下零引导零提示**。改法:请求失败/超时也用本地条件(版本号或 localStorage)照常 dispatch;清掉死键 `firstRunDone`(只读无写)。
|
|
53
|
+
- [ ] 🔴 **卖点功能与向导显示不一致**:`associativeMemoryEnabled` 出厂 `false`(`lib/index.js:349`),而向导把它渲染成「推荐 + 默认开」(`lib/client.js:3229/3303` 用 `tg.def !== false` 显示 ON)→ 用户不点 = 看到 ON 实际 OFF(幻觉 ON)。
|
|
54
|
+
改法(不动后端默认值):向导如实显示"出厂关,这里帮你打开",点击即写入;同时修 `client.js:163` 与代码矛盾的"默认关"提示。
|
|
55
|
+
- [ ] 🟡 **引擎步自动下 129MB 且失败无重试**:进入引擎步即 `semanticDownload start`(`client.js:3168-3175`),失败态(`:3287`)只有报错没有重试按钮(`:3425-3439` 无重试分支)。改法:改为"点『安装』才下载"+ 加重试按钮(复用设置页 `semActionRetry` 同源逻辑)。
|
|
56
|
+
- [ ] 🟡 **`welcomeTourEnabled` 是死键**:host 从不读(`lib/index.js:317` 仅定义),所以设置里关掉也拦不住首启自动播放。改法:host 读该键并下发,client 在 `dispatchStartupDialog` 的 freshInstall 分支加门闸。
|
|
57
|
+
- [ ] 🟡 **术语改写 Top5**(走客户端 i18n,中英同步):水位 → 上下文余量;接续 → 换窗口续做;锚定 → 记忆索引;白板 PLAN/账本 → 项目看板/交接记录;唤起·固化·晋升 → 想起来/记下来/变成技能。
|
|
58
|
+
- [ ] ⚪ **装完给一句"她在工作"的反馈**:现在唯一信号是侧栏多了个「记忆」按钮;自动沉淀有 240 字符门槛,头几句闲聊会被跳过且无任何提示。
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## D. 分发与门面(npm / GitHub 对外)
|
|
63
|
+
|
|
64
|
+
- [ ] 🟡 **npm 包瘦身**:`files` 含整个 `docs`(同日实测 **11.0 MB / 167 文件**,其中 `docs/screenshots` 约占 7.9 MB、内部工程文档 101/106 个 md)。改法:`docs/internal/**` 与 `docs/prompts/**` 移出包;**必须保留** `docs/screenshots/**` + 3 篇论文 + `system-map.html`(README/USER-GUIDE 的图片走相对路径,整目录删会让 npm 页图全断)。
|
|
65
|
+
- [ ] 🟡 **`package.json` 对外字段**:缺 `homepage`(README 现在挂的是 htmlpreview 伪首页)、缺 `scripts`(无 `test` 入口,陌生人无法一条命令验证)、缺 `engines`(Node 版本无约束);`description` 331 字符被 npm 截到 255(中文段全丢)。
|
|
66
|
+
- [ ] 🟡 **首页托管**:`docs/landing/index.html`(1,745 行自包含单文件)目前只在 `preview` 分支,靠第三方 `htmlpreview.github.io` 代理渲染(README.md:9)→ 单点依赖。建议改 GitHub Pages(`.github/workflows/` 现在已存在,可加 `pages.yml`)或至少加 `gh-pages` 分支。
|
|
67
|
+
- [ ] ⚪ **.github 门面件**:`issue 模板 / CONTRIBUTING / SECURITY / CODE_OF_CONDUCT` 仍缺(`.github/` 目前只有 CI 与云函数)。已有真实外部贡献(PR #12、Issue #10),却无模板与指南。
|
|
68
|
+
- [ ] ⚪ **`CHANGELOG.md` 不在 tarball 里**,而 README 末尾链接指向它 → npm 视图是死链。二选一:把 CHANGELOG 加进 `files`,或改链接。
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## E. 文档体系
|
|
73
|
+
|
|
74
|
+
- [ ] 🟡 **README 大改**(490 行 / 说明书式内容占 30%):落地 `docs/NEXT-MAJOR-README-DRAFT.zh.md` 前必须先修 —— 它沿用「十个页签」旧错(实为 12)、含违反 `PROMO-STYLE-GUIDE.md:70` 的句式、`memory_search` 工具名不存在(实为 `memory_recall`)、安装位置反而更靠后。目标 ≤120 行五段式。
|
|
75
|
+
- [ ] 🟡 **USER-GUIDE 改任务导向**(现各 382 行、页面导向、61 个配置键):目标 ≤260 行;删 `-pre` 期命名泄漏与不可用命令(`node tools/subagent-gc.mjs` —— `tools/` 根本没打包)。
|
|
76
|
+
- [ ] 🟡 **6 处事实性错误**:①handoff「默认开启」写 4 处(实为 false)②「Ten tabs」实为 12 ③`memory_search` 实为 `memory_recall` ④`README.md:294` 的 `/n/n` ⑤英文 README 的 H1 是中文 ⑥配置路径写成非 `-pre` 名(`dsh-auto-memory.json` vs 实际 `dsh-auto-memory-pre.json`)。
|
|
77
|
+
- [ ] 🟡 **双语对账脚本**(`tools/check-readme-parity.mjs`):锁标题序列 + 命令块 + 关键数字(0GB/130MB/563MB、0.75/0.80、8000/200000)逐字符一致 —— 现在只靠人工纪律,已漏过。
|
|
78
|
+
- [ ] ⚪ **27 张配图重拍**(22 张界面图 + 7 张六幕宣传图,旧 29 张中部分已不再被引用):必须在 UI 定稿后做,顺序为"UI 冻结 → 重拍 → 改文"。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## F. 界面技术债(= 大排期主体,量化见预研 §2.6)
|
|
83
|
+
|
|
84
|
+
- [ ] 🔴 **令牌层**:硬编码颜色 **320 处 / 132 种**;圆角 **25 种**、padding **47 种**、gap 14 种、字号 19 种、阴影 20 种、毛玻璃 **8 套配方**;同一个 `--dam-accent` 有 **5 个不同 fallback**;**3 个幽灵令牌**(`--dsw-alias-text-primary` / `-warn` / `-danger` 上游根本不存在,9 处永久走 fallback)。
|
|
85
|
+
目标:颜色/边框 **100% 走 `--dsw-*`**;自建 `--dam-*` 度量令牌(间距 4/8/12/16/24、圆角 4/8/12、字号 11/12/13/15/18);向导美术隔离到 `--dam-tour-*`。
|
|
86
|
+
- [ ] 🔴 **组件层**:内联 style **228 个** vs `className` **4 处**;**6 套卡片**、4 种"表格"宽度、4 种标题写法、4 套浮层、2 套页签并存。目标:抽 `Card / Row / Field / Badge / Tabs / Modal / Toolbar` 七个原语。
|
|
87
|
+
- [ ] 🟡 **巨型函数**:`SettingsPage` **582 行**(8 分组+目录浏览器+模型抽屉+环境检测+向导挂载+更新检查+调试中心)、`DialogHost` **478 行**(7 种弹窗+首启向导状态机)。
|
|
88
|
+
- [ ] 🟡 **状态与可访问性**:`:focus` / `:focus-visible` **各 0 条**;`aria-*` 仅 9 个、`tabIndex` 0;危险操作 `window.confirm` **0 次**(`StorageTab` 删除是**单击即删**)。
|
|
89
|
+
- [ ] 🟡 **i18n 双机制**:`I18N` 字典 + **约 354 处内联 `locale === 'zh' ? … : …`**;`t()` 的兜底是中文 → 英文 UI 必然中英混杂。
|
|
90
|
+
- [ ] ⚪ **幽灵配置键 `pythonGpu`**:`lib/client.js` 会写、`lib/index.js` **零引用**(宿主从不读)→ 删客户端那一处即可。
|
|
91
|
+
- [ ] 🟡 **IA 重排**:12 个页签塞进 **440×560 浮层**(拖到 300px 时表单控件只剩 ~48px)。DSH 有约 58 个原生插槽可用(`sidebar.panellist` / `sidebar.right.tab.document` / `conversation.session.header.utilities` …),`slots.inject/register` 是通用 API —— **挂原生位不需要改后端**。开工第一步:向 `sidebar.panellist` 注册一个空面板做 spike,失败则回退为"保留浮层但按三组重排"。
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## G. 群反馈 / 日报 CI(2026-09-12~13 新建,ZCode 线)
|
|
96
|
+
|
|
97
|
+
**已有资产**:`.github/cloud/qq-webhook/`(腾讯 SCF 云函数:`index.js` / `index.zip` / `scf_bootstrap`)、`.github/scripts/{group-digest,group-listener,qq-capture-openid,qq-send,report-sync}.mjs`、`.github/workflows/{group-digest,group-report-status}.yml`、`.github/digest/{NOTES,PREVIEW}.md`、说明文档 `docs/internal/GROUP-{LISTENER,DIGEST,WEBHOOK}-SETUP.md`;插件侧新增按需报告端点 `?report=N`(`9461cea`)。
|
|
98
|
+
|
|
99
|
+
- [ ] 🟡 **安全面收口(待确认)**:webhook 公网入口是否校验签名/时间戳与来源白名单?是否限流防重放?密钥是否只存 GitHub Secrets / SCF 环境变量(**绝不入库**)?
|
|
100
|
+
- [ ] 🟡 **运维面(待确认)**:定时班时区与失败告警(现在 11:40/20:40 + 55 分兜底重试);SCF 免费额度/成本;`index.zip` 这类二进制是否应入库(建议改为构建产物,不入 git)。
|
|
101
|
+
- [ ] ⚪ **文档化**:把三份 `GROUP-*-SETUP.md` 合并成一份"从零部署"清单(含所需 secrets 名、验证命令、回滚方式)。
|
|
102
|
+
- [ ] ⚪ **与插件的关系**:`.github/**` 不在 npm `files` 里(不会随包发布)—— 保持这样;若日报内容要面向用户,走 `docs/` 或 landing,不要塞进包。
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## H. 插件功能侧遗留
|
|
107
|
+
|
|
108
|
+
- [ ] ⚪ **子代理通知无法跨会话继承**:durable 侧 `SessionHeader.parentSession` 是 readonly,`coldResume → authorizeLineage` 抛 UNAUTHORIZED,服务契约无 re-parent/transfer/attach → 新会话**收不到旧会话的子代理完成通知**。可选路线见 `docs/internal/SUBAGENT-REPORT-ROUTING-PRE-RESEARCH.md`。
|
|
109
|
+
- [ ] ⚪ **上下文桥不推子代理会话的 context**:被观测的子代理会话 runtime 从未 `capturePaths`(整段 drop);本机已做 30s 窗口限流 + 首条附 runtime key,**工作区归属改造未做**。
|
|
110
|
+
- [ ] ⚪ **两个"界面零引用"端点不可删**:`/activation-inbox-pre`(契约测试 + 注入入口)、`/subagent-gc`(CLI `tools/subagent-gc.mjs`)—— 已有白名单锁守着,新增宿主独有端点必须更新白名单并注明消费者。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## I. 长期纪律(违反会直接出事故)
|
|
115
|
+
|
|
116
|
+
- **发布必须获用户明确授权**;未经要求不 `npm publish` / push / 打 tag。
|
|
117
|
+
- **不重启 dsh web 宿主**(会截断用户会话);host 改完只改文件 + 提示用户重启。
|
|
118
|
+
- **写任何文件禁 BOM**(BOM 会让 dsh web 起不来,历史事故)。
|
|
119
|
+
- **`release.mjs` 的源就是工作区**:脏树整体进包且不可回溯 → 发版前核实 `git status` 范围 + 全量回归(`tests/smoke` 69 套件)。
|
|
120
|
+
- **凭据只走行内 URL / 环境变量**,绝不写进任何会上传 GitHub 或 npm 的文件(凭据位置见 `RELEASE-PROCESS.md`)。
|
|
121
|
+
- **发版 10 步唯一检查表**:`docs/internal/RELEASE-PROCESS.md`;其中「改 CHANGELOG + 同步软件内版本标识」由 `tools/release.mjs` §5.05 闸门强制。
|
|
122
|
+
- **npm 相关两个坑**:本机 `.npmrc` 是 npmmirror(查询/发布必须显式 `--registry=https://registry.npmjs.org`);`npm view` 命中本地缓存(判定用 registry `/latest` 或 `--prefer-update`/`--prefer-online`)。
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## J. ✅ 已关闭(存档,别重复劳动)
|
|
127
|
+
|
|
128
|
+
- ✅ **55 份未入 git 的 docs 已收编**(`f83e144`)—— 此前"npm tarball 是唯一副本"的数据丢失风险解除。
|
|
129
|
+
- ✅ **`.github/` 从不存在到就位**(2026-09-12~13 建起 CI + 云函数)。
|
|
130
|
+
- ✅ **v2.4.1 / v2.4.2 已发布**,三处一致(npm latest = 2.4.2 / main = tag = `243dee1`)。
|
|
131
|
+
- ✅ **`autoContinueEnabled` 出厂默认改 `false` + 反向锁**(v2.4.2;用户本机也已关)。
|
|
132
|
+
- ✅ **界面路径表一致性锁**(`tests/smoke/smoke-test-api-paths-pre.mjs`:客户端 ⊆ 宿主、表外零裸字面量)。
|
|
133
|
+
- ✅ **发版版本标识闸门**(`release.mjs` §5.05:缺 CHANGELOG 小节 / 应用内更新说明条目 / 界面指纹行即拒绝构建)。
|
|
134
|
+
- ✅ **第 0 步止血**:写配置 4 条路径 → `saveConfigPatch()`;10 处重复的语义刷新 → `refreshSem()`。
|
|
135
|
+
- ✅ **`smoke-test-m53-pre.mjs` 顺序敏感 flake**(固定 `sleep(900)` → 有界轮询)。
|
|
136
|
+
- ✅ **真实自动接续实机验证通过**(修复后首次 `auto-continue host-executed`)。
|
|
137
|
+
- ✅ **B 区三项深改落地 v2.5.0**(2026-09-13,未发版):①水位口径——分母改官方声明窗口(`min(win, hardWin)`),reserve 退出分母只留"距硬墙余量"展示与预测性硬墙判据(`estTokens + reserve > 判定窗`),反向锁断言"不得把 reserve 计入分母";②接续序号——`~/.dsh/memory/cont-seq.json` 持久计数器(全局单调、跨工作区不重号、写包失败回滚不跳号、历史标题扫描兜底),新增 `smoke-test-contseq-pre.mjs`(20 断言);③流程外包——四份自包含任务书(`docs/prompts/{RELEASE,REGRESSION,DOCS-AUDIT,TRACE-PATROL}-AGENT.md`)+ `RELEASE-PROCESS.md` 角色分工(主对话派活后只读等待)。全量回归 70 套件 0 失败。
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# DOCS-AUDIT-AGENT · 双语文档对账子代理任务书
|
|
2
|
+
|
|
3
|
+
> **给使用者的说明**:一整段投喂(自包含)。用途:README / 用户说明书双语发版前对账、大改文档后的漂移盘点。**默认只读**;修复要等主对话拿着对账单裁决后另行派发。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是 dsh-auto-memory 的文档对账子代理。工作目录:`D:\dsh-auto-memory`。对象四份:`README.md` ↔ `README.zh-CN.md`、`docs/USER-GUIDE.en.md` ↔ `docs/USER-GUIDE.zh-CN.md`。**默认禁止改任何文件**,产出对账单即完成。
|
|
8
|
+
|
|
9
|
+
## 对账维度(逐条执行,逐条给证据)
|
|
10
|
+
|
|
11
|
+
1. **结构对齐**:两两对比章节标题序列(`^#{1,3} `),列出单侧多出/缺失/改名的小节。
|
|
12
|
+
2. **事实一致性**:以下数字与键名在四份文档里必须同值,逐一摘录出处(`文件:行号`):
|
|
13
|
+
- 配置键名与默认值(以 `lib/index.js` 配置表为准,如 `waterLevelThreshold: 0.75`、`autoContinueEnabled: false`)
|
|
14
|
+
- 端点/路由数(46)、工具数(14)、冒烟套件数(`ls tests/smoke/*.mjs | wc -l`)
|
|
15
|
+
- 版本号引用(`package.json` 为准)
|
|
16
|
+
- 触发口径类描述(水位分母=官方声明窗口、阈值 0.75、官方 80% 压缩——2026-09-13 起的口径)
|
|
17
|
+
3. **链接与资源**:相对路径引用(如 `docs/screenshots/…`)逐一验证目标文件存在;外链只查格式不访问。
|
|
18
|
+
4. **文风黑名单**(`docs/PROMO-STYLE-GUIDE.md`):厂商腔("赋能""引领""革命性")、夸大承诺、逐字模板腔;产品拟人称"她"是否被误改成"它/该产品"。
|
|
19
|
+
5. **代码块可运行性抽查**:安装/命令类代码块里的包名 `@a9i5k4/dsh-auto-memory`、registry 提示(npmmirror 坑)、pnpm `minimumReleaseAge` 拦 24h 新版的提示是否存在且未过期。
|
|
20
|
+
|
|
21
|
+
## 回报格式(原样 JSON + 附表)
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{ "ok": true, "files": ["README.md", "README.zh-CN.md", "docs/USER-GUIDE.en.md", "docs/USER-GUIDE.zh-CN.md"], "structure_diffs": [], "fact_mismatches": [], "broken_links": [], "style_violations": [], "summary": "一句话总评" }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
每条 finding 格式:`"文件:行号 — 问题 — 建议改法(不执行)"`。零问题也要回报空数组,不许省略维度。
|
|
28
|
+
|
|
29
|
+
## 边界
|
|
30
|
+
|
|
31
|
+
- 文档里"待大改"的方向性内容(界面×文档×首页排期)不属于对账范围,不要评论设计。
|
|
32
|
+
- 发现疑似代码与文档都不一致时,以**代码为准**记录,不改代码。
|
package/docs/prompts/README.md
CHANGED
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
| `P10-importance-calibration.md` | importance 效应定标:`0.5+w×imp` 可配置 + 真实查询翻转率实测 | **P9 结论后** | `lib/index.js` dense 臂一行 + 配置项 | 低 |
|
|
64
64
|
| `P11-silent-catch-observability.md` | fail-soft 空 catch 统一加 diag(**只加日志不改行为**,可与 P9 并行) | 无 | `lib/index.js` catch 行 | 低 |
|
|
65
65
|
| **`FIX-AGENT-P11.md`** | **⭐ 复制即投喂:静默 catch 可观测(自包含,可与 P9 并行给另一 Agent)** | 无 | `lib/index.js` catch 行 | 低 |
|
|
66
|
+
| **`RELEASE-AGENT.md`** | **⭐ 复制即投喂:发版执行子代理(自包含;主对话=决策者只做开闸/放行/处置,见 `docs/internal/RELEASE-PROCESS.md` 顶部角色分工)** | 前置门已过 | `CHANGELOG.md` + `lib/client.js` 版本标识三处 | 高(单向门) |
|
|
67
|
+
| **`REGRESSION-AGENT.md`** | **⭐ 复制即投喂:全量回归执行(只读;失败套件单跑复验规则)** | 无 | 无(只跑不改) | 无 |
|
|
68
|
+
| **`DOCS-AUDIT-AGENT.md`** | **⭐ 复制即投喂:README/USER-GUIDE 双语对账(只读,产出对账单)** | 无 | 无(只读) | 无 |
|
|
69
|
+
| **`TRACE-PATROL-AGENT.md`** | **⭐ 复制即投喂:子代理痕迹巡检(只读盘点 + GC 备份区 + 残留风险)** | 无 | 无(只读) | 无 |
|
|
66
70
|
| `EXEC-ORDER.md` | 执行顺序与冲突提示 | — | — | — |
|
|
67
71
|
|
|
68
72
|
---
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# REGRESSION-AGENT · 全量回归执行子代理任务书
|
|
2
|
+
|
|
3
|
+
> **给使用者的说明**:一整段投喂(自包含)。用途:发版前置门、大改后的体检、接手陌生工作区时的基线盘点。**只读任务:不得改任何文件。**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是 dsh-auto-memory 的回归执行子代理。工作目录:`D:\dsh-auto-memory`(Node.js 项目,零运行时依赖)。任务:**跑完全量冒烟回归并回报结构化结果**。禁止改任何文件、禁止 `git add/commit`、禁止重启 dsh web。
|
|
8
|
+
|
|
9
|
+
## 步骤
|
|
10
|
+
|
|
11
|
+
1. 记录基线:`git log --oneline -3` 与 `git status --short`(只记录,不处理)。
|
|
12
|
+
2. 逐个运行全部套件(PowerShell):
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
cd D:\dsh-auto-memory
|
|
16
|
+
$results = @()
|
|
17
|
+
Get-ChildItem tests\smoke -File -Filter *.mjs | ForEach-Object {
|
|
18
|
+
$out = node $_.FullName 2>&1
|
|
19
|
+
$code = $LASTEXITCODE
|
|
20
|
+
$results += [pscustomobject]@{ name = $_.Name; code = $code; tail = ($out | Select-Object -Last 3) -join ' | ' }
|
|
21
|
+
}
|
|
22
|
+
$results | Where-Object code -ne 0
|
|
23
|
+
$results | Measure-Object | Select-Object -ExpandProperty Count
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. **失败套件单跑复验一次**:批量连跑受机器负载影响(历史案例:`smoke-test-m53-*` 连跑误报、单跑全绿)。复验仍失败才算真失败,回报里注明"连跑 FAIL / 单跑 FAIL"两行。
|
|
27
|
+
4. 附带静态检查:`node --check lib\index.js; node --check lib\client.js`;对改动过的文件抽查前三字节非 `EF BB BF`(BOM 会让 dsh web 起不来)。
|
|
28
|
+
|
|
29
|
+
## 回报格式(原样 JSON)
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{ "ok": true, "total": 70, "failed": [], "flaky": [], "node_check": "ok", "bom": "ok", "head": "<git log -3 首行>", "dirty": ["<git status --short 逐条>"], "durationMs": 0, "error_tail": "" }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `failed`:`["<套件名>: <失败断言行摘要≤120字符>"]`;`flaky`:连跑挂但单跑绿的套件名。
|
|
36
|
+
- `dirty` 原样列出——**脏树范围核实是主对话的决策项**,子代理只如实上报。
|
|
37
|
+
- 有任何套件既非 0 也非 1 的退出码、或套件文件无法运行 → `ok:false` + `error_tail`(≤20 行)。
|
|
38
|
+
|
|
39
|
+
## 边界
|
|
40
|
+
|
|
41
|
+
- 不诊断根因、不修东西——那是主对话/修复子代理的事。
|
|
42
|
+
- 测试会创建临时目录(`os.tmpdir()` 下的 `dam-*`),跑完不清理不算失败;但**不得**去清理 `D:\dsh-auto-memory` 仓库内任何文件。
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# RELEASE-AGENT · 发版执行子代理任务书
|
|
2
|
+
|
|
3
|
+
> **给使用者的说明**:**本任务书由主对话读取、填入版本号后原文派发给执行子代理**;用户不必、也不要绕过主对话直接投喂(两条入口并存才会产生并发冲突)。自包含,无需 `_COMMON.md`。
|
|
4
|
+
> 出错即停:任何一步失败或前置不满足,**立即停下回报**,绝不允许跳步、重试超过 1 次、或"先做完再说"。
|
|
5
|
+
> ⚠️ 子代理**不得重启 dsh web**;发布后宿主半边的生效由用户自己重启。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
你是 dsh-auto-memory 的发版执行子代理。工作目录:`D:\dsh-auto-memory`(pre 线;发布基座在 `D:\dsh_debug\_publish_dsh-auto-memory`,下称 REL)。**本次要发布的版本号:`<ver>`(由主对话填入,形如 2.5.0)。**
|
|
10
|
+
|
|
11
|
+
## 硬性禁令(违反任何一条=任务失败)
|
|
12
|
+
|
|
13
|
+
1. **版本标识闸门未过 → 不得真构建;dry-run 未过 → 不得真构建;未真构建 → 不得提交 REL / push / publish。** 单向门按顺序开。
|
|
14
|
+
2. **只允许改动**:`CHANGELOG.md`、`lib/client.js`(仅版本标识三处:CHANGELOG 字典、指纹行)、`package.json`(由 release.mjs 回写)。**其余任何文件出现改动立即停止回报。**
|
|
15
|
+
3. **凭据纪律**:PAT 与 npm token 只从 `~/.dsh/memory/workspaces/--D--dsh_debug--/MEMORY.md` 里读,只拼进行内 URL/命令行;**严禁写进任何文件、日志、回报**(回报里凭据一律以 `<redacted>` 代替)。该记忆文件明确「只存本地,严禁写入任何会上传 GitHub/npm 的文件」。
|
|
16
|
+
4. **不得 push 除非 PAT 读取成功**;**不得 `npm publish` 除非 push 成功且 tag 已打**。
|
|
17
|
+
5. 全程**不得重启 dsh web**、不得动 `~/.dsh` 下运行中宿主的任何文件、不得并发跑第二个写盘任务。
|
|
18
|
+
|
|
19
|
+
## 阶段 0 · 前置门(任一不满足 → 停,回报 `failed_step: 0`)
|
|
20
|
+
|
|
21
|
+
```powershell
|
|
22
|
+
cd D:\dsh-auto-memory
|
|
23
|
+
Get-ChildItem tests\smoke -File -Filter *.mjs | ForEach-Object { node $_.FullName > $null 2>&1; if ($LASTEXITCODE -ne 0) { "FAIL: " + $_.Name } }
|
|
24
|
+
node --check lib\index.js; node --check lib\client.js
|
|
25
|
+
git status --short
|
|
26
|
+
git log --oneline -3
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- 全量回归 **0 失败**(批量连跑受负载影响的个别套件允许**单跑复验一次**,以单跑为准)。
|
|
30
|
+
- **脏树范围核实**:`release.mjs` 只复制 `lib/ tests/ python/ docs/ .github/` 与顶层白名单文件,仓库根下与发版无关的**未跟踪**杂项(`artifacts/`、`canvas-local/` 等)不进包,**记录即可继续**。必须停下回报的是:①任何**已跟踪**文件出现主对话告知的预期清单之外的改动 ②`lib/ tests/ docs/ .github/` 内出现预期外的新文件 ③删除型改动。
|
|
31
|
+
- **凭据读取**在阶段 6/7 才需要;读取失败就到那时再停,阶段 0-5 不需要碰凭据。
|
|
32
|
+
|
|
33
|
+
## 阶段 1 · CHANGELOG(第 1 笔)
|
|
34
|
+
|
|
35
|
+
- `CHANGELOG.md` 顶部新增 `## [<ver>] — <日期> · <一句话主题>` 小节。内容要点由主对话提供;主对话没给 → 停,回报缺输入。
|
|
36
|
+
- 闸门会校验文件里出现 `## [<ver>]`,缺失即拒绝构建。
|
|
37
|
+
|
|
38
|
+
## 阶段 2 · 软件内版本标识(第 2 笔,`lib/client.js`)
|
|
39
|
+
|
|
40
|
+
- `var CHANGELOG = { ... }` 顶部新增 `'<ver>': { zh: [...], en: [...] }`(应用内"更新说明"弹窗,文案取自 CHANGELOG 小节)。
|
|
41
|
+
- 第 10 行指纹行改为 `console.log('[dsh-auto-memory] client v<ver> fingerprint: ...')`(fingerprint 短语沿用当版,可顺手换新词)。
|
|
42
|
+
- `package.json.version` 不手改(release.mjs 构建时回写)。
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
node --check lib\client.js
|
|
46
|
+
git add lib tests docs CHANGELOG.md tools\release.mjs
|
|
47
|
+
git -c user.name="Aik358" -c user.email="aik358@users.noreply.github.com" commit -m "v<ver>: <一句话>"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
(`git status --short` 核对已暂存清单与主对话给的预期一致后才 commit;仓库根的无关未跟踪杂项**不要** `git add -A` 进来。)
|
|
51
|
+
|
|
52
|
+
## 阶段 3 · dry-run 验闸门
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
node tools\release.mjs <ver> --dry-run
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
必须看到:`版本标识一致性: OK(CHANGELOG / 应用内更新说明 / 界面指纹行)` + `语法 ✓ BOM ✓ 无 pre/dev 残留 ✓` + `python/ 运行时完整 ✓ bench 已排除 ✓`。否则**停**,回报 `failed_step: 5.05` + 闸门原文(error_tail)。
|
|
59
|
+
|
|
60
|
+
## 阶段 4 · 真构建
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
node tools\release.mjs <ver>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
复核输出含 `开发树版本回写: … → <ver>`。构建会写 REL 树。
|
|
67
|
+
|
|
68
|
+
## 阶段 5 · REL 提交 + tag
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
cd D:\dsh_debug\_publish_dsh-auto-memory
|
|
72
|
+
git add -A -- . ':(exclude).github/cloud/qq-webhook/index.zip'
|
|
73
|
+
git -c user.name="Aik358" -c user.email="aik358@users.noreply.github.com" commit -m "v<ver>: <一句话>"
|
|
74
|
+
git tag -f v<ver>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
(index.zip 是 SCF 部署产物,不入库——这是群日报会话定下的先例;REL 树里若再出现其他构建产物同样排除。)
|
|
78
|
+
|
|
79
|
+
## 阶段 6 · push(带 PAT 行内 URL)
|
|
80
|
+
|
|
81
|
+
从凭据记忆文件读 PAT 后(读取失败 → 停,`failed_step: 8`):
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
git -c credential.helper= push https://x-access-token:<PAT>@github.com/Aik358/dsh-auto-memory.git main --tags --force
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 阶段 7 · npm publish(单向门,最后一步)
|
|
88
|
+
|
|
89
|
+
npm token 同样从凭据记忆文件读(需 bypass-2FA 的那个):
|
|
90
|
+
|
|
91
|
+
```powershell
|
|
92
|
+
npm publish . --registry=https://registry.npmjs.org/ --//registry.npmjs.org/:_authToken=<token> --access public
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**坑**:本机 `~/.npmrc` 指向 npmmirror;`npm view` 会命中本地缓存回读旧版本。判定一律用权威接口:
|
|
96
|
+
|
|
97
|
+
```powershell
|
|
98
|
+
Invoke-RestMethod 'https://registry.npmjs.org/@a9i5k4%2Fdsh-auto-memory/latest' | Select-Object -ExpandProperty version
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 阶段 8 · 三处复核(缺一不算发完)
|
|
102
|
+
|
|
103
|
+
1. registry `/latest` 的 version = `<ver>`(用上面的权威接口)
|
|
104
|
+
2. `git ls-remote https://github.com/Aik358/dsh-auto-memory.git refs/heads/main`
|
|
105
|
+
3. `git ls-remote https://github.com/Aik358/dsh-auto-memory.git refs/tags/v<ver>`
|
|
106
|
+
|
|
107
|
+
三者一致才算 ok。
|
|
108
|
+
|
|
109
|
+
## 回报格式(原样 JSON,不追加散文)
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{ "ok": true, "version": "<ver>", "pre_sha": "<pre 线 commit 短 sha>", "rel_sha": "<REL commit 短 sha>", "tag": "v<ver>", "npm_latest": "<ver>", "failed_step": "", "error_tail": "" }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
失败时:`ok:false`,`failed_step` 填阶段号(0/1/2/3/4/5/6/7/8;dry-run 闸门不过记 3),`error_tail` 贴**最后 ≤20 行**原始报错(凭据替换为 `<redacted>`)。已知会出现且**不算失败**的情况:无。任何意外都按失败回报,由主对话决定处置。
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
*回滚提示(供主对话,子代理不执行):未 push/publish 前一切可回滚(pre 线 `git reset`、REL 线 `git reset` + `git tag -d`);publish 后不可撤,只能发 patch。*
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# TRACE-PATROL-AGENT · 子代理痕迹巡检任务书
|
|
2
|
+
|
|
3
|
+
> **给使用者的说明**:一整段投喂(自包含)。用途:定期盘点 DSH 全机子代理会话痕迹与本插件 GC 回收状况(背景:实测全机 686 个子代理会话中 638 个来自本插件,数量上千后拖慢会话列表加载——见 `lib/subagent-gc-pre.js` 头注)。**只读巡检**;回收动作由插件自身在运行时做,巡检子代理不得手动移动/删除任何会话目录。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是 dsh-auto-memory 的痕迹巡检子代理。宿主数据根:`~/.dsh`(Windows = `C:\Users\<user>\.dsh`)。**禁止写/删/移动任何文件,禁止重启 dsh web。**
|
|
8
|
+
|
|
9
|
+
## 巡检项(逐项产出数字与证据)
|
|
10
|
+
|
|
11
|
+
1. **会话总量盘点**:`~/.dsh/sessions/<工作区>/<会话目录>/` 逐层统计:
|
|
12
|
+
- 会话目录总数、`session-*` 前缀(普通会话)数、裸 uuid(子代理)数。
|
|
13
|
+
- 子代理判别:读会话目录下 `session.jsonl[.zstd]` 首行 header,`origin==='subagent'`;`descriptor.label` 以 `auto-memory-` 开头(`PLUGIN_LABEL_PREFIX`)即本插件产物。zstd 文件可跳过首行解压、只统计目录名(裸 uuid + 无 `session-` 前缀),并在回报里注明"未解压判定"的比例。
|
|
14
|
+
2. **GC 备份区**:`~/.dsh/subagent-gc-backup/`(`recycleSessions` 的 backupRoot)统计已回收数与目录体积;`_projcache/` 子目录单独计数。
|
|
15
|
+
3. **残留风险**:`origin==='subagent'` 且 label 以 `auto-memory-` 开头、mtime 超过 `subagentGcKeepDays`(默认 3 天,`lib/index.js` 配置表)却仍在 sessions 目录的——列前 20 条(工作区/会话名/mtime)。这是"任务结束即回收"路径的漏网指标。
|
|
16
|
+
4. **投影缓存**:`~/.dsh/` 下投影缓存目录(`*_projcache*` 类名样,实际以 `~/.dsh` 顶层目录列表为准)的文件数与体积;超过 2,000 文件标记 `warn`。
|
|
17
|
+
5. **配置核对**:读 `~/.dsh/dsh-auto-memory-pre.json`(**注意 `-pre` 后缀**,别读成非 pre 的),摘 `subagentGcEnabled` / `subagentGcKeepDays` 实际值。
|
|
18
|
+
|
|
19
|
+
## 回报格式(原样 JSON)
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "ok": true, "sessions_total": 0, "sessions_normal": 0, "subagent_total": 0, "subagent_plugin": 0, "backup": { "count": 0, "projcache": 0 }, "stale_over_keepdays": 0, "stale_sample": [], "projcache_files": 0, "warn": [], "config": { "subagentGcEnabled": true, "subagentGcKeepDays": 3 }, "summary": "一句话总评" }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 边界
|
|
26
|
+
|
|
27
|
+
- 大目录统计用流式计数,不要把会话文件内容整读进内存。
|
|
28
|
+
- 若 `~/.dsh/sessions` 结构与上述不符(宿主版本变更),停下回报实际结构,禁止猜。
|
|
29
|
+
- 发现宿主正在写某会话文件(mtime < 5 分钟)→ 跳过该条并在 `warn` 注明,避免与运行中会话竞争。
|
package/lib/client.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
|
|
8
8
|
* Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
|
|
9
9
|
*/
|
|
10
|
-
console.log('[dsh-auto-memory] client v2.
|
|
10
|
+
console.log('[dsh-auto-memory] client v2.5.0 fingerprint: official-window-water')
|
|
11
11
|
window.__ModuleLoader__.load({
|
|
12
12
|
id: '@a9i5k4/dsh-auto-memory',
|
|
13
13
|
factory: (require) => {
|
|
@@ -275,7 +275,7 @@ window.__ModuleLoader__.load({
|
|
|
275
275
|
fWaterWindow: '水位估计窗口(token)', fWaterWindowHint: '会话消息按官方公式(4 字符≈1 token)估算达到该值即视为上下文将满;0=关闭。默认 65536(128K 窗口的保守半量),按所用模型调整。',
|
|
276
276
|
fWaterAdvisory: '水位交接建议', fWaterAdvisoryHint: '水位越阈时在动态快照注入交接建议(写账本/刷新白板/建议开新窗);无人值守时静默。',
|
|
277
277
|
fWaterAuto: '水位自动骨架账本', fWaterAutoHint: '越阈时自动写一篇系统骨架账本(每会话一次),防止模型忽视建议时交接材料缺失。',
|
|
278
|
-
fWaterThreshold: '水位建议阈值', fWaterThresholdHint: '
|
|
278
|
+
fWaterThreshold: '水位建议阈值', fWaterThresholdHint: '水位比例 = 上下文实占 ÷ 官方声明窗口(不再扣预留输出);超过该值(0.1-1.5)即触发交接建议/骨架账本;默认 0.75——官方自动压缩阈值是 80%,必须留出余量才来得及走完交接。',
|
|
279
279
|
fHandoffPlan: '白板注入预算(字符)', fHandoffPlanHint: 'PLAN.md 全貌注入动态快照的硬截断预算;全文可经 memory_read 或面板白板页查看。默认 1200。',
|
|
280
280
|
fHandoffLedger: '账本注入预算(字符)', fHandoffLedgerHint: '最新一篇交接账本注入动态快照的硬截断预算。默认 800。',
|
|
281
281
|
fConsSchedule: '定时做梦式固化', fConsScheduleHint: '每天到点自动读最近日志发散提炼长期要点,写入项目笔记/用户级记忆(等价 memory_consolidate)。',
|
|
@@ -283,8 +283,8 @@ window.__ModuleLoader__.load({
|
|
|
283
283
|
fConsScheduleDays: '固化回看天数', fConsScheduleDaysHint: '定时固化读取最近 N 天日志;默认 7。',
|
|
284
284
|
fMaintSchedule: '定时 30 天蒸馏', fMaintScheduleHint: '每天到点自动把超过 30 天的旧日志蒸馏归档(等价 memory_maintain);无旧日志时零成本跳过。',
|
|
285
285
|
fMaintScheduleTime: '蒸馏触发时间', fMaintScheduleTimeHint: 'HH:MM,默认 10:00(与固化时间错开)。',
|
|
286
|
-
waterCardTitle: '上下文水位', waterAutoSrc: '自动检测', waterManualSrc: '手动设定', waterOfficialSrc: '官方路由容量', waterFallbackSrc: '回退默认值', waterNotMeasured: '本会话尚未测量(发送一轮后自动更新)', waterMeterOfficial: '官方计量', waterMeterHeuristic: '启发式降级', waterThresholdHint: '水位达到 {t} 时注入交接建议并自动补写账本;计量优先用官方 token-meter(与聊天框 context ring 同源),不可用时降级启发式估算;窗口优先取官方路由容量(request/context 的 contextWindow),其次按当前模型查 settings.yaml contextWindow
|
|
287
|
-
autoContTitle: '自动接续', autoContHint: '开启后:水位达到阈值、且旧会话已空闲(harness 权威 running 位在轮次边界转为 false,取代旧「两轮水位持平」)时,弹出确认卡——同意=立即接续,拒绝=本轮跳过(同一边界不再提示),30-40 秒无操作视为挂机自动接续;接续前先让旧 Agent 刷新白板 PLAN 与交接账本,再建新会话(沿用工作区/模型/思考档位)并注入分层交接材料;触发后默认 30 分钟内不重复。', autoContOn: '自动接续:开', autoContOff: '自动接续:关', autoContThreshold: '阈值', autoContCountdown: '⏳ {s} 秒后自动接续…', autoContCancel: '取消', autoContConfirm: '
|
|
286
|
+
waterCardTitle: '上下文水位', waterAutoSrc: '自动检测', waterManualSrc: '手动设定', waterOfficialSrc: '官方路由容量', waterFallbackSrc: '回退默认值', waterNotMeasured: '本会话尚未测量(发送一轮后自动更新)', waterMeterOfficial: '官方计量', waterMeterHeuristic: '启发式降级', waterThresholdHint: '水位达到 {t} 时注入交接建议并自动补写账本;计量优先用官方 token-meter(与聊天框 context ring 同源),不可用时降级启发式估算;窗口优先取官方路由容量(request/context 的 contextWindow),其次按当前模型查 settings.yaml contextWindow,也可在下方手动覆盖;触发分母=官方声明窗口(provider 自报过硬限时取较小值),预留输出不参与扣减。',
|
|
287
|
+
autoContTitle: '自动接续', autoContHint: '开启后:水位达到阈值、且旧会话已空闲(harness 权威 running 位在轮次边界转为 false,取代旧「两轮水位持平」)时,弹出确认卡——同意=立即接续,拒绝=本轮跳过(同一边界不再提示),30-40 秒无操作视为挂机自动接续;接续前先让旧 Agent 刷新白板 PLAN 与交接账本,再建新会话(沿用工作区/模型/思考档位)并注入分层交接材料;触发后默认 30 分钟内不重复。', autoContOn: '自动接续:开', autoContOff: '自动接续:关', autoContThreshold: '阈值', autoContCountdown: '⏳ {s} 秒后自动接续…', autoContCancel: '取消', autoContConfirm: '本会话上下文已用 {p}%(按官方声明窗口计,官方约 80% 才压缩),旧会话已空闲(轮次边界)——现在接续到新会话?', autoContAgree: '同意接续', autoContReject: '拒绝', autoContTimeout: '{s} 秒后自动接续(视为挂机)', autoContRejected: '已跳过本次接续(下一个轮次边界再提醒)',
|
|
288
288
|
continueCardTitle: '一键接续', continueCardHint: '写好交接账本→创建新会话→把交接材料作为首条消息预载,并自动切换到新会话继续任务(接续材料含白板节选+最新账本;当前工作区无账本时自动取全局最近一篇)。', continueBtn: '一键接续到新会话', continueBusy: '处理中…',
|
|
289
289
|
fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
|
|
290
290
|
fLocale: '界面语言', fLocaleHint: '默认跟随 DSH 系统语言;也可手动指定中文 / English。', followSystem: '跟随系统语言',
|
|
@@ -426,7 +426,7 @@ window.__ModuleLoader__.load({
|
|
|
426
426
|
fWaterWindow: 'Water-level window (tokens)', fWaterWindowHint: 'Session messages priced with the official meter (4 chars ≈ 1 token); past this, context is considered nearly full; 0 disables. Default 65536 — a conservative half of a 128K window.',
|
|
427
427
|
fWaterAdvisory: 'Water-level handoff advisory', fWaterAdvisoryHint: 'Injects a handoff advisory past the threshold (write ledger / refresh plan / suggest a new window); silent in unattended mode.',
|
|
428
428
|
fWaterAuto: 'Auto skeleton ledger', fWaterAutoHint: 'Past the threshold, auto-writes one system skeleton ledger per session as a safety net if the model ignores the advisory.',
|
|
429
|
-
fWaterThreshold: 'Water-level threshold', fWaterThresholdHint: '
|
|
429
|
+
fWaterThreshold: 'Water-level threshold', fWaterThresholdHint: 'The ratio counts session tokens against the official declared window (the reserved output budget is no longer deducted); past this value (0.1-1.5) the handoff advisory / skeleton ledger fires; default 0.75 — the official auto-compaction threshold is 80%, so a 5% margin is needed to finish the handoff first.',
|
|
430
430
|
fHandoffPlan: 'Plan injection budget (chars)', fHandoffPlanHint: 'Hard truncation budget for injecting the PLAN.md snapshot into the dynamic memory; full text via memory_read or the Whiteboard tab. Default 1200.',
|
|
431
431
|
fHandoffLedger: 'Ledger injection budget (chars)', fHandoffLedgerHint: 'Hard truncation budget for injecting the latest handoff ledger. Default 800.',
|
|
432
432
|
fConsSchedule: 'Scheduled deep consolidation', fConsScheduleHint: 'Once a day at the set time, read recent logs and distill long-term value into project notes / user memory (same flow as memory_consolidate).',
|
|
@@ -434,8 +434,8 @@ window.__ModuleLoader__.load({
|
|
|
434
434
|
fConsScheduleDays: 'Consolidation lookback (days)', fConsScheduleDaysHint: 'How many recent days of logs the scheduled consolidation reads; default 7.',
|
|
435
435
|
fMaintSchedule: 'Scheduled 30-day distill', fMaintScheduleHint: 'Once a day at the set time, distill & archive logs older than 30 days (same flow as memory_maintain); zero cost when nothing to archive.',
|
|
436
436
|
fMaintScheduleTime: 'Distill time', fMaintScheduleTimeHint: 'HH:MM, default 10:00 (staggered from the consolidation time).',
|
|
437
|
-
waterCardTitle: 'Context water level', waterAutoSrc: 'auto-detected', waterManualSrc: 'manual', waterOfficialSrc: 'route capacity', waterFallbackSrc: 'fallback default', waterNotMeasured: 'Not measured for this session yet (updates after the next turn)', waterMeterOfficial: 'official meter', waterMeterHeuristic: 'heuristic fallback', waterThresholdHint: 'Past {t} of the window, the handoff advisory is injected and the skeleton ledger is backfilled; metering prefers the official token-meter (same source as the chat context ring), falling back to the heuristic when unavailable; the window prefers the official route capacity (request/context contextWindow), then the active model in settings.yaml, or your manual override below.',
|
|
438
|
-
autoContTitle: 'Auto continue', autoContHint: 'When on: once the water level passes the threshold AND the old session is idle (the authoritative harness running bit flipped to false at a turn boundary, replacing the old "level unchanged across two polls"), a confirm card appears — Agree = continue now, Decline = skip this boundary (no re-prompt for it), no answer for 30-40s = unattended, continue automatically. Before continuing, the old agent refreshes PLAN + ledger; the new session keeps workspace/model/reasoning effort and receives layered handoff material; suppressed for 30 minutes after each run.', autoContOn: 'Auto continue: on', autoContOff: 'Auto continue: off', autoContThreshold: 'Threshold', autoContCountdown: '⏳ Auto-continue in {s}s…', autoContCancel: 'Cancel', autoContConfirm: '
|
|
437
|
+
waterCardTitle: 'Context water level', waterAutoSrc: 'auto-detected', waterManualSrc: 'manual', waterOfficialSrc: 'route capacity', waterFallbackSrc: 'fallback default', waterNotMeasured: 'Not measured for this session yet (updates after the next turn)', waterMeterOfficial: 'official meter', waterMeterHeuristic: 'heuristic fallback', waterThresholdHint: 'Past {t} of the window, the handoff advisory is injected and the skeleton ledger is backfilled; metering prefers the official token-meter (same source as the chat context ring), falling back to the heuristic when unavailable; the window prefers the official route capacity (request/context contextWindow), then the active model in settings.yaml, or your manual override below; the trigger denominator is the official declared window (min with any provider-reported hard limit) — the reserved output budget is not deducted.',
|
|
438
|
+
autoContTitle: 'Auto continue', autoContHint: 'When on: once the water level passes the threshold AND the old session is idle (the authoritative harness running bit flipped to false at a turn boundary, replacing the old "level unchanged across two polls"), a confirm card appears — Agree = continue now, Decline = skip this boundary (no re-prompt for it), no answer for 30-40s = unattended, continue automatically. Before continuing, the old agent refreshes PLAN + ledger; the new session keeps workspace/model/reasoning effort and receives layered handoff material; suppressed for 30 minutes after each run.', autoContOn: 'Auto continue: on', autoContOff: 'Auto continue: off', autoContThreshold: 'Threshold', autoContCountdown: '⏳ Auto-continue in {s}s…', autoContCancel: 'Cancel', autoContConfirm: 'Context at {p}% of the official declared window (official compaction starts ~80%) and the old session is idle (turn boundary) — continue into a new session now?', autoContAgree: 'Continue', autoContReject: 'Decline', autoContTimeout: 'auto-continue in {s}s (unattended)', autoContRejected: 'Skipped this handoff (will ask again at the next turn boundary)',
|
|
439
439
|
continueCardTitle: 'One-click continue', continueCardHint: 'Writes the handoff ledger → creates a new session → injects the handoff material as the first message → switches to the new session automatically (material = PLAN excerpt + latest ledger; falls back to the most recent global ledger when this workspace has none).', continueBtn: 'Continue in a new session', continueBusy: 'Working…',
|
|
440
440
|
fStyle: 'Reflection style', fStyleHint: 'Life-style / Professional / Auto.',
|
|
441
441
|
fLocale: 'UI language', fLocaleHint: 'Follows the DSH system language by default; you can also pin Chinese / English.', followSystem: 'Follow system language',
|
|
@@ -567,6 +567,21 @@ window.__ModuleLoader__.load({
|
|
|
567
567
|
|
|
568
568
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
569
569
|
var CHANGELOG = {
|
|
570
|
+
'2.5.0': { zh: [
|
|
571
|
+
'★ 水位口径修正:触发分母改用**官方声明窗口**(不再扣预留输出)——阈值 0.75 的触发点从约 46 万 token 后移到约 78.6 万,与官方「约 80% 才压缩」同一条线,不再"刚过半就接续"。',
|
|
572
|
+
'★ 新增预测性硬墙:上下文实占 + 预留输出 > 窗口(下一次请求必被拒,即 400 事故边界)时立即硬触发;压缩/溢出硬触发不变。本机这类大预留路由会由硬墙率先兜底,属"最后一格"而非浪费。',
|
|
573
|
+
'★ 接续序号改持久计数器(~/.dsh/memory/cont-seq.json):全局单调、换工作区不重号、转写包落盘失败自动回滚不跳号;计数器缺失时从历史「接续 #N」标题解析兜底。新窗口的接续标题不再错乱/丢失。',
|
|
574
|
+
'修复:接续序号为空时新会话标题退回自动生成的问题(宿主与浏览器两条路径现在都保证有序号);浏览器路径 rename 失败不再静默。',
|
|
575
|
+
'流程:发版/回归/双语对账/痕迹巡检四份子代理任务书固化(docs/prompts),主对话只做决策——对你无感,只是发版更稳。',
|
|
576
|
+
'注:自动接续出厂默认仍为关;新口径验证满意后你可以在设置里打开,或等下一版评估翻回默认。',
|
|
577
|
+
], en: [
|
|
578
|
+
'★ Water-level caliber fixed: the trigger denominator is now the officially declared window (the reserved output budget is no longer deducted) — at threshold 0.75 the trigger point moves from ~460K to ~786K tokens on a 1M window, aligning with the official ~80% compaction point.',
|
|
579
|
+
'★ Predictive hard wall added: when session tokens + reserved output exceed the window (the next request would be rejected — the exact 400-incident boundary), continue immediately; compaction / overflow hard triggers unchanged.',
|
|
580
|
+
'★ Continue sequence numbers now use a persistent counter (~/.dsh/memory/cont-seq.json): globally monotonic, no duplicates across workspaces, automatic rollback when the transcript pack fails to write (no skipped numbers), and a title-scan fallback for pre-existing data.',
|
|
581
|
+
'Fix: empty sequence numbers no longer skip the session rename (both host and browser paths guarantee one); browser-side rename failures are no longer silent.',
|
|
582
|
+
'Process: four self-contained subagent task books (release / regression / docs audit / trace patrol) — invisible to you, just steadier releases.',
|
|
583
|
+
'Note: auto-continue factory default stays off; after verifying the new caliber, turn it on in settings (or wait for the next release to re-evaluate the default).',
|
|
584
|
+
] },
|
|
570
585
|
'2.4.2': { zh: [
|
|
571
586
|
'变更:自动接续改为**出厂默认关闭**(老用户已落盘的显式设置不受影响)。原因:现有水位判据把「预留输出」从分母里扣掉,实测在上下文刚过半时就触发接续,与官方「约 80% 才压缩」相差约 45%,对不熟悉的人就是白烧 token —— 先默认关,等判据改成与官方压缩同一坐标系后再评估翻回。',
|
|
572
587
|
'想用的人可以在「白板 / 接续」页签里手动打开(开关一直没动,只是默认值变了)。',
|
|
@@ -2157,7 +2172,7 @@ window.__ModuleLoader__.load({
|
|
|
2157
2172
|
// 接续序号标题:让用户一眼区分新会话 vs 延续会话
|
|
2158
2173
|
if (d.contSeq && rf.session.rename && typeof rf.session.rename === 'function') {
|
|
2159
2174
|
var contTitle = (locale === 'zh' ? '接续 #' : 'Cont.#') + String(d.contSeq) + (d.wsBase ? ' · ' + d.wsBase : '')
|
|
2160
|
-
try { await rf.session.rename({ sessionId: newId, title: contTitle }) } catch (eRen) {}
|
|
2175
|
+
try { await rf.session.rename({ sessionId: newId, title: contTitle }) } catch (eRen) { try { console.warn('[dsh-auto-memory] rename failed: ' + ((eRen && eRen.message) || eRen)) } catch (eRen2) {} }
|
|
2161
2176
|
}
|
|
2162
2177
|
// 沿用旧会话模型(provider/model/reasoningEffort 由 host 从旧会话 request/header 折叠而来;不可用则静默回退路由默认)
|
|
2163
2178
|
if (d.provider && d.model && typeof rf.session.selectModel === 'function') {
|
package/lib/index.js
CHANGED
|
@@ -267,9 +267,8 @@ const DEFAULT_CONFIG = {
|
|
|
267
267
|
/** 水位越阈时自动写一篇系统骨架账本(每会话一次;模型仍应自己写正式交接)。 */
|
|
268
268
|
waterLevelAutoHandoff: true,
|
|
269
269
|
/** 自动接续(默认关):水位≥阈值 且 harness 权威 running==false(轮次边界,取代旧"两轮水位持平")时弹确认卡。 */
|
|
270
|
-
// 2026-09-10
|
|
271
|
-
//
|
|
272
|
-
// 待口径改为与官方压缩同坐标系后再评估是否翻回(见 docs/internal/NEXT-VERSION-TODO.md)。
|
|
270
|
+
// 2026-09-10 改为默认关(口径未修前刚过半就触发,纯浪费)。2026-09-13 口径已修:分母=官方声明窗口
|
|
271
|
+
// (不再扣预留输出),与官方压缩同坐标系 —— 是否翻回默认开由用户验证后定夺,此处保持 false 不动。
|
|
273
272
|
// 注:老用户已落盘的显式值不受影响,只有新装用户吃这个默认。
|
|
274
273
|
autoContinueEnabled: false,
|
|
275
274
|
/** 自动接续水位阈值(0.5-0.95);2026-09-08 与 waterLevelThreshold 同步下调到 0.75(官方自动压缩阈值 80%,必须留余量)。 */
|
|
@@ -1901,7 +1900,14 @@ class MemoryEngine {
|
|
|
1901
1900
|
sig = scanPressureSignalsPre(events)
|
|
1902
1901
|
} catch (eSig) {}
|
|
1903
1902
|
const reserve = Number(sessModel.maxTokens) || Number(sig.reservedTokens) || 0
|
|
1904
|
-
|
|
1903
|
+
// ② 判定窗(2026-09-13 口径修正,NEXT-VERSION-TODO 改点1):正常触发线与官方压缩同坐标系 ——
|
|
1904
|
+
// 分母 = 官方声明窗口 win,provider 自报过硬限(400 文本)时取二者较小值。**不再把预留输出 reserve
|
|
1905
|
+
// 从分母里扣**:旧口径(分母 = win − reserve =「单请求可用额度」)让水位被系统性放大 —— 本机
|
|
1906
|
+
// 1,048,576 − 384,000 = 664,576,上下文刚过半(≈46 万)就触发接续,而官方约 80%(≈83.9 万)才压缩。
|
|
1907
|
+
// reserve 收缩为两个职责:①「距硬墙余量」展示(waterLevelWall) ②硬判据 estTokens + reserve > 判定窗
|
|
1908
|
+
// (下一次请求就会被 provider 拒绝时才硬触发;本机即 400 事故的复现边界:666,044 + 384,000 > 1,048,576)。
|
|
1909
|
+
const hardWin = Number(sig.overflow && sig.overflow.windowTokens) || 0
|
|
1910
|
+
const triggerWin = hardWin > 0 ? Math.min(win, hardWin) : win
|
|
1905
1911
|
let estTokens = 0
|
|
1906
1912
|
let meterLayer = 'heuristic'
|
|
1907
1913
|
try {
|
|
@@ -1944,39 +1950,41 @@ class MemoryEngine {
|
|
|
1944
1950
|
} catch (e) {}
|
|
1945
1951
|
// 旧实现硬截断到 1.5 → 任何超额水位都显示成 150%,掩盖真实占用(用户实测 761692/131072 被显示为 150%)。
|
|
1946
1952
|
// 现在如实上报,仅做防溢出的 99 倍上限。
|
|
1947
|
-
const ratio =
|
|
1953
|
+
const ratio = triggerWin > 0 && Number.isFinite(estTokens) ? Math.min(estTokens / triggerWin, 99) : 0
|
|
1948
1954
|
const threshold = Math.max(Number(this.config.waterLevelThreshold) || 0.75, 0.1)
|
|
1949
1955
|
// 硬触发:官方已经压缩过/已撞过窗口墙 —— 这一刻必须接续,不依赖比例是否算得准。
|
|
1950
|
-
|
|
1956
|
+
// 2026-09-13 增加预测性硬墙:estTokens + reserve > 判定窗 ⇒ 下一次请求必被 provider 拒绝
|
|
1957
|
+
// (消息实占 + 预留输出 > 上限,v2.4.1 之前的 400 事故就是这个等式)。
|
|
1958
|
+
const hardWall = estTokens > 0 && (estTokens + reserve > triggerWin)
|
|
1959
|
+
const hard = compacted || overflowed || hardWall
|
|
1951
1960
|
const armRatio = hard ? Math.max(ratio, threshold) : ratio
|
|
1952
1961
|
rt.waterLevel = armRatio
|
|
1953
1962
|
rt.waterLevelTokens = estTokens
|
|
1954
1963
|
// 2026-09-10:pre-step 的 arm 走的是 runtime 字段(见 checkWaterLevelAtStep → armAutoContinue),
|
|
1955
1964
|
// 这里只写了 ratio/tokens、漏写 window/source,于是确认卡显示「512,311 / 0 token」(实测)。
|
|
1956
1965
|
// 与 state 同源同值,一处漏写两处都缺 —— 必须成对维护。
|
|
1957
|
-
rt.waterLevelWindow =
|
|
1966
|
+
rt.waterLevelWindow = triggerWin
|
|
1958
1967
|
rt.waterLevelSource = winSource
|
|
1959
1968
|
this.state.waterLevelRatio = armRatio
|
|
1960
1969
|
this.state.waterLevelTokens = estTokens
|
|
1961
|
-
this.state.waterLevelWindow =
|
|
1970
|
+
this.state.waterLevelWindow = triggerWin
|
|
1962
1971
|
this.state.waterLevelSource = winSource
|
|
1963
1972
|
this.state.waterLevelMeter = meterLayer
|
|
1964
1973
|
this.state.waterLevelModel = sessModel.model || winInfo.model || ''
|
|
1965
1974
|
this.state.waterLevelAt = Date.now()
|
|
1966
1975
|
this.state.waterLevelMeasuredTokens = estTokens
|
|
1967
1976
|
this.state.waterLevelReserve = reserve
|
|
1968
|
-
// 双口径(2026-09-
|
|
1969
|
-
//
|
|
1970
|
-
//
|
|
1971
|
-
this.state.waterLevelRing = (
|
|
1977
|
+
// 双口径(2026-09-13 起):水位/触发/ring 同用一个分母(判定窗 = 官方声明窗口,provider 自报过硬限时取 min)。
|
|
1978
|
+
// 旧「可用额度 vs 声明窗口」双口径随 reserve 退出分母而消失;保留的第二个数是「距硬墙余量」(hardWin − reserve,
|
|
1979
|
+
// 只有撞过 400 的会话才有 hardWin)。ring 与 ratio 同分母:硬限未知的普通会话里 ring 就等于官方小圈读数。
|
|
1980
|
+
this.state.waterLevelRing = (triggerWin > 0 && Number.isFinite(estTokens)) ? (estTokens / triggerWin) : 0
|
|
1972
1981
|
// provider 自报的硬上限只有撞过墙(400 文本)才拿得到;拿得到就把「真实可写上限」也算出来,
|
|
1973
1982
|
// 让界面能显示「距硬墙还剩多少 token」(本机 = 1,048,576 − 384,000 = 664,576)。
|
|
1974
|
-
const hardWin = Number(sig.overflow && sig.overflow.windowTokens) || 0
|
|
1975
1983
|
this.state.waterLevelWall = hardWin > 0 ? Math.max(0, hardWin - reserve) : 0
|
|
1976
|
-
this.state.waterLevelHardTrigger = hard ? (compacted ? 'compaction' : 'overflow') : ''
|
|
1984
|
+
this.state.waterLevelHardTrigger = hard ? (compacted ? 'compaction' : overflowed ? 'overflow' : 'wall') : ''
|
|
1977
1985
|
// 会话级记录:切会话时 /handoff-state?sessionId= 直接取这份,不再显示别的会话的数
|
|
1978
1986
|
this.rememberWaterRecord(agent && agent.session && agent.session.id ? String(agent.session.id) : '', {
|
|
1979
|
-
ratio: armRatio, measuredRatio: ratio, tokens: estTokens, measuredTokens: estTokens, window:
|
|
1987
|
+
ratio: armRatio, measuredRatio: ratio, tokens: estTokens, measuredTokens: estTokens, window: triggerWin,
|
|
1980
1988
|
windowRaw: win, reserve, source: winSource, meter: meterLayer, model: this.state.waterLevelModel || '',
|
|
1981
1989
|
threshold, hardTrigger: this.state.waterLevelHardTrigger, at: Date.now(), live: true,
|
|
1982
1990
|
})
|
|
@@ -1985,7 +1993,7 @@ class MemoryEngine {
|
|
|
1985
1993
|
if (!rt.waterLevelAdvised || compacted || overflowed) {
|
|
1986
1994
|
rt.waterLevelAdvised = true
|
|
1987
1995
|
diag('water level advisory: ratio=' + ratio.toFixed(2) + (hard ? '→' + armRatio.toFixed(2) + '(hard:' + this.state.waterLevelHardTrigger + ')' : '') +
|
|
1988
|
-
' tokens=' + estTokens + '/' +
|
|
1996
|
+
' tokens=' + estTokens + '/' + triggerWin + '(win=' + win + ' reserve=' + reserve + (hardWin > 0 ? ' hardWin=' + hardWin : '') + ')' +
|
|
1989
1997
|
' src=' + (winSource || '?') + ' meter=' + meterLayer + (compacted ? ' compaction-detected' : ''))
|
|
1990
1998
|
}
|
|
1991
1999
|
if (this.config.waterLevelAutoHandoff !== false && (!rt.waterLevelAutoHandoffDone || compacted || overflowed)) {
|
|
@@ -2092,6 +2100,96 @@ class MemoryEngine {
|
|
|
2092
2100
|
return true
|
|
2093
2101
|
}
|
|
2094
2102
|
|
|
2103
|
+
// ---------- 接续序号 v2(2026-09-13,NEXT-VERSION-TODO 改点2):持久计数器 ----------
|
|
2104
|
+
/** 接续序号计数器文件(~/.dsh/memory/cont-seq.json)。放在全局记忆根而非各工作区 handoff/ 目录:
|
|
2105
|
+
* handoffDir 按工作区解析,放那里既会被"落盘失败/清理"带偏计数,也无法保证「换工作区接续不重复已有序号」;
|
|
2106
|
+
* 全局单文件 + 工作区记账键(byWorkspace,键=工作区路径,仅作记账与兜底)既跨重启持久、又全局单调不重号。
|
|
2107
|
+
* 旧口径(数 handoff 目录里 prev-session-*.md 的文件数 +1)在落盘失败/跨工作区/carry 复用时重复或跳号,
|
|
2108
|
+
* 且取数为空时 rename 被整段跳过、标题退回自动生成 —— 均为用户实机观察过的现象。 */
|
|
2109
|
+
contSeqFile() { return path.join(dshHome(), 'memory', 'cont-seq.json') }
|
|
2110
|
+
|
|
2111
|
+
loadContSeqState() {
|
|
2112
|
+
try {
|
|
2113
|
+
const obj = JSON.parse(readFileSync(this.contSeqFile(), 'utf8'))
|
|
2114
|
+
if (obj && typeof obj === 'object') {
|
|
2115
|
+
return {
|
|
2116
|
+
last: Number(obj.last) || 0,
|
|
2117
|
+
byWs: (obj.byWorkspace && typeof obj.byWorkspace === 'object') ? obj.byWorkspace : {},
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
} catch (e) {}
|
|
2121
|
+
return null
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
saveContSeqState(st) {
|
|
2125
|
+
try {
|
|
2126
|
+
mkdirSync(path.dirname(this.contSeqFile()), { recursive: true })
|
|
2127
|
+
writeFileSync(this.contSeqFile(), JSON.stringify({ last: st.last, byWorkspace: st.byWs, updatedAt: Date.now() }, null, 2), 'utf8')
|
|
2128
|
+
return true
|
|
2129
|
+
} catch (e) { diag('cont-seq persist failed (in-memory only): ' + ((e && e.message) || e)); return false }
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
/** 兼容老数据:计数器文件缺失/为空时,从既有会话日志解析历史「接续 #N / Cont.#N」标题的最大序号。
|
|
2133
|
+
* 扫描 ~/.dsh/sessions/<工作区>/<会话>/session.jsonl[.zstd],按 mtime 新→旧最多 120 个;找不到返回 0。 */
|
|
2134
|
+
async scanMaxContSeq() {
|
|
2135
|
+
try {
|
|
2136
|
+
const sessionsDir = path.join(dshHome(), 'sessions')
|
|
2137
|
+
const wsDirs = await readdir(sessionsDir).catch(() => [])
|
|
2138
|
+
const cands = []
|
|
2139
|
+
for (const w of wsDirs) {
|
|
2140
|
+
const sids = await readdir(path.join(sessionsDir, w)).catch(() => [])
|
|
2141
|
+
for (const sid of sids) {
|
|
2142
|
+
const file = await this.resolveSessionFile(path.join(sessionsDir, w, sid))
|
|
2143
|
+
if (!file) continue
|
|
2144
|
+
const st = await stat(file).catch(() => null)
|
|
2145
|
+
if (st && st.isFile()) cands.push({ file, mt: Number(st.mtimeMs) || 0 })
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
cands.sort((a, b) => b.mt - a.mt)
|
|
2149
|
+
let max = 0
|
|
2150
|
+
for (const { file } of cands.slice(0, 120)) {
|
|
2151
|
+
try {
|
|
2152
|
+
const buf = await readFile(file)
|
|
2153
|
+
const text = file.endsWith('.zstd') ? (zstdDec ? zstdDecodeAllFrames(buf) : '') : buf.toString('utf8')
|
|
2154
|
+
if (!text) continue
|
|
2155
|
+
const re = /(?:接续 #|Cont\.#)(\d+)/g
|
|
2156
|
+
let m
|
|
2157
|
+
while ((m = re.exec(text))) { const n = Number(m[1]) || 0; if (n > max) max = n }
|
|
2158
|
+
} catch (eF) {}
|
|
2159
|
+
}
|
|
2160
|
+
return max
|
|
2161
|
+
} catch (e) { return 0 }
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
/** 分配下一个接续序号:全局单调递增(换工作区也不重号),持久化到 cont-seq.json。
|
|
2165
|
+
* 文件缺失时先从历史标题解析最大值兜底(兼容 v2.4.2 及之前的文件计数老数据)。
|
|
2166
|
+
* 持久化失败只记 diag、序号照常返回(退化为进程内计数),不得阻塞接续。 */
|
|
2167
|
+
async allocContSeq(wsKey) {
|
|
2168
|
+
const st = this.loadContSeqState() || { last: 0, byWs: {} }
|
|
2169
|
+
if (!(st.last > 0)) {
|
|
2170
|
+
const scanned = await this.scanMaxContSeq().catch(() => 0)
|
|
2171
|
+
if (scanned > st.last) st.last = scanned
|
|
2172
|
+
}
|
|
2173
|
+
for (const v of Object.values(st.byWs)) { const n = Number(v) || 0; if (n > st.last) st.last = n }
|
|
2174
|
+
const next = st.last + 1
|
|
2175
|
+
st.last = next
|
|
2176
|
+
if (wsKey) st.byWs[String(wsKey)] = next
|
|
2177
|
+
this.saveContSeqState(st)
|
|
2178
|
+
return next
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/** 回滚刚分配的序号(转写包落盘失败时调用,保证不跳号)。只撤自己刚发出的那个号。 */
|
|
2182
|
+
rollbackContSeq(wsKey, seq) {
|
|
2183
|
+
try {
|
|
2184
|
+
const n = Number(seq) || 0
|
|
2185
|
+
if (!n) return
|
|
2186
|
+
const st = this.loadContSeqState() || { last: n, byWs: {} }
|
|
2187
|
+
if (st.last === n) st.last = n - 1
|
|
2188
|
+
if (wsKey && Number(st.byWs[String(wsKey)]) === n) delete st.byWs[String(wsKey)]
|
|
2189
|
+
this.saveContSeqState(st)
|
|
2190
|
+
} catch (e) { diag('cont-seq rollback failed: ' + ((e && e.message) || e)) }
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2095
2193
|
/** M-CM6-C·宿主兜底自动接续(2026-09-08,2.2.6):浏览器端 AutoContinueHost 被后台节流/关闭时,
|
|
2096
2194
|
* 宿主自身在倒计时到期后直接建新会话。协议:turn-stopping 且水位≥阈值 → arm 倒计时(confirmSeconds,
|
|
2097
2195
|
* 默认 35s,写入 _autoContState)→ 浏览器轮询 GET auto-continue-state 显示确认卡(同意=POST decide
|
|
@@ -2535,6 +2633,10 @@ class MemoryEngine {
|
|
|
2535
2633
|
const NL = String.fromCharCode(10)
|
|
2536
2634
|
const stamp = new Date().toISOString().slice(11, 19).replace(/:/g, '')
|
|
2537
2635
|
const outPath = path.join(p.handoffDir, 'prev-session-' + sid.slice(0, 8) + '-' + stamp + '.md')
|
|
2636
|
+
// 接续序号 v2(2026-09-13):先分配序号、再写包;写包失败回滚计数器(不跳号)。
|
|
2637
|
+
// 序号属于这次接续(新会话标题「接续 #N」用),分配即持久化,重启/换工作区都不重号。
|
|
2638
|
+
let contSeq = 0
|
|
2639
|
+
try { contSeq = await this.allocContSeq(p.ws) } catch (eSeq) { diag('allocContSeq error: ' + ((eSeq && eSeq.message) || eSeq)) }
|
|
2538
2640
|
const body = [
|
|
2539
2641
|
'# 旧会话完整转写(' + sid + ')', '',
|
|
2540
2642
|
'- 旧会话 ID: ' + sid,
|
|
@@ -2551,11 +2653,17 @@ class MemoryEngine {
|
|
|
2551
2653
|
body.push(chunk)
|
|
2552
2654
|
total += chunk.length
|
|
2553
2655
|
}
|
|
2554
|
-
|
|
2555
|
-
|
|
2656
|
+
try {
|
|
2657
|
+
await mkdir(p.handoffDir, { recursive: true })
|
|
2658
|
+
await writeFile(outPath, body.join(NL), 'utf8')
|
|
2659
|
+
} catch (eW) {
|
|
2660
|
+
// 包落盘失败 → 回滚刚分配的序号,保证不跳号(carry 会用持久计数器重新兜底)
|
|
2661
|
+
if (contSeq) { try { this.rollbackContSeq(p.ws, contSeq) } catch (eR) {} }
|
|
2662
|
+
throw eW
|
|
2663
|
+
}
|
|
2556
2664
|
const tail = msgs.slice(-20).map((m) => m.role + ': ' + m.text.slice(0, 700)).join(NL + '---' + NL)
|
|
2557
|
-
diag('prev-session pack built: sid=' + sid + ' msgs=' + msgs.length + ' model=' + (model || '-') + '@' + (provider || '-') + ' effort=' + (reasoningEffort || '-') + ' -> ' + outPath)
|
|
2558
|
-
return { sessionId: sid, transcriptPath: outPath, provider, model, reasoningEffort, agentPreset: preset, cwd: cwd || p.ws, tailText: tail, msgCount: msgs.length }
|
|
2665
|
+
diag('prev-session pack built: sid=' + sid + ' msgs=' + msgs.length + ' model=' + (model || '-') + '@' + (provider || '-') + ' effort=' + (reasoningEffort || '-') + ' contSeq=' + contSeq + ' -> ' + outPath)
|
|
2666
|
+
return { sessionId: sid, transcriptPath: outPath, contSeq, provider, model, reasoningEffort, agentPreset: preset, cwd: cwd || p.ws, tailText: tail, msgCount: msgs.length }
|
|
2559
2667
|
} catch (e) { diag('buildPrevSessionPack error: ' + (e && e.message)); return null }
|
|
2560
2668
|
}
|
|
2561
2669
|
|
|
@@ -2647,9 +2755,16 @@ class MemoryEngine {
|
|
|
2647
2755
|
parts.push('【锚点表 · 记忆条目地图(按需下钻,不必通读)】' + NL + anchorSections.join(NL))
|
|
2648
2756
|
}
|
|
2649
2757
|
} catch (eAnchor) {}
|
|
2650
|
-
//
|
|
2651
|
-
|
|
2652
|
-
|
|
2758
|
+
// 接续序号 v2(2026-09-13,NEXT-VERSION-TODO 改点2):持久计数器,全局单调(跨工作区不重号)。
|
|
2759
|
+
// 取值链:转写包路径已分配的(pack.contSeq)→ 包缺失/失败时用持久计数器兜底分配 → 计数器彻底不可用
|
|
2760
|
+
// 再退旧「文件数+1」口径 → 最后退 1。contSeq 必须有值:为空时两侧 rename 都被跳过、新会话标题
|
|
2761
|
+
// 退回自动生成(v2.4.2 前的老毛病),这里从源头保证非空。
|
|
2762
|
+
let contSeq = Number(pack && pack.contSeq) || 0
|
|
2763
|
+
if (!contSeq) { try { contSeq = await this.allocContSeq(p.ws) } catch (eSeq2) {} }
|
|
2764
|
+
if (!contSeq) {
|
|
2765
|
+
try { const prevs = (await readdir(p.handoffDir).catch(() => [])).filter((f) => /^prev-session-.*\.md$/.test(f)); contSeq = prevs.length + 1 } catch (eS) {}
|
|
2766
|
+
}
|
|
2767
|
+
if (!contSeq) contSeq = 1
|
|
2653
2768
|
const wsBase = String((pack && pack.cwd) || p.ws || '').split(/[\\/]/).filter(Boolean).pop() || ''
|
|
2654
2769
|
return {
|
|
2655
2770
|
ok: true,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a9i5k4/dsh-auto-memory",
|
|
3
3
|
"description": "Proactive associative memory for DSH: zero-prompt recall injected before the model speaks, three-layer auto-consolidation, skill crystallization, and Astra-style context management - handoff ledgers, PLAN whiteboard, water-level sensing. Local-first, model-agnostic, zero deps. 主动联想记忆+Astra 式上下文管理:自动唤回/自动沉淀/技能固化/交接账本与白板跨窗口续命/水位感知。",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|