@co0ontty/wand 4.93.0 → 4.94.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.
Files changed (172) hide show
  1. package/README.md +25 -30
  2. package/dist/ai-text-runner.d.ts +2 -10
  3. package/dist/ai-text-runner.js +13 -74
  4. package/dist/build-info.json +3 -3
  5. package/dist/cli.js +47 -9
  6. package/dist/config.d.ts +2 -3
  7. package/dist/config.js +26 -5
  8. package/dist/core-status-cli.d.ts +1 -1
  9. package/dist/core-status-cli.js +3 -1
  10. package/dist/daemon-connection.d.ts +10 -0
  11. package/dist/daemon-connection.js +28 -0
  12. package/dist/diagnostic-logs.d.ts +10 -0
  13. package/dist/diagnostic-logs.js +121 -0
  14. package/dist/employee-text.js +1 -3
  15. package/dist/git-quick-commit.d.ts +1 -1
  16. package/dist/git-quick-commit.js +2 -9
  17. package/dist/layout-tree.d.ts +3 -1
  18. package/dist/layout-tree.js +8 -0
  19. package/dist/local-model-process.js +20 -0
  20. package/dist/local-model-setup.js +5 -3
  21. package/dist/managed-temp.d.ts +32 -0
  22. package/dist/managed-temp.js +176 -0
  23. package/dist/middleware/api-scope.js +1 -0
  24. package/dist/models/shared.d.ts +0 -6
  25. package/dist/models/shared.js +0 -12
  26. package/dist/models/thinking.d.ts +0 -2
  27. package/dist/models/thinking.js +0 -2
  28. package/dist/models.d.ts +0 -1
  29. package/dist/models.js +3 -11
  30. package/dist/native-session-title.d.ts +1 -1
  31. package/dist/native-session-title.js +1 -1
  32. package/dist/pi-model-discovery.js +5 -17
  33. package/dist/pi-resource-recommendation.d.ts +1 -1
  34. package/dist/pi-resource-recommendation.js +129 -42
  35. package/dist/process-manager-native-history.js +1 -6
  36. package/dist/process-manager.d.ts +0 -1
  37. package/dist/process-manager.js +5 -18
  38. package/dist/provider-catalog.d.ts +1 -2
  39. package/dist/provider-catalog.js +1 -6
  40. package/dist/provider-cli-updater.js +1 -9
  41. package/dist/provider-history-scanner.d.ts +0 -3
  42. package/dist/provider-history-scanner.js +0 -26
  43. package/dist/render-daemon-client.js +6 -29
  44. package/dist/render-structured-client.js +2 -7
  45. package/dist/retention.js +1 -6
  46. package/dist/run-command.d.ts +5 -0
  47. package/dist/run-command.js +14 -1
  48. package/dist/server-session-lifecycle-routes.js +1 -1
  49. package/dist/server-storage-cleanup-routes.d.ts +3 -0
  50. package/dist/server-storage-cleanup-routes.js +19 -0
  51. package/dist/server-workspace-routes.d.ts +4 -1
  52. package/dist/server-workspace-routes.js +75 -9
  53. package/dist/server.js +21 -16
  54. package/dist/service-command-privilege.d.ts +17 -0
  55. package/dist/service-command-privilege.js +55 -0
  56. package/dist/service-manager/index.d.ts +3 -0
  57. package/dist/service-manager/index.js +3 -0
  58. package/dist/service-manager/launchd.d.ts +1 -0
  59. package/dist/service-manager/launchd.js +15 -0
  60. package/dist/service-manager/systemd.d.ts +1 -0
  61. package/dist/service-manager/systemd.js +11 -0
  62. package/dist/session-ai-context.js +17 -6
  63. package/dist/session-completion-state.d.ts +2 -0
  64. package/dist/session-completion-state.js +6 -2
  65. package/dist/session-logger.js +7 -1
  66. package/dist/session-model.d.ts +4 -0
  67. package/dist/session-model.js +13 -4
  68. package/dist/session-registry.d.ts +1 -1
  69. package/dist/session-registry.js +0 -3
  70. package/dist/silicon-employee-draft.js +0 -1
  71. package/dist/speech-service.js +18 -8
  72. package/dist/storage/mappers.js +6 -10
  73. package/dist/storage/migrations.js +14 -0
  74. package/dist/storage/uploads-repository.d.ts +36 -0
  75. package/dist/storage/uploads-repository.js +98 -0
  76. package/dist/storage-cleanup.d.ts +50 -0
  77. package/dist/storage-cleanup.js +344 -0
  78. package/dist/storage.d.ts +3 -1
  79. package/dist/storage.js +4 -0
  80. package/dist/structured-failure.d.ts +2 -0
  81. package/dist/structured-failure.js +9 -0
  82. package/dist/structured-grok-adapter.js +4 -4
  83. package/dist/structured-opencode-adapter.js +4 -9
  84. package/dist/structured-provider-common.d.ts +1 -2
  85. package/dist/structured-provider-common.js +3 -6
  86. package/dist/structured-session-manager-replay.js +2 -6
  87. package/dist/structured-session-manager.d.ts +1 -2
  88. package/dist/structured-session-manager.js +32 -25
  89. package/dist/task-organize-contract.d.ts +41 -0
  90. package/dist/task-organize-contract.js +4 -0
  91. package/dist/task-title-organize-service.d.ts +21 -0
  92. package/dist/task-title-organize-service.js +168 -0
  93. package/dist/task-title-organize.d.ts +77 -0
  94. package/dist/task-title-organize.js +346 -0
  95. package/dist/terminal-daemon-client.js +10 -48
  96. package/dist/time-utils.d.ts +2 -0
  97. package/dist/time-utils.js +7 -0
  98. package/dist/tui/attach.js +6 -5
  99. package/dist/tui/commands.d.ts +4 -1
  100. package/dist/tui/commands.js +36 -25
  101. package/dist/tui/index.js +2 -2
  102. package/dist/tui/service-actions.js +5 -4
  103. package/dist/tui/service-command.d.ts +6 -0
  104. package/dist/tui/service-command.js +40 -0
  105. package/dist/tui/service-panel.js +9 -8
  106. package/dist/types.d.ts +4 -4
  107. package/dist/upload-reference-guard.d.ts +4 -0
  108. package/dist/upload-reference-guard.js +29 -0
  109. package/dist/upload-routes.d.ts +4 -0
  110. package/dist/upload-routes.js +121 -21
  111. package/dist/user-profile.d.ts +1 -1
  112. package/dist/user-profile.js +8 -3
  113. package/dist/web-ui/content/ai-teams.js +9 -9
  114. package/dist/web-ui/content/module-manifest.json +1 -1
  115. package/dist/web-ui/content/modules/{2548dbe0d0783577/chat-render-EODYMQF6.js → 0051bd3d55f10ab7/chat-render-RY2I2CF3.js} +1 -1
  116. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-ZTEQSXPA.js → 0051bd3d55f10ab7/chunk-2F6J2MKX.js} +1 -1
  117. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-A33YPCY6.js → 0051bd3d55f10ab7/chunk-7AFM245W.js} +1 -1
  118. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-QNUWUMKW.js → 0051bd3d55f10ab7/chunk-MHX2QXIS.js} +1 -1
  119. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-WGADUEIT.js → 0051bd3d55f10ab7/chunk-RAAPETNH.js} +1 -1
  120. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-I3UYDQLS.js → 0051bd3d55f10ab7/chunk-RXKBHYUR.js} +2 -2
  121. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-3TCUG766.js → 0051bd3d55f10ab7/chunk-U4EDP4TI.js} +1 -1
  122. package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-HK7RBU3M.js → 0051bd3d55f10ab7/chunk-WHXQR7JT.js} +1 -1
  123. package/dist/web-ui/content/modules/0051bd3d55f10ab7/employee-card-NV34TZIH.js +1 -0
  124. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-2PU6TV2I.js → 0051bd3d55f10ab7/host-2IVIZ2YC.js} +1 -1
  125. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-BPYNMKFC.js → 0051bd3d55f10ab7/host-AZXNFCRW.js} +1 -1
  126. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-HZIKPWYY.js → 0051bd3d55f10ab7/host-EPEQX22R.js} +1 -1
  127. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-YELXZID4.js → 0051bd3d55f10ab7/host-FZYIMOLC.js} +1 -1
  128. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-OGD6AWHL.js → 0051bd3d55f10ab7/host-G6GL4SNL.js} +1 -1
  129. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-E3SCPOIU.js → 0051bd3d55f10ab7/host-PEILC5NU.js} +1 -1
  130. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-OWD32FZ3.js → 0051bd3d55f10ab7/host-PHHNGVNL.js} +2 -2
  131. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-CXDAFMXX.js → 0051bd3d55f10ab7/host-Q524TMSR.js} +1 -1
  132. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-RO5MB6RB.js +69 -0
  133. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-J5XRAQFP.js → 0051bd3d55f10ab7/host-WOQRK6NG.js} +1 -1
  134. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-HKXE2DOT.js → 0051bd3d55f10ab7/host-XNIR3NTJ.js} +1 -1
  135. package/dist/web-ui/content/modules/{2548dbe0d0783577/host-4E4H4JZJ.js → 0051bd3d55f10ab7/host-XRVOIEP6.js} +1 -1
  136. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-ZDKOQEMS.js +1 -0
  137. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-bridge-PMQZRG5G.js +1 -0
  138. package/dist/web-ui/content/modules/{2548dbe0d0783577/library-bridge-Y3Y24CPQ.js → 0051bd3d55f10ab7/library-bridge-DTMQIHVJ.js} +1 -1
  139. package/dist/web-ui/content/modules/{2548dbe0d0783577/page-GQBUU7T7.js → 0051bd3d55f10ab7/page-PY4JUL4W.js} +1 -1
  140. package/dist/web-ui/content/modules/{2548dbe0d0783577/panel-PG662OC5.js → 0051bd3d55f10ab7/panel-BBQFC55Q.js} +1 -1
  141. package/dist/web-ui/content/modules/0051bd3d55f10ab7/runtime.js +974 -0
  142. package/dist/web-ui/content/modules/0051bd3d55f10ab7/task-board-host-PXZSWBBG.js +2 -0
  143. package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-agent-picker-DJSECSY6.js +1 -0
  144. package/dist/web-ui/content/modules/{2548dbe0d0783577/workspace-window-HV4T3WKR.js → 0051bd3d55f10ab7/workspace-window-MNMKSOBG.js} +1 -1
  145. package/dist/web-ui/content/styles.css +1 -1
  146. package/dist/web-ui/embedded-assets.d.ts +27 -27
  147. package/dist/web-ui/embedded-assets.js +4 -4
  148. package/dist/web-ui/provider-identity.d.ts +4 -5
  149. package/dist/web-ui/provider-identity.js +4 -10
  150. package/dist/web-ui/react/workspaces/sidebar-manage.d.ts +39 -0
  151. package/dist/web-ui/react/workspaces/sidebar-manage.js +149 -0
  152. package/dist/web-ui/react/workspaces/types.d.ts +408 -0
  153. package/dist/web-ui/react/workspaces/types.js +16 -0
  154. package/dist/web-ui/session-activity.js +1 -1
  155. package/package.json +2 -2
  156. package/dist/structured-gemini-adapter.d.ts +0 -40
  157. package/dist/structured-gemini-adapter.js +0 -208
  158. package/dist/web-ui/content/modules/2548dbe0d0783577/chunk-XGVT4ZWT.js +0 -1
  159. package/dist/web-ui/content/modules/2548dbe0d0783577/employee-card-LJ3TX72B.js +0 -1
  160. package/dist/web-ui/content/modules/2548dbe0d0783577/host-ITV2QZ4A.js +0 -1
  161. package/dist/web-ui/content/modules/2548dbe0d0783577/host-XNWC7KPR.js +0 -69
  162. package/dist/web-ui/content/modules/2548dbe0d0783577/host-bridge-G5HEVADT.js +0 -1
  163. package/dist/web-ui/content/modules/2548dbe0d0783577/runtime.js +0 -946
  164. package/dist/web-ui/content/modules/2548dbe0d0783577/task-board-host-RAN2Z2KR.js +0 -2
  165. package/dist/web-ui/content/modules/2548dbe0d0783577/workspace-agent-picker-FXLH6N5K.js +0 -1
  166. /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-YX475LXO.js → 0051bd3d55f10ab7/chunk-6C6A2RHG.js} +0 -0
  167. /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-NP35GDQ3.js → 0051bd3d55f10ab7/chunk-ECWYA3U5.js} +0 -0
  168. /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-47EVOSJS.js → 0051bd3d55f10ab7/chunk-T35D57FW.js} +0 -0
  169. /package/dist/web-ui/content/modules/{2548dbe0d0783577 → 0051bd3d55f10ab7}/chunk-YOXXNXOX.js +0 -0
  170. /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-C3K34WJD.js → 0051bd3d55f10ab7/chunk-YQJ6XYV7.js} +0 -0
  171. /package/dist/web-ui/content/modules/{2548dbe0d0783577/host-J6HBDK4A.js → 0051bd3d55f10ab7/host-2AQAIUNO.js} +0 -0
  172. /package/dist/web-ui/content/modules/{2548dbe0d0783577 → 0051bd3d55f10ab7}/sha256-ANXV72UC.js +0 -0
