@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.
- package/README.md +25 -30
- package/dist/ai-text-runner.d.ts +2 -10
- package/dist/ai-text-runner.js +13 -74
- package/dist/build-info.json +3 -3
- package/dist/cli.js +47 -9
- package/dist/config.d.ts +2 -3
- package/dist/config.js +26 -5
- package/dist/core-status-cli.d.ts +1 -1
- package/dist/core-status-cli.js +3 -1
- package/dist/daemon-connection.d.ts +10 -0
- package/dist/daemon-connection.js +28 -0
- package/dist/diagnostic-logs.d.ts +10 -0
- package/dist/diagnostic-logs.js +121 -0
- package/dist/employee-text.js +1 -3
- package/dist/git-quick-commit.d.ts +1 -1
- package/dist/git-quick-commit.js +2 -9
- package/dist/layout-tree.d.ts +3 -1
- package/dist/layout-tree.js +8 -0
- package/dist/local-model-process.js +20 -0
- package/dist/local-model-setup.js +5 -3
- package/dist/managed-temp.d.ts +32 -0
- package/dist/managed-temp.js +176 -0
- package/dist/middleware/api-scope.js +1 -0
- package/dist/models/shared.d.ts +0 -6
- package/dist/models/shared.js +0 -12
- package/dist/models/thinking.d.ts +0 -2
- package/dist/models/thinking.js +0 -2
- package/dist/models.d.ts +0 -1
- package/dist/models.js +3 -11
- package/dist/native-session-title.d.ts +1 -1
- package/dist/native-session-title.js +1 -1
- package/dist/pi-model-discovery.js +5 -17
- package/dist/pi-resource-recommendation.d.ts +1 -1
- package/dist/pi-resource-recommendation.js +129 -42
- package/dist/process-manager-native-history.js +1 -6
- package/dist/process-manager.d.ts +0 -1
- package/dist/process-manager.js +5 -18
- package/dist/provider-catalog.d.ts +1 -2
- package/dist/provider-catalog.js +1 -6
- package/dist/provider-cli-updater.js +1 -9
- package/dist/provider-history-scanner.d.ts +0 -3
- package/dist/provider-history-scanner.js +0 -26
- package/dist/render-daemon-client.js +6 -29
- package/dist/render-structured-client.js +2 -7
- package/dist/retention.js +1 -6
- package/dist/run-command.d.ts +5 -0
- package/dist/run-command.js +14 -1
- package/dist/server-session-lifecycle-routes.js +1 -1
- package/dist/server-storage-cleanup-routes.d.ts +3 -0
- package/dist/server-storage-cleanup-routes.js +19 -0
- package/dist/server-workspace-routes.d.ts +4 -1
- package/dist/server-workspace-routes.js +75 -9
- package/dist/server.js +21 -16
- package/dist/service-command-privilege.d.ts +17 -0
- package/dist/service-command-privilege.js +55 -0
- package/dist/service-manager/index.d.ts +3 -0
- package/dist/service-manager/index.js +3 -0
- package/dist/service-manager/launchd.d.ts +1 -0
- package/dist/service-manager/launchd.js +15 -0
- package/dist/service-manager/systemd.d.ts +1 -0
- package/dist/service-manager/systemd.js +11 -0
- package/dist/session-ai-context.js +17 -6
- package/dist/session-completion-state.d.ts +2 -0
- package/dist/session-completion-state.js +6 -2
- package/dist/session-logger.js +7 -1
- package/dist/session-model.d.ts +4 -0
- package/dist/session-model.js +13 -4
- package/dist/session-registry.d.ts +1 -1
- package/dist/session-registry.js +0 -3
- package/dist/silicon-employee-draft.js +0 -1
- package/dist/speech-service.js +18 -8
- package/dist/storage/mappers.js +6 -10
- package/dist/storage/migrations.js +14 -0
- package/dist/storage/uploads-repository.d.ts +36 -0
- package/dist/storage/uploads-repository.js +98 -0
- package/dist/storage-cleanup.d.ts +50 -0
- package/dist/storage-cleanup.js +344 -0
- package/dist/storage.d.ts +3 -1
- package/dist/storage.js +4 -0
- package/dist/structured-failure.d.ts +2 -0
- package/dist/structured-failure.js +9 -0
- package/dist/structured-grok-adapter.js +4 -4
- package/dist/structured-opencode-adapter.js +4 -9
- package/dist/structured-provider-common.d.ts +1 -2
- package/dist/structured-provider-common.js +3 -6
- package/dist/structured-session-manager-replay.js +2 -6
- package/dist/structured-session-manager.d.ts +1 -2
- package/dist/structured-session-manager.js +32 -25
- package/dist/task-organize-contract.d.ts +41 -0
- package/dist/task-organize-contract.js +4 -0
- package/dist/task-title-organize-service.d.ts +21 -0
- package/dist/task-title-organize-service.js +168 -0
- package/dist/task-title-organize.d.ts +77 -0
- package/dist/task-title-organize.js +346 -0
- package/dist/terminal-daemon-client.js +10 -48
- package/dist/time-utils.d.ts +2 -0
- package/dist/time-utils.js +7 -0
- package/dist/tui/attach.js +6 -5
- package/dist/tui/commands.d.ts +4 -1
- package/dist/tui/commands.js +36 -25
- package/dist/tui/index.js +2 -2
- package/dist/tui/service-actions.js +5 -4
- package/dist/tui/service-command.d.ts +6 -0
- package/dist/tui/service-command.js +40 -0
- package/dist/tui/service-panel.js +9 -8
- package/dist/types.d.ts +4 -4
- package/dist/upload-reference-guard.d.ts +4 -0
- package/dist/upload-reference-guard.js +29 -0
- package/dist/upload-routes.d.ts +4 -0
- package/dist/upload-routes.js +121 -21
- package/dist/user-profile.d.ts +1 -1
- package/dist/user-profile.js +8 -3
- package/dist/web-ui/content/ai-teams.js +9 -9
- package/dist/web-ui/content/module-manifest.json +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chat-render-EODYMQF6.js → 0051bd3d55f10ab7/chat-render-RY2I2CF3.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-ZTEQSXPA.js → 0051bd3d55f10ab7/chunk-2F6J2MKX.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-A33YPCY6.js → 0051bd3d55f10ab7/chunk-7AFM245W.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-QNUWUMKW.js → 0051bd3d55f10ab7/chunk-MHX2QXIS.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-WGADUEIT.js → 0051bd3d55f10ab7/chunk-RAAPETNH.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-I3UYDQLS.js → 0051bd3d55f10ab7/chunk-RXKBHYUR.js} +2 -2
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-3TCUG766.js → 0051bd3d55f10ab7/chunk-U4EDP4TI.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-HK7RBU3M.js → 0051bd3d55f10ab7/chunk-WHXQR7JT.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/employee-card-NV34TZIH.js +1 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-2PU6TV2I.js → 0051bd3d55f10ab7/host-2IVIZ2YC.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-BPYNMKFC.js → 0051bd3d55f10ab7/host-AZXNFCRW.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-HZIKPWYY.js → 0051bd3d55f10ab7/host-EPEQX22R.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-YELXZID4.js → 0051bd3d55f10ab7/host-FZYIMOLC.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-OGD6AWHL.js → 0051bd3d55f10ab7/host-G6GL4SNL.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-E3SCPOIU.js → 0051bd3d55f10ab7/host-PEILC5NU.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-OWD32FZ3.js → 0051bd3d55f10ab7/host-PHHNGVNL.js} +2 -2
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-CXDAFMXX.js → 0051bd3d55f10ab7/host-Q524TMSR.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-RO5MB6RB.js +69 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-J5XRAQFP.js → 0051bd3d55f10ab7/host-WOQRK6NG.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-HKXE2DOT.js → 0051bd3d55f10ab7/host-XNIR3NTJ.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-4E4H4JZJ.js → 0051bd3d55f10ab7/host-XRVOIEP6.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-ZDKOQEMS.js +1 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-bridge-PMQZRG5G.js +1 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/library-bridge-Y3Y24CPQ.js → 0051bd3d55f10ab7/library-bridge-DTMQIHVJ.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/page-GQBUU7T7.js → 0051bd3d55f10ab7/page-PY4JUL4W.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/panel-PG662OC5.js → 0051bd3d55f10ab7/panel-BBQFC55Q.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/runtime.js +974 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/task-board-host-PXZSWBBG.js +2 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-agent-picker-DJSECSY6.js +1 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/workspace-window-HV4T3WKR.js → 0051bd3d55f10ab7/workspace-window-MNMKSOBG.js} +1 -1
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +27 -27
- package/dist/web-ui/embedded-assets.js +4 -4
- package/dist/web-ui/provider-identity.d.ts +4 -5
- package/dist/web-ui/provider-identity.js +4 -10
- package/dist/web-ui/react/workspaces/sidebar-manage.d.ts +39 -0
- package/dist/web-ui/react/workspaces/sidebar-manage.js +149 -0
- package/dist/web-ui/react/workspaces/types.d.ts +408 -0
- package/dist/web-ui/react/workspaces/types.js +16 -0
- package/dist/web-ui/session-activity.js +1 -1
- package/package.json +2 -2
- package/dist/structured-gemini-adapter.d.ts +0 -40
- package/dist/structured-gemini-adapter.js +0 -208
- package/dist/web-ui/content/modules/2548dbe0d0783577/chunk-XGVT4ZWT.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/employee-card-LJ3TX72B.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/host-ITV2QZ4A.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/host-XNWC7KPR.js +0 -69
- package/dist/web-ui/content/modules/2548dbe0d0783577/host-bridge-G5HEVADT.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/runtime.js +0 -946
- package/dist/web-ui/content/modules/2548dbe0d0783577/task-board-host-RAN2Z2KR.js +0 -2
- package/dist/web-ui/content/modules/2548dbe0d0783577/workspace-agent-picker-FXLH6N5K.js +0 -1
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-YX475LXO.js → 0051bd3d55f10ab7/chunk-6C6A2RHG.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-NP35GDQ3.js → 0051bd3d55f10ab7/chunk-ECWYA3U5.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-47EVOSJS.js → 0051bd3d55f10ab7/chunk-T35D57FW.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577 → 0051bd3d55f10ab7}/chunk-YOXXNXOX.js +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-C3K34WJD.js → 0051bd3d55f10ab7/chunk-YQJ6XYV7.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/host-J6HBDK4A.js → 0051bd3d55f10ab7/host-2AQAIUNO.js} +0 -0
- /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 {
|
|
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,
|
|
258
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
:
|
|
1156
|
-
? "
|
|
1157
|
-
:
|
|
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 (
|
|
1388
|
-
|
|
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,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
|
+
}
|