@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
package/dist/storage.d.ts CHANGED
@@ -12,6 +12,7 @@ import { UserMemoryRepository } from "./storage/user-memory-repository.js";
12
12
  import { AiTeamRepository } from "./storage/ai-teams-repository.js";
13
13
  import { MissionRepository } from "./storage/missions-repository.js";
14
14
  import { SessionRepository } from "./storage/sessions-repository.js";
15
+ import { UploadRepository } from "./storage/uploads-repository.js";
15
16
  export { parseWandTaskAgent };
16
17
  export type { ConnectorMeta };
17
18
  export { ensureDatabaseFile } from "./storage-schema.js";
@@ -19,7 +20,7 @@ export { ensureDatabaseFile } from "./storage-schema.js";
19
20
  type RepositoryMethods<T> = Pick<T, {
20
21
  [K in keyof T]: T[K] extends (...args: never[]) => unknown ? K : never;
21
22
  }[keyof T]>;
22
- export interface WandStorage extends RepositoryMethods<ConversationRepository>, RepositoryMethods<PreferencesRepository>, RepositoryMethods<WorkspaceRepository>, RepositoryMethods<TaskRepository>, RepositoryMethods<SecretsRepository>, RepositoryMethods<EmployeeRepository>, RepositoryMethods<UserMemoryRepository>, RepositoryMethods<AiTeamRepository>, RepositoryMethods<MissionRepository>, RepositoryMethods<SessionRepository> {
23
+ export interface WandStorage extends RepositoryMethods<ConversationRepository>, RepositoryMethods<PreferencesRepository>, RepositoryMethods<WorkspaceRepository>, RepositoryMethods<TaskRepository>, RepositoryMethods<SecretsRepository>, RepositoryMethods<EmployeeRepository>, RepositoryMethods<UserMemoryRepository>, RepositoryMethods<AiTeamRepository>, RepositoryMethods<MissionRepository>, RepositoryMethods<SessionRepository>, RepositoryMethods<UploadRepository> {
23
24
  }
24
25
  export declare class WandStorage {
25
26
  readonly conversations: ConversationRepository;
@@ -32,6 +33,7 @@ export declare class WandStorage {
32
33
  readonly aiTeams: AiTeamRepository;
33
34
  readonly missions: MissionRepository;
34
35
  readonly sessions: SessionRepository;
36
+ readonly uploads: UploadRepository;
35
37
  private readonly closeListeners;
36
38
  private readonly taskTitleListeners;
37
39
  private titleNoticePending;
package/dist/storage.js CHANGED
@@ -15,6 +15,7 @@ import { UserMemoryRepository } from "./storage/user-memory-repository.js";
15
15
  import { AiTeamRepository } from "./storage/ai-teams-repository.js";
16
16
  import { MissionRepository } from "./storage/missions-repository.js";
17
17
  import { SessionRepository } from "./storage/sessions-repository.js";
18
+ import { UploadRepository } from "./storage/uploads-repository.js";
18
19
  export { parseWandTaskAgent };
19
20
  export { ensureDatabaseFile } from "./storage-schema.js";
20
21
  export class WandStorage {
@@ -28,6 +29,7 @@ export class WandStorage {
28
29
  aiTeams;
29
30
  missions;
30
31
  sessions;
32
+ uploads;
31
33
  closeListeners = new Set();
32
34
  taskTitleListeners = new Set();
33
35
  titleNoticePending = false;
@@ -88,6 +90,7 @@ export class WandStorage {
88
90
  this.aiTeams = new AiTeamRepository(this.db, this);
89
91
  this.missions = new MissionRepository(this.db, this);
90
92
  this.sessions = new SessionRepository(this.db, this);
93
+ this.uploads = new UploadRepository(this.db, this);
91
94
  this.schedules = new ScheduledTaskRepository(this.db, action => this.transaction(action));
92
95
  this.secrets.openVault();
93
96
  const brokenLinks = this.integrityReport().filter((item) => item.count > 0);
@@ -230,6 +233,7 @@ const REPOSITORY_FORWARDS = [
230
233
  ["aiTeams", AiTeamRepository],
231
234
  ["missions", MissionRepository],
232
235
  ["sessions", SessionRepository],
236
+ ["uploads", UploadRepository],
233
237
  ];
234
238
  for (const [field, repository] of REPOSITORY_FORWARDS) {
235
239
  for (const name of Object.getOwnPropertyNames(repository.prototype)) {
@@ -26,6 +26,8 @@ export declare function cliUnacceptedFacts(input: {
26
26
  /** Adapters whose protocol echoes the failure text into `result` state what really ran. */
27
27
  progressed?: boolean;
28
28
  }): StructuredCliUnacceptedFacts;
29
+ /** Assemble common CLI exit fields while the adapter retains protocol/error observations. */
30
+ export declare function buildStructuredCliResult(input: Parameters<typeof cliUnacceptedFacts>[0] & Pick<StructuredRunnerResult, "exitCode" | "stderr">): StructuredRunnerResult;
29
31
  /** Only call for a provider protocol error, never assistant text, stderr or a caught transport error. */
30
32
  export declare function classifyProviderRejection(message: string | null | undefined): ProviderRejectionKind | null;
31
33
  /** Monotonic observations matter: a final error can replace earlier usage or content. */
@@ -12,6 +12,15 @@ export function cliUnacceptedFacts(input) {
12
12
  return {};
13
13
  return { inputAccepted: false, rejection: classifyProviderRejection(input.primaryError) ?? "model-unavailable" };
14
14
  }
15
+ /** Assemble common CLI exit fields while the adapter retains protocol/error observations. */
16
+ export function buildStructuredCliResult(input) {
17
+ const { state, exitCode, signal, stderr, primaryError, spawnError } = input;
18
+ return {
19
+ state, exitCode, signal, stderr, primaryError,
20
+ ...(spawnError ? { spawnError } : {}),
21
+ ...cliUnacceptedFacts(input),
22
+ };
23
+ }
15
24
  /** Only call for a provider protocol error, never assistant text, stderr or a caught transport error. */
16
25
  export function classifyProviderRejection(message) {
17
26
  const text = message?.trim() ?? "";
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { startStructuredCli } from "./structured-exec-pump.js";
3
3
  import { asRecord } from "./structured-content.js";
4
- import { cliUnacceptedFacts } from "./structured-failure.js";
4
+ import { buildStructuredCliResult } from "./structured-failure.js";
5
5
  import { systemPromptArgs, thinkingEffortToGrokEffort } from "./structured-provider-common.js";
6
6
  function grokText(value) {
7
7
  if (typeof value === "string")
@@ -253,9 +253,9 @@ export class GrokRunner {
253
253
  unparsedStdout = true; /* Non-protocol stdout is not evidence of a safe refusal. */
254
254
  }
255
255
  },
256
- finalize: (ctx, exitCode, signal, spawnError) => ({
257
- state, exitCode, signal, stderr: ctx.stderr, primaryError, ...(spawnError ? { spawnError } : {}),
258
- ...cliUnacceptedFacts({ state, primaryError, protocolError, unparsedStdout, signal, ...(spawnError ? { spawnError } : {}) }),
256
+ finalize: (ctx, exitCode, signal, spawnError) => buildStructuredCliResult({
257
+ state, exitCode, signal, stderr: ctx.stderr, primaryError,
258
+ protocolError, unparsedStdout, spawnError,
259
259
  }),
260
260
  });
261
261
  }
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
3
  import { startStructuredCli } from "./structured-exec-pump.js";
4
4
  import { asRecord } from "./structured-content.js";
5
- import { cliUnacceptedFacts } from "./structured-failure.js";
5
+ import { buildStructuredCliResult } from "./structured-failure.js";
6
6
  import { thinkingEffortToOpenCodeVariant, promptWithSystemFallback } from "./structured-provider-common.js";
7
7
  function extractStructuredText(value) {
8
8
  if (typeof value === "string")
@@ -165,14 +165,9 @@ export class OpenCodeRunner {
165
165
  }
166
166
  observer.onUpdate(state);
167
167
  },
168
- finalize: (ctx, exitCode, signal, spawnError) => ({
169
- state,
170
- exitCode,
171
- signal,
172
- stderr: ctx.stderr,
173
- primaryError,
174
- ...(spawnError ? { spawnError } : {}),
175
- ...cliUnacceptedFacts({ state, primaryError, protocolError, unparsedStdout, signal, ...(spawnError ? { spawnError } : {}) }),
168
+ finalize: (ctx, exitCode, signal, spawnError) => buildStructuredCliResult({
169
+ state, exitCode, signal, stderr: ctx.stderr, primaryError,
170
+ protocolError, unparsedStdout, spawnError,
176
171
  }),
177
172
  });
178
173
  }
@@ -1,8 +1,7 @@
1
1
  import type { SessionProvider, SessionRunner, SessionSnapshot, StructuredSessionState } from "./types.js";
2
2
  /**
3
3
  * 把会话级系统提示交给 CLI 的开关。Claude / Qoder / Pi 用 `--append-system-prompt`,
4
- * Grok 用 `--rules`;Codex / OpenCode / Gemini 没有这个入口,只能退回并入本轮消息
5
- * (Gemini 只有 `GEMINI_SYSTEM_MD`,那是整体替换而非追加)。
4
+ * Grok 用 `--rules`;Codex / OpenCode 没有这个入口,只能退回并入本轮消息。
6
5
  * 各 provider 的支持情况由 `--help` 实测得出,新增 provider 前先验。
7
6
  */
8
7
  export declare function systemPromptFlag(provider: SessionProvider | null | undefined): string | null;
@@ -2,8 +2,7 @@ import { isNativeThinkingEffort } from "./provider-catalog.js";
2
2
  import { shellQuote } from "./shell-quote.js";
3
3
  /**
4
4
  * 把会话级系统提示交给 CLI 的开关。Claude / Qoder / Pi 用 `--append-system-prompt`,
5
- * Grok 用 `--rules`;Codex / OpenCode / Gemini 没有这个入口,只能退回并入本轮消息
6
- * (Gemini 只有 `GEMINI_SYSTEM_MD`,那是整体替换而非追加)。
5
+ * Grok 用 `--rules`;Codex / OpenCode 没有这个入口,只能退回并入本轮消息。
7
6
  * 各 provider 的支持情况由 `--help` 实测得出,新增 provider 前先验。
8
7
  */
9
8
  export function systemPromptFlag(provider) {
@@ -34,7 +33,7 @@ export function promptWithSystemFallback(session, prompt) {
34
33
  if (systemPromptFlag(session.provider))
35
34
  return prompt;
36
35
  const base = (session.messages?.length ?? 0) <= 1 ? session.systemPrompt?.trim() : "";
37
- // Knowledge is live per turn, including resumed Codex/OpenCode/Gemini conversations.
36
+ // Knowledge is live per turn, including resumed Codex/OpenCode conversations.
38
37
  const text = [base, session.runtimeSystemPrompt?.trim()].filter(Boolean).join("\n\n");
39
38
  return text ? composeSystemFallback(text, prompt) : prompt;
40
39
  }
@@ -86,7 +85,7 @@ export function isStructuredRunnerForProvider(provider, runner) {
86
85
  return runner === "qoder-cli-print";
87
86
  if (provider === "pi")
88
87
  return runner === "pi-cli-json";
89
- return runner === "gemini-cli-json";
88
+ return false;
90
89
  }
91
90
  export function defaultStructuredRunner(provider) {
92
91
  if (provider === "codex")
@@ -99,8 +98,6 @@ export function defaultStructuredRunner(provider) {
99
98
  return "qoder-cli-print";
100
99
  if (provider === "pi")
101
100
  return "pi-cli-json";
102
- if (provider === "gemini")
103
- return "gemini-cli-json";
104
101
  return "claude-cli-print";
105
102
  }
106
103
  /**
@@ -7,7 +7,6 @@ import { applyOpenCodeEvent } from "./structured-opencode-adapter.js";
7
7
  import { applyGrokEvent } from "./structured-grok-adapter.js";
8
8
  import { applyPiEvent } from "./structured-pi-adapter.js";
9
9
  import { lastPiAssistantMessage } from "./pi-event-projection.js";
10
- import { applyGeminiEvent } from "./structured-gemini-adapter.js";
11
10
  import { normalizeTurnAnchors } from "./structured-provider-common.js";
12
11
  import { enrichStructuredMessages, WAND_PROTOCOL_VERSION } from "./structured-client-protocol.js";
13
12
  export function buildReplayProcessor(session) {
@@ -70,7 +69,7 @@ export function buildReplayProcessor(session) {
70
69
  };
71
70
  return processor;
72
71
  }
73
- // grok-cli-headless / opencode-cli-run / pi-cli-json / gemini-cli-json share the same shape.
72
+ // grok-cli-headless / opencode-cli-run / pi-cli-json share the same shape.
74
73
  const state = {
75
74
  blocks: [],
76
75
  result: "",
@@ -97,9 +96,7 @@ export function buildReplayProcessor(session) {
97
96
  ? applyGrokEvent(state, event)
98
97
  : runner === "opencode-cli-run"
99
98
  ? applyOpenCodeEvent(state, event)
100
- : runner === "gemini-cli-json"
101
- ? applyGeminiEvent(state, event)
102
- : applyPiEvent(state, event, observedAt);
99
+ : applyPiEvent(state, event, observedAt);
103
100
  if (error)
104
101
  processor.primaryError = error;
105
102
  else if (runner === "pi-cli-json") {
@@ -121,7 +118,6 @@ export function recoveredCommandLabel(runner) {
121
118
  case "grok-cli-headless": return "grok -p --output-format streaming-json";
122
119
  case "qoder-cli-print": return "qodercli -p --output-format stream-json";
123
120
  case "pi-cli-json": return "pi --mode json";
124
- case "gemini-cli-json": return "gemini -p --output-format stream-json";
125
121
  default: return "claude -p";
126
122
  }
127
123
  }
@@ -24,7 +24,6 @@ export interface StructuredSessionManagerRunners {
24
24
  grok?: StructuredRunnerAdapter;
25
25
  qoder?: StructuredRunnerAdapter;
26
26
  pi?: StructuredRunnerAdapter;
27
- gemini?: StructuredRunnerAdapter;
28
27
  }
29
28
  interface CreateStructuredSessionOptions {
30
29
  title?: string;
@@ -126,7 +125,7 @@ export declare class StructuredSessionManager {
126
125
  private readonly coreRunner;
127
126
  private readonly coreRunnerSupplied;
128
127
  private coreRestartDrains;
129
- private readonly geminiRunner;
128
+ private readonly unsupportedStructuredProviders;
130
129
  /** Structured CLI runs that were mid-flight when the previous web process died. */
131
130
  private pendingRecoveryIds;
132
131
  private detachedRecoveryPromise;
@@ -24,7 +24,7 @@ import { SessionMetadataService } from "./session-metadata-service.js";
24
24
  import { resolveSessionCwd } from "./session-cwd.js";
25
25
  import { isScheduledConversationSession } from "./conversation-session-preview.js";
26
26
  import { isSessionProvider, providerCliCommand } from "./session-provider.js";
27
- import { resolveSystemAiContext } from "./session-ai-context.js";
27
+ import { resolveSessionProvider, resolveSystemAiContext } from "./session-ai-context.js";
28
28
  import { CodexRunner } from "./structured-codex-adapter.js";
29
29
  import { ClaudeCliRunner } from "./structured-claude-adapter.js";
30
30
  import { OpenCodeRunner } from "./structured-opencode-adapter.js";
@@ -34,7 +34,6 @@ import { PiRunner, isMissingPiSession } from "./structured-pi-adapter.js";
34
34
  import { PiOutputFile } from "./pi-output-file.js";
35
35
  import { PiProtocolReader } from "./pi-protocol-reader.js";
36
36
  import { PiTmuxHost } from "./pi-tmux-host.js";
37
- import { GeminiRunner, isMissingGeminiSession } from "./structured-gemini-adapter.js";
38
37
  import { structuredRunId } from "./structured-exec-host.js";
39
38
  import { defaultStructuredRunner, defaultStructuredState, isStructuredRunnerForProvider, normalizeThinkingEffort, resolveStructuredRunner } from "./structured-provider-common.js";
40
39
  import { TurnQuietHeartbeat } from "./turn-heartbeat.js";
@@ -148,7 +147,7 @@ export class StructuredSessionManager {
148
147
  coreRunner;
149
148
  coreRunnerSupplied;
150
149
  coreRestartDrains = 0;
151
- geminiRunner;
150
+ unsupportedStructuredProviders = new Set();
152
151
  /** Structured CLI runs that were mid-flight when the previous web process died. */
153
152
  pendingRecoveryIds = [];
154
153
  detachedRecoveryPromise = null;
@@ -179,7 +178,6 @@ export class StructuredSessionManager {
179
178
  this.coreRunner = withModelGroups(runners.core ?? new CoreRunner({ config: this.config,
180
179
  decisionAccess: decisionRuntime, openRouter }), config);
181
180
  this.coreRunnerSupplied = runners.core !== undefined;
182
- this.geminiRunner = wrap(runners.gemini ?? new GeminiRunner(undefined, this.execHost));
183
181
  this.metadata = new SessionMetadataService({
184
182
  isDisposed: () => this.disposed,
185
183
  current: (id) => this.sessions.get(id),
@@ -199,8 +197,29 @@ export class StructuredSessionManager {
199
197
  if ((snapshot.sessionKind ?? "pty") !== "structured")
200
198
  continue;
201
199
  const restoredStatus = snapshot.status === "running" ? "idle" : snapshot.status;
202
- const storedProvider = snapshot.provider ?? snapshot.structuredState?.provider;
203
- const provider = isSessionProvider(storedProvider) ? storedProvider : "claude";
200
+ let provider;
201
+ try {
202
+ provider = resolveSessionProvider(snapshot);
203
+ }
204
+ catch (error) {
205
+ // Keep removed/unknown tools as read-only history. Never rewrite their
206
+ // durable identity to another provider or try to adopt their process.
207
+ this.unsupportedStructuredProviders.add(snapshot.id);
208
+ this.sessions.set(snapshot.id, {
209
+ ...snapshot,
210
+ status: "failed",
211
+ titleGenerating: false,
212
+ ...(snapshot.structuredState ? { structuredState: {
213
+ ...snapshot.structuredState,
214
+ inFlight: false,
215
+ activeRequestId: null,
216
+ turnStartedAt: null,
217
+ lastActivityAt: null,
218
+ lastError: error instanceof Error ? error.message : String(error),
219
+ } } : {}),
220
+ });
221
+ continue;
222
+ }
204
223
  const storedRunner = snapshot.runner ?? snapshot.structuredState?.runner;
205
224
  // Legacy/corrupt snapshots are normalized on restore so send dispatch can
206
225
  // rely on the provider/runner invariant without making startup fail.
@@ -1152,11 +1171,9 @@ export class StructuredSessionManager {
1152
1171
  ? "qodercli -p --output-format stream-json"
1153
1172
  : provider === "pi"
1154
1173
  ? "pi --mode json --print"
1155
- : provider === "gemini"
1156
- ? "gemini -p --output-format stream-json"
1157
- : runner === "claude-cli-print"
1158
- ? "claude -p (stream-json)"
1159
- : "claude -p --output-format stream-json",
1174
+ : runner === "claude-cli-print"
1175
+ ? "claude -p (stream-json)"
1176
+ : "claude -p --output-format stream-json",
1160
1177
  cwd: worktreeSetup?.cwd ?? baseCwd,
1161
1178
  mode: options.mode,
1162
1179
  worktreeEnabled: Boolean(worktreeSetup),
@@ -1384,8 +1401,10 @@ export class StructuredSessionManager {
1384
1401
  const userTurn = buildUserTurn(prompt, pendingAsk, userTurnAt);
1385
1402
  const requestId = randomUUID();
1386
1403
  this.unacceptedTeamStarts.delete(id);
1387
- if ((session.provider === "pi" && isMissingPiSession(session.structuredState?.lastError, session.claudeSessionId))
1388
- || (session.provider === "gemini" && isMissingGeminiSession(session.structuredState?.lastError))) {
1404
+ if (this.unsupportedStructuredProviders.has(id)) {
1405
+ throw new Error(`会话使用的 provider 已不受支持,不能继续发送。`);
1406
+ }
1407
+ if (session.provider === "pi" && isMissingPiSession(session.structuredState?.lastError, session.claudeSessionId)) {
1389
1408
  session = { ...session, claudeSessionId: null };
1390
1409
  }
1391
1410
  const updated = {
@@ -1599,15 +1618,6 @@ export class StructuredSessionManager {
1599
1618
  else if (provider === "pi") {
1600
1619
  await this.runPiTurn(id, updated, prompt, requestId);
1601
1620
  }
1602
- else if (provider === "gemini") {
1603
- await this.runStructuredStreaming(id, updated, prompt, requestId, {
1604
- runner: this.geminiRunner,
1605
- provider: "gemini",
1606
- commandLabel: "gemini -p --output-format stream-json",
1607
- logKind: "gemini-json",
1608
- installHint: "请安装 @google/gemini-cli ≥ 0.11(`npm i -g @google/gemini-cli@latest`),或重跑 `wand service:install` 刷新服务的 PATH",
1609
- });
1610
- }
1611
1621
  else {
1612
1622
  await this.runStructuredStreaming(id, updated, cliClaudePrompt, requestId);
1613
1623
  }
@@ -2582,9 +2592,6 @@ export class StructuredSessionManager {
2582
2592
  if (provider === "pi" && isMissingPiSession(result.stderr, current.claudeSessionId)) {
2583
2593
  failed.claudeSessionId = null;
2584
2594
  }
2585
- if (provider === "gemini" && isMissingGeminiSession(`${result.stderr}\n${errorText}`)) {
2586
- failed.claudeSessionId = null;
2587
- }
2588
2595
  this.sessions.set(sessionId, failed);
2589
2596
  this.saveAuthoritativeSession(failed);
2590
2597
  this.emitStructuredSnapshot(failed);
@@ -0,0 +1,41 @@
1
+ export type TaskOrganizeMode = "auto" | "all";
2
+ export interface OrganizeByTitleResult {
3
+ ok: true;
4
+ foundTasks: number;
5
+ eligibleTasks: number;
6
+ skippedTasks: number;
7
+ movedSessions: Array<{
8
+ sessionId: string;
9
+ taskId: string;
10
+ taskName: string;
11
+ }>;
12
+ categorizedTasks: Array<{
13
+ taskId: string;
14
+ parentTaskId: string;
15
+ parentTitle: string;
16
+ }>;
17
+ refused: Array<{
18
+ id: string;
19
+ kind: "session" | "task";
20
+ reason: string;
21
+ }>;
22
+ }
23
+ /** Server resolves full membership, independent of sidebar pagination or search. */
24
+ export type TaskOrganizeTarget = {
25
+ kind: "all";
26
+ } | {
27
+ kind: "workspace";
28
+ workspaceId: string;
29
+ } | {
30
+ kind: "directory";
31
+ cwd: string;
32
+ };
33
+ export declare const TASK_ORGANIZE_OPTIONS: ({
34
+ value: "auto";
35
+ label: string;
36
+ hint: string;
37
+ } | {
38
+ value: "all";
39
+ label: string;
40
+ hint: string;
41
+ })[];
@@ -0,0 +1,4 @@
1
+ export const TASK_ORGANIZE_OPTIONS = [
2
+ { value: "auto", label: "自动任务归类", hint: "仅系统自动标题,跳过用户自定标题" },
3
+ { value: "all", label: "所有任务归类", hint: "包含用户自定标题的任务" },
4
+ ];
@@ -0,0 +1,21 @@
1
+ import { type QuickCommitAiOptions } from "./ai-text-runner.js";
2
+ import type { AutoTaskTitleOptions } from "./task-title-service.js";
3
+ import { type TitleJudge } from "./task-title-organize.js";
4
+ import type { WandStorage } from "./storage.js";
5
+ import type { OrganizeByTitleResult } from "./task-organize-contract.js";
6
+ export type { OrganizeByTitleResult } from "./task-organize-contract.js";
7
+ export declare function titleJudgeForStorage(storage: WandStorage, options: AutoTaskTitleOptions): TitleJudge | undefined;
8
+ export declare function systemTitleJudge(ai: QuickCommitAiOptions, cwd: string): TitleJudge;
9
+ /**
10
+ * Classify the selected tasks by title and move in-scope sessions to the task
11
+ * whose title is the same work. Sessions of selected tasks are in scope even
12
+ * when the sidebar selection did not tick them individually.
13
+ */
14
+ export declare function organizeSelectedByTitle(storage: WandStorage, input: {
15
+ taskIds: readonly string[];
16
+ sessionIds?: readonly string[];
17
+ mode?: "auto" | "all";
18
+ }, options?: {
19
+ judge?: TitleJudge;
20
+ refreshSession?(sessionId: string): void;
21
+ }): Promise<OrganizeByTitleResult>;
@@ -0,0 +1,168 @@
1
+ import { callConfiguredAiText } from "./ai-text-runner.js";
2
+ import { getErrorMessage } from "./error-utils.js";
3
+ import { resolveSystemAiContext } from "./session-ai-context.js";
4
+ import { parentTaskIdFrom } from "./task-agent-parsing.js";
5
+ import { planTitleOrganization, parseTitleJudgment, titleJudgmentPrompt, } from "./task-title-organize.js";
6
+ import { isGlobalWorkspace, normalizeProjectCwd, projectCwdForSession } from "./workspace-binding.js";
7
+ export function titleJudgeForStorage(storage, options) {
8
+ const config = options.config;
9
+ if (!config)
10
+ return undefined;
11
+ const provider = config.defaultProvider;
12
+ const ai = resolveSystemAiContext({
13
+ provider,
14
+ structuredState: undefined,
15
+ runner: undefined,
16
+ command: provider === "qoder" ? "qodercli" : (provider || "pi"),
17
+ selectedModel: null,
18
+ thinkingEffort: "off",
19
+ }, config, storage.getSystemSiliconEmployee(), options.free);
20
+ if (ai.employeeChannelOnly && !ai.cliCandidates?.length)
21
+ return undefined;
22
+ return systemTitleJudge(ai, options.cwd || config.defaultCwd || process.cwd());
23
+ }
24
+ export function systemTitleJudge(ai, cwd) {
25
+ return async (input) => {
26
+ const request = titleJudgmentPrompt(input);
27
+ return callConfiguredAiText(request, cwd || process.cwd(), "", {
28
+ ...ai,
29
+ thinkingEffort: "off",
30
+ }, parseTitleJudgment);
31
+ };
32
+ }
33
+ function directoryKeyForTask(storage, task) {
34
+ const workspace = storage.getWorkspace(task.workspaceId);
35
+ if (workspace && !isGlobalWorkspace(workspace))
36
+ return normalizeProjectCwd(workspace.cwd);
37
+ return normalizeProjectCwd(task.cwd || workspace?.cwd || "");
38
+ }
39
+ function sessionRecord(session, teamOwned) {
40
+ return {
41
+ id: session.id,
42
+ title: session.title?.trim() || "",
43
+ directoryKey: projectCwdForSession(session),
44
+ workspaceTaskId: session.workspaceTaskId ?? null,
45
+ teamOwned,
46
+ archived: session.archived === true,
47
+ };
48
+ }
49
+ /**
50
+ * Classify the selected tasks by title and move in-scope sessions to the task
51
+ * whose title is the same work. Sessions of selected tasks are in scope even
52
+ * when the sidebar selection did not tick them individually.
53
+ */
54
+ export async function organizeSelectedByTitle(storage, input, options = {}) {
55
+ const taskIds = [...new Set(input.taskIds.map((id) => id.trim()).filter(Boolean))];
56
+ const sessionIds = [...new Set((input.sessionIds ?? []).map((id) => id.trim()).filter(Boolean))];
57
+ const tasks = [];
58
+ const snapshots = new Map();
59
+ let foundTasks = 0;
60
+ let skippedTasks = 0;
61
+ const refused = [];
62
+ for (const id of taskIds) {
63
+ const task = storage.getWorkspaceTask(id);
64
+ const card = task ? storage.getWandTaskByWorkspaceTaskId(task.id) : null;
65
+ if (!task || !card) {
66
+ refused.push({ id, kind: "task", reason: "未找到该任务" });
67
+ continue;
68
+ }
69
+ foundTasks++;
70
+ if ((input.mode ?? "auto") === "auto" && card.titleSource !== "auto") {
71
+ skippedTasks++;
72
+ continue;
73
+ }
74
+ snapshots.set(task.id, { name: task.name, title: card.title, titleSource: card.titleSource });
75
+ tasks.push({
76
+ id: task.id,
77
+ cardId: card.id,
78
+ titles: [task.name, card.title],
79
+ workspaceId: card.workspaceId,
80
+ directoryKey: directoryKeyForTask(storage, task),
81
+ status: card.status,
82
+ parentTaskId: card.parentTaskId,
83
+ createdAt: task.createdAt,
84
+ archived: card.status === "archived" || task.archived === true,
85
+ });
86
+ }
87
+ if (!tasks.length) {
88
+ return { ok: true, foundTasks, eligibleTasks: 0, skippedTasks, movedSessions: [], categorizedTasks: [], refused };
89
+ }
90
+ const teamIds = new Set([
91
+ ...storage.listAiTeamRunChatMarkers().keys(),
92
+ ...storage.listAiTeamStepSessionMarkers().keys(),
93
+ ]);
94
+ const sessions = new Map();
95
+ for (const task of tasks) {
96
+ for (const session of storage.listSessionsByWorkspaceTaskSlim(task.id)) {
97
+ sessions.set(session.id, sessionRecord(session, teamIds.has(session.id)));
98
+ }
99
+ }
100
+ for (const id of sessionIds) {
101
+ if (sessions.has(id))
102
+ continue;
103
+ const session = storage.getSessionSlim(id);
104
+ if (!session) {
105
+ refused.push({ id, kind: "session", reason: "未找到该会话" });
106
+ continue;
107
+ }
108
+ // Explicit session selection must not pull user-named tasks into auto mode.
109
+ if ((input.mode ?? "auto") === "auto" && session.workspaceTaskId
110
+ && storage.getWandTaskByWorkspaceTaskId(session.workspaceTaskId)?.titleSource !== "auto")
111
+ continue;
112
+ sessions.set(session.id, sessionRecord(session, teamIds.has(session.id)));
113
+ }
114
+ const plan = await planTitleOrganization(tasks, [...sessions.values()], options.judge);
115
+ refused.push(...plan.refused);
116
+ // A semantic judgment is asynchronous: manual renames during it invalidate the pass.
117
+ if (tasks.some((task) => {
118
+ const current = storage.getWorkspaceTask(task.id);
119
+ const card = storage.getWandTask(task.cardId);
120
+ const before = snapshots.get(task.id);
121
+ return !current || !card || current.name !== before.name || card.title !== before.title
122
+ || card.titleSource !== before.titleSource || card.status !== task.status
123
+ || card.parentTaskId !== task.parentTaskId || card.workspaceId !== task.workspaceId;
124
+ })) {
125
+ refused.push({ id: "changed", kind: "task", reason: "任务在归类期间已改变,请重新归类" });
126
+ return { ok: true, foundTasks, eligibleTasks: tasks.length, skippedTasks, movedSessions: [], categorizedTasks: [], refused };
127
+ }
128
+ const movedSessions = [];
129
+ for (const move of plan.sessionMoves) {
130
+ const before = sessions.get(move.sessionId);
131
+ const current = storage.getSessionSlim(move.sessionId);
132
+ if (!before || !current || current.archived || (current.workspaceTaskId ?? null) !== before.workspaceTaskId
133
+ || (current.title?.trim() || "") !== before.title || projectCwdForSession(current) !== before.directoryKey
134
+ || ((input.mode ?? "auto") === "auto" && current.workspaceTaskId
135
+ && storage.getWandTaskByWorkspaceTaskId(current.workspaceTaskId)?.titleSource !== "auto")) {
136
+ refused.push({ id: move.sessionId, kind: "session", reason: "会话在归类期间已改变,未改归属" });
137
+ continue;
138
+ }
139
+ const task = storage.getWorkspaceTask(move.taskId);
140
+ try {
141
+ storage.moveSessionToWorkspaceTask(move.sessionId, move.taskId);
142
+ options.refreshSession?.(move.sessionId);
143
+ movedSessions.push({ sessionId: move.sessionId, taskId: move.taskId, taskName: task?.name ?? "" });
144
+ }
145
+ catch (error) {
146
+ refused.push({ id: move.sessionId, kind: "session", reason: getErrorMessage(error, "无法移动会话") });
147
+ }
148
+ }
149
+ const categorizedTasks = [];
150
+ for (const link of plan.taskParents) {
151
+ const task = tasks.find((item) => item.id === link.workspaceTaskId);
152
+ const card = task ? storage.getWandTask(task.cardId) : null;
153
+ const parent = storage.getWandTask(link.parentCardId);
154
+ if (!task || !card || !parent) {
155
+ refused.push({ id: link.workspaceTaskId, kind: "task", reason: "未找到要归入的任务" });
156
+ continue;
157
+ }
158
+ try {
159
+ const parentTaskId = parentTaskIdFrom(storage, link.parentCardId, card.workspaceId, card.id, card.parentTaskId);
160
+ storage.updateWandTask(card.id, { parentTaskId });
161
+ categorizedTasks.push({ taskId: task.id, parentTaskId: parent.id, parentTitle: parent.title });
162
+ }
163
+ catch (error) {
164
+ refused.push({ id: task.id, kind: "task", reason: getErrorMessage(error, "无法归类任务") });
165
+ }
166
+ }
167
+ return { ok: true, foundTasks, eligibleTasks: tasks.length, skippedTasks, movedSessions, categorizedTasks, refused };
168
+ }