dsh-retrace 0.4.8 → 0.4.10
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/README.md +68 -9
- package/README.zh.md +56 -6
- package/lib/dynamic-host.js +40 -8
- package/lib/host-core.js +40 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,44 @@ Desktop app (both share the same Web frontend).
|
|
|
18
18
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
|
+
> ## ⚡ Install in one minute
|
|
22
|
+
>
|
|
23
|
+
> **Option A — one command (recommended):** with DeepSeek Harness's `dsh` CLI:
|
|
24
|
+
>
|
|
25
|
+
> ```sh
|
|
26
|
+
> dsh plugin --profile desktop add dsh-retrace # DSH Desktop
|
|
27
|
+
> # or: dsh plugin --profile web add dsh-retrace # standalone Web
|
|
28
|
+
> ```
|
|
29
|
+
>
|
|
30
|
+
> **Option B — downloaded this repo as ZIP (or handing this link to an AI):**
|
|
31
|
+
> unpack it and run `dsh plugin --profile desktop add <folder>` — or install
|
|
32
|
+
> straight from GitHub, no unpacking:
|
|
33
|
+
>
|
|
34
|
+
> ```sh
|
|
35
|
+
> dsh plugin --profile desktop add github:yamingmou/dsh-retrace
|
|
36
|
+
> ```
|
|
37
|
+
>
|
|
38
|
+
> Follow [📦 Installation](#-installation) → *Manual install* for the exact
|
|
39
|
+
> file-edit steps.
|
|
40
|
+
>
|
|
41
|
+
> **Option C — no command line at all:** install the community plugin market
|
|
42
|
+
> once, then install dsh-retrace from its UI:
|
|
43
|
+
>
|
|
44
|
+
> ```sh
|
|
45
|
+
> dsh plugin --profile desktop add dshmarket # one time
|
|
46
|
+
> ```
|
|
47
|
+
>
|
|
48
|
+
> Restart, then **Settings → Plugin Market** → search **dsh-retrace** →
|
|
49
|
+
> **Install** (one click). The market lists plugins from the curated
|
|
50
|
+
> [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
51
|
+
> registry; dsh-retrace is listed there.
|
|
52
|
+
>
|
|
53
|
+
> > ⚠️ **Restart required after install.** Quit and reopen **DSH Desktop**
|
|
54
|
+
> > (or restart the `dsh` process for a standalone Web deployment) — a running
|
|
55
|
+
> > app keeps the previous bundle in memory and will not hot-reload it.
|
|
56
|
+
>
|
|
57
|
+
> Hover any assistant reply → **↩ / ↻**; any user message → **✎** — that's it.
|
|
58
|
+
|
|
21
59
|
DeepSeek Harness stores every conversation as an **append-only event log**, so there is
|
|
22
60
|
no built-in "undo". `dsh-retrace` brings back the three moves every chat deserves —
|
|
23
61
|
**撤回 (recall)**, **编辑重发 (edit-and-resend)**, **重新生成 (regenerate)** — and then
|
|
@@ -68,6 +106,9 @@ in the conversation — all **inside the same session**, no session-switching.
|
|
|
68
106
|
|
|
69
107
|
## 🚀 Quick start
|
|
70
108
|
|
|
109
|
+
The one-line install is at the top of this page (**⚡ Install in one minute**).
|
|
110
|
+
This section covers the same ground with more detail.
|
|
111
|
+
|
|
71
112
|
> Requires DeepSeek Harness with the `dsh` CLI. Installs the plugin as a profile
|
|
72
113
|
> bundle and automatically rebuilds the Web client:
|
|
73
114
|
|
|
@@ -83,8 +124,7 @@ dsh plugin --profile web add dsh-retrace
|
|
|
83
124
|
> in memory, so **quit and reopen DSH Desktop** (or restart the `dsh` process for
|
|
84
125
|
> a standalone Web deployment) before the plugin activates.
|
|
85
126
|
|
|
86
|
-
|
|
87
|
-
and use ↩ / ✎ / ↻.
|
|
127
|
+
After the restart, hover any assistant reply, or any user message, and use ↩ / ✎ / ↻.
|
|
88
128
|
|
|
89
129
|
---
|
|
90
130
|
|
|
@@ -105,14 +145,17 @@ dsh plugin --profile <name> add dsh-retrace
|
|
|
105
145
|
> deployment) to load the plugin. To uninstall:
|
|
106
146
|
> `dsh plugin --profile <name> remove dsh-retrace` (then restart again).
|
|
107
147
|
|
|
108
|
-
It also shows up in [dsh-market](https://github.com/dsh-market/dsh-market) for
|
|
109
|
-
one-click install from inside Settings (same restart applies).
|
|
110
|
-
|
|
111
148
|
### 2. Manual install (no `dsh` CLI)
|
|
112
149
|
|
|
113
150
|
The same result with plain file edits and `pnpm` — exactly the steps
|
|
114
151
|
`dsh plugin add` performs for you:
|
|
115
152
|
|
|
153
|
+
> **Downloaded this repo as a ZIP?** Unpack it somewhere stable (e.g.
|
|
154
|
+
> `~/plugins/dsh-retrace`), then either
|
|
155
|
+
> `dsh plugin --profile desktop add ~/plugins/dsh-retrace`, or follow the
|
|
156
|
+
> steps below with the dependency line pointing at the folder:
|
|
157
|
+
> `"dsh-retrace": "file:~/plugins/dsh-retrace"`.
|
|
158
|
+
|
|
116
159
|
1. Open the profile manifest (defaults: `~/.dsh/profiles/desktop` on DSH
|
|
117
160
|
Desktop, `~/.dsh/profiles/web` for standalone Web) and add **both** the
|
|
118
161
|
dependency and the bundle-layer entry:
|
|
@@ -148,6 +191,9 @@ The same result with plain file edits and `pnpm` — exactly the steps
|
|
|
148
191
|
For local development, point the dependency at a checkout instead of the
|
|
149
192
|
registry: `"dsh-retrace": "file:/path/to/dsh-retrace"` — or let
|
|
150
193
|
`dsh` do it: `dsh plugin --profile <name> add /path/to/dsh-retrace`.
|
|
194
|
+
For the latest GitHub commit without a release: use
|
|
195
|
+
`"dsh-retrace": "github:yamingmou/dsh-retrace"` (standard pnpm git
|
|
196
|
+
dependency syntax) in the same `dependencies` block, then `pnpm install`.
|
|
151
197
|
|
|
152
198
|
### 3. npm package + composition (classic)
|
|
153
199
|
|
|
@@ -311,10 +357,23 @@ and the [issue tracker](https://github.com/yamingmou/dsh-retrace/issues).
|
|
|
311
357
|
|
|
312
358
|
## 📚 Ecosystem
|
|
313
359
|
|
|
314
|
-
Listed on the [dsh-plugin topic](https://github.com/topics/dsh-plugin)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
360
|
+
Listed on the [dsh-plugin topic](https://github.com/topics/dsh-plugin).
|
|
361
|
+
|
|
362
|
+
> **Install straight from GitHub** (no npm registry needed — handy when you
|
|
363
|
+
> hand this repo's link to an AI or want the latest commit):
|
|
364
|
+
>
|
|
365
|
+
> ```sh
|
|
366
|
+
> dsh plugin --profile desktop add github:yamingmou/dsh-retrace
|
|
367
|
+
> # or with pnpm directly into a profile:
|
|
368
|
+
> cd ~/.dsh/profiles/desktop && pnpm add github:yamingmou/dsh-retrace
|
|
369
|
+
> ```
|
|
370
|
+
>
|
|
371
|
+
> Then restart DSH Desktop as usual. The `dsh-log-contract` dependency is
|
|
372
|
+
> pulled in automatically.
|
|
373
|
+
|
|
374
|
+
A curated overview of the DeepSeek Harness plugin ecosystem lives at
|
|
375
|
+
[awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
376
|
+
(third-party listing — verify availability before relying on it).
|
|
318
377
|
|
|
319
378
|
---
|
|
320
379
|
|
package/README.zh.md
CHANGED
|
@@ -17,6 +17,41 @@
|
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
|
+
> ## ⚡ 一分钟安装
|
|
21
|
+
>
|
|
22
|
+
> **方式 A —— 一条命令(推荐):** 需要带 `dsh` CLI 的 DeepSeek Harness:
|
|
23
|
+
>
|
|
24
|
+
> ```sh
|
|
25
|
+
> dsh plugin --profile desktop add dsh-retrace # DSH 桌面版
|
|
26
|
+
> # 或:dsh plugin --profile web add dsh-retrace # 独立 Web 部署
|
|
27
|
+
> ```
|
|
28
|
+
>
|
|
29
|
+
> **方式 B —— 从 GitHub 下载了 ZIP(或把本仓库链接直接丢给 AI)?** 解压到固定位置
|
|
30
|
+
> (如 `~/plugins/dsh-retrace`)后执行 `dsh plugin --profile desktop add ~/plugins/dsh-retrace`;
|
|
31
|
+
> 或**直接从 GitHub 安装,无需解压**:
|
|
32
|
+
>
|
|
33
|
+
> ```sh
|
|
34
|
+
> dsh plugin --profile desktop add github:yamingmou/dsh-retrace
|
|
35
|
+
> ```
|
|
36
|
+
>
|
|
37
|
+
> 更详细的文件级步骤见 [📦 安装](#-安装) → *手动安装*。
|
|
38
|
+
>
|
|
39
|
+
> **方式 C —— 完全没有命令行?** 先装一次社区插件市场,再从市场 UI 一键安装
|
|
40
|
+
> dsh-retrace:
|
|
41
|
+
>
|
|
42
|
+
> ```sh
|
|
43
|
+
> dsh plugin --profile desktop add dshmarket # 只需一次
|
|
44
|
+
> ```
|
|
45
|
+
>
|
|
46
|
+
> 重启后进入 **设置 → Plugin Market** → 搜索 **dsh-retrace** → **安装**(一键)。
|
|
47
|
+
> 市场收录自 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
48
|
+
> 精选目录,dsh-retrace 已在其中。
|
|
49
|
+
>
|
|
50
|
+
> > ⚠️ **安装后必须重启。** 运行中的应用不会热加载 bundle,请**退出并重新打开
|
|
51
|
+
> > DSH Desktop**(独立 Web 部署则重启 `dsh` 进程)后插件才会生效。
|
|
52
|
+
>
|
|
53
|
+
> 重启后悬停任意助手回复 → **↩ / ↻**;任意用户消息 → **✎**,即可使用。
|
|
54
|
+
|
|
20
55
|
DeepSeek Harness 的对话是「只追加(append-only)」的事件日志,本身没有撤销能力。
|
|
21
56
|
`dsh-retrace` 先为对话补上聊天本该有的三个操作 —— **撤回**、**编辑重发**、
|
|
22
57
|
**重新生成**;再往前一步:撤回只回退了**上下文**,而智能体已经改过的**产物文件**
|
|
@@ -62,6 +97,8 @@ DeepSeek Harness 的对话是「只追加(append-only)」的事件日志,
|
|
|
62
97
|
|
|
63
98
|
## 🚀 快速开始
|
|
64
99
|
|
|
100
|
+
页首的 **⚡ 一分钟安装** 是最短路径;本节给出同样内容的更详细说明。
|
|
101
|
+
|
|
65
102
|
> 需要带 `dsh` CLI 的 DeepSeek Harness。以 profile bundle 方式安装,并自动重建 Web 客户端:
|
|
66
103
|
|
|
67
104
|
```sh
|
|
@@ -94,13 +131,14 @@ dsh plugin --profile <name> add dsh-retrace
|
|
|
94
131
|
> `dsh` 进程)来加载插件。卸载:`dsh plugin --profile <name> remove
|
|
95
132
|
> dsh-retrace`(卸载后同样需要重启)。
|
|
96
133
|
|
|
97
|
-
同时可在 [dsh-market](https://github.com/dsh-market/dsh-market) 里一键安装
|
|
98
|
-
(安装后同样需要重启)。
|
|
99
|
-
|
|
100
134
|
### 2. 手动安装(不依赖 `dsh` CLI)
|
|
101
135
|
|
|
102
136
|
用纯文件编辑 + `pnpm` 装进同一个 profile —— 也就是 `dsh plugin add` 帮你做的那些步骤:
|
|
103
137
|
|
|
138
|
+
> **从 GitHub 下载了 ZIP?** 解压到固定位置(如 `~/plugins/dsh-retrace`),
|
|
139
|
+
> 然后执行 `dsh plugin --profile desktop add ~/plugins/dsh-retrace`;或按下面步骤,
|
|
140
|
+
> 把依赖行指向该文件夹:`"dsh-retrace": "file:~/plugins/dsh-retrace"`。
|
|
141
|
+
|
|
104
142
|
1. 打开 profile 清单(默认位置:DSH Desktop 为 `~/.dsh/profiles/desktop`,
|
|
105
143
|
独立 Web 为 `~/.dsh/profiles/web`),同时加入依赖**和** bundle 层条目:
|
|
106
144
|
|
|
@@ -276,9 +314,21 @@ npm pack --dry-run # 校验发布文件清单
|
|
|
276
314
|
|
|
277
315
|
## 📚 生态
|
|
278
316
|
|
|
279
|
-
收录于 [dsh-plugin topic](https://github.com/topics/dsh-plugin)
|
|
280
|
-
|
|
281
|
-
|
|
317
|
+
收录于 [dsh-plugin topic](https://github.com/topics/dsh-plugin)。
|
|
318
|
+
|
|
319
|
+
> **直接从 GitHub 安装**(无需 npm registry —— 适合把本仓库链接丢给 AI,或想装最新提交):
|
|
320
|
+
>
|
|
321
|
+
> ```sh
|
|
322
|
+
> dsh plugin --profile desktop add github:yamingmou/dsh-retrace
|
|
323
|
+
> # 或直接用 pnpm 装进 profile:
|
|
324
|
+
> cd ~/.dsh/profiles/desktop && pnpm add github:yamingmou/dsh-retrace
|
|
325
|
+
> ```
|
|
326
|
+
>
|
|
327
|
+
> 然后照常重启 DSH Desktop。`dsh-log-contract` 依赖会自动带上。
|
|
328
|
+
|
|
329
|
+
DeepSeek Harness 插件生态的精选总览见
|
|
330
|
+
[awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
331
|
+
(第三方收录,使用前请自行确认可用性)。
|
|
282
332
|
|
|
283
333
|
---
|
|
284
334
|
|
package/lib/dynamic-host.js
CHANGED
|
@@ -121,13 +121,19 @@ return {
|
|
|
121
121
|
content: [],
|
|
122
122
|
source: { kind: 'model', provider: model.provider, model: model.model },
|
|
123
123
|
}
|
|
124
|
-
// R2
|
|
125
|
-
//
|
|
126
|
-
// 无打开 step
|
|
124
|
+
// R2 根治(2026-08-30 事故闭环 v2):
|
|
125
|
+
// - 有打开的 step(回合中编辑):marker 携带该 step 的 turn/step → token-meter 配对通过;
|
|
126
|
+
// - 无打开 step(轮次间编辑,常态):**自动开一个临时 step 包裹 marker**
|
|
127
|
+
// (step/start → marker → step/end,turn 用 nextTurn,step 恒 1)。
|
|
128
|
+
// 官方 token-meter 要求 assistant/message 必须有打开的 step(lib/index.js:590,
|
|
129
|
+
// stepStart===void 0 即抛)——turn-null 或伪造 turn/step 都过不了;
|
|
130
|
+
// 临时 step 是唯一能让轮次间 marker 合法化的形态(foldSurface/token-meter/客户端
|
|
131
|
+
// Location boundary 三层验证全过)。不再产生 turn-null marker → 不再刷屏。
|
|
127
132
|
const openStep = findOpenStep(session)
|
|
133
|
+
const nextTurn = nextTurnOf(session)
|
|
128
134
|
const data = {
|
|
129
|
-
turn: openStep ? openStep.turn :
|
|
130
|
-
step: openStep ? openStep.step :
|
|
135
|
+
turn: openStep ? openStep.turn : nextTurn,
|
|
136
|
+
step: openStep ? openStep.step : 1,
|
|
131
137
|
message: marker,
|
|
132
138
|
editor: {
|
|
133
139
|
targetSeq,
|
|
@@ -139,11 +145,20 @@ return {
|
|
|
139
145
|
if (typeof validate === 'function') {
|
|
140
146
|
const result = await validate(session, { type: 'assistant/message', data, surfaceOp, sourceEventSeqs })
|
|
141
147
|
if (result && result.t1Ok === false) {
|
|
142
|
-
//
|
|
148
|
+
// 理论上临时 step 后 T1 恒通过;残留 fallback 标注(防御)。
|
|
143
149
|
data.editor.markerT1Broken = true
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
|
-
|
|
152
|
+
// 轮次间编辑:开临时 step 包裹(marker 前后成对,token-meter 需要打开的 step)
|
|
153
|
+
const openedStep = openStep === null
|
|
154
|
+
if (openedStep) {
|
|
155
|
+
session.append('step/start', { turn: nextTurn, step: 1 })
|
|
156
|
+
}
|
|
157
|
+
const markerEvent = session.append('assistant/message', data, { surfaceOp, sourceEventSeqs })
|
|
158
|
+
if (openedStep) {
|
|
159
|
+
session.append('step/end', { turn: nextTurn, step: 1 })
|
|
160
|
+
}
|
|
161
|
+
return markerEvent
|
|
147
162
|
}
|
|
148
163
|
|
|
149
164
|
/**
|
|
@@ -164,6 +179,20 @@ return {
|
|
|
164
179
|
return open
|
|
165
180
|
}
|
|
166
181
|
|
|
182
|
+
/**
|
|
183
|
+
* 下一个 turn 号:scan 最大 turn(turn/start、step/start、assistant/message、user/message
|
|
184
|
+
* 的 data.turn 中取最大)+1。无任何 turn 时从 1 起。
|
|
185
|
+
*/
|
|
186
|
+
function nextTurnOf(session) {
|
|
187
|
+
const events = Array.isArray(session?.events) ? session.events : []
|
|
188
|
+
let max = 0
|
|
189
|
+
for (const event of events) {
|
|
190
|
+
const t = event?.data?.turn
|
|
191
|
+
if (typeof t === 'number' && Number.isSafeInteger(t) && t > max) max = t
|
|
192
|
+
}
|
|
193
|
+
return max + 1
|
|
194
|
+
}
|
|
195
|
+
|
|
167
196
|
function createEditorApi(ctx, sessions, agents, log = () => {}, hooks = {}) {
|
|
168
197
|
/** One in-flight op per session; later ops wait for the earlier one. */
|
|
169
198
|
const locks = new Map()
|
|
@@ -204,7 +233,10 @@ return {
|
|
|
204
233
|
if (!agent || typeof agent.status !== 'string' || agent.status !== 'running') return
|
|
205
234
|
if (typeof agent.cancel === 'function' && typeof agent.whenIdle === 'function') {
|
|
206
235
|
try {
|
|
207
|
-
|
|
236
|
+
// 官方 AgentCancelCause 类型只有 user/parent/hook/disposed 四种
|
|
237
|
+
// (dsh-commands typert.host.js:166)——用 { kind: 'user' } 与 UI 停止按钮同义,
|
|
238
|
+
// 中断的 turn/end 会按官方语义记录 reason。
|
|
239
|
+
agent.cancel({ kind: 'user' })
|
|
208
240
|
await agent.whenIdle()
|
|
209
241
|
return
|
|
210
242
|
} catch (error) {
|
package/lib/host-core.js
CHANGED
|
@@ -112,13 +112,19 @@ export async function appendEditorMarker(session, span, op, targetSeq, originalT
|
|
|
112
112
|
content: [],
|
|
113
113
|
source: { kind: 'model', provider: model.provider, model: model.model },
|
|
114
114
|
}
|
|
115
|
-
// R2
|
|
116
|
-
//
|
|
117
|
-
// 无打开 step
|
|
115
|
+
// R2 根治(2026-08-30 事故闭环 v2):
|
|
116
|
+
// - 有打开的 step(回合中编辑):marker 携带该 step 的 turn/step → token-meter 配对通过;
|
|
117
|
+
// - 无打开 step(轮次间编辑,常态):**自动开一个临时 step 包裹 marker**
|
|
118
|
+
// (step/start → marker → step/end,turn 用 nextTurn,step 恒 1)。
|
|
119
|
+
// 官方 token-meter 要求 assistant/message 必须有打开的 step(lib/index.js:590,
|
|
120
|
+
// stepStart===void 0 即抛)——turn-null 或伪造 turn/step 都过不了;
|
|
121
|
+
// 临时 step 是唯一能让轮次间 marker 合法化的形态(foldSurface/token-meter/客户端
|
|
122
|
+
// Location boundary 三层验证全过)。不再产生 turn-null marker → 不再刷屏。
|
|
118
123
|
const openStep = findOpenStep(session)
|
|
124
|
+
const nextTurn = nextTurnOf(session)
|
|
119
125
|
const data = {
|
|
120
|
-
turn: openStep ? openStep.turn :
|
|
121
|
-
step: openStep ? openStep.step :
|
|
126
|
+
turn: openStep ? openStep.turn : nextTurn,
|
|
127
|
+
step: openStep ? openStep.step : 1,
|
|
122
128
|
message: marker,
|
|
123
129
|
editor: {
|
|
124
130
|
targetSeq,
|
|
@@ -130,11 +136,20 @@ export async function appendEditorMarker(session, span, op, targetSeq, originalT
|
|
|
130
136
|
if (typeof validate === 'function') {
|
|
131
137
|
const result = await validate(session, { type: 'assistant/message', data, surfaceOp, sourceEventSeqs })
|
|
132
138
|
if (result && result.t1Ok === false) {
|
|
133
|
-
//
|
|
139
|
+
// 理论上临时 step 后 T1 恒通过;残留 fallback 标注(防御)。
|
|
134
140
|
data.editor.markerT1Broken = true
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
|
-
|
|
143
|
+
// 轮次间编辑:开临时 step 包裹(marker 前后成对,token-meter 需要打开的 step)
|
|
144
|
+
const openedStep = openStep === null
|
|
145
|
+
if (openedStep) {
|
|
146
|
+
session.append('step/start', { turn: nextTurn, step: 1 })
|
|
147
|
+
}
|
|
148
|
+
const markerEvent = session.append('assistant/message', data, { surfaceOp, sourceEventSeqs })
|
|
149
|
+
if (openedStep) {
|
|
150
|
+
session.append('step/end', { turn: nextTurn, step: 1 })
|
|
151
|
+
}
|
|
152
|
+
return markerEvent
|
|
138
153
|
}
|
|
139
154
|
|
|
140
155
|
/**
|
|
@@ -155,6 +170,20 @@ export function findOpenStep(session) {
|
|
|
155
170
|
return open
|
|
156
171
|
}
|
|
157
172
|
|
|
173
|
+
/**
|
|
174
|
+
* 下一个 turn 号:scan 最大 turn(turn/start、step/start、assistant/message、user/message
|
|
175
|
+
* 的 data.turn 中取最大)+1。无任何 turn 时从 1 起。
|
|
176
|
+
*/
|
|
177
|
+
export function nextTurnOf(session) {
|
|
178
|
+
const events = Array.isArray(session?.events) ? session.events : []
|
|
179
|
+
let max = 0
|
|
180
|
+
for (const event of events) {
|
|
181
|
+
const t = event?.data?.turn
|
|
182
|
+
if (typeof t === 'number' && Number.isSafeInteger(t) && t > max) max = t
|
|
183
|
+
}
|
|
184
|
+
return max + 1
|
|
185
|
+
}
|
|
186
|
+
|
|
158
187
|
export function createEditorApi(ctx, sessions, agents, log = () => {}, hooks = {}) {
|
|
159
188
|
/** One in-flight op per session; later ops wait for the earlier one. */
|
|
160
189
|
const locks = new Map()
|
|
@@ -195,7 +224,10 @@ export function createEditorApi(ctx, sessions, agents, log = () => {}, hooks = {
|
|
|
195
224
|
if (!agent || typeof agent.status !== 'string' || agent.status !== 'running') return
|
|
196
225
|
if (typeof agent.cancel === 'function' && typeof agent.whenIdle === 'function') {
|
|
197
226
|
try {
|
|
198
|
-
|
|
227
|
+
// 官方 AgentCancelCause 类型只有 user/parent/hook/disposed 四种
|
|
228
|
+
// (dsh-commands typert.host.js:166)——用 { kind: 'user' } 与 UI 停止按钮同义,
|
|
229
|
+
// 中断的 turn/end 会按官方语义记录 reason。
|
|
230
|
+
agent.cancel({ kind: 'user' })
|
|
199
231
|
await agent.whenIdle()
|
|
200
232
|
return
|
|
201
233
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-retrace",
|
|
3
3
|
"description": "Retrace · 回溯 — Recall, edit-and-resend, regenerate, and conversation/artifact versioning (timeline, rollback, fork map) for DeepSeek Harness — Web and Desktop",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|