@co0ontty/wand 4.27.1 → 4.28.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 +6 -6
- package/dist/build-info.json +3 -3
- package/dist/cli-api.d.ts +11 -0
- package/dist/cli-api.js +76 -0
- package/dist/cli.js +97 -0
- package/dist/config.d.ts +4 -3
- package/dist/config.js +20 -3
- package/dist/git-quick-commit.js +47 -1
- package/dist/git-worktree.d.ts +11 -0
- package/dist/git-worktree.js +72 -3
- package/dist/mission-diff.d.ts +8 -0
- package/dist/mission-diff.js +65 -0
- package/dist/mission-types.d.ts +90 -0
- package/dist/mission-types.js +1 -0
- package/dist/missions.d.ts +32 -0
- package/dist/missions.js +344 -0
- package/dist/models.d.ts +1 -0
- package/dist/models.js +10 -1
- package/dist/path-repair.js +1 -1
- package/dist/process-manager.js +14 -1
- package/dist/prompt-optimizer.d.ts +2 -2
- package/dist/prompt-optimizer.js +11 -32
- package/dist/provider-cli-updater.d.ts +1 -1
- package/dist/provider-cli-updater.js +8 -0
- package/dist/resume-policy.js +5 -3
- package/dist/server-mission-routes.d.ts +3 -0
- package/dist/server-mission-routes.js +81 -0
- package/dist/server-session-routes.js +5 -3
- package/dist/server-settings-routes.js +5 -0
- package/dist/server-update-routes.js +1 -1
- package/dist/server.js +31 -5
- package/dist/session-ai-context.d.ts +3 -3
- package/dist/session-ai-context.js +14 -18
- package/dist/storage.d.ts +15 -0
- package/dist/storage.js +221 -1
- package/dist/structured-pi-adapter.d.ts +11 -0
- package/dist/structured-pi-adapter.js +135 -0
- package/dist/structured-provider-common.d.ts +1 -0
- package/dist/structured-provider-common.js +16 -1
- package/dist/structured-session-manager.d.ts +4 -0
- package/dist/structured-session-manager.js +21 -7
- package/dist/system-ai.js +1 -1
- package/dist/types.d.ts +9 -4
- package/dist/web-ui/content/scripts.js +146 -69
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/web-ui/provider-identity.d.ts +2 -1
- package/dist/web-ui/provider-identity.js +9 -1
- package/package.json +1 -1
package/dist/prompt-optimizer.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { callSystemAiTextWithFallback } from "./system-ai.js";
|
|
3
|
-
const CLAUDE_TIMEOUT_MS = 60_000;
|
|
1
|
+
import { callConfiguredAiText } from "./git-quick-commit.js";
|
|
4
2
|
const MAX_INPUT_LENGTH = 8000;
|
|
5
3
|
export class PromptOptimizeError extends Error {
|
|
6
4
|
code;
|
|
@@ -10,24 +8,6 @@ export class PromptOptimizeError extends Error {
|
|
|
10
8
|
this.name = "PromptOptimizeError";
|
|
11
9
|
}
|
|
12
10
|
}
|
|
13
|
-
async function callClaudeText(prompt, cwd, language) {
|
|
14
|
-
try {
|
|
15
|
-
return await runClaudePrint(prompt, { cwd, timeoutMs: CLAUDE_TIMEOUT_MS, language });
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
if (error instanceof ClaudeRunError) {
|
|
19
|
-
// 翻译成 prompt-optimizer 自己的话术 + 错误码(与原文案保持一致)。
|
|
20
|
-
if (error.code === "CLAUDE_TIMEOUT") {
|
|
21
|
-
throw new PromptOptimizeError("Claude 优化超时,请稍后重试。", "CLAUDE_TIMEOUT");
|
|
22
|
-
}
|
|
23
|
-
if (error.code === "CLAUDE_EMPTY_RESULT") {
|
|
24
|
-
throw new PromptOptimizeError("Claude 返回了空结果。", "EMPTY_RESULT");
|
|
25
|
-
}
|
|
26
|
-
throw new PromptOptimizeError(error.message, error.code);
|
|
27
|
-
}
|
|
28
|
-
throw error;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
11
|
function buildOptimizePrompt(userInput, language) {
|
|
32
12
|
const lang = (language || "").trim() || "中文";
|
|
33
13
|
return [
|
|
@@ -42,7 +22,7 @@ function buildOptimizePrompt(userInput, language) {
|
|
|
42
22
|
userInput,
|
|
43
23
|
].join("\n");
|
|
44
24
|
}
|
|
45
|
-
export async function optimizePrompt(rawText, language, cwd,
|
|
25
|
+
export async function optimizePrompt(rawText, language, cwd, ai = {}) {
|
|
46
26
|
const text = (rawText || "").trim();
|
|
47
27
|
if (!text) {
|
|
48
28
|
throw new PromptOptimizeError("请先输入要优化的内容。", "EMPTY_INPUT");
|
|
@@ -52,16 +32,15 @@ export async function optimizePrompt(rawText, language, cwd, systemAi) {
|
|
|
52
32
|
}
|
|
53
33
|
const prompt = buildOptimizePrompt(text, language);
|
|
54
34
|
let raw;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
raw = await callSystemAiTextWithFallback(prompt, systemAi);
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
raw = await callClaudeText(prompt, cwd, language);
|
|
61
|
-
}
|
|
35
|
+
try {
|
|
36
|
+
raw = await callConfiguredAiText(prompt, cwd ?? process.cwd(), language, ai);
|
|
62
37
|
}
|
|
63
|
-
|
|
64
|
-
|
|
38
|
+
catch (error) {
|
|
39
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
40
|
+
const code = error && typeof error === "object" && "code" in error && typeof error.code === "string"
|
|
41
|
+
? error.code
|
|
42
|
+
: "AI_OPTIMIZE_FAILED";
|
|
43
|
+
throw new PromptOptimizeError(message, code);
|
|
65
44
|
}
|
|
66
45
|
const cleaned = raw
|
|
67
46
|
.replace(/^```[a-zA-Z]*\n?/, "")
|
|
@@ -69,7 +48,7 @@ export async function optimizePrompt(rawText, language, cwd, systemAi) {
|
|
|
69
48
|
.replace(/^["'`]+|["'`]+$/g, "")
|
|
70
49
|
.trim();
|
|
71
50
|
if (!cleaned) {
|
|
72
|
-
throw new PromptOptimizeError("
|
|
51
|
+
throw new PromptOptimizeError("AI 返回了空结果。", "EMPTY_RESULT");
|
|
73
52
|
}
|
|
74
53
|
return cleaned;
|
|
75
54
|
}
|
|
@@ -44,6 +44,14 @@ const PROVIDER_CLI_SPECS = [
|
|
|
44
44
|
versionArgs: ["--version"],
|
|
45
45
|
updateArgs: ["update"],
|
|
46
46
|
},
|
|
47
|
+
{
|
|
48
|
+
id: "pi",
|
|
49
|
+
label: "Pi CLI",
|
|
50
|
+
command: "pi",
|
|
51
|
+
npmPackage: "@mariozechner/pi-coding-agent",
|
|
52
|
+
versionArgs: ["--version"],
|
|
53
|
+
updateArgs: ["update", "self"],
|
|
54
|
+
},
|
|
47
55
|
];
|
|
48
56
|
function childEnv(options) {
|
|
49
57
|
return options.env ?? buildChildEnv(options.inheritEnv !== false);
|
package/dist/resume-policy.js
CHANGED
|
@@ -18,7 +18,7 @@ function resumeArgumentPattern(provider) {
|
|
|
18
18
|
if (provider === "codex") {
|
|
19
19
|
return new RegExp(`(?:^|\\s)resume\\s+${SAFE_PROVIDER_SESSION_ID_SOURCE}(?=\\s|$)`, "i");
|
|
20
20
|
}
|
|
21
|
-
if (provider === "opencode") {
|
|
21
|
+
if (provider === "opencode" || provider === "pi") {
|
|
22
22
|
return new RegExp(`(?:^|\\s)(?:--session|-s)\\s+${SAFE_PROVIDER_SESSION_ID_SOURCE}(?=\\s|$)`, "i");
|
|
23
23
|
}
|
|
24
24
|
return new RegExp(`(?:^|\\s)(?:--resume|-r)\\s+${SAFE_PROVIDER_SESSION_ID_SOURCE}(?=\\s|$)`, "i");
|
|
@@ -34,13 +34,13 @@ export function getProviderCommandSessionId(provider, command) {
|
|
|
34
34
|
const resumed = getProviderResumeCommandSessionId(provider, command);
|
|
35
35
|
if (resumed)
|
|
36
36
|
return resumed;
|
|
37
|
-
if (provider === "codex" || provider === "opencode")
|
|
37
|
+
if (provider === "codex" || provider === "opencode" || provider === "pi")
|
|
38
38
|
return null;
|
|
39
39
|
return assignedSessionIdPattern().exec(command)?.[1] ?? null;
|
|
40
40
|
}
|
|
41
41
|
function stripProviderResumeArgument(provider, command) {
|
|
42
42
|
const withoutResume = command.replace(resumeArgumentPattern(provider), " ");
|
|
43
|
-
const withoutAssignedId = provider === "codex" || provider === "opencode"
|
|
43
|
+
const withoutAssignedId = provider === "codex" || provider === "opencode" || provider === "pi"
|
|
44
44
|
? withoutResume
|
|
45
45
|
: withoutResume.replace(assignedSessionIdPattern(), " ");
|
|
46
46
|
return withoutAssignedId.replace(/\s+/g, " ").trim();
|
|
@@ -55,5 +55,7 @@ export function buildProviderResumeCommand(provider, command, providerSessionId)
|
|
|
55
55
|
return `${base} resume ${providerSessionId}`;
|
|
56
56
|
if (provider === "opencode")
|
|
57
57
|
return `${base} --session ${providerSessionId}`;
|
|
58
|
+
if (provider === "pi")
|
|
59
|
+
return `${base} --session ${providerSessionId}`;
|
|
58
60
|
return `${base} --resume ${providerSessionId}`;
|
|
59
61
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
2
|
+
function sendMissionError(res, error) {
|
|
3
|
+
const message = getErrorMessage(error, "任务操作失败。");
|
|
4
|
+
const missing = /^(任务不存在|任务 attempt 不存在)|没有关联会话|当前不可用/.test(message);
|
|
5
|
+
res.status(missing ? 404 : 400).json({ error: message });
|
|
6
|
+
}
|
|
7
|
+
export function registerMissionRoutes(app, missions) {
|
|
8
|
+
app.get("/api/inbox", (_req, res) => {
|
|
9
|
+
res.json({ items: missions.inbox() });
|
|
10
|
+
});
|
|
11
|
+
app.post("/api/inbox/read", (req, res) => {
|
|
12
|
+
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId : undefined;
|
|
13
|
+
missions.markInboxRead(sessionId);
|
|
14
|
+
res.json({ ok: true });
|
|
15
|
+
});
|
|
16
|
+
app.get("/api/missions", (_req, res) => {
|
|
17
|
+
res.json({ missions: missions.list() });
|
|
18
|
+
});
|
|
19
|
+
app.post("/api/missions", (req, res) => {
|
|
20
|
+
try {
|
|
21
|
+
res.status(201).json(missions.create(req.body));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
sendMissionError(res, error);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
app.get("/api/missions/:missionId", (req, res) => {
|
|
28
|
+
const mission = missions.get(req.params.missionId);
|
|
29
|
+
if (!mission) {
|
|
30
|
+
res.status(404).json({ error: "任务不存在。" });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
res.json(mission);
|
|
34
|
+
});
|
|
35
|
+
app.post("/api/missions/:missionId/archive", (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
res.json(missions.archive(req.params.missionId));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
sendMissionError(res, error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
app.get("/api/missions/:missionId/attempts/:attemptId/diff", (req, res) => {
|
|
44
|
+
try {
|
|
45
|
+
res.json(missions.diff(req.params.missionId, req.params.attemptId));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
sendMissionError(res, error);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
app.post("/api/missions/:missionId/attempts/:attemptId/comments", (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
res.status(201).json(missions.addReviewComment(req.params.missionId, req.params.attemptId, req.body));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
sendMissionError(res, error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
app.post("/api/missions/:missionId/attempts/:attemptId/review/send", (req, res) => {
|
|
60
|
+
try {
|
|
61
|
+
const commentIds = Array.isArray(req.body?.commentIds)
|
|
62
|
+
? req.body.commentIds.filter((id) => typeof id === "string")
|
|
63
|
+
: undefined;
|
|
64
|
+
res.status(202).json({ comments: missions.sendReview(req.params.missionId, req.params.attemptId, commentIds) });
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
sendMissionError(res, error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
app.post("/api/missions/:missionId/attempts/:attemptId/review/resolve", (req, res) => {
|
|
71
|
+
try {
|
|
72
|
+
const commentIds = Array.isArray(req.body?.commentIds)
|
|
73
|
+
? req.body.commentIds.filter((id) => typeof id === "string")
|
|
74
|
+
: [];
|
|
75
|
+
res.json({ comments: missions.resolveReview(req.params.missionId, req.params.attemptId, commentIds) });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
sendMissionError(res, error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -319,6 +319,8 @@ function resolvePtyResumeProvider(snapshot) {
|
|
|
319
319
|
return "grok";
|
|
320
320
|
if (/^qodercli\b/.test(command))
|
|
321
321
|
return "qoder";
|
|
322
|
+
if (/^pi\b/.test(command))
|
|
323
|
+
return "pi";
|
|
322
324
|
return "claude";
|
|
323
325
|
}
|
|
324
326
|
function isPtyProviderCommand(provider, command) {
|
|
@@ -375,7 +377,7 @@ function canAutoResumePtyForInput(snapshot, input) {
|
|
|
375
377
|
return Boolean(snapshot
|
|
376
378
|
&& (snapshot.sessionKind ?? "pty") === "pty"
|
|
377
379
|
&& snapshot.status !== "running"
|
|
378
|
-
&& (snapshot.provider === "claude" || snapshot.provider === "codex" || snapshot.provider === "opencode" || snapshot.provider === "grok" || snapshot.provider === "qoder"
|
|
380
|
+
&& (snapshot.provider === "claude" || snapshot.provider === "codex" || snapshot.provider === "opencode" || snapshot.provider === "grok" || snapshot.provider === "qoder" || snapshot.provider === "pi"
|
|
379
381
|
|| /^(?:claude|codex|opencode|grok|qodercli)\b/.test(snapshot.command.trim()))
|
|
380
382
|
&& snapshot.claudeSessionId
|
|
381
383
|
&& input);
|
|
@@ -418,11 +420,11 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
418
420
|
app.post("/api/structured-sessions", asyncRoute(async (req, res) => {
|
|
419
421
|
const body = req.body;
|
|
420
422
|
try {
|
|
421
|
-
if (body.provider && body.provider !== "claude" && body.provider !== "codex" && body.provider !== "opencode" && body.provider !== "grok" && body.provider !== "qoder") {
|
|
423
|
+
if (body.provider && body.provider !== "claude" && body.provider !== "codex" && body.provider !== "opencode" && body.provider !== "grok" && body.provider !== "qoder" && body.provider !== "pi") {
|
|
422
424
|
res.status(400).json({ error: "结构化会话当前仅支持 Claude、Codex、OpenCode、Grok 或 Qoder provider。" });
|
|
423
425
|
return;
|
|
424
426
|
}
|
|
425
|
-
const provider = body.provider === "codex" || body.provider === "opencode" || body.provider === "grok" || body.provider === "qoder" ? body.provider : "claude";
|
|
427
|
+
const provider = body.provider === "codex" || body.provider === "opencode" || body.provider === "grok" || body.provider === "qoder" || body.provider === "pi" ? body.provider : "claude";
|
|
426
428
|
const rawModel = typeof body.model === "string" ? body.model.trim() : "";
|
|
427
429
|
const origin = parseSessionCreationOrigin(body);
|
|
428
430
|
const snapshot = structured.createSession({
|
|
@@ -25,6 +25,7 @@ function publicConfig(config) {
|
|
|
25
25
|
defaultOpenCodeModel: defaultModels.opencode,
|
|
26
26
|
defaultGrokModel: defaultModels.grok,
|
|
27
27
|
defaultQoderModel: defaultModels.qoder,
|
|
28
|
+
defaultPiModel: defaultModels.pi,
|
|
28
29
|
defaultModels,
|
|
29
30
|
};
|
|
30
31
|
}
|
|
@@ -283,6 +284,8 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
283
284
|
stagePreference("defaultGrokModel", body.defaultModels.grok);
|
|
284
285
|
if (Object.hasOwn(body.defaultModels, "qoder"))
|
|
285
286
|
stagePreference("defaultQoderModel", body.defaultModels.qoder);
|
|
287
|
+
if (Object.hasOwn(body.defaultModels, "pi"))
|
|
288
|
+
stagePreference("defaultPiModel", body.defaultModels.pi);
|
|
286
289
|
}
|
|
287
290
|
if (body.systemAi !== undefined) {
|
|
288
291
|
if (!body.systemAi || typeof body.systemAi !== "object" || Array.isArray(body.systemAi)) {
|
|
@@ -397,6 +400,7 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
397
400
|
defaultOpenCodeModel: defaults.opencode,
|
|
398
401
|
defaultGrokModel: defaults.grok,
|
|
399
402
|
defaultQoderModel: defaults.qoder,
|
|
403
|
+
defaultPiModel: defaults.pi,
|
|
400
404
|
defaultModels: defaults,
|
|
401
405
|
});
|
|
402
406
|
});
|
|
@@ -411,6 +415,7 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
411
415
|
defaultOpenCodeModel: defaults.opencode,
|
|
412
416
|
defaultGrokModel: defaults.grok,
|
|
413
417
|
defaultQoderModel: defaults.qoder,
|
|
418
|
+
defaultPiModel: defaults.pi,
|
|
414
419
|
defaultModels: defaults,
|
|
415
420
|
});
|
|
416
421
|
}
|
|
@@ -126,7 +126,7 @@ export function registerAdminUpdateRoutes(app, deps) {
|
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
const rawIds = Array.isArray(req.body?.ids) ? req.body.ids : [];
|
|
129
|
-
const ids = rawIds.filter((value) => value === "claude" || value === "codex" || value === "opencode" || value === "qoder");
|
|
129
|
+
const ids = rawIds.filter((value) => value === "claude" || value === "codex" || value === "opencode" || value === "qoder" || value === "pi");
|
|
130
130
|
state.providerCliUpdateInFlight = true;
|
|
131
131
|
try {
|
|
132
132
|
const before = await refreshProviderCliUpdateState(state, config);
|
package/dist/server.js
CHANGED
|
@@ -18,9 +18,12 @@ import { ModelCatalogService } from "./models.js";
|
|
|
18
18
|
import { ProcessManager } from "./process-manager.js";
|
|
19
19
|
import { SessionLogger } from "./session-logger.js";
|
|
20
20
|
import { SessionRegistry } from "./session-registry.js";
|
|
21
|
+
import { resolveSessionAiContext, resolveSystemAiContext } from "./session-ai-context.js";
|
|
21
22
|
import { StructuredSessionManager } from "./structured-session-manager.js";
|
|
22
23
|
import { recordRecentPath, registerFileRoutes } from "./server-file-routes.js";
|
|
23
24
|
import { registerSettingsRoutes } from "./server-settings-routes.js";
|
|
25
|
+
import { registerMissionRoutes } from "./server-mission-routes.js";
|
|
26
|
+
import { Missions } from "./missions.js";
|
|
24
27
|
import { refreshProviderCliUpdateState, registerAdminUpdateRoutes, registerPublicUpdateRoutes, ServerUpdateState, } from "./server-update-routes.js";
|
|
25
28
|
import { parseSessionCreationOrigin, registerClaudeHistoryRoutes, registerSessionRoutes } from "./server-session-routes.js";
|
|
26
29
|
import { resolveSessionCwd } from "./session-cwd.js";
|
|
@@ -500,6 +503,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
500
503
|
const structuredLogger = new SessionLogger(configDir, config.shortcutLogMaxBytes);
|
|
501
504
|
const structuredSessions = new StructuredSessionManager(storage, config, structuredLogger);
|
|
502
505
|
const sessionRegistry = new SessionRegistry(processes, structuredSessions, storage);
|
|
506
|
+
const missions = new Missions(storage, structuredSessions, sessionRegistry);
|
|
503
507
|
const updateState = new ServerUpdateState();
|
|
504
508
|
const getUpdateChannel = () => normalizeUpdateChannel(storage.getConfigValue("updateChannel"));
|
|
505
509
|
let disconnectAuthenticatedSockets = () => { };
|
|
@@ -700,6 +704,8 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
700
704
|
"/api/claude-sessions",
|
|
701
705
|
"/api/codex-sessions",
|
|
702
706
|
"/api/optimize-prompt",
|
|
707
|
+
"/api/inbox",
|
|
708
|
+
"/api/missions",
|
|
703
709
|
], requireSessions);
|
|
704
710
|
app.use([
|
|
705
711
|
"/api/directory",
|
|
@@ -929,18 +935,34 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
929
935
|
recordRecentPath(storage, cwd);
|
|
930
936
|
});
|
|
931
937
|
registerClaudeHistoryRoutes(app, processes, structuredSessions, storage, sessionRegistry);
|
|
938
|
+
registerMissionRoutes(app, missions);
|
|
932
939
|
registerUploadRoutes(app, processes);
|
|
933
940
|
app.post("/api/optimize-prompt", asyncRoute(async (req, res) => {
|
|
934
941
|
const body = (req.body ?? {});
|
|
935
942
|
const text = typeof body.text === "string" ? body.text : "";
|
|
936
943
|
let cwd;
|
|
944
|
+
let ai;
|
|
937
945
|
if (typeof body.sessionId === "string" && body.sessionId.length > 0) {
|
|
938
|
-
const
|
|
939
|
-
if (
|
|
940
|
-
cwd =
|
|
946
|
+
const snapshot = sessionRegistry.getLatest(body.sessionId);
|
|
947
|
+
if (snapshot?.cwd)
|
|
948
|
+
cwd = snapshot.cwd;
|
|
949
|
+
if (snapshot)
|
|
950
|
+
ai = resolveSystemAiContext(snapshot, config);
|
|
951
|
+
}
|
|
952
|
+
if (!ai) {
|
|
953
|
+
const defaultSession = {
|
|
954
|
+
provider: config.defaultProvider,
|
|
955
|
+
structuredState: undefined,
|
|
956
|
+
runner: undefined,
|
|
957
|
+
command: config.defaultProvider === "qoder" ? "qodercli" : config.defaultProvider ?? "claude",
|
|
958
|
+
selectedModel: null,
|
|
959
|
+
thinkingEffort: config.defaultThinkingEffort,
|
|
960
|
+
};
|
|
961
|
+
const cli = resolveSessionAiContext(defaultSession, config);
|
|
962
|
+
ai = config.systemAi?.enabled ? { ...cli, systemAi: config.systemAi } : cli;
|
|
941
963
|
}
|
|
942
964
|
try {
|
|
943
|
-
const optimized = await optimizePrompt(text, config.language ?? "", cwd,
|
|
965
|
+
const optimized = await optimizePrompt(text, config.language ?? "", cwd, ai);
|
|
944
966
|
res.json({ optimized });
|
|
945
967
|
}
|
|
946
968
|
catch (error) {
|
|
@@ -976,7 +998,9 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
976
998
|
? "grok"
|
|
977
999
|
: body.provider === "qoder" || /^qodercli\b/.test(body.command.trim())
|
|
978
1000
|
? "qoder"
|
|
979
|
-
: "
|
|
1001
|
+
: body.provider === "pi" || /^pi\b/.test(body.command.trim())
|
|
1002
|
+
? "pi"
|
|
1003
|
+
: "claude";
|
|
980
1004
|
// Older clients used the provider id as the PTY command. Qoder's executable
|
|
981
1005
|
// is named qodercli, so keep those clients working while preserving custom commands.
|
|
982
1006
|
const command = provider === "qoder" && body.command.trim() === "qoder"
|
|
@@ -1059,9 +1083,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1059
1083
|
});
|
|
1060
1084
|
// Wire process events to WebSocket broadcast
|
|
1061
1085
|
processes.on("process", (event) => {
|
|
1086
|
+
missions.ingest(event);
|
|
1062
1087
|
wsManager.emitEvent(event);
|
|
1063
1088
|
});
|
|
1064
1089
|
structuredSessions.setEventEmitter((event) => {
|
|
1090
|
+
missions.ingest(event);
|
|
1065
1091
|
wsManager.emitEvent(event);
|
|
1066
1092
|
});
|
|
1067
1093
|
// ── Restart endpoint (needs server + wss in scope) ──
|
|
@@ -14,8 +14,8 @@ export interface SessionAiContext {
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolveSessionProvider(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command">): SessionProvider;
|
|
16
16
|
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
17
|
-
export declare function resolveSessionAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultThinkingEffort" | "inheritEnv">): SessionAiContext;
|
|
17
|
+
export declare function resolveSessionAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultPiModel" | "defaultThinkingEffort" | "inheritEnv">): SessionAiContext;
|
|
18
18
|
/** Build the source order for Wand-owned AI features such as titles. */
|
|
19
|
-
export declare function resolveSystemAiContext(snapshot: Parameters<typeof resolveSessionAiContext>[0], config: Parameters<typeof resolveSessionAiContext>[1] & Pick<WandConfig, "systemAi"
|
|
19
|
+
export declare function resolveSystemAiContext(snapshot: Parameters<typeof resolveSessionAiContext>[0], config: Parameters<typeof resolveSessionAiContext>[1] & Pick<WandConfig, "systemAi">): SessionAiContext;
|
|
20
20
|
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
21
|
-
export declare function resolveCommitAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultThinkingEffort" | "inheritEnv" | "commitAiSource" | "systemAi">, discoverApis?: typeof discoverCliSystemAiConfigs): SessionAiContext;
|
|
21
|
+
export declare function resolveCommitAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultPiModel" | "defaultThinkingEffort" | "inheritEnv" | "commitAiSource" | "systemAi">, discoverApis?: typeof discoverCliSystemAiConfigs): SessionAiContext;
|
|
@@ -10,14 +10,16 @@ export function resolveSessionProvider(snapshot) {
|
|
|
10
10
|
|| snapshot.provider === "codex"
|
|
11
11
|
|| snapshot.provider === "opencode"
|
|
12
12
|
|| snapshot.provider === "grok"
|
|
13
|
-
|| snapshot.provider === "qoder"
|
|
13
|
+
|| snapshot.provider === "qoder"
|
|
14
|
+
|| snapshot.provider === "pi") {
|
|
14
15
|
return snapshot.provider;
|
|
15
16
|
}
|
|
16
17
|
if (snapshot.structuredState?.provider === "claude"
|
|
17
18
|
|| snapshot.structuredState?.provider === "codex"
|
|
18
19
|
|| snapshot.structuredState?.provider === "opencode"
|
|
19
20
|
|| snapshot.structuredState?.provider === "grok"
|
|
20
|
-
|| snapshot.structuredState?.provider === "qoder"
|
|
21
|
+
|| snapshot.structuredState?.provider === "qoder"
|
|
22
|
+
|| snapshot.structuredState?.provider === "pi") {
|
|
21
23
|
return snapshot.structuredState.provider;
|
|
22
24
|
}
|
|
23
25
|
const runner = snapshot.runner ?? snapshot.structuredState?.runner;
|
|
@@ -29,6 +31,8 @@ export function resolveSessionProvider(snapshot) {
|
|
|
29
31
|
return "grok";
|
|
30
32
|
if (runner === "qoder-cli-print")
|
|
31
33
|
return "qoder";
|
|
34
|
+
if (runner === "pi-cli-json")
|
|
35
|
+
return "pi";
|
|
32
36
|
if (runner === "claude-cli" || runner === "claude-cli-print" || runner === "claude-sdk")
|
|
33
37
|
return "claude";
|
|
34
38
|
if (/^codex\b/i.test(snapshot.command.trim()))
|
|
@@ -39,6 +43,8 @@ export function resolveSessionProvider(snapshot) {
|
|
|
39
43
|
return "grok";
|
|
40
44
|
if (/^qodercli\b/i.test(snapshot.command.trim()))
|
|
41
45
|
return "qoder";
|
|
46
|
+
if (/^pi\b/i.test(snapshot.command.trim()))
|
|
47
|
+
return "pi";
|
|
42
48
|
return "claude";
|
|
43
49
|
}
|
|
44
50
|
function normalizeModel(value) {
|
|
@@ -66,28 +72,18 @@ export function resolveSessionAiContext(snapshot, config) {
|
|
|
66
72
|
export function resolveSystemAiContext(snapshot, config) {
|
|
67
73
|
const sessionContext = resolveSessionAiContext(snapshot, config);
|
|
68
74
|
const directApi = config.systemAi ? usableSystemAi(config.systemAi) : undefined;
|
|
69
|
-
|
|
70
|
-
...sessionContext,
|
|
71
|
-
|
|
72
|
-
model: normalizeModel(config.commitModel),
|
|
73
|
-
};
|
|
74
|
-
return directApi && config.systemAi?.enabled ? { ...cliContext, systemAi: directApi } : cliContext;
|
|
75
|
+
return directApi && config.systemAi?.enabled
|
|
76
|
+
? { ...sessionContext, systemAi: directApi }
|
|
77
|
+
: sessionContext;
|
|
75
78
|
}
|
|
76
79
|
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
77
80
|
export function resolveCommitAiContext(snapshot, config, discoverApis = discoverCliSystemAiConfigs) {
|
|
78
81
|
const sessionContext = resolveSessionAiContext(snapshot, config);
|
|
79
|
-
const commitContext = {
|
|
80
|
-
...sessionContext,
|
|
81
|
-
// Commit messages and tags are short classification/summarization tasks.
|
|
82
|
-
// Keep them on the minimum explicit provider effort instead of inheriting
|
|
83
|
-
// an expensive deep/max setting from the conversation.
|
|
84
|
-
thinkingEffort: "standard",
|
|
85
|
-
};
|
|
86
82
|
if (config.commitAiSource !== "api")
|
|
87
|
-
return
|
|
88
|
-
const directApi = mergeSystemAiConfigs(config.systemAi, discoverApis(
|
|
83
|
+
return sessionContext;
|
|
84
|
+
const directApi = mergeSystemAiConfigs(config.systemAi, discoverApis(sessionContext.provider));
|
|
89
85
|
return {
|
|
90
|
-
...
|
|
86
|
+
...sessionContext,
|
|
91
87
|
...(directApi ? { systemAi: directApi } : {}),
|
|
92
88
|
};
|
|
93
89
|
}
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SessionSnapshot, ConversationTurn, StructuredSessionState } from "./types.js";
|
|
2
|
+
import type { AgentActivityItem, Mission, MissionAttempt, MissionReviewComment, MissionReviewStatus, MissionStatus } from "./mission-types.js";
|
|
2
3
|
import { type PasswordVault, type PasswordVaultItem, type PasswordVaultItemFilter, type PasswordVaultItemInput } from "./password-manager.js";
|
|
3
4
|
export declare const DEFAULT_DB_FILE = "wand.db";
|
|
4
5
|
export type AuthPrincipalKind = "browser-admin" | "connected-app";
|
|
@@ -60,6 +61,20 @@ export declare class WandStorage {
|
|
|
60
61
|
deleteAuthSession(token: string): void;
|
|
61
62
|
deleteAllAuthSessions(): void;
|
|
62
63
|
deleteExpiredAuthSessions(now: number): void;
|
|
64
|
+
saveMission(mission: Mission): void;
|
|
65
|
+
getMission(id: string): Mission | null;
|
|
66
|
+
listMissions(includeArchived?: boolean): Mission[];
|
|
67
|
+
updateMissionStatus(id: string, status: MissionStatus, updatedAt?: string): void;
|
|
68
|
+
saveMissionAttempt(attempt: MissionAttempt): void;
|
|
69
|
+
getMissionAttempt(id: string): MissionAttempt | null;
|
|
70
|
+
getMissionAttemptBySession(sessionId: string): MissionAttempt | null;
|
|
71
|
+
listMissionAttempts(missionId: string): MissionAttempt[];
|
|
72
|
+
saveMissionReviewComment(comment: MissionReviewComment): void;
|
|
73
|
+
listMissionReviewComments(missionId: string, attemptId?: string): MissionReviewComment[];
|
|
74
|
+
updateMissionReviewStatus(ids: string[], status: MissionReviewStatus, at?: string): void;
|
|
75
|
+
upsertAgentActivity(item: AgentActivityItem): void;
|
|
76
|
+
listAgentActivity(): AgentActivityItem[];
|
|
77
|
+
markAgentActivityRead(sessionId?: string): void;
|
|
63
78
|
saveSession(snapshot: SessionSnapshot): void;
|
|
64
79
|
/** Update runtime/scalar fields without serializing or rewriting messages/output. */
|
|
65
80
|
updateSessionRuntimeMetadata(snapshot: SessionSnapshot): void;
|