dsh-rewind-plugin 0.15.2 → 0.16.0-alpha.1

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/SECURITY.md CHANGED
@@ -120,8 +120,8 @@ hides the `[target, marker]` span from the rendered transcript.
120
120
  - **Restores name only recorded paths**: the store contains resolved display
121
121
  paths of the session's own write-class tool calls (plus boundary re-checks
122
122
  over that same tracked set) — a restore can never write an arbitrary path.
123
- - **Path resolution rule**: relative paths resolve against the session
124
- workspace cwd, mirroring the fs tools' own rule (`src/session-cwd.ts`).
123
+ - **Path resolution rule**: relative paths resolve against the session's current
124
+ working directory, read from the same service the fs tools resolve through.
125
125
  - **Bounded backups**: `prune` keeps the newest 100 anchor groups per session
126
126
  (`MAX_ANCHOR_GROUPS`), except groups a non-terminal restore journal still
127
127
  references (pinned so the op can be finished), so backup accumulation stays
package/lib/client.js CHANGED
@@ -1359,9 +1359,9 @@ function createRewindBridge(deps) {
1359
1359
  }
1360
1360
 
1361
1361
  // src/client/build-info.ts
1362
- var PLUGIN_VERSION = true ? "0.15.2" : "dev";
1362
+ var PLUGIN_VERSION = true ? "0.16.0-alpha.1" : "dev";
1363
1363
  var PLUGIN_PACKAGE = "dsh-rewind-plugin";
1364
- var BUILD_HASH = true ? "75d20acc" : "dev";
1364
+ var BUILD_HASH = true ? "f36f8be8" : "dev";
1365
1365
 
1366
1366
  // src/client/locales.ts
