@chatcode/chatcode-cli 3.0.4-rc.2 → 3.0.4-rc.3

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.
Files changed (39) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +2 -0
  3. package/README.zh.md +2 -0
  4. package/lib/{command-errors-CScdshCl.js → command-errors-BJ_ZWzUU.js} +12 -12
  5. package/lib/{command-trees-Dc17MYfV.js → command-trees-C9b7-bci.js} +2 -2
  6. package/lib/{component-identity-DFJD_d-P.js → component-identity-Jb1DYjke.js} +3 -3
  7. package/lib/{extension-events-qXmhzmHo.js → extension-events-DKX4LXOd.js} +20 -20
  8. package/lib/{host-access-B0CRAwsX.js → host-access-CMg6rsWt.js} +1 -1
  9. package/lib/{keymap-CIoVICBI.js → keymap-CrC4pSNH.js} +2 -2
  10. package/lib/{plugin-BKQsL2xn.js → plugin-CmyMCGfb.js} +350 -339
  11. package/lib/{scenes-BRf-zMpF.js → scenes-Cil9RnwU.js} +3 -3
  12. package/lib/{settings-sections-4gMhveN_.js → settings-sections-jVCqXwdH.js} +3 -3
  13. package/lib/{statusBarSettings-CnmdSHkO.js → statusBarSettings-DNoBH3Cq.js} +3 -3
  14. package/lib/{themes-Ce97Zsri.js → themes-BdgeY3It.js} +12 -12
  15. package/lib/tui-compat-command-trees.js +1 -1
  16. package/lib/tui-compat-extensions.js +3 -3
  17. package/lib/tui-compat-plugin-host.js +5 -5
  18. package/lib/tui-compat-scenes.js +1 -1
  19. package/lib/tui-compat-settings-sections.js +1 -1
  20. package/lib/tui-compat-working-activity.js +1 -1
  21. package/lib/tui-compat-workspaces.js +1 -1
  22. package/lib/tui-compat.js +5 -5
  23. package/lib/{workspaces-1xrm-e3V.js → workspaces-DOatJRYr.js} +2 -2
  24. package/package.json +31 -31
  25. package/runtime/manifest.json +2 -2
  26. package/runtime/node_modules/@deepseek-ai/dsh-app-boot/lib/index.js +2 -1
  27. package/runtime/node_modules/@deepseek-ai/dsh-atomic-write/lib/index.js +167 -44
  28. package/runtime/node_modules/@deepseek-ai/dsh-atomic-write/lib/types/index.d.ts +14 -6
  29. package/runtime/node_modules/@deepseek-ai/dsh-atomic-write/lib/types/linux-lock.d.ts +18 -0
  30. package/runtime/node_modules/@deepseek-ai/dsh-atomic-write/lib/types/windows-lock.d.ts +7 -0
  31. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-account/lib/client.js +2 -2
  32. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +1 -1
  33. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +1 -3
  34. package/runtime/node_modules/@deepseek-ai/dsh-llm-deepseek/lib/types/adapter.d.ts +1 -1
  35. package/runtime/node_modules/@deepseek-ai/dsh-llm-deepseek/lib/types/config.d.ts +3 -3
  36. package/runtime/node_modules/@deepseek-ai/dsh-llm-deepseek-account/lib/types/config.d.ts +2 -2
  37. package/runtime/node_modules/@deepseek-ai/dsh-llm-deepseek-api-key/lib/types/config.d.ts +2 -2
  38. package/runtime/node_modules/@deepseek-ai/dsh-tool-cordis/lib/types/api-catalog.js +1 -1
  39. package/scripts/launch.mjs +8 -0
