Skip to content

Releases

v1.0.1 —— 修复保存后界面不同步

Choose a tag to compare

@OraSkyC OraSkyC released this 08 Oct 18:07

修复:点了开关显示「已保存」,开关却纹丝不动

Field / BoolField 把 /settings 的响应整个丢掉了,界面只能等下一次轮询
(默认 30 秒)才更新。用户看到的是「已保存」但开关没动,退出重进设置页才显示新值
—— 因为那是重新拉了一次 /state。

文本框是同一个根因,只是症状不同:dirty 一直挂在 true 上,「未保存」不消失。
所有走 /settings 的控件都中招,不只是开关。

修法(两处,缺一不可)

  1. /settings 回一份完整状态快照(buildState()),不再只回
    effective / defaults / …。派生字段(工作区标题、解析出来的目录、回收站统计、
    清理预览)根本不在原来的响应里,所以它们也必须一起回。这样客户端也不必再写
    逐字段的合并逻辑 —— 少一处可以漏的地方。

  2. 面板新增「保存后广播」订阅:保存成功即把响应并回面板状态,并且作废在途的轮询
    (否则一个更早发出的 /state 可能在保存之后才回来,把刚写进去的值盖回旧的
    —— 那就变成了「有时候灵有时候不灵」)。

用订阅而不是给每个字段传 prop,是因为「保存后同步界面」这件事每个字段都必须做,
而漏掉一个的症状是静默的。订阅让新增字段自动获得这个行为 —— DangerZone 里嵌套的
那个字段正是传 prop 最容易漏掉的一处。

顺带:保存后显示的是宿主重算过的值

不是本地乐观值。所以非法输入被夹取(pollSeconds 填 99999 → 600)会立刻如实显示,
不用等半分钟;保存失败时开关也不会自己翻过去,而是显示宿主的错误。

测试脚手架修正:迷你 React 改成每个组件一份 hook 状态

以前整棵树共用一个扁平数组,于是「条件渲染掉一个带 hook 的子组件」会让它后面所有组件的
槽位整体前移,读到别的组件的状态 —— 测试里真的崩在 DangerZone 的 typed.trim() 上,
因为 typed 读到了相邻组件的 null。

真实 React 是按组件隔离 hook 的,所以那是脚手架在说谎,不是产品 bug。
但它会挡住正当的测试(比如测 enabled 开关),所以必须修。

测试

之前 现在
宿主 59 59
客户端 20 24

新增 [J] 保存后界面立即同步(回归):切换开关后界面必须立刻反映新值
(且重渲染不产生额外请求)、保存文本框后要显示宿主夹取后的值且「未保存」消失、
版本小标可见、保存失败时开关保持原状并显示宿主错误。

该回归验证过:把 broadcastSettingsSaved(body) 注释掉,测试立刻以
「保存后开关必须立刻反映新值」失败。

升级

插件安装后需要重启 DSH 才加载。升级请先「卸载」再装,然后重启。
重启后插件页标题右侧会显示 v1.0.1 —— 没看到就说明新构建还没生效。

完整变更:v1.0.0...v1.0.1

v1.0.0 — 记忆管理(首个版本)

Choose a tag to compare

@OraSkyC OraSkyC released this 08 Oct 16:47

首个版本。

这是什么

给智能体一个跟着工作区走的记忆库。开启后,记忆默认存在当前工作目录的 ./memory/ 下,
根目录放一个自动维护的 MEMORY.md 索引;需要分类的按需建子目录,不需要分类的直接写在根目录。

<你的工作目录>/
└── memory/
    ├── MEMORY.md                 ← 索引,插件自动生成
    ├── 用户偏好用-pnpm.md          ← 不需要分类的,直接写在根目录
    └── 项目约定/                  ← 需要分类的,按需建子目录
        └── 提交信息用中文.md

记忆跟着工作区走,是因为根目录取自会话的工作目录(exec.agent.session.header.cwd):
在 A 项目记的东西在 B 项目看不到,不会串味;记忆跟着代码走,想提交进版本库也可以。

四个工具

工具 作用
memory_save 存一条。给 title + content;category 可选(最多 3 级)——省略就直接写在根目录。同名默认报错而非静默覆盖。
memory_recall 不带参数 → 返回索引(先看它);带 name → 读某一条。支持相对路径与唯一后缀匹配,匹配到多条会报错列出候选,不猜。
memory_search 跨全部记忆全文检索(标题/标签/正文,大小写不敏感),带命中摘录。
memory_forget 删一条。只接受单个 .md,永远不删目录。

两个值得说的设计

索引是「投影」,不是「账本」。 MEMORY.md 每次都由磁盘上的记忆文件重新生成,
所以不存在「索引和实际内容不一致」这种状态 —— 那是最容易腐坏的东西。
代价是手工写进索引的内容会在下次重建时被覆盖,文件头写明了这点。
每条记忆是 front matter(title/tags/created/updated)+ Markdown 正文,
刻意不引入 YAML 解析器:坏行忽略而不是抛错,没有 front matter 的文件也照样收录。

写入路径四层防护。 智能体给的 name/category 最终会变成文件路径,所以:

  1. 片段校验 —— 拒绝 ..、.、绝对路径、路径分隔符、Windows 非法字符、控制字符、
    系统保留名(CON/NUL/COM1…)、以点或空格结尾、超过 64 字符;
  2. 层级限制 —— category 与 root 最多 3 级;
  3. 拼完再验一次 —— 用 relative() 确认结果仍在记忆根目录内;
  4. 删除只接受 .md 文件。

四层都有测试覆盖,包括嵌套 .. 的穿越尝试。

安装

设置 → 插件 → 添加插件,填入:

https://github.com/OraSkyC/dsh-bundle-memory

或命令行:

dsh plugin --profile desktop add https://github.com/OraSkyC/dsh-bundle-memory

装完重启 DSH。

设置

插件页里有设置卡,改动立即生效、无需重启:
enabled、root、indexFile、autoIndex、maxEntryBytes、searchMaxResults。

这一版没有做的

记忆索引不会自动注入到智能体的上下文 —— 它需要主动调 memory_recall 或
memory_search。建议在项目的 AGENTS.md 里写一句「开工前先 memory_recall 看索引」。

自动注入是可行的后续方向,但需要构造会话消息并插进消息流(agent/pre-step 钩子),
有破坏会话格式的风险,单独做更稳妥。

要求

Node >= 22.19.0 · 无 npm 依赖 · 无构建步骤。

用法与设计细节见 README.md。