1367
1367
  var zh = {
@@ -1586,10 +1586,10 @@ function apply(ctx) {
1586
1586
  };
1587
1587
  const restoreImages = async (sessionId, images) => {
1588
1588
  if (images.length === 0) return;
1589
- const ui = uiConversation();
1589
+ const session = sessionOf(sessionId);
1590
1590
  const conversation = ctx.get("conversation");
1591
1591
  const scope = ctx.sessions.scope?.(sessionId);
1592
- if (ui?.imageUrl === void 0 || conversation?.input === void 0 || conversation.createDrafts === void 0 || scope === void 0) {
1592
+ if (session === void 0 || conversation?.input === void 0 || conversation.createDrafts === void 0 || scope === void 0) {
1593
1593
  rewindLog.warn("refill", "image restore unavailable (conversation services not ready)");
1594
1594
  return;
1595
1595
  }
@@ -1601,11 +1601,12 @@ function apply(ctx) {
1601
1601
  continue;
1602
1602
  }
1603
1603
  try {
1604
- const url = await ui.imageUrl(sessionId, ref);
1605
- const blob = await (await fetch(url)).blob();
1606
- files.push(new File([blob], ref.name ?? `image-${index + 1}.${extension}`, { type: ref.mediaType }));
1604
+ const result = await session.readAttachment(ref.attachmentId);
1605
+ if (!result.ok) throw result.error;
1606
+ const bytes = result.value.data;
1607
+ files.push(new File([bytes], ref.name ?? `image-${index + 1}.${extension}`, { type: ref.mediaType }));
1607
1608
  } catch (error) {
1608
- rewindLog.warn("refill", `image fetch threw for ${ref.attachmentId}`, error);
1609
+ rewindLog.warn("refill", `image read threw for ${ref.attachmentId}`, error);
1609
1610
  }
1610
1611
  }
1611
1612
  if (files.length === 0) return;
package/lib/index.js CHANGED
@@ -224,14 +224,6 @@ function planRewind(events, surface, target) {
224
224
  };
225
225
  }
226
226
 
227
- // src/session-cwd.ts
228
- function sessionCwd(cwd) {
229
- return cwd;
230
- }
231
- function execSessionCwd(exec) {
232
- return sessionCwd(exec.agent?.session.header.cwd);
233
- }
234
-
235
227
  // src/snapshot.ts
236
228
  import { createHash } from "node:crypto";
237
229
  import { chmod, copyFile, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -2388,10 +2380,7 @@ function anchorSeqOf(session, cache) {
2388
2380
  }
2389
2381
  async function resolveTarget(fs, path, cwd, signal) {
2390
2382
  try {
2391
- return await fs.resolve(path, {
2392
- ...cwd !== void 0 ? { cwd } : {},
2393
- signal
2394
- });
2383
+ return await fs.resolve(path, { cwd, signal });
2395
2384
  } catch {
2396
2385
  return void 0;
2397
2386
  }
@@ -2400,20 +2389,20 @@ function isNotFoundError(error) {
2400
2389
  const code = error?.code;
2401
2390
  return code === "ENOENT" || code === "FS_NOT_FOUND";
2402
2391
  }
2403
- async function captureBefore(fs, store, exec, pending) {
2392
+ async function captureBefore(fs, workingDirectory, store, exec, pending) {
2404
2393
  if (!TRACKED_TOOLS.has(exec.name)) return;
2405
- const session = exec.agent?.session;
2406
- if (session !== void 0 && isSubagentSession(session)) return;
2394
+ const agent = exec.agent;
2395
+ if (agent === void 0) return;
2396
+ if (isSubagentSession(agent.session)) return;
2407
2397
  const path = mutationPathOf(exec);
2408
2398
  if (path === void 0) return;
2409
- const cwd = execSessionCwd(exec);
2399
+ const cwd = await workingDirectory.ensure(agent, exec.signal);
2410
2400
  const target = await resolveTarget(fs, path, cwd, exec.signal);
2411
2401
  if (target === void 0) return;
2412
2402
  const info = await fs.stat(target, exec.signal).catch((error) => {
2413
2403
  if (isNotFoundError(error)) return void 0;
2414
2404
  throw error;
2415
2405
  });
2416
- if (session === void 0) return;
2417
2406
  if (info !== void 0 && info.type !== "file") return;
2418
2407
  if (info !== void 0 && store.isOverFileCap(info.size)) return;
2419
2408
  const key = `${exec.agent?.id ?? "anon"}:${exec.callId}`;
@@ -2421,7 +2410,7 @@ async function captureBefore(fs, store, exec, pending) {
2421
2410
  pending.set(key, { path: target.displayPath, backup: null });
2422
2411
  return;
2423
2412
  }
2424
- const staged = await store.stageCapture(session.id, key);
2413
+ const staged = await store.stageCapture(agent.session.id, key);
2425
2414
  let backup;
2426
2415
  try {
2427
2416
  await copyFile2(target.displayPath, staged);
@@ -2959,12 +2948,13 @@ function apply(ctx, config) {
2959
2948
  }
2960
2949
  })();
2961
2950
  }, { global: true });
2962
- ctx.inject(["fs"], (scope) => {
2951
+ ctx.inject(["fs", "workingDirectory"], (scope) => {
2963
2952
  const fs = scope.fs;
2953
+ const workingDirectory = scope.workingDirectory;
2964
2954
  fsService = fs;
2965
2955
  scope.on("tools/execute", async (exec, next) => {
2966
2956
  try {
2967
- await captureBefore(fs, store, exec, pending);
2957
+ await captureBefore(fs, workingDirectory, store, exec, pending);
2968
2958
  } catch (error) {
2969
2959
  ctx.logger.warn(`[dsh-rewind] before-capture failed for ${exec.name}: ${error instanceof Error ? error.message : String(error)}`);
2970
2960
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-rewind-plugin",
3
- "version": "0.15.2",
3
+ "version": "0.16.0-alpha.1",
4
4
  "description": "In-window conversation rewind with workspace file restore · 同窗口内对话回退并可还原工作区文件",
5
5
  "icon": "./assets/icon.svg",
6
6
  "keywords": [
@@ -20,7 +20,7 @@
20
20
  "license": "MIT",
21
21
  "engines": {
22
22
  "node": "^22.19.0 || >=24.0.0",
23
- "dsh": ">=0.2.0-rc.1"
23
+ "dsh": ">=0.2.1-alpha.2"
24
24
  },
25
25
  "type": "module",
26
26
  "main": "lib/index.js",
@@ -74,20 +74,21 @@
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@deepseek-ai/cordis": "^4.0.1",
77
- "@deepseek-ai/dsh-client-locale": "^0.2.0-rc.1",
78
- "@deepseek-ai/dsh-client-ui-commands": "^0.2.0-rc.1",
79
- "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.0-rc.1",
80
- "@deepseek-ai/dsh-client-ui-primitives": "^0.2.0-rc.1",
81
- "@deepseek-ai/dsh-client-ui-settings": "^0.2.0-rc.1",
82
- "@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.1",
83
- "@deepseek-ai/dsh-commands": "^0.2.0-rc.1",
84
- "@deepseek-ai/dsh-fs": "^0.2.0-rc.1",
85
- "@deepseek-ai/dsh-home-paths": "^0.2.0-rc.1",
86
- "@deepseek-ai/dsh-llm": "^0.2.0-rc.1",
87
- "@deepseek-ai/dsh-sandbox": "^0.2.0-rc.1",
88
- "@deepseek-ai/dsh-session": "^0.2.0-rc.1",
89
- "@deepseek-ai/dsh-settings": "^0.2.0-rc.1",
90
- "@deepseek-ai/dsh-tools": "^0.2.0-rc.1",
77
+ "@deepseek-ai/dsh-client-locale": "^0.2.1-alpha.2",
78
+ "@deepseek-ai/dsh-client-ui-commands": "^0.2.1-alpha.2",
79
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.1-alpha.2",
80
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.2.1-alpha.2",
81
+ "@deepseek-ai/dsh-client-ui-settings": "^0.2.1-alpha.2",
82
+ "@deepseek-ai/dsh-client-ui-slots": "^0.2.1-alpha.2",
83
+ "@deepseek-ai/dsh-commands": "^0.2.1-alpha.2",
84
+ "@deepseek-ai/dsh-fs": "^0.2.1-alpha.2",
85
+ "@deepseek-ai/dsh-home-paths": "^0.2.1-alpha.2",
86
+ "@deepseek-ai/dsh-llm": "^0.2.1-alpha.2",
87
+ "@deepseek-ai/dsh-sandbox": "^0.2.1-alpha.2",
88
+ "@deepseek-ai/dsh-session": "^0.2.1-alpha.2",
89
+ "@deepseek-ai/dsh-settings": "^0.2.1-alpha.2",
90
+ "@deepseek-ai/dsh-tools": "^0.2.1-alpha.2",
91
+ "@deepseek-ai/dsh-working-directory": "^0.2.1-alpha.2",
91
92
  "@deepseek-ai/schemastery": "^3.18.4"
92
93
  },
93
94
  "peerDependenciesMeta": {
@@ -136,6 +137,9 @@
136
137
  "@deepseek-ai/dsh-tools": {
137
138
  "optional": true
138
139
  },
140
+ "@deepseek-ai/dsh-working-directory": {
141
+ "optional": true
142
+ },
139
143
  "@deepseek-ai/schemastery": {
140
144
  "optional": true
141
145
  }
@@ -143,41 +147,42 @@
143
147
  "devDependencies": {
144
148
  "@deepseek-ai/cordis": "4.0.3",
145
149
  "@deepseek-ai/cosmokit": "1.8.4",
146
- "@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
147
- "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.1",
148
- "@deepseek-ai/dsh-attachment": "0.2.1-alpha.1",
149
- "@deepseek-ai/dsh-client-connection": "0.2.1-alpha.1",
150
- "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.1",
151
- "@deepseek-ai/dsh-client-store": "0.2.1-alpha.1",
152
- "@deepseek-ai/dsh-client-ui-commands": "0.2.1-alpha.1",
153
- "@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.1",
154
- "@deepseek-ai/dsh-client-ui-input-trigger": "0.2.1-alpha.1",
155
- "@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.1-alpha.1",
156
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.1",
157
- "@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.1",
158
- "@deepseek-ai/dsh-client-ui-settings": "0.2.1-alpha.1",
159
- "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.1",
160
- "@deepseek-ai/dsh-command-compact": "0.2.1-alpha.1",
161
- "@deepseek-ai/dsh-commands": "0.2.1-alpha.1",
162
- "@deepseek-ai/dsh-compaction": "0.2.1-alpha.1",
163
- "@deepseek-ai/dsh-compaction-basic": "0.2.1-alpha.1",
164
- "@deepseek-ai/dsh-compaction-image-offload": "0.2.1-alpha.1",
165
- "@deepseek-ai/dsh-fs": "0.2.1-alpha.1",
166
- "@deepseek-ai/dsh-goal": "0.2.1-alpha.1",
167
- "@deepseek-ai/dsh-home-paths": "0.2.1-alpha.1",
168
- "@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
169
- "@deepseek-ai/dsh-plan-mode": "0.2.1-alpha.1",
170
- "@deepseek-ai/dsh-sandbox": "0.2.1-alpha.1",
171
- "@deepseek-ai/dsh-scope": "0.2.1-alpha.1",
172
- "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
173
- "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
174
- "@deepseek-ai/dsh-session-stats": "0.2.1-alpha.1",
175
- "@deepseek-ai/dsh-session-title": "0.2.1-alpha.1",
176
- "@deepseek-ai/dsh-settings": "0.2.1-alpha.1",
177
- "@deepseek-ai/dsh-token-meter": "0.2.1-alpha.1",
178
- "@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
179
- "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1",
180
- "@deepseek-ai/dsh-user-questions": "0.2.1-alpha.1",
150
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
151
+ "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.2",
152
+ "@deepseek-ai/dsh-attachment": "0.2.1-alpha.2",
153
+ "@deepseek-ai/dsh-client-connection": "0.2.1-alpha.2",
154
+ "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.2",
155
+ "@deepseek-ai/dsh-client-store": "0.2.1-alpha.2",
156
+ "@deepseek-ai/dsh-client-ui-commands": "0.2.1-alpha.2",
157
+ "@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.2",
158
+ "@deepseek-ai/dsh-client-ui-input-trigger": "0.2.1-alpha.2",
159
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.1-alpha.2",
160
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.2",
161
+ "@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.2",
162
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.1-alpha.2",
163
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.2",
164
+ "@deepseek-ai/dsh-command-compact": "0.2.1-alpha.2",
165
+ "@deepseek-ai/dsh-commands": "0.2.1-alpha.2",
166
+ "@deepseek-ai/dsh-compaction": "0.2.1-alpha.2",
167
+ "@deepseek-ai/dsh-compaction-basic": "0.2.1-alpha.2",
168
+ "@deepseek-ai/dsh-compaction-image-offload": "0.2.1-alpha.2",
169
+ "@deepseek-ai/dsh-fs": "0.2.1-alpha.2",
170
+ "@deepseek-ai/dsh-goal": "0.2.1-alpha.2",
171
+ "@deepseek-ai/dsh-home-paths": "0.2.1-alpha.2",
172
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
173
+ "@deepseek-ai/dsh-plan-mode": "0.2.1-alpha.2",
174
+ "@deepseek-ai/dsh-sandbox": "0.2.1-alpha.2",
175
+ "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
176
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
177
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
178
+ "@deepseek-ai/dsh-session-stats": "0.2.1-alpha.2",
179
+ "@deepseek-ai/dsh-session-title": "0.2.1-alpha.2",
180
+ "@deepseek-ai/dsh-settings": "0.2.1-alpha.2",
181
+ "@deepseek-ai/dsh-token-meter": "0.2.1-alpha.2",
182
+ "@deepseek-ai/dsh-tools": "0.2.1-alpha.2",
183
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
184
+ "@deepseek-ai/dsh-user-questions": "0.2.1-alpha.2",
185
+ "@deepseek-ai/dsh-working-directory": "0.2.1-alpha.2",
181
186
  "@deepseek-ai/schemastery": "3.18.4",
182
187
  "@types/node": "^24.0.0",
183
188
  "@types/react": "^18.3.31",
@@ -1,27 +0,0 @@
1
- /**
2
- * Session-cwd resolution for snapshot tracking reads, mirroring the fs tools'
3
- * own rule (`@deepseek-ai/dsh-tool-fs/session-cwd.ts`): relative paths
4
- * resolve against the calling agent's session workspace
5
- * (`exec.agent.session.header.cwd`), not the server's launch dir.
6
- *
7
- * Pure functions: the session cwd is returned verbatim. The fs tools no longer
8
- * canonicalize a parent-traversing cwd (alpha line), so neither does the
9
- * plugin — snapshot tracking must resolve to the same base the tool wrote to.
10
- *
11
- * @module dsh-rewind/session-cwd
12
- */
13
- import type { ToolExecution } from '@deepseek-ai/dsh-tools';
14
- /**
15
- * The session workspace cwd to resolve a requested path against, or undefined
16
- * when no session cwd applies (the filesystem backend then uses its own
17
- * default base).
18
- * @param cwd - the session's `header.cwd`, if any.
19
- * @returns the cwd unchanged.
20
- */
21
- export declare function sessionCwd(cwd: string | undefined): string | undefined;
22
- /**
23
- * Session cwd for one tool execution (same rule as the fs tools).
24
- * @param exec - the tool-execution context; only its optional `agent` is read.
25
- * @returns the calling agent's session cwd, or undefined for a non-agent caller.
26
- */
27
- export declare function execSessionCwd(exec: ToolExecution): string | undefined;