@@ -0,0 +1,121 @@
1
+ import { constants, closeSync, fstatSync, lstatSync, openSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { assertPlainTempPath } from "./managed-temp.js";
4
+ export const DIAGNOSTIC_LOG_MAX_BYTES = 10 * 1024 * 1024;
5
+ export const DIAGNOSTIC_LOG_ROTATIONS = 3;
6
+ export const DIAGNOSTIC_LOG_RETENTION_DAYS = 14;
7
+ const FILES = new Set(["web.log", "web-error.log", "terminald.log", "terminald-error.log", "structured-stdout.log", "structured-stderr.log", "structured-spawns.jsonl"]);
8
+ export const DIAGNOSTIC_RECEIPT_SUFFIX = ".wand-log-receipt.json";
9
+ function checkFile(file) {
10
+ assertPlainTempPath(file);
11
+ try {
12
+ const stat = lstatSync(file);
13
+ if (!stat.isFile() || (process.getuid && stat.uid !== process.getuid()))
14
+ throw new Error("Diagnostic log is not an owned file.");
15
+ return stat;
16
+ }
17
+ catch (error) {
18
+ if (error.code === "ENOENT")
19
+ return null;
20
+ throw error;
21
+ }
22
+ }
23
+ function receipt(file) {
24
+ const stat = checkFile(file);
25
+ const target = file + DIAGNOSTIC_RECEIPT_SUFFIX;
26
+ checkFile(target);
27
+ writeFileSync(target, JSON.stringify({ app: "wand-diagnostic-log", version: 1, device: stat.dev, inode: stat.ino,
28
+ size: stat.size, mtimeMs: stat.mtimeMs, ctimeMs: stat.ctimeMs }), { mode: 0o600 });
29
+ }
30
+ export function diagnosticReceiptMatches(file) {
31
+ const stat = checkFile(file), marker = checkFile(file + DIAGNOSTIC_RECEIPT_SUFFIX);
32
+ if (!stat || !marker || marker.size > 4096)
33
+ return false;
34
+ const fd = openSync(file + DIAGNOSTIC_RECEIPT_SUFFIX, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
35
+ try {
36
+ const value = JSON.parse(readFileSync(fd, "utf8"));
37
+ return value.app === "wand-diagnostic-log" && value.version === 1 && value.device === stat.dev
38
+ && value.inode === stat.ino && value.size === stat.size && value.mtimeMs === stat.mtimeMs && value.ctimeMs === stat.ctimeMs;
39
+ }
40
+ finally {
41
+ closeSync(fd);
42
+ }
43
+ }
44
+ /** Only redundant diagnostics: PTY history and native protocol sources never enter this writer. */
45
+ export function appendBoundedDiagnostic(file, data, maxBytes = DIAGNOSTIC_LOG_MAX_BYTES, rotations = DIAGNOSTIC_LOG_ROTATIONS) {
46
+ if (!FILES.has(path.basename(file)) || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || !Number.isSafeInteger(rotations) || rotations < 1 || rotations > 3) {
47
+ throw new Error("Unknown diagnostic log or invalid limits.");
48
+ }
49
+ const target = path.join(realpathSync(path.dirname(file)), path.basename(file));
50
+ let bytes = Buffer.isBuffer(data) ? data : Buffer.from(data);
51
+ if (bytes.length > maxBytes)
52
+ bytes = bytes.subarray(bytes.length - maxBytes);
53
+ const stat = checkFile(target);
54
+ if (stat && stat.size + bytes.length > maxBytes) {
55
+ // Preflight all slots before renaming: unknown/replaced files are never overwritten.
56
+ for (let i = 1; i <= rotations; i++) {
57
+ const old = `${target}.${i}`;
58
+ const archive = checkFile(old), marker = checkFile(old + DIAGNOSTIC_RECEIPT_SUFFIX);
59
+ if (archive ? !diagnosticReceiptMatches(old) : marker)
60
+ throw new Error("Diagnostic archive ownership changed.");
61
+ }
62
+ const oldest = `${target}.${rotations}`;
63
+ if (checkFile(oldest)) {
64
+ unlinkSync(oldest);
65
+ unlinkSync(oldest + DIAGNOSTIC_RECEIPT_SUFFIX);
66
+ }
67
+ for (let i = rotations - 1; i >= 1; i--) {
68
+ const from = `${target}.${i}`, to = `${target}.${i + 1}`;
69
+ if (checkFile(from)) {
70
+ renameSync(from, to);
71
+ unlinkSync(from + DIAGNOSTIC_RECEIPT_SUFFIX);
72
+ receipt(to);
73
+ }
74
+ }
75
+ renameSync(target, `${target}.1`);
76
+ receipt(`${target}.1`);
77
+ }
78
+ const fd = openSync(target, constants.O_WRONLY | constants.O_CREAT | constants.O_APPEND | (constants.O_NOFOLLOW ?? 0), 0o600);
79
+ try {
80
+ const current = fstatSync(fd);
81
+ if (!current.isFile() || (process.getuid && current.uid !== process.getuid()))
82
+ throw new Error("Diagnostic output changed.");
83
+ let offset = 0;
84
+ while (offset < bytes.length)
85
+ offset += writeSync(fd, bytes, offset, bytes.length - offset);
86
+ }
87
+ finally {
88
+ closeSync(fd);
89
+ }
90
+ }
91
+ /** Attach only to launchd/file redirections matching this instance; keep TTYs and journald untouched. */
92
+ export function installServiceDiagnosticRotation(configDir, kind) {
93
+ const restore = [];
94
+ for (const [stream, file, fd] of [[process.stdout, `${kind}.log`, 1], [process.stderr, `${kind}-error.log`, 2]]) {
95
+ try {
96
+ const target = path.join(realpathSync(configDir), file), stat = checkFile(target), output = fstatSync(fd);
97
+ if (!stat || stat.dev !== output.dev || stat.ino !== output.ino)
98
+ continue;
99
+ const original = stream.write;
100
+ stream.write = function (chunk, encoding, callback) {
101
+ const cb = typeof encoding === "function" ? encoding : callback;
102
+ try {
103
+ appendBoundedDiagnostic(target, typeof chunk === "string" ? Buffer.from(chunk, typeof encoding === "string" ? encoding : "utf8") : Buffer.from(chunk));
104
+ if (cb)
105
+ queueMicrotask(() => cb(null));
106
+ return true;
107
+ }
108
+ catch {
109
+ return original.apply(stream, arguments);
110
+ }
111
+ };
112
+ restore.push(() => { stream.write = original; });
113
+ }
114
+ catch { /* Never prevent startup or redirect unrelated output. */ }
115
+ }
116
+ return () => { for (const undo of restore)
117
+ undo(); };
118
+ }
119
+ export function isDiagnosticArchive(name) {
120
+ return [...FILES].some(file => [1, 2, 3].some(index => name === `${file}.${index}`));
121
+ }
@@ -74,9 +74,7 @@ export async function prepareEmployeeTextCandidate(deps, agent, request, signal)
74
74
  if (agent.engine !== "sdk") {
75
75
  if (!providerCliInstalled(agent.provider) || isOpenRouterFreeSelector(agent.model))
76
76
  throw new Error("CLI候选不可用。");
77
- const { callCliAiText, assertGeminiTextCapability } = await import("./ai-text-runner.js");
78
- if (agent.provider === "gemini")
79
- assertGeminiTextCapability();
77
+ const { callCliAiText } = await import("./ai-text-runner.js");
80
78
  // One adapter call, never re-enter the candidate chain.
81
79
  return async () => {
82
80
  signal.throwIfAborted();
@@ -1,7 +1,7 @@
1
1
  import { GitStatusResult, PushResult, QuickCommitResult, SessionProvider, SessionSnapshot, TagHeadResult } from "./types.js";
2
2
  /** provider 缺省值:无法识别时回落到 Claude。 */
3
3
  import { type QuickCommitAiOptions } from "./ai-text-runner.js";
4
- export { assertGeminiTextCapability, callCliAiText, callConfiguredAiText, withOpsPersona } from "./ai-text-runner.js";
4
+ export { callCliAiText, callConfiguredAiText, withOpsPersona } from "./ai-text-runner.js";
5
5
  export type { QuickCommitAiOptions } from "./ai-text-runner.js";
6
6
  export { QuickCommitError, type QuickCommitErrorCode } from "./command-error.js";
7
7
  /** Non-blocking status implementation used by HTTP routes. */
@@ -2,9 +2,9 @@ import { existsSync } from "node:fs";
2
2
  import { getGitErrorMessage, runGitAsync as runGitAsyncBase, runGitRawAsync as runGitRawAsyncBase, } from "./git-utils.js";
3
3
  import { thinkingEffortToClaudeCliEffort, thinkingEffortToCodexReasoningEffort, thinkingEffortToOpenCodeVariant } from "./structured-provider-common.js";
4
4
  /** provider 缺省值:无法识别时回落到 Claude。 */
5
- import { buildGeminiTextArgs, buildGrokTextArgs, buildPiTextArgs, buildQoderTextArgs, callConfiguredAiText, contentWithSystemPrompt, defaultProvider, normalizeAiText, runCliText, systemPromptArgsFrom, withOpsPersona } from "./ai-text-runner.js";
5
+ import { buildGrokTextArgs, buildPiTextArgs, buildQoderTextArgs, callConfiguredAiText, contentWithSystemPrompt, defaultProvider, normalizeAiText, runCliText, systemPromptArgsFrom, withOpsPersona } from "./ai-text-runner.js";
6
6
  import { QuickCommitError } from "./command-error.js";
7
- export { assertGeminiTextCapability, callCliAiText, callConfiguredAiText, withOpsPersona } from "./ai-text-runner.js";
7
+ export { callCliAiText, callConfiguredAiText, withOpsPersona } from "./ai-text-runner.js";
8
8
  export { QuickCommitError } from "./command-error.js";
9
9
  const GIT_TIMEOUT_MS = 1500;
10
10
  const GIT_PUSH_TIMEOUT_MS = 30_000;
@@ -929,13 +929,6 @@ async function runQuickCommitFallbackCli(opts, priorError) {
929
929
  inheritEnv: opts.inheritEnv,
930
930
  });
931
931
  }
932
- else if (provider === "gemini") {
933
- await runCliText("gemini", buildGeminiTextArgs(opts, true), contentWithSystemPrompt("gemini", request), {
934
- cwd: opts.cwd,
935
- timeoutMs: QUICK_COMMIT_CLI_TIMEOUT_MS,
936
- inheritEnv: opts.inheritEnv,
937
- });
938
- }
939
932
  else {
940
933
  const args = [
941
934
  "-p",
@@ -1,6 +1,8 @@
1
- import type { LayoutNode } from "./types.js";
1
+ import type { LayoutNode, TaskWindowLayout } from "./types.js";
2
2
  /**
3
3
  * 取布局树中最左上角可用的 tab id:pane 优先用它记住的 active,
4
4
  * 否则取第一个 tab;split 递归左侧再右侧。
5
5
  */
6
6
  export declare function firstLayoutTabId(node: LayoutNode): string | undefined;
7
+ /** Wrap a legacy tree after each caller applies its own write/read acceptance policy. */
8
+ export declare function wrapLegacyTaskLayout(layout: LayoutNode): TaskWindowLayout;
@@ -7,3 +7,11 @@ export function firstLayoutTabId(node) {
7
7
  return node.tabs[node.active]?.id ?? node.tabs[0]?.id;
8
8
  return firstLayoutTabId(node.children[0]) ?? firstLayoutTabId(node.children[1]);
9
9
  }
10
+ /** Wrap a legacy tree after each caller applies its own write/read acceptance policy. */
11
+ export function wrapLegacyTaskLayout(layout) {
12
+ return {
13
+ type: "windows",
14
+ windows: [{ id: "window-legacy", layout, activeTabId: firstLayoutTabId(layout) }],
15
+ activeWindowId: "window-legacy",
16
+ };
17
+ }
@@ -1,5 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { buildChildEnv, systemEnvValue } from "./env-utils.js";
3
+ import { registerTemporaryChild } from "./managed-temp.js";
3
4
  /** Runs one fixed deployment command. Only whitelisted server code constructs executable/args. */
4
5
  export function runModelSetup(executable, args, cwd, signal, progress, timeoutMs = 20 * 60_000) {
5
6
  return new Promise((resolve, reject) => {
@@ -13,6 +14,25 @@ export function runModelSetup(executable, args, cwd, signal, progress, timeoutMs
13
14
  HTTPS_PROXY: systemEnvValue("HTTPS_PROXY"), HTTP_PROXY: systemEnvValue("HTTP_PROXY"), NO_PROXY: systemEnvValue("NO_PROXY"),
14
15
  WAND_LAYA_PYTHON_BIN: systemEnvValue("WAND_LAYA_PYTHON_BIN"),
15
16
  }) });
17
+ const workIndex = args.indexOf("--work-dir");
18
+ if (workIndex !== -1) {
19
+ try {
20
+ registerTemporaryChild(args[workIndex + 1], child.pid, grouped);
21
+ }
22
+ catch {
23
+ child.on("error", () => { });
24
+ child.once("close", () => { reject(new Error("Model setup temporary lease unavailable")); });
25
+ if (grouped && child.pid) {
26
+ try {
27
+ process.kill(-child.pid, "SIGKILL");
28
+ }
29
+ catch { }
30
+ }
31
+ else
32
+ child.kill("SIGKILL");
33
+ return;
34
+ }
35
+ }
16
36
  let failed = false, buffer = "", kill;
17
37
  const terminate = () => {
18
38
  failed = true;
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
2
+ import { mkdir, readFile, rm } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { DecisionError } from "./decision-types.js";
@@ -11,6 +11,7 @@ import { speechSupport } from "./speech-support.js";
11
11
  import { systemEnvValue } from "./env-utils.js";
12
12
  import { decisionHardware } from "./decision-hardware.js";
13
13
  import { DECISION_EXPERT_ID } from "./decision-expert-identity.js";
14
+ import { configTemporaryRoot, createManagedTemporary } from "./managed-temp.js";
14
15
  /** Deployment jobs only; existing inference owners retain their protocols, rate limits and capabilities. */
15
16
  export class LocalModelSetupService {
16
17
  deps;
@@ -244,12 +245,13 @@ export class LocalModelSetupService {
244
245
  throw new Error("服务器使用自定义语音运行时,请由管理员核对环境配置;不会覆盖它。");
245
246
  phase("runtime", "安装固定 whisper.cpp 运行时(需 Git、CMake、C++工具链;CUDA需已安装Toolkit)");
246
247
  await mkdir(path.join(this.deps.configDir, "speech"), { recursive: true, mode: 0o700 });
247
- const work = await mkdtemp(path.join(this.deps.configDir, "speech", ".setup-work-"));
248
+ const temporary = createManagedTemporary("speech-setup", configTemporaryRoot(this.deps.configDir));
249
+ const work = temporary.directory;
248
250
  try {
249
251
  await (this.deps.run ?? runModelSetup)(process.execPath, [this.installer("install-speech-runtime.js"), "--dir", path.join(this.deps.configDir, "speech"), "--backend", backend, "--work-dir", work, "--events"], this.deps.configDir, signal, (name, message) => phase(name === "verifying" ? "verifying" : "runtime", message));
250
252
  }
251
253
  finally {
252
- await rm(work, { recursive: true, force: true }).catch(() => { });
254
+ await temporary.closeAsync();
253
255
  }
254
256
  }
255
257
  phase("initializing", action === "activate" ? "正在检查语音模型,完成后自动启用。" : "加载所选语音模型并用合成静音检查,未启用识别或重启服务");
@@ -0,0 +1,32 @@
1
+ export declare const TEMP_ROOT_MARKER = ".wand-temporary-root.json";
2
+ export declare const TEMP_LEASE = ".wand-temporary-lease.json";
3
+ export declare const TEMP_KINDS: readonly ["speech-check", "speech-voice", "speech-setup", "test-run", "upload-stage"];
4
+ export type TempKind = typeof TEMP_KINDS[number];
5
+ export declare function systemTemporaryRoot(): string;
6
+ /** Keep test fixture Unix sockets below the platform path length limit. */
7
+ export declare function testTemporaryRoot(): string;
8
+ export declare function configTemporaryRoot(configDir: string): string;
9
+ /** Never follow a redirect beneath the trusted, canonical deployment/temp path. */
10
+ export declare function assertPlainTempPath(target: string): void;
11
+ export declare function validateTemporaryRoot(root: string): void;
12
+ export interface TempLease {
13
+ app: "wand-temporary";
14
+ version: 1;
15
+ kind: TempKind;
16
+ id: string;
17
+ ownerPid: number;
18
+ children: {
19
+ pid: number;
20
+ group: boolean;
21
+ }[];
22
+ }
23
+ export declare function readTemporaryLease(directory: string): TempLease;
24
+ /** ESRCH alone establishes absence; permissions and unexpected errors fail closed. */
25
+ export declare function temporaryPidAlive(pid: number, group?: boolean): boolean;
26
+ export declare function temporaryChildrenAlive(lease: TempLease): boolean;
27
+ export declare function registerTemporaryChild(directory: string, pid: number | undefined, group?: boolean): void;
28
+ export declare function createManagedTemporary(kind: TempKind, root?: string): {
29
+ directory: string;
30
+ close(): boolean;
31
+ closeAsync(): Promise<boolean>;
32
+ };
@@ -0,0 +1,176 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { constants, chownSync, closeSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { rm } from "node:fs/promises";
6
+ export const TEMP_ROOT_MARKER = ".wand-temporary-root.json";
7
+ export const TEMP_LEASE = ".wand-temporary-lease.json";
8
+ export const TEMP_KINDS = ["speech-check", "speech-voice", "speech-setup", "test-run", "upload-stage"];
9
+ const uid = () => process.getuid?.() ?? null;
10
+ export function systemTemporaryRoot() {
11
+ return path.join(realpathSync(os.tmpdir()), `wand-transient-${uid() ?? "user"}`);
12
+ }
13
+ /** Keep test fixture Unix sockets below the platform path length limit. */
14
+ export function testTemporaryRoot() {
15
+ return process.platform === "win32" ? systemTemporaryRoot()
16
+ : path.join(realpathSync("/tmp"), `wand-transient-${uid() ?? "user"}`);
17
+ }
18
+ export function configTemporaryRoot(configDir) {
19
+ let base = path.resolve(configDir);
20
+ const missing = [];
21
+ for (;;) {
22
+ try {
23
+ base = realpathSync(base);
24
+ break;
25
+ }
26
+ catch (error) {
27
+ if (error.code !== "ENOENT" || base === path.dirname(base))
28
+ throw error;
29
+ missing.unshift(path.basename(base));
30
+ base = path.dirname(base);
31
+ }
32
+ }
33
+ return path.join(base, ...missing, "cache", "temporary");
34
+ }
35
+ /** Never follow a redirect beneath the trusted, canonical deployment/temp path. */
36
+ export function assertPlainTempPath(target) {
37
+ for (let current = path.resolve(target);; current = path.dirname(current)) {
38
+ try {
39
+ if (lstatSync(current).isSymbolicLink())
40
+ throw new Error("Temporary path contains a symbolic link.");
41
+ }
42
+ catch (error) {
43
+ if (error.code !== "ENOENT")
44
+ throw error;
45
+ }
46
+ if (current === path.dirname(current))
47
+ break;
48
+ }
49
+ }
50
+ function readMarker(file) {
51
+ const fd = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
52
+ try {
53
+ const stat = fstatSync(fd);
54
+ if (!stat.isFile() || stat.size > 4096 || (uid() !== null && stat.uid !== uid()))
55
+ throw new Error("Invalid temporary marker.");
56
+ const value = JSON.parse(readFileSync(fd, "utf8"));
57
+ if (!value || typeof value !== "object" || Array.isArray(value))
58
+ throw new Error("Invalid temporary marker.");
59
+ return value;
60
+ }
61
+ finally {
62
+ closeSync(fd);
63
+ }
64
+ }
65
+ export function validateTemporaryRoot(root) {
66
+ assertPlainTempPath(root);
67
+ const stat = lstatSync(root);
68
+ if (!stat.isDirectory() || (uid() !== null && (stat.uid !== uid() || (stat.mode & 0o077) !== 0))) {
69
+ throw new Error("Temporary root is not private to this user.");
70
+ }
71
+ const marker = readMarker(path.join(root, TEMP_ROOT_MARKER));
72
+ if (marker.app !== "wand-temporary" || marker.version !== 1 || marker.uid !== uid())
73
+ throw new Error("Unmanaged temporary root.");
74
+ }
75
+ export function readTemporaryLease(directory) {
76
+ assertPlainTempPath(directory);
77
+ const value = readMarker(path.join(directory, TEMP_LEASE));
78
+ if (value.app !== "wand-temporary" || value.version !== 1 || !TEMP_KINDS.includes(value.kind)
79
+ || !path.basename(directory).startsWith(`${value.kind}-`) || typeof value.id !== "string"
80
+ || !/^[a-f0-9-]{36}$/.test(value.id) || !Number.isSafeInteger(value.ownerPid) || value.ownerPid <= 0
81
+ || !Array.isArray(value.children) || value.children.length > 16
82
+ || value.children.some(child => !child || !Number.isSafeInteger(child.pid) || child.pid <= 0 || typeof child.group !== "boolean")) {
83
+ throw new Error("Invalid temporary lease.");
84
+ }
85
+ return value;
86
+ }
87
+ /** ESRCH alone establishes absence; permissions and unexpected errors fail closed. */
88
+ export function temporaryPidAlive(pid, group = false) {
89
+ try {
90
+ process.kill(group && process.platform !== "win32" ? -pid : pid, 0);
91
+ return true;
92
+ }
93
+ catch (error) {
94
+ return error.code !== "ESRCH";
95
+ }
96
+ }
97
+ export function temporaryChildrenAlive(lease) {
98
+ return lease.children.some(child => temporaryPidAlive(child.pid, child.group));
99
+ }
100
+ export function registerTemporaryChild(directory, pid, group = false) {
101
+ if (!pid)
102
+ return;
103
+ validateTemporaryRoot(path.dirname(directory));
104
+ const lease = readTemporaryLease(directory);
105
+ if (lease.ownerPid !== process.pid)
106
+ throw new Error("Temporary lease belongs to another process.");
107
+ lease.children = lease.children.filter(child => temporaryPidAlive(child.pid, child.group));
108
+ if (!lease.children.some(child => child.pid === pid))
109
+ lease.children.push({ pid, group });
110
+ const file = path.join(directory, TEMP_LEASE);
111
+ const pending = path.join(directory, `${TEMP_LEASE}.${randomUUID()}.tmp`);
112
+ writeFileSync(pending, JSON.stringify(lease), { mode: 0o600, flag: "wx" });
113
+ renameSync(pending, file);
114
+ }
115
+ export function createManagedTemporary(kind, root = systemTemporaryRoot()) {
116
+ if (!TEMP_KINDS.includes(kind))
117
+ throw new Error("Unknown temporary purpose.");
118
+ assertPlainTempPath(root);
119
+ mkdirSync(root, { recursive: true, mode: 0o700 });
120
+ const marker = path.join(root, TEMP_ROOT_MARKER);
121
+ try {
122
+ writeFileSync(marker, JSON.stringify({ app: "wand-temporary", version: 1, uid: uid() }), { mode: 0o600, flag: "wx" });
123
+ }
124
+ catch (error) {
125
+ if (error.code !== "EEXIST")
126
+ throw error;
127
+ }
128
+ validateTemporaryRoot(root);
129
+ // macOS inherits /private/tmp's wheel group; fixtures should retain the caller's group semantics.
130
+ if (process.getgid && uid() !== null && lstatSync(root).gid !== process.getgid())
131
+ chownSync(root, uid(), process.getgid());
132
+ const directory = mkdtempSync(path.join(root, `${kind}-`));
133
+ const lease = { app: "wand-temporary", version: 1, kind, id: randomUUID(), ownerPid: process.pid, children: [] };
134
+ try {
135
+ writeFileSync(path.join(directory, TEMP_LEASE), JSON.stringify(lease), { mode: 0o600, flag: "wx" });
136
+ }
137
+ catch (error) {
138
+ rmSync(directory, { recursive: true, force: true });
139
+ throw error;
140
+ }
141
+ const removable = () => {
142
+ validateTemporaryRoot(root);
143
+ const current = readTemporaryLease(directory);
144
+ return current.id === lease.id && current.ownerPid === process.pid && !temporaryChildrenAlive(current);
145
+ };
146
+ const absent = () => {
147
+ try {
148
+ lstatSync(directory);
149
+ return false;
150
+ }
151
+ catch (error) {
152
+ return error.code === "ENOENT";
153
+ }
154
+ };
155
+ return { directory, close() {
156
+ try {
157
+ if (!removable())
158
+ return false;
159
+ rmSync(directory, { recursive: true, force: true });
160
+ return true;
161
+ }
162
+ catch {
163
+ return absent();
164
+ }
165
+ }, async closeAsync() {
166
+ try {
167
+ if (!removable())
168
+ return false;
169
+ await rm(directory, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
170
+ return true;
171
+ }
172
+ catch {
173
+ return absent();
174
+ }
175
+ } };
176
+ }
@@ -23,6 +23,7 @@ export const API_ROUTE_RULES = [
23
23
  { prefix: "/api/check-update", scope: "admin" },
24
24
  { prefix: "/api/auto-update", scope: "admin" },
25
25
  { prefix: "/api/provider-cli-updates", scope: "admin" },
26
+ { prefix: "/api/storage-cleanup", scope: "admin" },
26
27
  { prefix: "/api/android-apk", scope: "admin" },
27
28
  { prefix: "/api/macos-dmg", scope: "admin" },
28
29
  { prefix: "/api/ios-ipa", scope: "admin" },
@@ -8,11 +8,5 @@ export declare const OPENCODE_FALLBACK_MODELS: ClaudeModelInfo[];
8
8
  export declare const GROK_FALLBACK_MODELS: ClaudeModelInfo[];
9
9
  export declare const QODER_FALLBACK_MODELS: ClaudeModelInfo[];
10
10
  export declare const PI_FALLBACK_MODELS: ClaudeModelInfo[];
11
- /**
12
- * Gemini CLI 没有列模型的子命令,只能给出稳定别名(`packages/core/src/config/models.ts`
13
- * 里的 GEMINI_MODEL_ALIAS_*)。具体版本名随发布滚动,不适合写死在这里;
14
- * 设置页的模型输入框是自由文本,用户可以自己填完整模型名。
15
- */
16
- export declare const GEMINI_FALLBACK_MODELS: ClaudeModelInfo[];
17
11
  export declare function cloneModels(models: readonly ClaudeModelInfo[]): ClaudeModelInfo[];
18
12
  export { isRecord } from "../object-utils.js";
@@ -38,18 +38,6 @@ export const QODER_FALLBACK_MODELS = [
38
38
  export const PI_FALLBACK_MODELS = [
39
39
  { id: "default", label: "跟随 Pi 默认", alias: true },
40
40
  ];
41
- /**
42
- * Gemini CLI 没有列模型的子命令,只能给出稳定别名(`packages/core/src/config/models.ts`
43
- * 里的 GEMINI_MODEL_ALIAS_*)。具体版本名随发布滚动,不适合写死在这里;
44
- * 设置页的模型输入框是自由文本,用户可以自己填完整模型名。
45
- */
46
- export const GEMINI_FALLBACK_MODELS = [
47
- { id: "default", label: "跟随 Gemini 默认", alias: true },
48
- { id: "auto", label: "Auto(按任务自动选模型)" },
49
- { id: "pro", label: "Pro" },
50
- { id: "flash", label: "Flash" },
51
- { id: "flash-lite", label: "Flash Lite" },
52
- ];
53
41
  export function cloneModels(models) {
54
42
  return models.map((model) => ({
55
43
  ...model,
@@ -11,8 +11,6 @@ export interface ProviderThinkingEfforts {
11
11
  grok: ThinkingEffortLevel[];
12
12
  qoder: ThinkingEffortLevel[];
13
13
  pi: ThinkingEffortLevel[];
14
- /** Gemini CLI 没有思考档位开关,只保留「不覆盖」一项,不假装能调。 */
15
- gemini: ThinkingEffortLevel[];
16
14
  }
17
15
  /** 探针失败时的已知档位。下一次 CLI 成功应答会把它换掉。 */
18
16
  export declare const FALLBACK_PROVIDER_THINKING: ProviderThinkingEfforts;
@@ -7,7 +7,6 @@ export const FALLBACK_PROVIDER_THINKING = {
7
7
  grok: effortLevelsFromIds(["low", "medium", "high", "xhigh"]),
8
8
  qoder: effortLevelsFromIds(["auto", "none", "low", "medium", "high", "xhigh", "max", "ultracode"]),
9
9
  pi: effortLevelsFromIds(["minimal", "low", "medium", "high", "xhigh", "max"]),
10
- gemini: effortLevelsFromIds(["off"]),
11
10
  };
12
11
  export function cloneThinkingEfforts(efforts) {
13
12
  const copy = (levels) => levels.map((level) => ({ ...level }));
@@ -17,7 +16,6 @@ export function cloneThinkingEfforts(efforts) {
17
16
  grok: copy(efforts.grok),
18
17
  qoder: copy(efforts.qoder),
19
18
  pi: copy(efforts.pi),
20
- gemini: copy(efforts.gemini),
21
19
  };
22
20
  }
23
21
  export function effortLevelsFromIds(ids) {
package/dist/models.d.ts CHANGED
@@ -64,7 +64,6 @@ export interface ModelCache {
64
64
  grokModels: ClaudeModelInfo[];
65
65
  qoderModels: ClaudeModelInfo[];
66
66
  piModels: ClaudeModelInfo[];
67
- geminiModels: ClaudeModelInfo[];
68
67
  thinkingEfforts: ProviderThinkingEfforts;
69
68
  claudeVersion: string | null;
70
69
  opencodeVersion: string | null;
package/dist/models.js CHANGED
@@ -9,7 +9,7 @@ import { resolveChildEnv } from "./env-utils.js";
9
9
  import { discoverPiEndpointModels } from "./pi-model-discovery.js";
10
10
  import { extractSemver } from "./version-utils.js";
11
11
  import { AUTO_ASSIGN_LABEL, AUTO_ASSIGN_SELECTOR, FREE_MODEL_GROUP_ID, isAutoAssignSelector, isModelGroupSelector, modelGroupSelector, orderModelIds } from "./model-groups.js";
12
- import { MODEL_ID_PATTERN, QODER_MODEL_ID_PATTERN, CLAUDE_BUILTIN_MODELS, CODEX_FALLBACK_MODELS, OPENCODE_FALLBACK_MODELS, GROK_FALLBACK_MODELS, QODER_FALLBACK_MODELS, PI_FALLBACK_MODELS, GEMINI_FALLBACK_MODELS, cloneModels, isRecord } from "./models/shared.js";
12
+ import { MODEL_ID_PATTERN, QODER_MODEL_ID_PATTERN, CLAUDE_BUILTIN_MODELS, CODEX_FALLBACK_MODELS, OPENCODE_FALLBACK_MODELS, GROK_FALLBACK_MODELS, QODER_FALLBACK_MODELS, PI_FALLBACK_MODELS, cloneModels, isRecord } from "./models/shared.js";
13
13
  import { EFFORT_ID_PATTERN, FALLBACK_PROVIDER_THINKING, cloneThinkingEfforts, effortLevelsFromIds, unionModelEfforts, parseCliEffortList } from "./models/thinking.js";
14
14
  import { parseOpenCodeModelCatalog } from "./models/parsers/opencode.js";
15
15
  import { parseGrokModels } from "./models/parsers/grok.js";
@@ -45,7 +45,6 @@ const PROVIDER_MODEL_LIST_FIELDS = {
45
45
  grok: "grokModels",
46
46
  qoder: "qoderModels",
47
47
  pi: "piModels",
48
- gemini: "geminiModels",
49
48
  };
50
49
  /**
51
50
  * 目录里 `default` 项(「不传 --model」那一项)的标签补上服务端配置的默认模型名:
@@ -84,7 +83,6 @@ function cloneCache(cache) {
84
83
  grokModels: cloneModels(cache.grokModels),
85
84
  qoderModels: cloneModels(cache.qoderModels),
86
85
  piModels: cloneModels(cache.piModels),
87
- geminiModels: cloneModels(cache.geminiModels),
88
86
  thinkingEfforts: cloneThinkingEfforts(cache.thinkingEfforts),
89
87
  claudeVersion: cache.claudeVersion,
90
88
  opencodeVersion: cache.opencodeVersion,
@@ -253,7 +251,6 @@ function createInitialCache(options) {
253
251
  grokModels: cloneModels(GROK_FALLBACK_MODELS),
254
252
  qoderModels: cloneModels(QODER_FALLBACK_MODELS),
255
253
  piModels: cloneModels(PI_FALLBACK_MODELS),
256
- geminiModels: cloneModels(GEMINI_FALLBACK_MODELS),
257
254
  thinkingEfforts: cloneThinkingEfforts(FALLBACK_PROVIDER_THINKING),
258
255
  claudeVersion: null,
259
256
  opencodeVersion: null,
@@ -485,7 +482,6 @@ function catalogRevision(cache) {
485
482
  grokModels: cache.grokModels,
486
483
  qoderModels: cache.qoderModels,
487
484
  piModels: cache.piModels,
488
- geminiModels: cache.geminiModels,
489
485
  thinkingEfforts: cache.thinkingEfforts,
490
486
  claudeVersion: cache.claudeVersion,
491
487
  opencodeVersion: cache.opencodeVersion,
@@ -560,7 +556,7 @@ function parsePersistedThinkingEfforts(value) {
560
556
  const fallback = cloneThinkingEfforts(FALLBACK_PROVIDER_THINKING);
561
557
  if (!isRecord(value))
562
558
  return fallback;
563
- const providers = ["claude", "opencode", "grok", "qoder", "pi", "gemini"];
559
+ const providers = ["claude", "opencode", "grok", "qoder", "pi"];
564
560
  for (const provider of providers) {
565
561
  const levels = parsePersistedEffortLevels(value[provider]);
566
562
  if (levels)
@@ -595,9 +591,8 @@ function parsePersistedModelCatalog(value) {
595
591
  const grokModels = parsePersistedModelList(catalog.grokModels);
596
592
  const qoderModels = parsePersistedModelList(catalog.qoderModels);
597
593
  const piModels = parsePersistedModelList(catalog.piModels);
598
- const geminiModels = parsePersistedModelList(catalog.geminiModels);
599
594
  const refreshedAt = safePersistedString(catalog.refreshedAt, 64);
600
- if (!models || !codexModels || !opencodeModels || !grokModels || !qoderModels || !piModels || !geminiModels
595
+ if (!models || !codexModels || !opencodeModels || !grokModels || !qoderModels || !piModels
601
596
  || !refreshedAt || Number.isNaN(Date.parse(refreshedAt))) {
602
597
  return null;
603
598
  }
@@ -613,7 +608,6 @@ function parsePersistedModelCatalog(value) {
613
608
  grokModels,
614
609
  qoderModels,
615
610
  piModels,
616
- geminiModels,
617
611
  thinkingEfforts: parsePersistedThinkingEfforts(catalog.thinkingEfforts),
618
612
  claudeVersion,
619
613
  opencodeVersion,
@@ -704,7 +698,6 @@ async function discoverModelCache(options, previous) {
704
698
  grokModels: grokProbe.ok ? grokProbe.value : cloneModels(previous.grokModels),
705
699
  qoderModels: qoderProbe.ok ? qoderProbe.value : cloneModels(previous.qoderModels),
706
700
  piModels: piProbe.ok ? piProbe.value : cloneModels(previous.piModels),
707
- geminiModels: cloneModels(previous.geminiModels),
708
701
  thinkingEfforts: {
709
702
  claude: claudeEffortProbe.ok ? claudeEffortProbe.value : cloneThinkingEfforts(previous.thinkingEfforts).claude,
710
703
  opencode: opencodeProbe.models.ok && unionModelEfforts(opencodeProbe.models.value).length
@@ -717,7 +710,6 @@ async function discoverModelCache(options, previous) {
717
710
  : piEffortProbe.ok
718
711
  ? piEffortProbe.value
719
712
  : cloneThinkingEfforts(previous.thinkingEfforts).pi,
720
- gemini: cloneThinkingEfforts(previous.thinkingEfforts).gemini,
721
713
  },
722
714
  claudeVersion,
723
715
  opencodeVersion: opencodeProbe.version.ok ? opencodeProbe.version.value : previous.opencodeVersion,
@@ -10,7 +10,7 @@ export interface NativeTitleHomes {
10
10
  * provider 自己在运行期写下的会话标题:
11
11
  * codex `session_index.jsonl` 的 thread_name、qoder 转录里的 ai-title、
12
12
  * OpenCode SQLite 的 session.title、grok `summary.json` 的 generated_title。
13
- * Claude / Pi / Gemini 没有这个入口,返回空串让调用方回退系统硅基员工。
13
+ * Claude / Pi 没有这个入口,返回空串让调用方回退系统硅基员工。
14
14
  */
15
15
  export declare function readNativeSessionTitle(provider: SessionProvider, providerSessionId: string | null | undefined, cwd: string, homes?: NativeTitleHomes): Promise<string>;
16
16
  /** 只用于测试与降级:清掉按文件指纹缓存的原生标题。 */
@@ -11,7 +11,7 @@ const GROK_WALK_LIMIT = 4_000;
11
11
  * provider 自己在运行期写下的会话标题:
12
12
  * codex `session_index.jsonl` 的 thread_name、qoder 转录里的 ai-title、
13
13
  * OpenCode SQLite 的 session.title、grok `summary.json` 的 generated_title。
14
- * Claude / Pi / Gemini 没有这个入口,返回空串让调用方回退系统硅基员工。
14
+ * Claude / Pi 没有这个入口,返回空串让调用方回退系统硅基员工。
15
15
  */
16
16
  export async function readNativeSessionTitle(provider, providerSessionId, cwd, homes = {}) {
17
17
  const id = providerSessionId?.trim() ?? "";