dsh-recall-plugin 2.4.15 → 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/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
4
4
 
5
+ ## [2.5.0] - 2026-10-24
6
+
7
+ ### 新增
8
+
9
+ - **「编辑并重发」:就地改完直接发出(issue #26)**:用户消息动作行在「↶ 撤回」旁新增「✎ 编辑」——点击就地展开编辑框并预填原文,改完提交即新建会话并把改后的文本自动发出。复用撤回的 fork 链(preview 取切点 → execute → `sessions.fork` → 归档原会话),但 scope 固定 `session-only`:**编辑语义不动项目文件**,也不打安全快照。原消息的图片/文件附件经 `createDrafts → addAttachments` 一并带入新消息。
10
+ - 配置项 `editSubmitsImmediately`(默认 `true`):提交后自动发送;关闭则退化为「只回填输入框」,由用户按回车发送(适合只想改稿不想立刻发)。设置页「插件配置」卡片可直接切换,热生效;老 Host 不下发该字段时按 `true` 处理。
11
+ - 发送时机按草稿落地校验:`setDraft` → `actions.submit()` 之间读输入态快照比对草稿文本,未落地则最多等 6×50ms 再提交——`submit` 读的是输入态快照,同一 tick 内投影未刷新会按空草稿静默返回。快照不可读的跨版本面降级为让出一拍直接提交(草稿为空时官方自身拒发,最坏是「没发」,不会「发错」)。
12
+ - **版本翻页器 `‹ X/N ›`**:每次撤回/编辑重发 fork 出的会话构成同一段对话的版本家族,最后一轮回答的操作区嵌入 `‹ X/N ›`——点击切换:归档当前版本 → 解档目标版本 → 打开目标会话,侧栏始终只保留一个活动版本;切换后按 `data-chat-anchor-key` 恢复视口位置(消息内容不同、行高不同,只靠 `scrollTop` 会跳位)。家族登记在客户端 `localStorage`(`dsh-recall:versions:<根会话>`),与 host 侧 `lineage.json` 互补:丢了只影响翻页器,会话本身不受影响。
13
+
14
+ ### 变更
15
+
16
+ - **消息位置出现「官方同款」编辑卡**:点 ✎ 后气泡换成一张按官方 composer 配方画的卡片(panel 圆角 / `input-major` 底 / soft 阴影 / 14-24 字号行高),预填原文、随内容增高,**底部官方输入框保持原样**。卡内可用:Enter 发送(Shift+Enter 换行)、附件增删与粘贴/拖入、只读的权限 chip、可切换的模型与推理等级、`Esc` 取消。
17
+ - **为什么不是第二个官方输入框**:`conversation.composer.bar` 是 single 槽且已被宿主的 `conversation.content` 独占声明——插件渲染任何槽都要先在自己 children 里声明它,重复声明会在加载期抛错,组件实例也没有导出。因此这张卡是插件自绘的同款,官方样式变动时需要跟版。
18
+ - 附件语义改为显式:原消息附件渲染成可移除的 chip,提交时只带保留下来的那些;卡里新加的附件(粘贴/拖入/选择)与原图/文件一起交给 `fillDraft` 在子会话重建。图片附件显示为 56px 缩略图(原消息图片走官方 `loadImage` 通道、与消息里的大图同源;新加图片用 object URL,移除/关闭时回收),非图片仍按文件名 chip 展示。
19
+ - 编辑卡里的模型菜单按官方同构重建:主段两行「模型 / 推理等级」,点进去分别是模型列表(搜索 + 按供应商分组 + 当前项打勾)与档位列表(Default / Low / High / Xhigh)。选择走官方 `ctx.modelDirectories` 的 `directoryFor(session).select()`——与底部 chip、`/model` 命令同一条 `selectModel` 通道,选中后底部输入框的 chip 同步更新,fork 出的子会话沿用;换模型用该模型的默认档,重选同一模型保留当前档位。触发 chip 与官方同构:模型名用常规色、思考强度用更淡的字色,尾部带展开箭头(展开时翻转、给一层 hover 底)。服务缺失(旧宿主)时退回只读展示。
20
+ - 编辑态隐藏底部输入框的统计 dock(回合/步骤、tok/s、缓存命中、Trace):编辑焦点在消息处,dock 属全局概览,留着只会分心。规则挂在既有的 `data-editing` 上,不引入额外 JS 状态。
21
+
22
+ ### 修复
23
+
24
+ - **编辑态重复触发不再重置已改文本**:编辑态下不再渲染「✎」(点了也是空操作),`openEdit` 对 editing 幂等、error 态保留改过的文本与附件——否则会用原文覆盖用户改到一半的内容。
25
+ - 输入区回填链(文本 + 附件 + 可选发送)从 `recall-node.ts` 抽到新模块 `src/client/draft-fill.ts`:撤回回填与编辑重发必须共用同一条官方写入通道,分叉必然漂移;`defaultAttachmentName` 随迁并保持 `recall-node.js` 的 re-export 路径不变。
26
+ - 编辑渲染层独立成 `src/client/edit-node.ts`(与撤回确认面板的 stage 语义不同),骨架与执行链仍在 `recall-node.ts`;版本翻页器拆为 `src/client/version-tree.ts`(家族登记/查询)与 `src/client/version-pager.ts`(`conversation.chat.assistant-actions` 槽组件)。
27
+ - 撤回执行链 `executeRecall` 参数化(scope / 切点 / 预览摘要 / 编辑文本),编辑与撤回共用同一段 fork 收尾;scope 判定改白名单,避免被当作 `onClick` 处理器时把 React 合成事件当 scope 透传。
28
+
29
+ ### 边界
30
+
31
+ - 会话第一条用户消息没有更早的 `turn/end` 边界,编辑提交会被明确拒绝(错误面板),不静默降级为「仅回填」。
32
+ - fork 失败时不回填也不发送:原会话与原消息都还在,补发会变成同一会话里的重复消息。
33
+
34
+ ## [2.4.16] - 2026-10-18
35
+
36
+ ### 修复
37
+
38
+ - **用户消息气泡被压窄、二十来个字也要折两行**:新面(factory 分支)本层 `.dsh-recall-stack` 仍带旧版的 `max-width:min(525px,82%)`,而它包裹的官方 `UserContent` 内层 `.userStack` 自己已有一道 `max-width:…82%`——两层百分比叠加成 0.82×0.82≈0.67,气泡可用宽度只剩列宽的 2/3(1600px 视口实测 254px,20 字中文 15px 字号约需 300px,于是折行)。外层在官方面改为 `width:100%;max-width:100%`(新增 `dsh-recall-stack-official` 标记),只保留右对齐与间距,宽度交回官方内层按满列宽计算(修复后同一条消息 310px、单行);legacy 自绘面(0.1.x 与 factory 回落)仍保留原 `min(525px,82%)` 上限,长消息宽度不变。
39
+
5
40
  ## [2.4.15] - 2026-10-18
6
41
 
7
42
  ### 修复
package/README.en.md CHANGED
@@ -49,6 +49,8 @@ Abilities with a version in parentheses require that version or later; the rest
49
49
  - **Never touches your project's own git, and keeps it clean**: snapshots live in an independent shadow git repository — branches, staging area, and uncommitted changes are untouched; storage stays under `$DSH_HOME` regardless of the session's sandbox permission (workspace-write / read-only sessions work as usual), falling back to an in-project `.dsh-recall-snapshots` only when home itself is unwritable.
50
50
  - **See the list before you act — and change your mind as often as you like**: recall first shows the list of files that will change (modified / restored / deleted); nothing runs until you confirm. After a recall you can recall again to an even earlier point, and files overwritten during a recall always remain recoverable.
51
51
  - **Resend right after a recall** (2.3.15+): the message's text and attachments (images, files) return to the input box, so you can edit and send again without picking the attachments over.
52
+ - **Fixing one typo takes one click** (2.5.0+): the "✎ Edit" action grows an editor card at that message, styled after the official composer (prefilled with the original text, growing with content) while **the bottom composer stays exactly where it is**; edit inline, paste or drop attachments, then press Enter (or Send) to fork a new session and dispatch the edited text. It rides the same fork chain as a recall (the original session is archived and recoverable) but **only rolls back the conversation — project files stay untouched**. Original attachments appear as removable chips, permissions stay read-only, model and reasoning effort can be switched right there through an official-style menu (syncing to the bottom composer), and `Esc` cancels. Turn off "Send after editing" in the settings if you would rather review before pressing Enter.
53
+ - **Every version of the same turn is one click away** (2.5.0+): each recall or edit-and-resend forks a new session, and the action strip of the last answer gains a `‹ X/N ›` control — click to switch. Only the active version stays in the sidebar (the others are archived automatically), and the viewport returns to where you were.
52
54
  - **A guarded recall path** (2.0+; auto-rescue 2.1+): a new snapshot after the preview forces a fresh preview; a "pre-rollback" safety snapshot is taken before every recall, a failed rollback is rescued automatically, and a failed rescue gives you a copy-paste-ready manual recovery command.
53
55
  - **Disk-friendly, self-maintaining**: snapshots use git delta compression and large files are skipped automatically (threshold configurable); periodic lossless `git gc`, session-deletion cleanup, and cleanup by cap or retention age — plus a **workspace → session → snapshot** tree manager on the settings page with search and per-level deletion.
54
56
  - **Failures speak up and heal** (self-healing 2.1+): failures are classified by root cause (git missing / disk full / permission denied / lock conflict / directory conflict) with actionable guidance (the same fault only bothers you once per 10 minutes; reasons land in the settings card's "Recent errors"); leftover objects are pruned, 3 consecutive failures trigger exponential backoff, concurrent instances yield via heartbeats, and unindexable paths are skipped with a notice instead of failing the whole snapshot (they are neither restored nor deleted on recall).
@@ -59,7 +61,8 @@ Boundaries accepted by design and edge cases not yet covered — worth checking
59
61
 
60
62
  - Snapshots are created **when a message is sent**: messages from before the plugin was enabled have no snapshot and show no recall button.
61
63
  - Snapshots are **best-effort**: there is a 0.5–1.5 second window between the message arriving and `git add` (on Windows, a single PowerShell start alone costs about 0.4s). If a trivial task finishes editing files inside that window, the snapshot captures the turn's own changes — the file rollback then becomes a no-op (the preview panel reports "0 files will change") while the conversation rollback is unaffected.
62
- - The first user message of a session cannot roll back the conversation (files only), because fork requires an earlier turn boundary.
64
+ - The first user message of a session cannot roll back the conversation (files only), because fork requires an earlier turn boundary; "Edit and resend" hits the same limit — with no earlier cut point it refuses outright instead of silently degrading to a refill.
65
+ - The version pager only knows about versions this plugin created (recall / edit-and-resend); sessions forked by hand from the sidebar are not part of the family. The version registry lives in browser `localStorage`, so after switching browsers or clearing storage the pager no longer lists older versions (the sessions and archives themselves are unaffected).
63
66
  - Recall cannot be initiated while an agent is running in the target workspace (by design — stop the agent first).
64
67
  - Supports Windows (PowerShell 5.1/7 + git CLI) and Linux/macOS (bash + git CLI). Windows is thoroughly verified on real machines; Linux has been fully tested on WSL2 (Ubuntu 26.04, bash 5.3 + git 2.53), including Chinese paths, home fallback, session cleanup, and gc; the macOS side is written to be bash 3.2 compatible but has not been tested on real hardware yet.
65
68
  - Nested git repositories inside the workspace (subdirectories with their own `.git`) cannot be indexed: the snapshot proceeds for everything else (fail-open, with a notice listing the skipped paths), but their contents do not participate in recalls.
@@ -129,6 +132,10 @@ A full recall of one user message sent after the plugin was enabled goes like th
129
132
  2. Click it → the confirmation panel shows the list of files that will change (modified / restored / deleted), plus a scope choice: "Roll back files & conversation" (default) or "Conversation only" (files stay as they are).
130
133
  3. Click "Confirm rollback" (or "Confirm conversation recall") → files are restored to their state before that message was sent (files are untouched in conversation-only mode); the view switches to a new session (that message and everything after it is removed), while the original session is archived and can be recovered anytime.
131
134
 
135
+ To fix the wording only, click "✎ Edit" on the same row: an editor card styled after the official composer appears at that message, prefilled with its text — press Enter (or Send) to fork a new session with your edited text (project files untouched, attachments carried over); `Esc` cancels. The very first user message has no earlier cut point, so editing it is refused.
136
+
137
+ After a recall or an edit-and-resend, the last answer's action strip shows `‹ X/N ›`: step left or right to move between the versions of that stretch of conversation (the current one is archived and the target unarchived, so only one stays in the sidebar).
138
+
132
139
  ## Configuration
133
140
 
134
141
  All options can be edited visually in the "**Settings → Plugin Config → Recall Plugin**" card (saved changes apply live, no restart needed), or by restating the insert line under `id: recall` in the profile's `cordis.patch.yml`. Environment variables only override the two gc options and take top priority (fields locked by env are marked and uneditable in the card).
@@ -146,6 +153,7 @@ All options can be edited visually in the "**Settings → Plugin Config → Reca
146
153
  | `refillDraft` | true | Refill the recalled message (text and attachments) into the input box after a recall |
147
154
  | `snapshotEnabled` | true | Master snapshot switch (off = no new snapshots; existing snapshots remain recallable) |
148
155
  | `archiveOriginal` | true | Archive the original session after a recall (off = the original session stays in the session list) |
156
+ | `editSubmitsImmediately` | true | "Edit and resend" sends the edited message right away; off = only refill the input box so you can press Enter yourself |
149
157
  | `locale` | auto | UI language: `auto` follows the system (`navigator.language` starting with `zh` → Chinese, otherwise English), `zh` / `en` pin it explicitly. See "UI language" below |
150
158
 
151
159
  The settings card also offers "Restore defaults" (one-click reset of all fields) and a "Recent errors" viewer/clearer.
package/README.md CHANGED
@@ -46,6 +46,8 @@
46
46
  - **不碰项目自身的 git,目录保持干净**:快照存在独立的影子 git 仓库,分支、暂存区、未提交改动统统不受影响;存储默认落在 `$DSH_HOME` 下、与会话沙箱权限无关(workspace-write / read-only 照常工作),仅当 home 不可写才降级到项目内 `.dsh-recall-snapshots`。
47
47
  - **先看清单再动手,可反复后悔**:撤回前展示将变更的文件清单(修改 / 恢复 / 删除),确认后才执行;撤回后还能再撤到更早,被覆盖的文件一直找得回来。
48
48
  - **撤回完就能重发**(2.3.15+):消息的文本与附件(图片、文件)一起放回输入框,改完直接发送,不必重新挑一遍附件。
49
+ - **只改一个字也不用三连**(2.5.0+):消息旁的「✎ 编辑」在该消息位置长出一张与官方输入框同款的编辑卡(预填原文、随内容增高),**底部输入框保持原样**;卡内可改字、粘贴/拖入附件,按 Enter 或点发送即新建会话、把改后的文本自动发出(原会话照常归档可找回),但**只回退对话、不动项目文件**。原消息附件以可移除的标签显示,权限以只读标签展示,模型与推理等级可在卡内按官方同款菜单切换(同步到底部输入框);`Esc` 取消。不想立刻发可在设置里关掉「编辑后自动发送」,改为只回填输入框。
50
+ - **同一句话的多个版本可以来回翻**(2.5.0+):每次撤回或编辑重发都会 fork 出新会话,最后一轮回答的操作区出现 `‹ X/N ›`——点一下切到别的版本,侧栏始终只留当前那个(其余自动归档),切回时视口停回原位置。
49
51
  - **撤回全程有防护**(2.0+,救援 2.1+):预览后出现新快照会强制重新预览;执行前自动打「回退前」安全快照,回退失败自动救援,救援失败给出可直接复制执行的手动恢复命令。
50
52
  - **磁盘友好、自动维护**:快照走 git delta 增量压缩,大文件自动跳过(阈值可配);定期无损 `git gc`、会话删除联动清理、按条数与保留天数自动清理;设置页提供**工作区 → 会话 → 快照**三级树形管理,支持搜索与分级删除。
51
53
  - **失败不静默、能自愈**(自愈 2.1+):失败按根因分类(git 缺失 / 磁盘满 / 无权限 / 锁冲突 / 目录冲突)给出可行动提示,同类故障 10 分钟只打扰一次,原因进设置卡片「最近错误」;自动清理残骸、连续 3 次失败指数退避、多实例按心跳互让;无法索引的路径跳过并告知,不中断整条快照(撤回时也不触碰它们)。
@@ -56,7 +58,8 @@
56
58
 
57
59
  - 快照在**消息发送时**创建:插件启用前的历史消息没有快照,不显示撤回按钮。
58
60
  - 快照是**尽力捕获**:从收到消息到 `git add` 之间有约 0.5–1.5 秒窗口(Windows 上仅一次 PowerShell 启动就占约 0.4 秒)。秒级任务若在这个窗口内改完文件,该快照会连带捕获本轮改动——撤回时文件回退成为空操作(预览面板显示「共 0 个文件将变更」),对话回退不受影响。
59
- - 会话第一条用户消息无法回退对话(仅文件回退),因为 fork 需要更早的 turn 边界。
61
+ - 会话第一条用户消息无法回退对话(仅文件回退),因为 fork 需要更早的 turn 边界;「编辑并重发」同样受此限制——没有更早的切点时会明确拒绝,而不是静默降级成只回填。
62
+ - 版本翻页器只识别本插件产生的版本(撤回 / 编辑重发);会话列表里手动 fork 出来的会话不计入家族。版本登记存在浏览器 `localStorage`,换浏览器或清缓存后翻页器不再显示历史版本(会话本身与归档不受影响)。
60
63
  - 目标工作区的 agent 正在运行时无法发起撤回(防护设计,先停止 agent 再撤回)。
61
64
  - 支持 Windows(PowerShell 5.1/7 + git CLI)与 Linux/macOS(bash + git CLI)。Windows 真机验证充分;Linux 已在 WSL2(Ubuntu 26.04,bash 5.3 + git 2.53)实测全流程(含中文路径、home 降级、会话清理、gc);macOS 侧脚本按 bash 3.2 兼容编写,尚未真机实测。
62
65
  - 工作区内嵌套的其他 git 仓库(子目录自带 `.git`)无法索引:其余部分快照照常(fail-open,页面会提示跳过了哪些路径),但其内容不参与回退。
@@ -126,6 +129,10 @@ peerDependencies 按 minor 版本线开窗,与 `dsh.compatibility.dshReleases`
126
129
  2. 点击 → 确认面板展示将变更的文件清单(修改 / 恢复 / 删除),并可选撤回范围:「回退文件与对话」(默认)或「仅撤回对话」(文件保持当前状态)。
127
130
  3. 点「确认回退」(或「确认撤回对话」)→ 文件恢复到该消息发送前的状态(仅撤回对话时文件不动);视图切到新会话(该消息及之后的对话移除),原会话归档、随时可找回。
128
131
 
132
+ 只想改文字重发,点同一行的「✎ 编辑」:该消息位置出现同款编辑卡并预填原文,改完按 Enter(或点发送)——新建会话并把改后的消息发出(项目文件不动,附件一并带过去);`Esc` 取消。第一条用户消息没有可回退的对话切点,编辑会被明确拒绝。
133
+
134
+ 撤回或编辑重发之后,最后一轮回答的操作区出现 `‹ X/N ›`:左右切换即在该段对话的各版本间跳转(当前版本自动归档、目标版本自动恢复,侧栏只留一个)。
135
+
129
136
  ## 配置项
130
137
 
131
138
  全部配置可在「**设置 → 插件配置 → 撤回插件**」卡片可视化修改(保存即热生效,无需重启),也可在 profile 的 `cordis.patch.yml` 按 `id: recall` 重述 insert 行改写;env 变量仅覆盖 gc 两项且优先级最高(设了 env 的字段在卡片里锁定)。
@@ -143,6 +150,7 @@ peerDependencies 按 minor 版本线开窗,与 `dsh.compatibility.dshReleases`
143
150
  | `refillDraft` | true | 撤回后把被撤回的消息(文本与附件)回填到输入框 |
144
151
  | `snapshotEnabled` | true | 快照总开关(关闭只冻结新建,已有快照仍可撤回) |
145
152
  | `archiveOriginal` | true | 撤回后归档原会话(关闭后原会话保留在会话列表中) |
153
+ | `editSubmitsImmediately` | true | 「编辑并重发」提交后自动发出改后的消息;关闭则只回填输入框,由你按回车发送 |
146
154
  | `locale` | auto | 界面语言:`auto` 跟随系统(`navigator.language` 以 `zh` 开头用中文,否则英文)、`zh` / `en` 显式锁定。见下方「界面语言」 |
147
155
 
148
156
  设置卡片另提供「恢复默认」(一键重置全部字段)与「最近错误」查看/清空。