dsh-recall-plugin 2.3.23 → 2.3.24
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 +6 -0
- package/README.en.md +4 -3
- package/README.md +4 -3
- package/lib/client.js +71 -11
- package/lib/routes-core.js +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [2.3.24] - 2026-09-18
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
|
|
9
|
+
- **「仅撤回对话」模式(撤回范围二选一)**:对生成结果不满意、但文件改动恰是想要(或已人工修整)时,整段回退会把文件一并覆盖。确认面板新增撤回范围 radio 组(原生 input、键盘可达;默认「回退文件与对话」与现状逐项一致;首条用户消息因对话无从回退不渲染)——选「仅撤回对话」后文件清单降级为参考语义、安全快照预告隐藏(不打安全快照:零文件改动即无不可逆操作缺口),按钮与 executing/done 文案随模式分叉。实现=`ExecuteArgs` 增 `scope?: 'both' | 'session-only'`(缺省/非法值回落 both:老 Client 与直调 API 行为不漂移),Host execute 开头分叉——session-only 不进串行队列、零 git 写操作(保留 NO_SNAPSHOT/AGENT_BUSY 两道护栏,跳过 STALE 校验/安全快照/rollback/rescue 全链),直接 `resolveCutSeq` + `resolveStaleQueueItemIds` 返回 `count: 0`;fork/归档/子会话导航/排队消息清理/lineage/草稿回填链与模式无关,原样复用。新增 `tests/unit/routes-scope.test.js` 5 例(session-only 零 git 调用与透传、agentBusy 拦截、NO_SNAPSHOT、非法 scope 回落 both、不传 scope 现状回归钉)。
|
|
10
|
+
|
|
5
11
|
## [2.3.23] - 2026-09-18
|
|
6
12
|
|
|
7
13
|
### 修复
|
package/README.en.md
CHANGED
|
@@ -45,7 +45,8 @@ Files and conversation roll back together: the workspace is first snapshotted in
|
|
|
45
45
|
|
|
46
46
|
Abilities with a version in parentheses require that version or later; the rest have no special version requirement.
|
|
47
47
|
|
|
48
|
-
- **Files + conversation, rolled back together**: recalling isn't just about chat history — files the agent modified go back to their original state too; immune to your project's `.gitattributes` conversion, with byte-level fidelity for line endings and binary content (2.1.1+).
|
|
48
|
+
- **Files + conversation, rolled back together**: recalling isn't just about chat history — files the agent modified go back to their original state too; immune to your project's `.gitattributes` conversion, with byte-level fidelity for line endings and binary content (2.1.1+).
|
|
49
|
+
- **Just want a fresh conversation? Leave the files alone** (2.3.24+): the confirmation panel offers a recall-scope choice — the default "Roll back files & conversation" behaves as before; picking "Conversation only" keeps your project files byte-for-byte untouched (no safety snapshot either) and rewinds only the conversation, ideal when you dislike the reply but want to keep the file changes.
|
|
49
50
|
- **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
51
|
- **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 (up to 500 snapshots per workspace by default).
|
|
51
52
|
- **Resend right after a recall** (2.3.15+): a recall places the message's text and attachments back into the input box — images and files return as well, so you can edit and send again without picking the attachments over.
|
|
@@ -93,8 +94,8 @@ pm2 restart <your-dsh-name> # if managed by pm2
|
|
|
93
94
|
A full recall of one user message sent after the plugin was enabled goes like this.
|
|
94
95
|
|
|
95
96
|
1. Hover over any user message sent **after the plugin was enabled** (including steering messages inserted while the agent is running) — "↶ Recall" appears to the left of the copy button.
|
|
96
|
-
2. Click it → the confirmation panel shows the list of files that will change (modified / restored / deleted).
|
|
97
|
-
3. Click "Confirm rollback" → files are restored to their state before that message was sent; 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.
|
|
97
|
+
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).
|
|
98
|
+
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.
|
|
98
99
|
|
|
99
100
|
## Configuration
|
|
100
101
|
|
package/README.md
CHANGED
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
|
|
44
44
|
带括号版本号的能力需要该版本或更高;其余无特殊版本要求。
|
|
45
45
|
|
|
46
|
-
- **文件 + 对话,整段回退**:撤回的不只是聊天记录,agent 改过的文件也一并回到原样;不受项目 `.gitattributes` 转换影响,换行与二进制内容字节级保真(2.1.1+)。
|
|
46
|
+
- **文件 + 对话,整段回退**:撤回的不只是聊天记录,agent 改过的文件也一并回到原样;不受项目 `.gitattributes` 转换影响,换行与二进制内容字节级保真(2.1.1+)。
|
|
47
|
+
- **只想重来对话?文件可以不动**(2.3.24+):确认面板里可二选一撤回范围——默认「回退文件与对话」同现状;选「仅撤回对话」则项目文件一个字节都不动(也不打安全快照),只有对话回退,适合对生成结果不满意、但文件改动恰是想要的场景。
|
|
47
48
|
- **不碰你项目自己的 git,目录保持干净**:快照存在独立的影子 git 仓库里,分支、暂存区、未提交改动统统不受影响;存储默认落在 `$DSH_HOME` 下,与会话的沙箱权限无关(workspace-write / read-only 照常工作),仅当 home 不可写才降级到项目内 `.dsh-recall-snapshots`。
|
|
48
49
|
- **先看清单再动手,可反复后悔**:撤回前展示将变更的文件清单(修改 / 恢复 / 删除),确认后才执行;撤回后还能再撤到更早,被覆盖的文件一直找得回来(默认每工作区保留 500 条)。
|
|
49
50
|
- **撤回完就能重发**(2.3.15+):撤回会把这条消息的文本与附件一起放回输入框——图片、文件原样回来,改完直接发送,不必重新挑一遍附件。
|
|
@@ -96,8 +97,8 @@ dsh plugin --profile web remove dsh-recall-plugin
|
|
|
96
97
|
## 使用
|
|
97
98
|
|
|
98
99
|
1. 鼠标悬停任意**插件启用后发送**的用户消息(含 agent 运行中插入的转向指令消息),复制按钮左侧出现「↶ 撤回」。
|
|
99
|
-
2. 点击 → 确认面板展示将变更的文件清单(修改 / 恢复 /
|
|
100
|
-
3.
|
|
100
|
+
2. 点击 → 确认面板展示将变更的文件清单(修改 / 恢复 / 删除),并可选撤回范围:「回退文件与对话」(默认)或「仅撤回对话」(文件保持当前状态)。
|
|
101
|
+
3. 点「确认回退」(或「确认撤回对话」)→ 文件恢复到该消息发送前的状态(仅撤回对话时文件不动);视图切到新会话(该消息及之后的对话移除),原会话归档、随时可找回。
|
|
101
102
|
|
|
102
103
|
## 配置项
|
|
103
104
|
|
package/lib/client.js
CHANGED
|
@@ -36,6 +36,16 @@
|
|
|
36
36
|
// grid-column 对非 grid 祖先(exclude/快照卡片的 flex 布局)自动无效,无害;
|
|
37
37
|
// 在 cfg-grid 内则保证操作区/占满行不被 auto-placement 塞进第一列撑爆列宽。
|
|
38
38
|
".dsh-recall-panel-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;margin-top:2px;grid-column:1/-1}",
|
|
39
|
+
// scope radio 组(撤回范围二选一):原生 input 保留 UA 交互(键盘方向键切换、
|
|
40
|
+
// 空格选中),accent-color 走品牌令牌让选中态随主题翻转;label 整体可点扩大
|
|
41
|
+
// 命中区,字色与 panel-note 同层级(secondary),hover 提亮示意可交互
|
|
42
|
+
".dsh-recall-scope{display:flex;flex-wrap:wrap;gap:4px 16px;padding:2px 0}",
|
|
43
|
+
".dsh-recall-scope-item{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-secondary)}",
|
|
44
|
+
".dsh-recall-scope-item:hover{color:var(--dsw-alias-label-primary)}",
|
|
45
|
+
".dsh-recall-scope-item input{accent-color:var(--dsw-alias-brand-primary);cursor:pointer;margin:0}",
|
|
46
|
+
// 焦点可见性对齐按钮类约定(brand-primary 2px outline):UA 默认环跨引擎不一,
|
|
47
|
+
// 统一收敛到与面板内其他控件同一套焦点语言
|
|
48
|
+
".dsh-recall-scope-item input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}",
|
|
39
49
|
// 按钮体系对齐官方设置卡(dsh-client-ui-settings-plugins PluginCard 底部按钮
|
|
40
50
|
// 组实测产物):次级 = 描边幽灵(discard 逐字配方——l2 描边 + 透明底,hover
|
|
41
51
|
// 升 label-dimmed 描边 + primary 字色);官方 discard/save 同高靠 save 也带
|
|
@@ -463,7 +473,7 @@
|
|
|
463
473
|
React.createElement("path", { d: "M4.5 7.5h5a3 3 0 0 1 0 6H8" })
|
|
464
474
|
);
|
|
465
475
|
}
|
|
466
|
-
function recallPanel(recall, closePanel, executeRecall) {
|
|
476
|
+
function recallPanel(recall, closePanel, executeRecall, scope, onScopeChange) {
|
|
467
477
|
if (recall.stage === "loading") {
|
|
468
478
|
return React.createElement(
|
|
469
479
|
"div",
|
|
@@ -500,30 +510,72 @@
|
|
|
500
510
|
React.createElement("span", { className: "dsh-recall-rel" }, c.rel || "")
|
|
501
511
|
);
|
|
502
512
|
});
|
|
513
|
+
const canRevertChat = typeof recall.cutSeq === "number";
|
|
514
|
+
const sessionOnly = canRevertChat && scope === "session-only";
|
|
503
515
|
if (recall.truncated) {
|
|
504
|
-
rows.push(React.createElement(
|
|
516
|
+
rows.push(React.createElement(
|
|
517
|
+
"div",
|
|
518
|
+
{ className: "dsh-recall-panel-note", key: "truncated" },
|
|
519
|
+
sessionOnly ? "\u2026\u4EC5\u663E\u793A\u524D " + changes.length + " \u6761\uFF0C\u5171 " + total + " \u5904\u5DEE\u5F02" : "\u2026\u4EC5\u663E\u793A\u524D " + changes.length + " \u6761\uFF0C\u5171 " + total + " \u4E2A\u6587\u4EF6\u5C06\u53D8\u66F4"
|
|
520
|
+
));
|
|
505
521
|
}
|
|
506
|
-
const canRevertChat = typeof recall.cutSeq === "number";
|
|
507
522
|
return React.createElement(
|
|
508
523
|
"div",
|
|
509
524
|
{ className: "dsh-recall-panel" },
|
|
510
525
|
React.createElement("div", { className: "dsh-recall-panel-title" }, "\u6574\u6BB5\u56DE\u9000"),
|
|
511
|
-
|
|
526
|
+
sessionOnly ? (
|
|
527
|
+
// session-only:零文件改动即无不可逆操作缺口——安全快照预告随之隐藏
|
|
528
|
+
//(不打安全快照),主说明换成模式语义
|
|
529
|
+
React.createElement(
|
|
530
|
+
"div",
|
|
531
|
+
{ className: "dsh-recall-panel-note" },
|
|
532
|
+
"\u9879\u76EE\u6587\u4EF6\u4FDD\u6301\u5F53\u524D\u72B6\u6001\uFF0C\u4E0D\u4F1A\u88AB\u56DE\u9000\u6216\u5220\u9664\uFF1B\u5BF9\u8BDD\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\u3002"
|
|
533
|
+
)
|
|
534
|
+
) : React.createElement(
|
|
512
535
|
"div",
|
|
513
536
|
{ className: "dsh-recall-panel-note" },
|
|
514
537
|
"\u5C06\u9879\u76EE\u6062\u590D\u5230" + (recall.time ? " " + clockText2(recall.time) + " " : " ") + "\u53D1\u9001\u8BE5\u6D88\u606F\u65F6\u7684\u72B6\u6001\u3002\u5171 " + total + " \u4E2A\u6587\u4EF6\u5C06\u53D8\u66F4" + (summaryText(counts) ? "\uFF08" + summaryText(counts) + "\uFF09" : "") + "\u3002\u6B64\u64CD\u4F5C\u4F1A\u8986\u76D6\u5F53\u524D\u6587\u4EF6\u5185\u5BB9\uFF1B\u56DE\u9000\u524D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u4E00\u4EFD\u5F53\u524D\u72B6\u6001\u7684\u5B89\u5168\u5FEB\u7167\uFF08\u4E0D\u542B\u5728\u4E0B\u65B9\u6E05\u5355\u5185\uFF09\u3002"
|
|
515
538
|
),
|
|
516
|
-
React.createElement(
|
|
539
|
+
sessionOnly ? null : React.createElement(
|
|
517
540
|
"div",
|
|
518
541
|
{ className: "dsh-recall-panel-note" },
|
|
519
542
|
canRevertChat ? "\u5BF9\u8BDD\u5C06\u4E00\u5E76\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\uFF1A\u8BE5\u6D88\u606F\u53CA\u4E4B\u540E\u7684\u5168\u90E8\u5BF9\u8BDD\u4F1A\u4ECE\u5F53\u524D\u89C6\u56FE\u79FB\u9664\uFF0C\u539F\u4F1A\u8BDD\u5F52\u6863\u4FDD\u5B58\uFF08\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF09\u3002" : "\u8BE5\u6D88\u606F\u662F\u672C\u4F1A\u8BDD\u4E2D\u7B2C\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF0C\u65E0\u6CD5\u56DE\u9000\u5BF9\u8BDD\uFF1B\u786E\u8BA4\u540E\u4EC5\u56DE\u9000\u9879\u76EE\u6587\u4EF6\u3002"
|
|
520
543
|
),
|
|
544
|
+
sessionOnly && changes.length > 0 ? (
|
|
545
|
+
// 文件清单保留展示但降级为参考语义:清单照常算(Host preview 链路
|
|
546
|
+
// 不分叉),只是告知用户所选模式不会真的改这些文件
|
|
547
|
+
React.createElement(
|
|
548
|
+
"div",
|
|
549
|
+
{ className: "dsh-recall-panel-note", key: "ref-note" },
|
|
550
|
+
"\u4EE5\u4E0B\u5DEE\u5F02\u4EC5\u4F5C\u53C2\u8003\uFF0C\u6240\u9009\u6A21\u5F0F\u4E0D\u4F1A\u6539\u52A8\u6587\u4EF6\u3002"
|
|
551
|
+
)
|
|
552
|
+
) : null,
|
|
521
553
|
changes.length > 0 ? React.createElement("div", { className: "dsh-recall-list" }, ...rows) : null,
|
|
554
|
+
canRevertChat ? (
|
|
555
|
+
// 模式二选一(原生 radio,键盘方向键可达):临场选择不设全局配置项;
|
|
556
|
+
// 默认 both 与现状一致,面板每次打开都复位到默认起点
|
|
557
|
+
React.createElement(
|
|
558
|
+
"div",
|
|
559
|
+
{ className: "dsh-recall-scope", role: "radiogroup", "aria-label": "\u64A4\u56DE\u8303\u56F4" },
|
|
560
|
+
React.createElement(
|
|
561
|
+
"label",
|
|
562
|
+
{ className: "dsh-recall-scope-item" },
|
|
563
|
+
React.createElement("input", { type: "radio", name: "dsh-recall-scope", checked: scope === "both", onChange: () => onScopeChange("both") }),
|
|
564
|
+
React.createElement("span", { className: "dsh-recall-scope-label" }, "\u56DE\u9000\u6587\u4EF6\u4E0E\u5BF9\u8BDD")
|
|
565
|
+
),
|
|
566
|
+
React.createElement(
|
|
567
|
+
"label",
|
|
568
|
+
{ className: "dsh-recall-scope-item" },
|
|
569
|
+
React.createElement("input", { type: "radio", name: "dsh-recall-scope", checked: scope === "session-only", onChange: () => onScopeChange("session-only") }),
|
|
570
|
+
React.createElement("span", { className: "dsh-recall-scope-label" }, "\u4EC5\u64A4\u56DE\u5BF9\u8BDD")
|
|
571
|
+
)
|
|
572
|
+
)
|
|
573
|
+
) : null,
|
|
522
574
|
React.createElement(
|
|
523
575
|
"div",
|
|
524
576
|
{ className: "dsh-recall-panel-actions" },
|
|
525
577
|
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, "\u53D6\u6D88"),
|
|
526
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-danger", onClick: executeRecall }, "\u786E\u8BA4\u56DE\u9000")
|
|
578
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-danger", onClick: executeRecall }, sessionOnly ? "\u786E\u8BA4\u64A4\u56DE\u5BF9\u8BDD" : "\u786E\u8BA4\u56DE\u9000")
|
|
527
579
|
)
|
|
528
580
|
);
|
|
529
581
|
}
|
|
@@ -531,7 +583,7 @@
|
|
|
531
583
|
return React.createElement(
|
|
532
584
|
"div",
|
|
533
585
|
{ className: "dsh-recall-panel" },
|
|
534
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "\u6B63\u5728\u56DE\u9000\u2026")
|
|
586
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, scope === "session-only" ? "\u6B63\u5728\u64A4\u56DE\u5BF9\u8BDD\u2026" : "\u6B63\u5728\u56DE\u9000\u2026")
|
|
535
587
|
);
|
|
536
588
|
}
|
|
537
589
|
if (recall.stage === "done") {
|
|
@@ -542,7 +594,10 @@
|
|
|
542
594
|
React.createElement(
|
|
543
595
|
"div",
|
|
544
596
|
{ className: "dsh-recall-panel-note" },
|
|
545
|
-
|
|
597
|
+
scope === "session-only" ? (
|
|
598
|
+
// session-only 文案矩阵:文件侧零改动是确定事实,失败时也只描述对话侧
|
|
599
|
+
recall.chatReverted ? "\u5BF9\u8BDD\u5DF2\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\uFF0C\u9879\u76EE\u6587\u4EF6\u4FDD\u6301\u5F53\u524D\u72B6\u6001\u3002\u65B0\u4F1A\u8BDD\u5DF2\u6253\u5F00\uFF0C\u539F\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF08\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF09\u3002" : "\u5BF9\u8BDD\u56DE\u9000\u5931\u8D25\uFF1A" + (recall.chatError || "\u672A\u77E5\u539F\u56E0") + "\u3002\u9879\u76EE\u6587\u4EF6\u672A\u505A\u4EFB\u4F55\u6539\u52A8\u3002"
|
|
600
|
+
) : recall.chatReverted ? "\u9879\u76EE\u6587\u4EF6\u4E0E\u5BF9\u8BDD\u5DF2\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\u3002\u65B0\u4F1A\u8BDD\u5DF2\u6253\u5F00\uFF0C\u539F\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF08\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF09\u3002" : "\u9879\u76EE\u5DF2\u6062\u590D\u5230\u53D1\u9001\u8BE5\u6D88\u606F\u65F6\u7684\u72B6\u6001\u3002" + (recall.chatError ? " \u5BF9\u8BDD\u56DE\u9000\u5931\u8D25\uFF1A" + recall.chatError : "")
|
|
546
601
|
),
|
|
547
602
|
React.createElement(
|
|
548
603
|
"div",
|
|
@@ -677,6 +732,7 @@
|
|
|
677
732
|
const [copied, setCopied] = React.useState(false);
|
|
678
733
|
const [hasSnapshot, setHasSnapshot] = React.useState(false);
|
|
679
734
|
const [recall, setRecall] = React.useState({ stage: "idle" });
|
|
735
|
+
const [scope, setScope] = React.useState("both");
|
|
680
736
|
React.useEffect(() => {
|
|
681
737
|
let alive = true;
|
|
682
738
|
let timer = null;
|
|
@@ -733,6 +789,7 @@
|
|
|
733
789
|
};
|
|
734
790
|
const openPreview = () => {
|
|
735
791
|
if (recall.stage === "loading" || recall.stage === "executing") return;
|
|
792
|
+
setScope("both");
|
|
736
793
|
setRecall({ stage: "loading" });
|
|
737
794
|
api("preview", { messageId, sessionId }).then((res) => {
|
|
738
795
|
if (!res || !res.ok) {
|
|
@@ -759,7 +816,7 @@
|
|
|
759
816
|
const previewTotal = typeof recall.total === "number" ? recall.total : changes.length;
|
|
760
817
|
const attachmentFiles = pluginConfig.refillDraft && attachmentRefs.length > 0 && sessionId ? preloadAttachmentFiles(sessionId, attachmentRefs) : null;
|
|
761
818
|
setRecall({ stage: "executing", changes });
|
|
762
|
-
api("execute", { messageId, sessionId, previewTotal, previewTreeId: recall.treeId || void 0, previewAt: Date.now() }).then(async (res) => {
|
|
819
|
+
api("execute", { messageId, sessionId, previewTotal, previewTreeId: recall.treeId || void 0, scope, previewAt: Date.now() }).then(async (res) => {
|
|
763
820
|
if (!res || !res.ok) {
|
|
764
821
|
if (res && res.code === "STALE") {
|
|
765
822
|
setRecall({ stage: "loading" });
|
|
@@ -821,7 +878,10 @@
|
|
|
821
878
|
setRecall({ stage: "error", message: String(error) });
|
|
822
879
|
});
|
|
823
880
|
};
|
|
824
|
-
const closePanel = () =>
|
|
881
|
+
const closePanel = () => {
|
|
882
|
+
setScope("both");
|
|
883
|
+
setRecall({ stage: "idle" });
|
|
884
|
+
};
|
|
825
885
|
const bubbleChildren = [];
|
|
826
886
|
if (imageBlocks.length && typeof renderMessageImages === "function") {
|
|
827
887
|
const render = renderMessageImages;
|
|
@@ -875,7 +935,7 @@
|
|
|
875
935
|
{ className: "dsh-recall-row", "data-time-hover-root": true },
|
|
876
936
|
bubbleChildren.length > 0 ? React.createElement("div", { className: "dsh-recall-stack", key: "stack" }, ...bubbleChildren) : null,
|
|
877
937
|
React.createElement("div", { className: "dsh-recall-actions", key: "actions" }, ...actions),
|
|
878
|
-
recallPanel(recall, closePanel, executeRecall)
|
|
938
|
+
recallPanel(recall, closePanel, executeRecall, scope, setScope)
|
|
879
939
|
);
|
|
880
940
|
}
|
|
881
941
|
return { UserRecallNode };
|
package/lib/routes-core.js
CHANGED
|
@@ -44,6 +44,15 @@ function createRoutesCore(deps) {
|
|
|
44
44
|
"execute": async (args) => {
|
|
45
45
|
const id = args && args.messageId ? String(args.messageId) : "";
|
|
46
46
|
const sessionId = args && args.sessionId ? String(args.sessionId) : null;
|
|
47
|
+
const scope = args && args.scope === "session-only" ? "session-only" : "both";
|
|
48
|
+
if (scope === "session-only") {
|
|
49
|
+
const snap = state.snapshots.get(id);
|
|
50
|
+
if (!snap) return { ok: false, code: E.RECALL_NO_SNAPSHOT, message: "\u8BE5\u6D88\u606F\u6CA1\u6709\u53EF\u7528\u7684\u9879\u76EE\u5FEB\u7167" };
|
|
51
|
+
if (agentBusy(sessionId, snap.root)) return { ok: false, code: E.RECALL_AGENT_BUSY, message: "Agent \u6B63\u5728\u8FD0\u884C\u4E2D\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u64A4\u56DE" };
|
|
52
|
+
const cutSeq2 = await snaps.resolveCutSeq(sessionId, id);
|
|
53
|
+
const staleQueueItemIds2 = await snaps.resolveStaleQueueItemIds(sessionId, cutSeq2);
|
|
54
|
+
return { ok: true, count: 0, cutSeq: cutSeq2, staleQueueItemIds: staleQueueItemIds2 };
|
|
55
|
+
}
|
|
47
56
|
const result = await enqueue(async () => {
|
|
48
57
|
const snap = state.snapshots.get(id);
|
|
49
58
|
if (!snap) return { ok: false, code: E.RECALL_NO_SNAPSHOT, message: "\u8BE5\u6D88\u606F\u6CA1\u6709\u53EF\u7528\u7684\u9879\u76EE\u5FEB\u7167" };
|
package/package.json
CHANGED