@co0ontty/wand 2.6.0 → 2.7.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/dist/build-info.json +3 -3
- package/dist/config.d.ts +1 -1
- package/dist/config.js +29 -0
- package/dist/server-session-routes.js +17 -5
- package/dist/session-ai-context.d.ts +2 -0
- package/dist/session-ai-context.js +9 -0
- package/dist/structured-session-manager.d.ts +10 -0
- package/dist/structured-session-manager.js +43 -0
- package/dist/types.d.ts +4 -0
- package/dist/web-ui/content/scripts.js +34 -33
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-07-
|
|
4
|
-
"version": "2.
|
|
2
|
+
"commit": "fe6482dc2c6a1a02fbe00800836b78af034d851a",
|
|
3
|
+
"builtAt": "2026-07-11T12:18:50.530Z",
|
|
4
|
+
"version": "2.7.0",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { WandStorage } from "./storage.js";
|
|
|
7
7
|
* 升级路径:老 JSON 里仍存有这些字段时,首次启动会被搬到 DB(见 migrateLegacyPreferencesToDb),
|
|
8
8
|
* 然后下一次 saveConfig 写回 JSON 时它们会被剥离(见 stripPreferenceFields)。
|
|
9
9
|
*/
|
|
10
|
-
export declare const PREFERENCE_KEYS: readonly ["defaultMode", "defaultCwd", "defaultModel", "defaultCodexModel", "defaultThinkingEffort", "structuredRunner", "language", "cardDefaults", "inheritEnv"];
|
|
10
|
+
export declare const PREFERENCE_KEYS: readonly ["defaultMode", "defaultCwd", "defaultModel", "defaultCodexModel", "commitCli", "commitModel", "defaultThinkingEffort", "structuredRunner", "language", "cardDefaults", "inheritEnv"];
|
|
11
11
|
export type PreferenceKey = (typeof PREFERENCE_KEYS)[number];
|
|
12
12
|
export declare function isPreferenceKey(key: string): key is PreferenceKey;
|
|
13
13
|
export declare const defaultConfig: () => WandConfig;
|
package/dist/config.js
CHANGED
|
@@ -18,6 +18,8 @@ export const PREFERENCE_KEYS = [
|
|
|
18
18
|
"defaultCwd",
|
|
19
19
|
"defaultModel",
|
|
20
20
|
"defaultCodexModel",
|
|
21
|
+
"commitCli",
|
|
22
|
+
"commitModel",
|
|
21
23
|
"defaultThinkingEffort",
|
|
22
24
|
"structuredRunner",
|
|
23
25
|
"language",
|
|
@@ -53,6 +55,8 @@ export const defaultConfig = () => ({
|
|
|
53
55
|
cardDefaults: defaultCardExpandDefaults(),
|
|
54
56
|
defaultModel: "",
|
|
55
57
|
defaultCodexModel: "",
|
|
58
|
+
commitCli: "claude",
|
|
59
|
+
commitModel: "",
|
|
56
60
|
defaultThinkingEffort: "off",
|
|
57
61
|
structuredRunner: "cli",
|
|
58
62
|
inheritEnv: true,
|
|
@@ -257,6 +261,16 @@ export function applyStoragePreferences(config, storage) {
|
|
|
257
261
|
if (typeof v === "string")
|
|
258
262
|
config.defaultCodexModel = v.trim();
|
|
259
263
|
}
|
|
264
|
+
if (storage.hasPreference(preferenceStorageKey("commitCli"))) {
|
|
265
|
+
const v = storage.getPreference(preferenceStorageKey("commitCli"), defaults.commitCli ?? "claude");
|
|
266
|
+
if (v === "claude" || v === "codex")
|
|
267
|
+
config.commitCli = v;
|
|
268
|
+
}
|
|
269
|
+
if (storage.hasPreference(preferenceStorageKey("commitModel"))) {
|
|
270
|
+
const v = storage.getPreference(preferenceStorageKey("commitModel"), defaults.commitModel ?? "");
|
|
271
|
+
if (typeof v === "string")
|
|
272
|
+
config.commitModel = v.trim();
|
|
273
|
+
}
|
|
260
274
|
if (storage.hasPreference(preferenceStorageKey("defaultThinkingEffort"))) {
|
|
261
275
|
const v = storage.getPreference(preferenceStorageKey("defaultThinkingEffort"), defaults.defaultThinkingEffort ?? "off");
|
|
262
276
|
if (v === "off" || v === "standard" || v === "deep" || v === "max")
|
|
@@ -311,6 +325,19 @@ export function writePreferenceToStorage(config, storage, key, value) {
|
|
|
311
325
|
config.defaultCodexModel = v;
|
|
312
326
|
break;
|
|
313
327
|
}
|
|
328
|
+
case "commitCli": {
|
|
329
|
+
if (value !== "claude" && value !== "codex")
|
|
330
|
+
throw new Error(`无效 commit CLI: ${value}`);
|
|
331
|
+
storage.setPreference(dbKey, value);
|
|
332
|
+
config.commitCli = value;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case "commitModel": {
|
|
336
|
+
const v = typeof value === "string" ? value.trim() : "";
|
|
337
|
+
storage.setPreference(dbKey, v);
|
|
338
|
+
config.commitModel = v;
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
314
341
|
case "defaultThinkingEffort": {
|
|
315
342
|
const v = value === "standard" || value === "deep" || value === "max" ? value : "off";
|
|
316
343
|
storage.setPreference(dbKey, v);
|
|
@@ -481,6 +508,8 @@ function mergeWithDefaults(input) {
|
|
|
481
508
|
cardDefaults: normalizeCardDefaults(input.cardDefaults),
|
|
482
509
|
defaultModel: typeof input.defaultModel === "string" ? input.defaultModel.trim() : defaults.defaultModel,
|
|
483
510
|
defaultCodexModel: typeof input.defaultCodexModel === "string" ? input.defaultCodexModel.trim() : defaults.defaultCodexModel,
|
|
511
|
+
commitCli: input.commitCli === "codex" ? "codex" : "claude",
|
|
512
|
+
commitModel: typeof input.commitModel === "string" ? input.commitModel.trim() : defaults.commitModel,
|
|
484
513
|
defaultThinkingEffort: input.defaultThinkingEffort === "standard"
|
|
485
514
|
|| input.defaultThinkingEffort === "deep"
|
|
486
515
|
|| input.defaultThinkingEffort === "max"
|
|
@@ -4,7 +4,7 @@ import { getDefaultModelForProvider, normalizeMode } from "./config.js";
|
|
|
4
4
|
import { blockWindowMessagesForTransport, sliceTurnBlocksForTransport, truncateMessagesForTransport, windowMessagesForTransport } from "./message-truncator.js";
|
|
5
5
|
import { checkSessionWorktreeMergeability, cleanupSessionWorktree, getWorktreeMergeErrorCode, mergeSessionWorktree, WorktreeMergeError } from "./git-worktree.js";
|
|
6
6
|
import { resolveSessionCwd } from "./session-cwd.js";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveCommitAiContext } from "./session-ai-context.js";
|
|
8
8
|
import { getGitStatus, QuickCommitError, runQuickCommitWithFallback, runTagHead, runPush, generateCommitMessageOnly, } from "./git-quick-commit.js";
|
|
9
9
|
import { getErrorMessage } from "./error-utils.js";
|
|
10
10
|
export { getErrorMessage };
|
|
@@ -21,6 +21,18 @@ function getInputErrorResponse(error, sessionId) {
|
|
|
21
21
|
},
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
+
const errorCode = error?.code;
|
|
25
|
+
if (errorCode === "duplicate_queued_message" || errorCode === "duplicate_idempotency_key") {
|
|
26
|
+
return {
|
|
27
|
+
statusCode: 409,
|
|
28
|
+
payload: {
|
|
29
|
+
error: getErrorMessage(error, "检测到重复发送,已拦截。"),
|
|
30
|
+
errorCode,
|
|
31
|
+
sessionId,
|
|
32
|
+
sessionStatus: null,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
24
36
|
return {
|
|
25
37
|
statusCode: 400,
|
|
26
38
|
payload: {
|
|
@@ -346,7 +358,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
346
358
|
}
|
|
347
359
|
catch (error) {
|
|
348
360
|
const errorCode = error?.code;
|
|
349
|
-
const status = errorCode === "duplicate_idempotency_key" ? 409 : 400;
|
|
361
|
+
const status = errorCode === "duplicate_idempotency_key" || errorCode === "duplicate_queued_message" ? 409 : 400;
|
|
350
362
|
res.status(status).json({
|
|
351
363
|
error: getErrorMessage(error, "无法发送结构化消息。"),
|
|
352
364
|
errorCode,
|
|
@@ -538,7 +550,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
538
550
|
}
|
|
539
551
|
const body = (req.body ?? {});
|
|
540
552
|
try {
|
|
541
|
-
const ai =
|
|
553
|
+
const ai = resolveCommitAiContext(snapshot, config);
|
|
542
554
|
const result = await runQuickCommitWithFallback({
|
|
543
555
|
cwd: snapshot.cwd,
|
|
544
556
|
language: config.language ?? "",
|
|
@@ -572,7 +584,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
572
584
|
return;
|
|
573
585
|
}
|
|
574
586
|
try {
|
|
575
|
-
const ai =
|
|
587
|
+
const ai = resolveCommitAiContext(snapshot, config);
|
|
576
588
|
const result = await generateCommitMessageOnly(snapshot.cwd, config.language ?? "", {
|
|
577
589
|
...ai,
|
|
578
590
|
});
|
|
@@ -598,7 +610,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
598
610
|
}
|
|
599
611
|
const body = (req.body ?? {});
|
|
600
612
|
try {
|
|
601
|
-
const ai =
|
|
613
|
+
const ai = resolveCommitAiContext(snapshot, config);
|
|
602
614
|
const result = await runTagHead({
|
|
603
615
|
cwd: snapshot.cwd,
|
|
604
616
|
language: config.language ?? "",
|
|
@@ -13,3 +13,5 @@ export interface SessionAiContext {
|
|
|
13
13
|
export declare function resolveSessionProvider(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command">): SessionProvider;
|
|
14
14
|
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
15
15
|
export declare function resolveSessionAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultThinkingEffort" | "inheritEnv">): SessionAiContext;
|
|
16
|
+
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
17
|
+
export declare function resolveCommitAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultThinkingEffort" | "inheritEnv" | "commitCli" | "commitModel">): SessionAiContext;
|
|
@@ -34,3 +34,12 @@ export function resolveSessionAiContext(snapshot, config) {
|
|
|
34
34
|
inheritEnv: config.inheritEnv,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
+
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
38
|
+
export function resolveCommitAiContext(snapshot, config) {
|
|
39
|
+
const sessionContext = resolveSessionAiContext(snapshot, config);
|
|
40
|
+
return {
|
|
41
|
+
...sessionContext,
|
|
42
|
+
provider: config.commitCli === "codex" ? "codex" : "claude",
|
|
43
|
+
model: normalizeModel(config.commitModel),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -56,6 +56,16 @@ export declare function buildCodexFileChangeBlocks(item: Record<string, unknown>
|
|
|
56
56
|
* model-produced text/tool arguments; the final provider value replaces this.
|
|
57
57
|
*/
|
|
58
58
|
export declare function estimateCodexOutputTokens(blocks: ContentBlock[]): number;
|
|
59
|
+
/**
|
|
60
|
+
* 返回最近一次真正提交给结构化会话的用户输入。
|
|
61
|
+
*
|
|
62
|
+
* 排队非空时,队尾才是“上一条提交”;否则回看当前正在处理的最后一个 user turn。
|
|
63
|
+
* 这里只接受可无损还原成字符串的 text / tool_result,避免把图片等结构化内容误判
|
|
64
|
+
* 成更早的纯文本输入。
|
|
65
|
+
*/
|
|
66
|
+
export declare function getLastSubmittedStructuredInput(snapshot: Pick<SessionSnapshot, "messages" | "queuedMessages">): string | null;
|
|
67
|
+
/** 仅用于 in-flight 排队分支:连续两次内容相同则把后一次视为输入重放。 */
|
|
68
|
+
export declare function isDuplicateStructuredQueueInput(snapshot: Pick<SessionSnapshot, "messages" | "queuedMessages">, input: string): boolean;
|
|
59
69
|
export declare class StructuredSessionManager {
|
|
60
70
|
private readonly storage;
|
|
61
71
|
private readonly config;
|
|
@@ -694,6 +694,44 @@ function buildStructuredOutputPayload(snapshot) {
|
|
|
694
694
|
summary: snapshot.description ?? snapshot.summary,
|
|
695
695
|
};
|
|
696
696
|
}
|
|
697
|
+
/**
|
|
698
|
+
* 返回最近一次真正提交给结构化会话的用户输入。
|
|
699
|
+
*
|
|
700
|
+
* 排队非空时,队尾才是“上一条提交”;否则回看当前正在处理的最后一个 user turn。
|
|
701
|
+
* 这里只接受可无损还原成字符串的 text / tool_result,避免把图片等结构化内容误判
|
|
702
|
+
* 成更早的纯文本输入。
|
|
703
|
+
*/
|
|
704
|
+
export function getLastSubmittedStructuredInput(snapshot) {
|
|
705
|
+
const queue = snapshot.queuedMessages ?? [];
|
|
706
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
707
|
+
const queued = queue[i]?.trim();
|
|
708
|
+
if (queued)
|
|
709
|
+
return queued;
|
|
710
|
+
}
|
|
711
|
+
const messages = snapshot.messages ?? [];
|
|
712
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
713
|
+
const turn = messages[i];
|
|
714
|
+
if (turn.role !== "user")
|
|
715
|
+
continue;
|
|
716
|
+
const textParts = turn.content
|
|
717
|
+
.filter((block) => block.type === "text")
|
|
718
|
+
.map((block) => block.text);
|
|
719
|
+
if (textParts.length > 0) {
|
|
720
|
+
const text = textParts.join("\n").trim();
|
|
721
|
+
return text || null;
|
|
722
|
+
}
|
|
723
|
+
const toolResult = turn.content.find((block) => block.type === "tool_result" && typeof block.content === "string");
|
|
724
|
+
return toolResult && typeof toolResult.content === "string" ? toolResult.content.trim() || null : null;
|
|
725
|
+
}
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
/** 仅用于 in-flight 排队分支:连续两次内容相同则把后一次视为输入重放。 */
|
|
729
|
+
export function isDuplicateStructuredQueueInput(snapshot, input) {
|
|
730
|
+
const prompt = input.trim();
|
|
731
|
+
if (!prompt)
|
|
732
|
+
return false;
|
|
733
|
+
return getLastSubmittedStructuredInput(snapshot) === prompt;
|
|
734
|
+
}
|
|
697
735
|
function buildIncrementalStructuredPayload(snapshot, cardDefaults) {
|
|
698
736
|
const messages = snapshot.messages ?? [];
|
|
699
737
|
const lastTurn = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
@@ -1001,6 +1039,11 @@ export class StructuredSessionManager {
|
|
|
1001
1039
|
}
|
|
1002
1040
|
else {
|
|
1003
1041
|
const queue = [...(session.queuedMessages ?? [])];
|
|
1042
|
+
if (isDuplicateStructuredQueueInput(session, prompt)) {
|
|
1043
|
+
const err = new Error("与上一条消息相同,已忽略,不会加入排队。");
|
|
1044
|
+
err.code = "duplicate_queued_message";
|
|
1045
|
+
throw err;
|
|
1046
|
+
}
|
|
1004
1047
|
if (queue.length >= 10) {
|
|
1005
1048
|
throw new Error("排队消息已满(最多 10 条),请等待当前消息处理完成。");
|
|
1006
1049
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -104,6 +104,10 @@ export interface WandConfig {
|
|
|
104
104
|
defaultModel?: string;
|
|
105
105
|
/** 新建 Codex 会话时默认使用的模型。留空则不传 --model,由 codex 自行决定。 */
|
|
106
106
|
defaultCodexModel?: string;
|
|
107
|
+
/** 快捷提交生成 commit message / tag 时使用的 CLI。 */
|
|
108
|
+
commitCli?: SessionProvider;
|
|
109
|
+
/** 快捷提交专用模型。留空则跟随所选 CLI 的默认模型。 */
|
|
110
|
+
commitModel?: string;
|
|
107
111
|
/** 新建会话时默认使用的思考深度。 */
|
|
108
112
|
defaultThinkingEffort?: "off" | "standard" | "deep" | "max";
|
|
109
113
|
/** 结构化会话使用的 runner: "cli"(默认,spawn claude -p)或 "sdk"(@anthropic-ai/claude-agent-sdk)。 */
|