@@ -1,6 +1,122 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
- import { lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
- import { dirname } from "node:path";
2
+ import { link, lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, win32 } from "node:path";
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ //#region lib/types/windows-lock.js
7
+ /** Exclusive Windows inspection for an empty or zero-filled lock left by a failed creator. */
8
+ const execute = promisify(execFile);
9
+ /**
10
+ * Read a small lock only when no other handle has the file open.
11
+ * @param path - lock path, protected by the caller's takeover claim.
12
+ * @returns its record, or undefined if exclusive access or inspection fails.
13
+ */
14
+ async function readUnopenedWindowsLock(path) {
15
+ const powershell = win32.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
16
+ const script = [
17
+ "$ErrorActionPreference = 'Stop'",
18
+ `$file = [IO.File]::Open('${path.replaceAll("'", "''")}', [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::None)`,
19
+ "try {",
20
+ " if ($file.Length -gt 64) { exit 1 }",
21
+ " $reader = [IO.StreamReader]::new($file, [Text.Encoding]::UTF8, $false)",
22
+ " try { [Console]::Out.Write([Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($reader.ReadToEnd()))) }",
23
+ " finally { $reader.Dispose() }",
24
+ "} finally { $file.Dispose() }"
25
+ ].join("\n");
26
+ try {
27
+ const { stdout } = await execute(powershell, [
28
+ "-NoLogo",
29
+ "-NoProfile",
30
+ "-NonInteractive",
31
+ "-EncodedCommand",
32
+ Buffer.from(script, "utf16le").toString("base64")
33
+ ], {
34
+ encoding: "utf8",
35
+ windowsHide: true,
36
+ timeout: 2e3,
37
+ maxBuffer: 4096,
38
+ env: Object.fromEntries(Object.entries(process.env).filter(([name]) => !/KEY|SECRET|TOKEN|PASSWORD/i.test(name)))
39
+ });
40
+ const value = stdout.trim();
41
+ const bytes = Buffer.from(value, "base64");
42
+ if (bytes.length > 64 || bytes.toString("base64") !== value) return void 0;
43
+ return bytes.toString("utf8");
44
+ } catch (error) {
45
+ return;
46
+ }
47
+ }
48
+ //#endregion
49
+ //#region lib/types/linux-lock.js
50
+ /** Linux writer identity and complete-record publication; other platforms retain PID records. */
51
+ const RECORD = /^(\d+)\r?\n(?:linux ([a-f0-9-]{36}) (\d+) ([a-f0-9]{24})\n)?$/u;
52
+ let ownIdentity;
53
+ /** Read kernel identity without treating inaccessible procfs as evidence of an exited writer. */
54
+ async function identity(pid) {
55
+ try {
56
+ const [boot, stat] = await Promise.all([readFile("/proc/sys/kernel/random/boot_id", "utf8"), readFile(`/proc/${pid}/stat`, "utf8")]);
57
+ const end = stat.lastIndexOf(")");
58
+ const start = end < 0 ? void 0 : stat.slice(end + 2).trim().split(/\s+/u)[19];
59
+ if (!/^[a-f0-9-]{36}$/u.test(boot.trim()) || start === void 0 || !/^\d+$/u.test(start)) return void 0;
60
+ return `${boot.trim()} ${start}`;
61
+ } catch (error) {
62
+ return;
63
+ }
64
+ }
65
+ /**
66
+ * Render a new acquisition's record; Linux adds boot identity, start ticks, and a unique claim token.
67
+ * @returns the PID record, with Linux identity when the kernel exposes it.
68
+ */
69
+ async function writerLockRecord() {
70
+ const pid = `${process.pid}\n`;
71
+ if (process.platform !== "linux") return pid;
72
+ const current = await (ownIdentity ??= identity(process.pid));
73
+ return current === void 0 ? pid : `${pid}linux ${current} ${randomBytes(12).toString("hex")}\n`;
74
+ }
75
+ /**
76
+ * Exclusively publish a complete Linux identity record, or create a legacy PID lock.
77
+ * @param path - lock sibling in an existing parent directory.
78
+ * @param record - one acquisition's record from writerLockRecord.
79
+ */
80
+ async function createWriterLock(path, record) {
81
+ const nonce = RECORD.exec(record)?.[4];
82
+ if (process.platform !== "linux" || nonce === void 0) {
83
+ await writeFile(path, record, {
84
+ mode: 384,
85
+ flag: "wx"
86
+ });
87
+ return;
88
+ }
89
+ const temp = `${path}.${nonce}.tmp`;
90
+ try {
91
+ await writeFile(temp, record, {
92
+ mode: 384,
93
+ flag: "wx"
94
+ });
95
+ await link(temp, path);
96
+ } finally {
97
+ await rm(temp, { force: true });
98
+ }
99
+ }
100
+ /**
101
+ * Determine whether a recorded writer exited, including Linux PID reuse and a previous boot.
102
+ * @param record - untrusted contents of an existing lock file.
103
+ * @returns true only for ESRCH or a different observable Linux process identity.
104
+ */
105
+ async function writerExited(record) {
106
+ const match = RECORD.exec(record);
107
+ if (match === null) return false;
108
+ const pid = Number(match[1]);
109
+ if (pid === 0 || pid > 2147483647) return false;
110
+ if (pid !== process.pid) try {
111
+ process.kill(pid, 0);
112
+ } catch (error) {
113
+ return error.code === "ESRCH";
114
+ }
115
+ if (process.platform !== "linux" || match[2] === void 0) return false;
116
+ const current = await identity(pid);
117
+ return current !== void 0 && current !== `${match[2]} ${match[3]}`;
118
+ }
119
+ //#endregion
4
120
  //#region lib/types/index.js
5
121
  /**
6
122
  * Zero-dependency atomic file replacement and writer coordination.
@@ -8,38 +124,41 @@ import { dirname } from "node:path";
8
124
  * the caller's permission bits, then renames it over the target, so readers
9
125
  * observe either the old or the new complete content and a replaced file ends
10
126
  * up with exactly the stated mode. `withFileLock` serializes cross-process
11
- * writers of one file through a `wx`-created `<file>.lock` sibling, so a
127
+ * writers of one file through an exclusively published `<file>.lock` sibling, so a
12
128
  * read-modify-write cycle can never resurrect a state another writer just
13
129
  * replaced; readers stay lock-free because the rename commit is atomic. A lock
14
- * whose recorded holder process no longer exists is taken over.
130
+ * whose recorded holder process no longer exists is taken over. Windows also
131
+ * recovers empty or zero-filled records after an exclusive read proves no
132
+ * creator still has the lock file open. Linux records also distinguish
133
+ * a previous boot or reused PID through kernel process identity.
15
134
  * @module @deepseek-ai/dsh-atomic-write
16
135
  */
17
- const WINDOWS_TRANSIENT_RENAME_ERRORS = new Set([
136
+ const WINDOWS_TRANSIENT_FILE_ERRORS = new Set([
18
137
  "EACCES",
19
138
  "EBUSY",
20
139
  "EPERM"
21
140
  ]);
22
- const WINDOWS_RENAME_RETRY_INITIAL_MS = 20;
23
- const WINDOWS_RENAME_RETRY_MAX_MS = 200;
24
- const WINDOWS_RENAME_RETRY_LIMIT = 8;
141
+ const WINDOWS_FILE_RETRY_INITIAL_MS = 20;
142
+ const WINDOWS_FILE_RETRY_MAX_MS = 200;
143
+ const WINDOWS_FILE_RETRY_LIMIT = 8;
25
144
  /** Whether Windows reported temporary interference with an atomic replacement. */
26
- function isTransientWindowsRenameError(error) {
145
+ function isTransientWindowsFileError(error) {
27
146
  if (process.platform !== "win32") return false;
28
- return WINDOWS_TRANSIENT_RENAME_ERRORS.has(error?.code ?? "");
147
+ return WINDOWS_TRANSIENT_FILE_ERRORS.has(error?.code ?? "");
29
148
  }
30
- /** Replace the target after bounded retries for transient Windows interference. */
31
- async function renameAtomicTemp(temp, filename) {
32
- let delay = WINDOWS_RENAME_RETRY_INITIAL_MS;
149
+ /** Retry a replacement or lock release while Windows briefly holds the path open. */
150
+ async function retryWindowsFileOperation(operation) {
151
+ let delay = WINDOWS_FILE_RETRY_INITIAL_MS;
33
152
  for (let retries = 0;; retries += 1) {
34
153
  try {
35
- await rename(temp, filename);
154
+ await operation();
36
155
  return;
37
156
  } catch (error) {
38
- if (!isTransientWindowsRenameError(error)) throw error;
39
- if (retries >= WINDOWS_RENAME_RETRY_LIMIT) throw error;
157
+ if (!isTransientWindowsFileError(error)) throw error;
158
+ if (retries >= WINDOWS_FILE_RETRY_LIMIT) throw error;
40
159
  }
41
160
  await new Promise((resolve) => setTimeout(resolve, delay));
42
- delay = Math.min(delay * 2, WINDOWS_RENAME_RETRY_MAX_MS);
161
+ delay = Math.min(delay * 2, WINDOWS_FILE_RETRY_MAX_MS);
43
162
  }
44
163
  }
45
164
  /**
@@ -69,7 +188,7 @@ async function writeFileAtomic(filename, content, options) {
69
188
  mode: options.mode,
70
189
  flag: "wx"
71
190
  });
72
- await renameAtomicTemp(temp, filename);
191
+ await retryWindowsFileOperation(() => rename(temp, filename));
73
192
  } catch (error) {
74
193
  await rm(temp, { force: true });
75
194
  throw error;
@@ -87,18 +206,13 @@ async function isLockContention(error, lockPath) {
87
206
  return false;
88
207
  }
89
208
  }
90
- /** Whether the holder a `<pid>\n` record names is proven gone: a signal probe finds no such process. */
91
- function holderExited(record) {
92
- if (!/^\d+\n$/.test(record)) return false;
93
- const pid = Number(record.trim());
94
- if (pid === 0 || pid > 2147483647) return false;
95
- if (pid === process.pid) return false;
96
- try {
97
- process.kill(pid, 0);
98
- return false;
99
- } catch (error) {
100
- return error.code === "ESRCH";
101
- }
209
+ /** Whether the recorded writer is absent or has a different observable Linux identity. */
210
+ async function holderExited(record) {
211
+ return writerExited(record);
212
+ }
213
+ /** A crash can leave an empty or zero-filled record before the holder's PID reaches disk. */
214
+ function damagedRecord(record) {
215
+ return record.length <= 64 && /^\0*$/.test(record);
102
216
  }
103
217
  /** The lock file's content, or undefined when it cannot be read. */
104
218
  async function readLockRecord(lockPath) {
@@ -109,18 +223,21 @@ async function readLockRecord(lockPath) {
109
223
  }
110
224
  }
111
225
  /**
112
- * Remove the lock when its recorded holder exited. Contenders that read the
226
+ * Remove an exited holder's lock or a closed, damaged Windows lock. Contenders that read the
113
227
  * same record serialize on a claim file named after it. Under the claim, the
114
228
  * claimant re-reads the lock and probes its PID again, and removes it only
115
- * when it still holds that record and that PID is still gone: the record's
229
+ * when it still holds that record and that PID is still gone. A damaged Windows
230
+ * record instead requires an exclusive read after claiming it. The record's
116
231
  * holder can no longer release it, and no other contender can remove it
117
232
  * without the claim, so a removal never deletes a lock another contender
118
233
  * acquired after the dead holder's, including one whose holder reused the PID.
119
- * @returns Whether this call removed the dead holder's lock.
234
+ * @returns Whether this call removed the abandoned lock.
120
235
  */
121
236
  async function takeOverExitedLock(lockPath) {
122
237
  const record = await readLockRecord(lockPath);
123
- if (record === void 0 || !holderExited(record)) return false;
238
+ if (record === void 0) return false;
239
+ const damaged = process.platform === "win32" && damagedRecord(record);
240
+ if (!damaged && !await holderExited(record)) return false;
124
241
  const claim = `${lockPath}.takeover-${createHash("sha256").update(record).digest("hex").slice(0, 16)}`;
125
242
  try {
126
243
  await writeFile(claim, `${process.pid}\n`, {
@@ -133,7 +250,10 @@ async function takeOverExitedLock(lockPath) {
133
250
  throw error;
134
251
  }
135
252
  try {
136
- if (await readLockRecord(lockPath) !== record || !holderExited(record)) return false;
253
+ if (await readLockRecord(lockPath) !== record) return false;
254
+ if (damaged) {
255
+ if (await readUnopenedWindowsLock(lockPath) !== record) return false;
256
+ } else if (!await holderExited(record)) return false;
137
257
  try {
138
258
  await rm(lockPath, { force: true });
139
259
  } catch (error) {
@@ -163,7 +283,7 @@ const LOCK_RETRY_MAX_MS = 200;
163
283
  const DEFAULT_LOCK_WAIT_MS = 2e3;
164
284
  /**
165
285
  * Hold the cross-process writer lock for `filename` around one operation. The
166
- * lock is a `wx`-created sibling (`<filename>.lock`); paired with the
286
+ * lock is an exclusively published sibling (`<filename>.lock`); paired with the
167
287
  * rename-based commit of {@link writeFileAtomic}, readers stay lock-free and
168
288
  * only writers contend. `EEXIST` is contention directly; an `EPERM` is
169
289
  * contention only when a fresh `lstat` confirms the lock path exists, covering
@@ -172,9 +292,14 @@ const DEFAULT_LOCK_WAIT_MS = 2e3;
172
292
  * permission error is rethrown. The lock records its holder's PID. A contender
173
293
  * removes the lock and retries at once when no process with that PID exists
174
294
  * (`ESRCH`); any other lock, including one whose holder exists under another
175
- * user (`EPERM`) or whose record is incomplete, is waited for. Contention backs
176
- * off exponentially and times out after the deadline. A holder whose PID a
177
- * live process reused keeps its lock until an operator removes it. Takeover
295
+ * user (`EPERM`) or whose record is incomplete, is waited for. On Windows,
296
+ * empty or zero-filled records of at most 64 bytes can also be recovered after
297
+ * exclusive inspection proves no creator still has the file open. A failed
298
+ * inspection preserves the lock. Windows lock release retries transient file
299
+ * interference for a bounded interval. Contention backs
300
+ * off exponentially and times out after the deadline. Linux identity records
301
+ * distinguish PID reuse and a previous boot; legacy PID-only records cannot.
302
+ * Missing or inaccessible identity metadata never authorizes takeover. Takeover
178
303
  * proves only that the recorded process exited: an operation that starts other
179
304
  * writers must stop them with it or leave its successor a way to find them.
180
305
  * PIDs are compared on the contender's host, so writers on other hosts or in
@@ -187,15 +312,13 @@ const DEFAULT_LOCK_WAIT_MS = 2e3;
187
312
  */
188
313
  async function withFileLock(filename, operation, options) {
189
314
  const lockPath = `${filename}.lock`;
315
+ const record = await writerLockRecord();
190
316
  const deadline = Date.now() + (options?.waitMs ?? DEFAULT_LOCK_WAIT_MS);
191
317
  let delay = LOCK_RETRY_INITIAL_MS;
192
318
  let retriedUnconfirmedPermissionError = false;
193
319
  for (;;) {
194
320
  try {
195
- await writeFile(lockPath, `${process.pid}\n`, {
196
- mode: 384,
197
- flag: "wx"
198
- });
321
+ await createWriterLock(lockPath, record);
199
322
  break;
200
323
  } catch (error) {
201
324
  if (!await isLockContention(error, lockPath)) {
@@ -210,7 +333,7 @@ async function withFileLock(filename, operation, options) {
210
333
  try {
211
334
  return await operation();
212
335
  } finally {
213
- await rm(lockPath, { force: true });
336
+ await retryWindowsFileOperation(() => rm(lockPath, { force: true }));
214
337
  }
215
338
  }
216
339
  //#endregion
@@ -4,10 +4,13 @@
4
4
  * the caller's permission bits, then renames it over the target, so readers
5
5
  * observe either the old or the new complete content and a replaced file ends
6
6
  * up with exactly the stated mode. `withFileLock` serializes cross-process
7
- * writers of one file through a `wx`-created `<file>.lock` sibling, so a
7
+ * writers of one file through an exclusively published `<file>.lock` sibling, so a
8
8
  * read-modify-write cycle can never resurrect a state another writer just
9
9
  * replaced; readers stay lock-free because the rename commit is atomic. A lock
10
- * whose recorded holder process no longer exists is taken over.
10
+ * whose recorded holder process no longer exists is taken over. Windows also
11
+ * recovers empty or zero-filled records after an exclusive read proves no
12
+ * creator still has the lock file open. Linux records also distinguish
13
+ * a previous boot or reused PID through kernel process identity.
11
14
  * @module @deepseek-ai/dsh-atomic-write
12
15
  */
13
16
  /**
@@ -58,7 +61,7 @@ export interface FileLockOptions {
58
61
  }
59
62
  /**
60
63
  * Hold the cross-process writer lock for `filename` around one operation. The
61
- * lock is a `wx`-created sibling (`<filename>.lock`); paired with the
64
+ * lock is an exclusively published sibling (`<filename>.lock`); paired with the
62
65
  * rename-based commit of {@link writeFileAtomic}, readers stay lock-free and
63
66
  * only writers contend. `EEXIST` is contention directly; an `EPERM` is
64
67
  * contention only when a fresh `lstat` confirms the lock path exists, covering
@@ -67,9 +70,14 @@ export interface FileLockOptions {
67
70
  * permission error is rethrown. The lock records its holder's PID. A contender
68
71
  * removes the lock and retries at once when no process with that PID exists
69
72
  * (`ESRCH`); any other lock, including one whose holder exists under another
70
- * user (`EPERM`) or whose record is incomplete, is waited for. Contention backs
71
- * off exponentially and times out after the deadline. A holder whose PID a
72
- * live process reused keeps its lock until an operator removes it. Takeover
73
+ * user (`EPERM`) or whose record is incomplete, is waited for. On Windows,
74
+ * empty or zero-filled records of at most 64 bytes can also be recovered after
75
+ * exclusive inspection proves no creator still has the file open. A failed
76
+ * inspection preserves the lock. Windows lock release retries transient file
77
+ * interference for a bounded interval. Contention backs
78
+ * off exponentially and times out after the deadline. Linux identity records
79
+ * distinguish PID reuse and a previous boot; legacy PID-only records cannot.
80
+ * Missing or inaccessible identity metadata never authorizes takeover. Takeover
73
81
  * proves only that the recorded process exited: an operation that starts other
74
82
  * writers must stop them with it or leave its successor a way to find them.
75
83
  * PIDs are compared on the contender's host, so writers on other hosts or in
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Render a new acquisition's record; Linux adds boot identity, start ticks, and a unique claim token.
3
+ * @returns the PID record, with Linux identity when the kernel exposes it.
4
+ */
5
+ export declare function writerLockRecord(): Promise<string>;
6
+ /**
7
+ * Exclusively publish a complete Linux identity record, or create a legacy PID lock.
8
+ * @param path - lock sibling in an existing parent directory.
9
+ * @param record - one acquisition's record from writerLockRecord.
10
+ */
11
+ export declare function createWriterLock(path: string, record: string): Promise<void>;
12
+ /**
13
+ * Determine whether a recorded writer exited, including Linux PID reuse and a previous boot.
14
+ * @param record - untrusted contents of an existing lock file.
15
+ * @returns true only for ESRCH or a different observable Linux process identity.
16
+ */
17
+ export declare function writerExited(record: string): Promise<boolean>;
18
+ //# sourceMappingURL=linux-lock.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Read a small lock only when no other handle has the file open.
3
+ * @param path - lock path, protected by the caller's takeover claim.
4
+ * @returns its record, or undefined if exclusive access or inspection fails.
5
+ */
6
+ export declare function readUnopenedWindowsLock(path: string): Promise<string | undefined>;
7
+ //# sourceMappingURL=windows-lock.d.ts.map
@@ -4125,7 +4125,7 @@ window.__ModuleLoader__.load({
4125
4125
  for (const listener of listeners) listener();
4126
4126
  };
4127
4127
  /** @returns the client identity for one account call, read at call time so it carries the language and zone in effect then. */
4128
- const client = () => accountClientMetadata(ctx.locale.getSnapshot().active, "3.0.4-rc.2");
4128
+ const client = () => accountClientMetadata(ctx.locale.getSnapshot().active, "3.0.4-rc.3");
4129
4129
  const notices = createBonusNoticeController({
4130
4130
  ackRetryDelayMs: config.bonusAckRetryDelayMs,
4131
4131
  ackRetryMaxDelayMs: config.bonusAckRetryMaxDelayMs,
@@ -4256,7 +4256,7 @@ window.__ModuleLoader__.load({
4256
4256
  refreshAccount,
4257
4257
  contactUs() {
4258
4258
  const url = contactUrl(config, {
4259
- version: "3.0.4-rc.2",
4259
+ version: "3.0.4-rc.3",
4260
4260
  locale: ctx.locale.getSnapshot().active === "zh" ? "zh-CN" : "en",
4261
4261
  width: window.screen.width,
4262
4262
  height: window.screen.height,
@@ -657,7 +657,7 @@ window.__ModuleLoader__.load({
657
657
  function CurrentVersionRow({ t }) {
658
658
  return (0, react_jsx_runtime.jsx)("div", {
659
659
  className: CurrentVersionRow_module_css_default.row,
660
- children: t("general.currentVersion", { version: "3.0.4-rc.2" })
660
+ children: t("general.currentVersion", { version: "3.0.4-rc.3" })
661
661
  });
662
662
  }
663
663
  //#endregion
@@ -167,9 +167,7 @@ window.__ModuleLoader__.load({
167
167
  const SCROLLBAR_LINGER_MS = 2e3;
168
168
  /** Format complete-build metadata for the local brand badge. */
169
169
  function localBuildVersion() {
170
- const commit = "4d610ce";
171
- const dirty = {}.CHATCODE_CLI_CLIENT_GIT_DIRTY ?? {}["DSH_CLIENT_GIT_DIRTY"];
172
- return `3.0.4-rc.2-${commit}` + (dirty === "true" ? "-dirty" : "");
170
+ return `3.0.4-rc.3-4d610ce-dirty`;
173
171
  }
174
172
  /** Each panel row subscribes only to its own selection state. */
175
173
  function PanelRow({ id, label, wide, usePanelInfo, selectPanel, renderSlot }) {
@@ -13,7 +13,7 @@ export declare class DeepSeekAdapter<C extends Connection = Connection> extends
13
13
  name: string;
14
14
  };
15
15
  providerRetryPolicy(_provider: string): import("@deepseek-ai/dsh-llm").ResolvedRetryPolicy;
16
- listModels(provider: string): Promise<import("@deepseek-ai/dsh-llm").LlmModelInfo[] | never[]>;
16
+ listModels(provider: string): Promise<never[] | import("@deepseek-ai/dsh-llm").LlmModelInfo[]>;
17
17
  resolveModel(provider: string, model: string, _signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-llm").LlmResolvedModelInfo>;
18
18
  imageRequestPricing(_provider: string, model: string): import("@deepseek-ai/dsh-llm").LlmImageRequestPricing;
19
19
  prepareCall(provider: string, model: string, _signal?: AbortSignal): Promise<PreparedAdapterCall>;
@@ -56,7 +56,7 @@ export declare function plainOptions(config: Config): Options;
56
56
  export declare const deepSeekConfigFields: {
57
57
  baseURL: z<string, string, "volatile">;
58
58
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
59
- reasoningEffort: z<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
59
+ reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
60
60
  maxTokens: z<number, number, "volatile-defined">;
61
61
  defaultContextWindow: z<number, number, "volatile-defined">;
62
62
  models: z<NoInfer<DeepSeekCatalogModel[]>, NoInfer<DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -76,7 +76,7 @@ export declare const deepSeekConfigFields: {
76
76
  export declare const Config: z<Schemastery.ObjectS<NoInfer<{
77
77
  baseURL: z<string, string, "volatile">;
78
78
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
79
- reasoningEffort: z<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
79
+ reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
80
80
  maxTokens: z<number, number, "volatile-defined">;
81
81
  defaultContextWindow: z<number, number, "volatile-defined">;
82
82
  models: z<NoInfer<DeepSeekCatalogModel[]>, NoInfer<DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -95,7 +95,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
95
95
  }>>, Schemastery.ObjectT<NoInfer<{
96
96
  baseURL: z<string, string, "volatile">;
97
97
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
98
- reasoningEffort: z<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
98
+ reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
99
99
  maxTokens: z<number, number, "volatile-defined">;
100
100
  defaultContextWindow: z<number, number, "volatile-defined">;
101
101
  models: z<NoInfer<DeepSeekCatalogModel[]>, NoInfer<DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -5,7 +5,7 @@ export type Config = ProtocolConfig;
5
5
  export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
6
6
  baseURL: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
7
7
  thinking: import("@deepseek-ai/schemastery").default<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
8
- reasoningEffort: import("@deepseek-ai/schemastery").default<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
8
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
9
9
  maxTokens: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
10
10
  defaultContextWindow: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
11
11
  models: import("@deepseek-ai/schemastery").default<NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -24,7 +24,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
24
24
  }>>, Schemastery.ObjectT<NoInfer<{
25
25
  baseURL: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
26
26
  thinking: import("@deepseek-ai/schemastery").default<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
27
- reasoningEffort: import("@deepseek-ai/schemastery").default<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
27
+ reasoningEffort: import("@deepseek-ai/schemastery").default<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
28
28
  maxTokens: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
29
29
  defaultContextWindow: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
30
30
  models: import("@deepseek-ai/schemastery").default<NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -14,7 +14,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
14
14
  apiKeyEnv: z<string, string, "volatile-defined">;
15
15
  baseURL: z<string, string, "volatile">;
16
16
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
17
- reasoningEffort: z<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
17
+ reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
18
18
  maxTokens: z<number, number, "volatile-defined">;
19
19
  defaultContextWindow: z<number, number, "volatile-defined">;
20
20
  models: z<NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -34,7 +34,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
34
34
  apiKeyEnv: z<string, string, "volatile-defined">;
35
35
  baseURL: z<string, string, "volatile">;
36
36
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
37
- reasoningEffort: z<"off" | "low" | "high" | "max", "off" | "low" | "high" | "max", "volatile">;
37
+ reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
38
38
  maxTokens: z<number, number, "volatile-defined">;
39
39
  defaultContextWindow: z<number, number, "volatile-defined">;
40
40
  models: z<NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, NoInfer<import("@deepseek-ai/dsh-llm-deepseek").DeepSeekCatalogModel[]>, "volatile-defined">;
@@ -4741,7 +4741,7 @@ export const TYPE_API = [
4741
4741
  },
4742
4742
  {
4743
4743
  name: 'CommandInputDescriptor',
4744
- declaration: 'export interface CommandInputDescriptor {\n readonly hint: string;\n readonly attachments?: boolean;\n}',
4744
+ declaration: 'export interface CommandInputDescriptor {\n readonly hint: string;\n readonly suggestions?: readonly {\n readonly value: string;\n readonly description: string;\n }[];\n readonly attachments?: boolean;\n}',
4745
4745
  },
4746
4746
  {
4747
4747
  name: 'CommandInvocation',
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { readFileSync } from 'node:fs'
4
+ import { enableCompileCache } from 'node:module'
4
5
  import { dirname, join, resolve } from 'node:path'
5
6
  import { fileURLToPath, pathToFileURL } from 'node:url'
6
7
  import { registerEmbeddedRuntime } from './register-embedded-runtime.mjs'
@@ -8,6 +9,13 @@ import { registerEmbeddedRuntime } from './register-embedded-runtime.mjs'
8
9
  const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '..')
9
10
  const manifest = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8'))
10
11
 
12
+ // Cache the installed module graph before its first import. Node owns cache
13
+ // permissions, NODE_COMPILE_CACHE and NODE_DISABLE_COMPILE_CACHE handling.
14
+ try { enableCompileCache() } catch (error) {
15
+ // A restricted filesystem must not prevent launching without the optional cache.
16
+ void error
17
+ }
18
+
11
19
  // Register before loading the CLI's static imports. Global installations may
12
20
  // belong to root; launching must not rewrite their dependency directories.
13
21
  if (!manifest.dependencies?.['@deepseek-ai/dsh-app-boot']?.startsWith('workspace:')) {