@co0ontty/wand 2.4.1 → 2.4.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "commit": "f3d51073dca38b18ea1104511c7fdebdd125b086",
3
- "builtAt": "2026-07-04T10:40:24.303Z",
4
- "version": "2.4.1",
2
+ "commit": "618521bbe5e23601be39a6599b110d008f469b01",
3
+ "builtAt": "2026-07-04T11:34:01.260Z",
4
+ "version": "2.4.2",
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", "defaultThinkingEffort", "structuredRunner", "language", "cardDefaults", "inheritEnv"];
10
+ export declare const PREFERENCE_KEYS: readonly ["defaultMode", "defaultCwd", "defaultModel", "defaultCodexModel", "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;
@@ -40,4 +40,9 @@ export declare function applyStoragePreferences(config: WandConfig, storage: Wan
40
40
  export declare function writePreferenceToStorage(config: WandConfig, storage: WandStorage, key: PreferenceKey, value: unknown): void;
41
41
  export declare function normalizeCardDefaults(input: unknown): CardExpandDefaults;
42
42
  export declare function isExecutionMode(value: unknown): value is ExecutionMode;
43
+ export declare function getProviderDefaultModels(config: Pick<WandConfig, "defaultModel" | "defaultCodexModel">): {
44
+ claude: string;
45
+ codex: string;
46
+ };
47
+ export declare function getDefaultModelForProvider(config: Pick<WandConfig, "defaultModel" | "defaultCodexModel">, provider: "claude" | "codex" | undefined): string;
43
48
  export declare function normalizeMode(input: string | undefined, fallback: ExecutionMode): ExecutionMode;
package/dist/config.js CHANGED
@@ -17,6 +17,7 @@ export const PREFERENCE_KEYS = [
17
17
  "defaultMode",
18
18
  "defaultCwd",
19
19
  "defaultModel",
20
+ "defaultCodexModel",
20
21
  "defaultThinkingEffort",
21
22
  "structuredRunner",
22
23
  "language",
@@ -51,6 +52,7 @@ export const defaultConfig = () => ({
51
52
  macos: defaultMacosDmgConfig(),
52
53
  cardDefaults: defaultCardExpandDefaults(),
53
54
  defaultModel: "",
55
+ defaultCodexModel: "",
54
56
  defaultThinkingEffort: "off",
55
57
  structuredRunner: "cli",
56
58
  inheritEnv: true,
@@ -250,6 +252,11 @@ export function applyStoragePreferences(config, storage) {
250
252
  if (typeof v === "string")
251
253
  config.defaultModel = v.trim();
252
254
  }
255
+ if (storage.hasPreference(preferenceStorageKey("defaultCodexModel"))) {
256
+ const v = storage.getPreference(preferenceStorageKey("defaultCodexModel"), defaults.defaultCodexModel ?? "");
257
+ if (typeof v === "string")
258
+ config.defaultCodexModel = v.trim();
259
+ }
253
260
  if (storage.hasPreference(preferenceStorageKey("defaultThinkingEffort"))) {
254
261
  const v = storage.getPreference(preferenceStorageKey("defaultThinkingEffort"), defaults.defaultThinkingEffort ?? "off");
255
262
  if (v === "off" || v === "standard" || v === "deep" || v === "max")
@@ -298,6 +305,12 @@ export function writePreferenceToStorage(config, storage, key, value) {
298
305
  config.defaultModel = v;
299
306
  break;
300
307
  }
308
+ case "defaultCodexModel": {
309
+ const v = typeof value === "string" ? value.trim() : "";
310
+ storage.setPreference(dbKey, v);
311
+ config.defaultCodexModel = v;
312
+ break;
313
+ }
301
314
  case "defaultThinkingEffort": {
302
315
  const v = value === "standard" || value === "deep" || value === "max" ? value : "off";
303
316
  storage.setPreference(dbKey, v);
@@ -467,6 +480,7 @@ function mergeWithDefaults(input) {
467
480
  macos: normalizeMacosDmgConfig(input.macos) ?? defaults.macos,
468
481
  cardDefaults: normalizeCardDefaults(input.cardDefaults),
469
482
  defaultModel: typeof input.defaultModel === "string" ? input.defaultModel.trim() : defaults.defaultModel,
483
+ defaultCodexModel: typeof input.defaultCodexModel === "string" ? input.defaultCodexModel.trim() : defaults.defaultCodexModel,
470
484
  defaultThinkingEffort: input.defaultThinkingEffort === "standard"
471
485
  || input.defaultThinkingEffort === "deep"
472
486
  || input.defaultThinkingEffort === "max"
@@ -479,6 +493,16 @@ function mergeWithDefaults(input) {
479
493
  export function isExecutionMode(value) {
480
494
  return value === "assist" || value === "agent" || value === "agent-max" || value === "auto-edit" || value === "default" || value === "full-access" || value === "native" || value === "managed";
481
495
  }
496
+ export function getProviderDefaultModels(config) {
497
+ return {
498
+ claude: (config.defaultModel ?? "").trim(),
499
+ codex: (config.defaultCodexModel ?? "").trim(),
500
+ };
501
+ }
502
+ export function getDefaultModelForProvider(config, provider) {
503
+ const defaults = getProviderDefaultModels(config);
504
+ return provider === "codex" ? defaults.codex : defaults.claude;
505
+ }
482
506
  export function normalizeMode(input, fallback) {
483
507
  return isExecutionMode(input) ? input : fallback;
484
508
  }
@@ -1,6 +1,6 @@
1
1
  import express from "express";
2
2
  import { SessionInputError } from "./process-manager.js";
3
- import { normalizeMode } from "./config.js";
3
+ 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";
@@ -221,13 +221,14 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
221
221
  return;
222
222
  }
223
223
  const provider = body.provider === "codex" ? "codex" : "claude";
224
+ const rawModel = typeof body.model === "string" ? body.model.trim() : "";
224
225
  const snapshot = structured.createSession({
225
226
  cwd: resolveSessionCwd(body.cwd, config.defaultCwd),
226
227
  mode: normalizeMode(body.mode, defaultMode),
227
228
  provider,
228
229
  runner: body.runner ?? (provider === "codex" ? "codex-cli-exec" : "claude-cli-print"),
229
230
  worktreeEnabled: body.worktreeEnabled === true,
230
- model: typeof body.model === "string" ? body.model.trim() : (config.defaultModel ?? "").trim() || undefined,
231
+ model: rawModel || getDefaultModelForProvider(config, provider) || undefined,
231
232
  thinkingEffort: typeof body.thinkingEffort === "string"
232
233
  ? body.thinkingEffort
233
234
  : config.defaultThinkingEffort,
@@ -540,7 +541,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
540
541
  cwd: snapshot.cwd,
541
542
  language: config.language ?? "",
542
543
  provider: snapshot.provider,
543
- model: snapshot.selectedModel ?? snapshot.structuredState?.model ?? config.defaultModel,
544
+ model: snapshot.selectedModel ?? snapshot.structuredState?.model ?? getDefaultModelForProvider(config, snapshot.provider),
544
545
  thinkingEffort: snapshot.thinkingEffort ?? config.defaultThinkingEffort,
545
546
  inheritEnv: config.inheritEnv,
546
547
  autoMessage: body.autoMessage !== false,
@@ -574,7 +575,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
574
575
  try {
575
576
  const result = await generateCommitMessageOnly(snapshot.cwd, config.language ?? "", {
576
577
  provider: snapshot.provider,
577
- model: snapshot.selectedModel ?? snapshot.structuredState?.model ?? config.defaultModel,
578
+ model: snapshot.selectedModel ?? snapshot.structuredState?.model ?? getDefaultModelForProvider(config, snapshot.provider),
578
579
  thinkingEffort: snapshot.thinkingEffort ?? config.defaultThinkingEffort,
579
580
  inheritEnv: config.inheritEnv,
580
581
  });
package/dist/server.js CHANGED
@@ -15,7 +15,7 @@ import { WebSocketServer } from "ws";
15
15
  import { createSession, readSessionCookie, revokeSession, SESSION_COOKIE_HTTP, SESSION_COOKIE_HTTPS, SESSION_COOKIE_LEGACY, setAuthStorage, validateSession, } from "./auth.js";
16
16
  import { ensureCertificates } from "./cert.js";
17
17
  import { buildChildEnv } from "./env-utils.js";
18
- import { isExecutionMode, normalizeMode, PREFERENCE_KEYS, resolveConfigDir, saveConfig, writePreferenceToStorage, } from "./config.js";
18
+ import { getDefaultModelForProvider, getProviderDefaultModels, isExecutionMode, normalizeMode, PREFERENCE_KEYS, resolveConfigDir, saveConfig, writePreferenceToStorage, } from "./config.js";
19
19
  import { getCachedModels, refreshModels } from "./models.js";
20
20
  import { ProcessManager } from "./process-manager.js";
21
21
  import { SessionLogger } from "./session-logger.js";
@@ -1305,12 +1305,15 @@ export async function startServer(config, configPath) {
1305
1305
  // ── Config & Session info ──
1306
1306
  app.get("/api/config", async (_req, res) => {
1307
1307
  const structuredChatPersona = await buildStructuredChatPersonaPayload(configPath, config);
1308
+ const defaultModels = getProviderDefaultModels(config);
1308
1309
  res.json({
1309
1310
  host: config.host,
1310
1311
  port: config.port,
1311
1312
  defaultMode: config.defaultMode,
1312
1313
  defaultCwd: config.defaultCwd,
1313
- defaultModel: config.defaultModel ?? "",
1314
+ defaultModel: defaultModels.claude,
1315
+ defaultCodexModel: defaultModels.codex,
1316
+ defaultModels,
1314
1317
  defaultThinkingEffort: config.defaultThinkingEffort ?? "off",
1315
1318
  commandPresets: config.commandPresets,
1316
1319
  structuredRunner: config.structuredRunner ?? "cli",
@@ -1449,6 +1452,7 @@ export async function startServer(config, configPath) {
1449
1452
  certPath: path.join(configDir, "server.crt"),
1450
1453
  };
1451
1454
  const { password: _pw, ...safeConfig } = config;
1455
+ const defaultModels = getProviderDefaultModels(config);
1452
1456
  const localApk = await resolveAndroidApkAsset(configDir, config);
1453
1457
  const ghApk = await fetchGitHubLatestApk();
1454
1458
  const apkDir = resolveAndroidApkDir(configDir, config);
@@ -1471,7 +1475,12 @@ export async function startServer(config, configPath) {
1471
1475
  packageName: PKG_NAME,
1472
1476
  nodeVersion: PKG_NODE_REQ,
1473
1477
  repoUrl: PKG_REPO_URL,
1474
- config: safeConfig,
1478
+ config: {
1479
+ ...safeConfig,
1480
+ defaultModel: defaultModels.claude,
1481
+ defaultCodexModel: defaultModels.codex,
1482
+ defaultModels,
1483
+ },
1475
1484
  hasCert: existsSync(certPaths.keyPath) && existsSync(certPaths.certPath),
1476
1485
  updateAvailable: cachedUpdateInfo?.updateAvailable ?? false,
1477
1486
  latestVersion: cachedUpdateInfo?.latest ?? null,
@@ -1645,6 +1654,23 @@ export async function startServer(config, configPath) {
1645
1654
  res.status(400).json({ error: `无效执行模式: ${body.defaultMode}` });
1646
1655
  return;
1647
1656
  }
1657
+ if (body.defaultModels && typeof body.defaultModels === "object") {
1658
+ const modelDefaults = body.defaultModels;
1659
+ try {
1660
+ if (Object.prototype.hasOwnProperty.call(modelDefaults, "claude")) {
1661
+ writePreferenceToStorage(config, storage, "defaultModel", modelDefaults.claude);
1662
+ touchedPreferenceField = true;
1663
+ }
1664
+ if (Object.prototype.hasOwnProperty.call(modelDefaults, "codex")) {
1665
+ writePreferenceToStorage(config, storage, "defaultCodexModel", modelDefaults.codex);
1666
+ touchedPreferenceField = true;
1667
+ }
1668
+ }
1669
+ catch (err) {
1670
+ res.status(400).json({ error: getErrorMessage(err, "默认模型配置校验失败") });
1671
+ return;
1672
+ }
1673
+ }
1648
1674
  for (const field of PREFERENCE_KEYS) {
1649
1675
  if (!(field in body) || body[field] === undefined)
1650
1676
  continue;
@@ -1666,8 +1692,18 @@ export async function startServer(config, configPath) {
1666
1692
  await saveConfig(configPath, config);
1667
1693
  }
1668
1694
  const { password: _pw, ...safeConfig } = config;
1695
+ const defaultModels = getProviderDefaultModels(config);
1669
1696
  // 只有部署字段才需要重启;偏好字段已经热生效。
1670
- res.json({ ok: true, config: safeConfig, restartRequired: touchedDeployField });
1697
+ res.json({
1698
+ ok: true,
1699
+ config: {
1700
+ ...safeConfig,
1701
+ defaultModel: defaultModels.claude,
1702
+ defaultCodexModel: defaultModels.codex,
1703
+ defaultModels,
1704
+ },
1705
+ restartRequired: touchedDeployField,
1706
+ });
1671
1707
  }
1672
1708
  catch (error) {
1673
1709
  res.status(500).json({ error: getErrorMessage(error, "保存配置失败。") });
@@ -1675,23 +1711,29 @@ export async function startServer(config, configPath) {
1675
1711
  });
1676
1712
  app.get("/api/models", (_req, res) => {
1677
1713
  const cached = getCachedModels();
1714
+ const defaultModels = getProviderDefaultModels(config);
1678
1715
  res.json({
1679
1716
  models: cached.models,
1680
1717
  codexModels: cached.codexModels,
1681
1718
  claudeVersion: cached.claudeVersion,
1682
1719
  refreshedAt: cached.refreshedAt,
1683
- defaultModel: config.defaultModel ?? "",
1720
+ defaultModel: defaultModels.claude,
1721
+ defaultCodexModel: defaultModels.codex,
1722
+ defaultModels,
1684
1723
  });
1685
1724
  });
1686
1725
  app.post("/api/models/refresh", async (_req, res) => {
1687
1726
  try {
1688
1727
  const refreshed = await refreshModels();
1728
+ const defaultModels = getProviderDefaultModels(config);
1689
1729
  res.json({
1690
1730
  models: refreshed.models,
1691
1731
  codexModels: refreshed.codexModels,
1692
1732
  claudeVersion: refreshed.claudeVersion,
1693
1733
  refreshedAt: refreshed.refreshedAt,
1694
- defaultModel: config.defaultModel ?? "",
1734
+ defaultModel: defaultModels.claude,
1735
+ defaultCodexModel: defaultModels.codex,
1736
+ defaultModels,
1695
1737
  });
1696
1738
  }
1697
1739
  catch (error) {
@@ -2263,12 +2305,13 @@ export async function startServer(config, configPath) {
2263
2305
  const initialInput = body.initialInput?.trim();
2264
2306
  try {
2265
2307
  const rawModel = typeof body.model === "string" ? body.model.trim() : "";
2266
- const effectiveModel = rawModel || (config.defaultModel ?? "").trim() || undefined;
2308
+ const provider = body.provider === "codex" || /^codex\b/.test(body.command.trim()) ? "codex" : "claude";
2309
+ const effectiveModel = rawModel || getDefaultModelForProvider(config, provider) || undefined;
2267
2310
  const reqCols = typeof body.cols === "number" && Number.isFinite(body.cols) ? body.cols : undefined;
2268
2311
  const reqRows = typeof body.rows === "number" && Number.isFinite(body.rows) ? body.rows : undefined;
2269
2312
  const snapshot = processes.start(body.command, body.cwd, normalizeMode(body.mode, config.defaultMode), initialInput || undefined, {
2270
2313
  worktreeEnabled: body.worktreeEnabled === true,
2271
- provider: body.provider,
2314
+ provider,
2272
2315
  model: effectiveModel,
2273
2316
  cols: reqCols,
2274
2317
  rows: reqRows,
@@ -1,6 +1,6 @@
1
1
  import { SessionLogger } from "./session-logger.js";
2
2
  import { WandStorage } from "./storage.js";
3
- import { ExecutionMode, ProcessEvent, SessionProvider, SessionRunner, SessionSnapshot, WandConfig } from "./types.js";
3
+ import { ContentBlock, ExecutionMode, ProcessEvent, SessionProvider, SessionRunner, SessionSnapshot, WandConfig } from "./types.js";
4
4
  interface CreateStructuredSessionOptions {
5
5
  cwd: string;
6
6
  mode: ExecutionMode;
@@ -8,7 +8,7 @@ interface CreateStructuredSessionOptions {
8
8
  provider?: SessionProvider;
9
9
  runner?: SessionRunner;
10
10
  worktreeEnabled?: boolean;
11
- /** 用户指定的 Claude 模型(别名或完整 ID)。留空则 spawn 时不加 --model。 */
11
+ /** 用户指定的模型(别名或完整 ID)。留空则 spawn 时不加 --model。 */
12
12
  model?: string;
13
13
  /** 用户预设的思考深度。留空 / null 视为 off。 */
14
14
  thinkingEffort?: SessionSnapshot["thinkingEffort"];
@@ -31,8 +31,9 @@ export declare function thinkingEffortToSdkBudget(effort: SessionSnapshot["think
31
31
  export declare function thinkingEffortToClaudeCliEffort(effort: SessionSnapshot["thinkingEffort"]): string | null;
32
32
  /** Claude PTY slash-command 用:off 表示恢复模型默认 effort。 */
33
33
  export declare function thinkingEffortToClaudeSlashEffort(effort: SessionSnapshot["thinkingEffort"]): string;
34
- /** Codex CLI 用:把 thinkingEffort 映射到 model_reasoning_effort 配置。off → minimal。 */
34
+ /** Codex CLI 用:把 thinkingEffort 映射到 model_reasoning_effort 配置。off → 不覆盖 Codex 默认。 */
35
35
  export declare function thinkingEffortToCodexReasoningEffort(effort: SessionSnapshot["thinkingEffort"]): string | null;
36
+ export declare function buildCodexPatchApplyBlocks(item: Record<string, unknown>): ContentBlock[];
36
37
  export declare class StructuredSessionManager {
37
38
  private readonly storage;
38
39
  private readonly config;
@@ -173,6 +174,10 @@ export declare class StructuredSessionManager {
173
174
  private resolveQueuedMessagesAfterInterrupt;
174
175
  private normalizeToolInput;
175
176
  private normalizeToolResultContent;
177
+ private unwrapCodexStreamEvent;
178
+ private applyCodexLooseEvent;
179
+ private codexFunctionToolUse;
180
+ private codexMcpToolBlocks;
176
181
  private extractCodexText;
177
182
  /**
178
183
  * Merge one codex `item.*` event into `turnState.blocks`.