@co0ontty/wand 4.57.0 → 4.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/ensure-node-pty-helper.d.ts +0 -1
  3. package/dist/ensure-node-pty-helper.js +0 -5
  4. package/dist/git-quick-commit.js +18 -19
  5. package/dist/github-connector.d.ts +11 -5
  6. package/dist/github-connector.js +105 -33
  7. package/dist/missions.d.ts +1 -1
  8. package/dist/missions.js +17 -4
  9. package/dist/server-file-routes.js +43 -2
  10. package/dist/server-github-routes.js +50 -53
  11. package/dist/server-mission-routes.js +11 -10
  12. package/dist/server-task-routes.d.ts +14 -2
  13. package/dist/server-task-routes.js +248 -84
  14. package/dist/server-workspace-routes.js +90 -16
  15. package/dist/server.js +2 -20
  16. package/dist/storage.d.ts +23 -6
  17. package/dist/storage.js +130 -53
  18. package/dist/structured-exec-host.d.ts +13 -2
  19. package/dist/structured-exec-host.js +71 -36
  20. package/dist/structured-exec-pump.d.ts +1 -1
  21. package/dist/structured-exec-pump.js +14 -8
  22. package/dist/task-types.d.ts +15 -0
  23. package/dist/terminal-daemon-client.d.ts +2 -0
  24. package/dist/terminal-daemon-client.js +7 -1
  25. package/dist/terminal-daemon-server.js +17 -18
  26. package/dist/tui/ipc-server.js +3 -1
  27. package/dist/types.d.ts +1 -0
  28. package/dist/wand-task-sync.d.ts +9 -0
  29. package/dist/wand-task-sync.js +50 -0
  30. package/dist/web-ui/content/scripts.js +85 -85
  31. package/dist/web-ui/content/styles.css +1 -1
  32. package/dist/web-ui/embedded-assets.js +2 -2
  33. package/dist/ws-broadcast.js +5 -1
  34. package/package.json +2 -3
  35. package/dist/taskboard-bridge.d.ts +0 -11
  36. package/dist/taskboard-bridge.js +0 -371
  37. package/vendor/codex-taskboard/LICENSE +0 -201
  38. package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +0 -12
  39. package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +0 -1
  40. package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +0 -39
  41. package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +0 -1
  42. package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +0 -1
  43. package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +0 -1
  44. package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +0 -36
  45. package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +0 -1
  46. package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +0 -132
  47. package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +0 -10
  48. package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +0 -1
  49. package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +0 -1
  50. package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -1
  51. package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +0 -1
  52. package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +0 -1
  53. package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +0 -231
  54. package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -15
  55. package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +0 -88
  56. package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
  57. package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +0 -10
  58. package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +0 -206
  59. package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +0 -125
  60. package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +0 -2
  61. package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +0 -156
  62. package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -1
  63. package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +0 -1
  64. package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +0 -161
  65. package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +0 -2
  66. package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +0 -62
  67. package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +0 -1
  68. package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +0 -1
  69. package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +0 -1
  70. package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +0 -70
  71. package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +0 -1
  72. package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -1
  73. package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +0 -32
  74. package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +0 -1
  75. package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +0 -1
  76. package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +0 -1
  77. package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +0 -1
  78. package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +0 -62
  79. package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +0 -321
  80. package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +0 -4
  81. package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +0 -1
  82. package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +0 -1
  83. package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +0 -30
  84. package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +0 -3
  85. package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +0 -24
  86. package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +0 -24
  87. package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +0 -41
  88. package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +0 -1
  89. package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +0 -1
  90. package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +0 -85
  91. package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +0 -1
  92. package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +0 -1
  93. package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +0 -292
  94. package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +0 -1
  95. package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +0 -106
  96. package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +0 -1
  97. package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +0 -1
  98. package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +0 -121
  99. package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +0 -1
  100. package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +0 -2
  101. package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +0 -1
  102. package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +0 -70
  103. package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +0 -139
  104. package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +0 -89
  105. package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +0 -257
  106. package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +0 -1
  107. package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +0 -1
  108. package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +0 -1
  109. package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +0 -7
  110. package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +0 -11
  111. package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +0 -96
  112. package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +0 -1
  113. package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +0 -1
  114. package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +0 -1
  115. package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +0 -1
  116. package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +0 -1
  117. package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +0 -39
  118. package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +0 -3
  119. package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +0 -7
  120. package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +0 -1
  121. package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +0 -1
  122. package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +0 -1
  123. package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +0 -1
  124. package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +0 -1
  125. package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +0 -1
  126. package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +0 -84
  127. package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
  128. package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +0 -1
  129. package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +0 -40
  130. package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +0 -162
  131. package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
  132. package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +0 -1
  133. package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +0 -1
  134. package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +0 -1
  135. package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +0 -2
  136. package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +0 -8
  137. package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +0 -120
  138. package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +0 -1
  139. package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +0 -1
  140. package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +0 -34
  141. package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +0 -1
  142. package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +0 -78
  143. package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +0 -7
  144. package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
  145. package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
  146. package/vendor/codex-taskboard/dist/web/favicon.svg +0 -7
  147. package/vendor/codex-taskboard/dist/web/index.html +0 -20
  148. package/vendor/codex-taskboard/server/ai-chat-catalog.mjs +0 -1031
  149. package/vendor/codex-taskboard/server/ai-chat-process.mjs +0 -462
  150. package/vendor/codex-taskboard/server/ai-chat.mjs +0 -1237
  151. package/vendor/codex-taskboard/server/ai-turn-owner.mjs +0 -39
  152. package/vendor/codex-taskboard/server/app.mjs +0 -3211
  153. package/vendor/codex-taskboard/server/cloud-config.mjs +0 -188
  154. package/vendor/codex-taskboard/server/cloud-proxy.mjs +0 -310
  155. package/vendor/codex-taskboard/server/codex-app-server.mjs +0 -376
  156. package/vendor/codex-taskboard/server/codex-slash-commands-0.139.0.json +0 -62
  157. package/vendor/codex-taskboard/server/database.mjs +0 -2845
  158. package/vendor/codex-taskboard/server/index.mjs +0 -41
  159. package/vendor/codex-taskboard/server/jira-config.mjs +0 -181
  160. package/vendor/codex-taskboard/server/jira-integration.mjs +0 -487
  161. package/vendor/codex-taskboard/server/project-summary.mjs +0 -159
  162. package/vendor/codex-taskboard/shared/api-fields.mjs +0 -163
  163. package/vendor/codex-taskboard/shared/codex-environment.mjs +0 -5
  164. package/vendor/codex-taskboard/shared/codex-executable.mjs +0 -77
  165. package/vendor/codex-taskboard/shared/codex-permissions.mjs +0 -9
  166. package/vendor/codex-taskboard/shared/composer-reference.d.mts +0 -19
  167. package/vendor/codex-taskboard/shared/composer-reference.mjs +0 -90
  168. package/vendor/codex-taskboard/shared/domain.mjs +0 -34
  169. package/vendor/codex-taskboard/shared/executable-command.mjs +0 -13
  170. package/vendor/codex-taskboard/shared/process-tree.mjs +0 -27
  171. package/vendor/codex-taskboard/shared/task-input.mjs +0 -308
  172. package/vendor/codex-taskboard/shared/task-records.mjs +0 -199
  173. package/vendor/codex-taskboard/shared/task-relations.mjs +0 -55
  174. package/vendor/codex-taskboard/shared/taskboard-automation.mjs +0 -277
  175. package/vendor/codex-taskboard/skills/manage-taskboard/SKILL.md +0 -45
  176. package/vendor/codex-taskboard/skills/manage-taskboard/agents/openai.yaml +0 -4
  177. package/vendor/codex-taskboard/skills/manage-taskboard/references/cli.md +0 -211
package/dist/server.js CHANGED
@@ -23,7 +23,6 @@ import { StructuredSessionManager } from "./structured-session-manager.js";
23
23
  import { recordRecentPath, registerFileRoutes } from "./server-file-routes.js";
24
24
  import { registerSettingsRoutes } from "./server-settings-routes.js";
25
25
  import { registerGithubRoutes } from "./server-github-routes.js";
26
- import { startTaskboardBridge } from "./taskboard-bridge.js";
27
26
  import { registerTaskRoutes } from "./server-task-routes.js";
28
27
  import { getGithubConnectorStatus } from "./github-connector.js";
29
28
  import { registerMissionRoutes } from "./server-mission-routes.js";
@@ -558,20 +557,6 @@ export async function startServer(config, configPath, options = {}) {
558
557
  const requireSessions = buildRequireScope("sessions");
559
558
  const requireFiles = buildRequireScope("files");
560
559
  const requirePasswordVault = buildRequireScope("password-vault");
561
- const taskboardBridge = await startTaskboardBridge(configDir, storage, sessionRegistry, structuredSessions, config.defaultCwd);
562
- app.use("/taskboard", (req, res, next) => {
563
- // Taskboard's immutable Vite assets are safe to serve without the Wand
564
- // session cookie. This matters behind HTTPS reverse proxies where iframe
565
- // subresource requests may not carry the parent authentication cookie.
566
- const staticAsset = req.path.startsWith("/assets/")
567
- || /^\/[^/]+\.(?:css|js|map|svg|png|jpg|jpeg|webp|ico)$/.test(req.path);
568
- if (staticAsset) {
569
- taskboardBridge.handler(req, res, next);
570
- return;
571
- }
572
- requireAuth(req, res, next);
573
- });
574
- app.use("/taskboard", taskboardBridge.handler);
575
560
  // Route-specific parsers must run before the global parser. Once body-parser
576
561
  // has consumed a request, a later express.json() cannot tighten or widen it.
577
562
  app.use("/api/optimize-prompt", express.json({ limit: "256kb" }));
@@ -984,7 +969,8 @@ export async function startServer(config, configPath, options = {}) {
984
969
  },
985
970
  });
986
971
  registerGithubRoutes(app, { storage, requireAdmin, sessions: sessionRegistry });
987
- registerTaskRoutes(app, storage, sessionRegistry, requireAdmin);
972
+ // 任务管理与 Missions 一样只需登录:原生 connected-app 也要能列/建/派发。
973
+ registerTaskRoutes(app, { storage, sessions: sessionRegistry, structured: structuredSessions, config });
988
974
  registerAdminUpdateRoutes(app, {
989
975
  storage,
990
976
  config,
@@ -1586,10 +1572,6 @@ export async function startServer(config, configPath, options = {}) {
1586
1572
  wss.close();
1587
1573
  }
1588
1574
  catch { /* ignore */ }
1589
- try {
1590
- await taskboardBridge.close();
1591
- }
1592
- catch { /* best-effort shutdown */ }
1593
1575
  try {
1594
1576
  await serverClosed;
1595
1577
  }
package/dist/storage.d.ts CHANGED
@@ -8,6 +8,11 @@ export interface ConnectorMeta {
8
8
  connectedAt: string | null;
9
9
  updatedAt: string;
10
10
  }
11
+ /**
12
+ * `wand_tasks.agent_json` 的读侧校验。历史行 / 手工写坏的值都退化成 null,
13
+ * 前端据此显示「未指定 CLI 工具」,而不是渲染出半个派发配置。
14
+ */
15
+ export declare function parseWandTaskAgent(raw: unknown): import("./task-types.js").WandTaskAgent | null;
11
16
  type AuthPrincipalKind = "browser-admin" | "connected-app";
12
17
  export type AuthScope = "admin" | "sessions" | "files" | "password-vault" | "session-preferences";
13
18
  export interface AuthPrincipal {
@@ -93,30 +98,41 @@ export declare class WandStorage {
93
98
  status?: WorkspaceTaskStatus;
94
99
  worktree?: WorkspaceTaskWorktree | null;
95
100
  }): void;
96
- saveWorkspaceTaskLayout(id: string, layout: TaskWindowLayout | null): void;
101
+ saveWorkspaceTaskLayout(id: string, layout: TaskWindowLayout | null, expectedRevision?: number): {
102
+ ok: true;
103
+ layoutRevision: number;
104
+ } | {
105
+ ok: false;
106
+ conflict: true;
107
+ layoutRevision: number;
108
+ layout: TaskWindowLayout | null;
109
+ };
97
110
  touchWorkspaceTask(id: string): void;
98
111
  deleteWorkspaceTask(id: string, options?: {
99
112
  cascade?: boolean;
100
113
  }): void;
114
+ tasksAggregateFingerprint(): string;
101
115
  listWandTasks(workspaceId?: string | null): import("./task-types.js").WandTask[];
116
+ private mapWandTaskRow;
102
117
  getWandTask(id: string): import("./task-types.js").WandTask | null;
118
+ getWandTaskByWorkspaceTaskId(workspaceTaskId: string): import("./task-types.js").WandTask | null;
119
+ findUnlinkedWandTask(workspaceId: string, title: string): import("./task-types.js").WandTask | null;
103
120
  createWandTask(input: {
104
121
  workspaceId?: string | null;
122
+ workspaceTaskId?: string | null;
105
123
  title: string;
106
124
  description?: string;
107
125
  status?: import("./task-types.js").WandTaskStatus;
108
126
  priority?: import("./task-types.js").WandTaskPriority;
109
127
  labels?: string[];
110
128
  dueDate?: string | null;
129
+ agent?: import("./task-types.js").WandTaskAgent | null;
111
130
  }): import("./task-types.js").WandTask;
112
- updateWandTask(id: string, patch: Partial<Pick<import("./task-types.js").WandTask, "workspaceId" | "title" | "description" | "status" | "priority" | "labels" | "dueDate" | "sortOrder">>): import("./task-types.js").WandTask | null;
131
+ updateWandTask(id: string, patch: Partial<Pick<import("./task-types.js").WandTask, "workspaceId" | "workspaceTaskId" | "title" | "description" | "status" | "priority" | "labels" | "dueDate" | "sortOrder" | "agent">>): import("./task-types.js").WandTask | null;
113
132
  deleteWandTask(id: string): void;
114
133
  listWandTaskSessionIds(taskId: string): string[];
115
134
  bindWandTaskSession(taskId: string, sessionId: string): void;
116
135
  unbindWandTaskSession(taskId: string, sessionId: string): void;
117
- bindTaskboardTaskSession(taskId: string, sessionId: string): void;
118
- listTaskboardTaskSessions(taskId: string): string[];
119
- unbindTaskboardTaskSession(taskId: string, sessionId: string): void;
120
136
  listGithubIssueBindings(owner: string, repo: string, issueNumber: number): Array<{
121
137
  sessionId: string;
122
138
  boundAt: string;
@@ -135,7 +151,8 @@ export declare class WandStorage {
135
151
  getAppSecret(): string | null;
136
152
  /** Persist appSecret in database (DB is the authoritative source after first migration) */
137
153
  setAppSecret(value: string): void;
138
- private encryptStoredPassword;
154
+ /** Encrypt an at-rest secret with the vault key; plaintext when no key exists. */
155
+ private encryptStoredSecret;
139
156
  private decryptPasswordItem;
140
157
  ensureDefaultPasswordVault(): void;
141
158
  listPasswordVaults(): PasswordVault[];
package/dist/storage.js CHANGED
@@ -15,6 +15,25 @@ function safeJsonParse(raw) {
15
15
  return undefined;
16
16
  }
17
17
  }
18
+ /**
19
+ * `wand_tasks.agent_json` 的读侧校验。历史行 / 手工写坏的值都退化成 null,
20
+ * 前端据此显示「未指定 CLI 工具」,而不是渲染出半个派发配置。
21
+ */
22
+ export function parseWandTaskAgent(raw) {
23
+ const value = typeof raw === "string" ? safeJsonParse(raw) : undefined;
24
+ if (!value || typeof value !== "object")
25
+ return null;
26
+ const provider = value.provider;
27
+ const model = value.model;
28
+ const thinkingEffort = value.thinkingEffort;
29
+ if (provider !== "claude" && provider !== "codex" && provider !== "opencode" && provider !== "grok" && provider !== "qoder" && provider !== "pi")
30
+ return null;
31
+ if (typeof model !== "string" || !model.trim() || model.trim().length > 128)
32
+ return null;
33
+ if (thinkingEffort !== "off" && thinkingEffort !== "standard" && thinkingEffort !== "deep" && thinkingEffort !== "max")
34
+ return null;
35
+ return { provider, model: model.trim(), thinkingEffort };
36
+ }
18
37
  const SESSION_OPTIONS_SCHEMA_VERSION = 1;
19
38
  function isRecord(value) {
20
39
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -315,6 +334,7 @@ function mapWorkspaceTaskRow(row) {
315
334
  status: (row.status === "done" ? "done" : "active"),
316
335
  createdAt: row.created_at,
317
336
  lastOpenedAt: row.last_opened_at,
337
+ layoutRevision: Number(row.layout_revision ?? 0) || 0,
318
338
  };
319
339
  }
320
340
  function sessionPersistFields() {
@@ -381,8 +401,8 @@ function sessionPersistValues(snapshot) {
381
401
  snapshot.output,
382
402
  snapshot.ptyOutputSeq ?? 0,
383
403
  snapshot.archived ? 1 : 0,
384
- snapshot.archivedAt,
385
- snapshot.claudeSessionId,
404
+ snapshot.archivedAt ?? null,
405
+ snapshot.claudeSessionId ?? null,
386
406
  snapshot.provider ?? null,
387
407
  snapshot.sessionKind ?? "pty",
388
408
  snapshot.runner ?? null,
@@ -415,8 +435,8 @@ function sessionRuntimeMetadataValues(snapshot) {
415
435
  snapshot.startedAt,
416
436
  snapshot.endedAt,
417
437
  snapshot.archived ? 1 : 0,
418
- snapshot.archivedAt,
419
- snapshot.claudeSessionId,
438
+ snapshot.archivedAt ?? null,
439
+ snapshot.claudeSessionId ?? null,
420
440
  snapshot.provider ?? null,
421
441
  snapshot.sessionKind ?? "pty",
422
442
  snapshot.runner ?? null,
@@ -668,6 +688,7 @@ const INIT_SQL = `
668
688
  cwd TEXT,
669
689
  created_at TEXT NOT NULL,
670
690
  last_opened_at TEXT,
691
+ layout_revision INTEGER NOT NULL DEFAULT 0,
671
692
  FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
672
693
  );
673
694
 
@@ -696,6 +717,8 @@ const INIT_SQL = `
696
717
  labels_json TEXT NOT NULL DEFAULT '[]',
697
718
  due_date TEXT,
698
719
  sort_order INTEGER NOT NULL DEFAULT 0,
720
+ agent_json TEXT,
721
+ workspace_task_id TEXT,
699
722
  created_at TEXT NOT NULL,
700
723
  updated_at TEXT NOT NULL,
701
724
  FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL
@@ -710,14 +733,6 @@ const INIT_SQL = `
710
733
  FOREIGN KEY (session_id) REFERENCES command_sessions(id) ON DELETE CASCADE
711
734
  );
712
735
  CREATE INDEX IF NOT EXISTS idx_wand_task_sessions_session ON wand_task_sessions(session_id);
713
- CREATE TABLE IF NOT EXISTS taskboard_session_bindings (
714
- task_id TEXT NOT NULL,
715
- session_id TEXT NOT NULL,
716
- bound_at TEXT NOT NULL,
717
- PRIMARY KEY (task_id, session_id),
718
- FOREIGN KEY (session_id) REFERENCES command_sessions(id) ON DELETE CASCADE
719
- );
720
- CREATE INDEX IF NOT EXISTS idx_taskboard_session_bindings_session ON taskboard_session_bindings(session_id);
721
736
  `;
722
737
  function ensureWandTaskSchema(db) {
723
738
  const columns = db.prepare("PRAGMA table_info(wand_tasks)").all();
@@ -726,7 +741,16 @@ function ensureWandTaskSchema(db) {
726
741
  db.exec("ALTER TABLE wand_tasks ADD COLUMN identifier TEXT");
727
742
  if (columns.length > 0 && !names.has("due_date"))
728
743
  db.exec("ALTER TABLE wand_tasks ADD COLUMN due_date TEXT");
744
+ // 任务的默认派发配置(CLI 工具 / 模型 / 思考深度)。只加列,历史行保持 NULL。
745
+ if (columns.length > 0 && !names.has("agent_json"))
746
+ db.exec("ALTER TABLE wand_tasks ADD COLUMN agent_json TEXT");
747
+ if (columns.length > 0 && !names.has("workspace_task_id"))
748
+ db.exec("ALTER TABLE wand_tasks ADD COLUMN workspace_task_id TEXT");
729
749
  if (columns.length > 0) {
750
+ // Index creation must wait until the column exists on legacy databases.
751
+ // INIT_SQL cannot create it: CREATE TABLE IF NOT EXISTS is a no-op on old
752
+ // wand_tasks, and CREATE INDEX would then fail with "no such column".
753
+ db.exec("CREATE INDEX IF NOT EXISTS idx_wand_tasks_workspace_task ON wand_tasks(workspace_task_id)");
730
754
  const rows = db.prepare("SELECT id FROM wand_tasks WHERE identifier IS NULL OR identifier = '' ORDER BY created_at, id").all();
731
755
  const update = db.prepare("UPDATE wand_tasks SET identifier = ? WHERE id = ?");
732
756
  rows.forEach((row, index) => update.run(`TASK-${index + 1}`, row.id));
@@ -743,6 +767,9 @@ function ensureWorkspaceSchema(db) {
743
767
  if (taskColumns.length > 0 && !taskNames.has("cwd")) {
744
768
  db.exec("ALTER TABLE workspace_tasks ADD COLUMN cwd TEXT");
745
769
  }
770
+ if (taskColumns.length > 0 && !taskNames.has("layout_revision")) {
771
+ db.exec("ALTER TABLE workspace_tasks ADD COLUMN layout_revision INTEGER NOT NULL DEFAULT 0");
772
+ }
746
773
  }
747
774
  function ensureConnectorSchema(db) {
748
775
  // The table is created by INIT_SQL for new databases; this repairs an
@@ -771,6 +798,7 @@ export function ensureDatabaseFile(dbPath) {
771
798
  ensureAuthSessionSchema(db);
772
799
  ensureCommandSessionSchema(db);
773
800
  ensureWorkspaceSchema(db);
801
+ ensureWandTaskSchema(db);
774
802
  ensureConnectorSchema(db);
775
803
  {
776
804
  const missionColumns = db.prepare("PRAGMA table_info(missions)").all();
@@ -902,7 +930,7 @@ export class WandStorage {
902
930
  // ============ Workspaces(多标签 / 分屏项目)============
903
931
  listWorkspaces() {
904
932
  const rows = this.db
905
- .prepare("SELECT id, name, cwd, kind, default_provider, layout_json, created_at, last_opened_at FROM workspaces ORDER BY created_at ASC, id ASC")
933
+ .prepare("SELECT id, name, cwd, kind, default_provider, layout_json, created_at, last_opened_at FROM workspaces ORDER BY created_at DESC, id DESC")
906
934
  .all();
907
935
  return rows.map(mapWorkspaceRow);
908
936
  }
@@ -1026,15 +1054,15 @@ export class WandStorage {
1026
1054
  // ── Workspace tasks(任务 = 命名 + 独立 worktree + 一组标签)──
1027
1055
  listWorkspaceTasks(workspaceId) {
1028
1056
  const rows = this.db
1029
- .prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at
1057
+ .prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at, layout_revision
1030
1058
  FROM workspace_tasks WHERE workspace_id = ?
1031
- ORDER BY COALESCE(last_opened_at, created_at) DESC`)
1059
+ ORDER BY created_at DESC, id DESC`)
1032
1060
  .all(workspaceId);
1033
1061
  return rows.map(mapWorkspaceTaskRow);
1034
1062
  }
1035
1063
  getWorkspaceTask(id) {
1036
1064
  const row = this.db
1037
- .prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at
1065
+ .prepare(`SELECT id, workspace_id, name, worktree_json, layout_json, status, cwd, created_at, last_opened_at, layout_revision
1038
1066
  FROM workspace_tasks WHERE id = ?`)
1039
1067
  .get(id);
1040
1068
  return row ? mapWorkspaceTaskRow(row) : null;
@@ -1079,10 +1107,19 @@ export class WandStorage {
1079
1107
  return;
1080
1108
  this.db.prepare(`UPDATE workspace_tasks SET ${assignments.join(", ")} WHERE id = ?`).run(...values, id);
1081
1109
  }
1082
- saveWorkspaceTaskLayout(id, layout) {
1110
+ saveWorkspaceTaskLayout(id, layout, expectedRevision) {
1111
+ const current = this.getWorkspaceTask(id);
1112
+ if (!current)
1113
+ throw new Error("未找到该任务。");
1114
+ const currentRevision = current.layoutRevision ?? 0;
1115
+ if (expectedRevision !== undefined && expectedRevision !== currentRevision) {
1116
+ return { ok: false, conflict: true, layoutRevision: currentRevision, layout: current.layout };
1117
+ }
1118
+ const nextRevision = currentRevision + 1;
1083
1119
  this.db
1084
- .prepare("UPDATE workspace_tasks SET layout_json = ? WHERE id = ?")
1085
- .run(layout ? JSON.stringify(layout) : null, id);
1120
+ .prepare("UPDATE workspace_tasks SET layout_json = ?, layout_revision = ? WHERE id = ?")
1121
+ .run(layout ? JSON.stringify(layout) : null, nextRevision, id);
1122
+ return { ok: true, layoutRevision: nextRevision };
1086
1123
  }
1087
1124
  touchWorkspaceTask(id) {
1088
1125
  this.db.prepare("UPDATE workspace_tasks SET last_opened_at = ? WHERE id = ?").run(nowIso(), id);
@@ -1096,14 +1133,36 @@ export class WandStorage {
1096
1133
  }
1097
1134
  this.db.prepare("DELETE FROM workspace_tasks WHERE id = ?").run(id);
1098
1135
  }
1136
+ tasksAggregateFingerprint() {
1137
+ const sessions = this.db.prepare(`SELECT COUNT(*) AS count,
1138
+ COALESCE(MAX(started_at), '') AS started,
1139
+ COALESCE(MAX(ended_at), '') AS ended,
1140
+ COALESCE(MAX(title), '') AS title
1141
+ FROM command_sessions`).get();
1142
+ const tasks = this.db.prepare(`SELECT COUNT(*) AS count,
1143
+ COALESCE(MAX(created_at), '') AS created,
1144
+ COALESCE(MAX(last_opened_at), '') AS opened,
1145
+ COALESCE(MAX(layout_revision), 0) AS revision
1146
+ FROM workspace_tasks`).get();
1147
+ const workspaces = this.db.prepare(`SELECT COUNT(*) AS count, COALESCE(MAX(last_opened_at), '') AS opened FROM workspaces`).get();
1148
+ return JSON.stringify({
1149
+ sessions,
1150
+ tasks,
1151
+ workspaces,
1152
+ });
1153
+ }
1099
1154
  listWandTasks(workspaceId) {
1100
- const rows = this.db.prepare(`SELECT id, identifier, workspace_id, title, description, status, priority, labels_json, due_date, sort_order, created_at, updated_at
1155
+ const rows = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at
1101
1156
  FROM wand_tasks ${workspaceId === undefined ? "" : "WHERE workspace_id IS ?"}
1102
1157
  ORDER BY status, sort_order, updated_at DESC`).all(...(workspaceId === undefined ? [] : [workspaceId]));
1103
- return rows.map((row) => ({
1158
+ return rows.map((row) => this.mapWandTaskRow(row));
1159
+ }
1160
+ mapWandTaskRow(row) {
1161
+ return {
1104
1162
  id: String(row.id),
1105
1163
  identifier: typeof row.identifier === "string" && row.identifier ? row.identifier : `TASK-${String(row.id).slice(0, 6)}`,
1106
1164
  workspaceId: typeof row.workspace_id === "string" ? row.workspace_id : null,
1165
+ workspaceTaskId: typeof row.workspace_task_id === "string" && row.workspace_task_id ? row.workspace_task_id : null,
1107
1166
  title: String(row.title),
1108
1167
  description: String(row.description ?? ""),
1109
1168
  status: row.status === "doing" || row.status === "done" ? row.status : "todo",
@@ -1111,13 +1170,26 @@ export class WandStorage {
1111
1170
  labels: (() => { const parsed = safeJsonParse(String(row.labels_json ?? "[]")); return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : []; })(),
1112
1171
  dueDate: typeof row.due_date === "string" ? row.due_date : null,
1113
1172
  sortOrder: Number(row.sort_order) || 0,
1173
+ agent: parseWandTaskAgent(row.agent_json),
1114
1174
  createdAt: String(row.created_at),
1115
1175
  updatedAt: String(row.updated_at),
1116
- }));
1176
+ };
1117
1177
  }
1118
1178
  getWandTask(id) {
1119
1179
  return this.listWandTasks().find((task) => task.id === id) ?? null;
1120
1180
  }
1181
+ getWandTaskByWorkspaceTaskId(workspaceTaskId) {
1182
+ const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at
1183
+ FROM wand_tasks WHERE workspace_task_id = ? ORDER BY updated_at DESC LIMIT 1`).get(workspaceTaskId);
1184
+ return row ? this.mapWandTaskRow(row) : null;
1185
+ }
1186
+ findUnlinkedWandTask(workspaceId, title) {
1187
+ const row = this.db.prepare(`SELECT id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at
1188
+ FROM wand_tasks
1189
+ WHERE workspace_id IS ? AND title = ? AND (workspace_task_id IS NULL OR workspace_task_id = '')
1190
+ ORDER BY updated_at DESC LIMIT 1`).get(workspaceId, title);
1191
+ return row ? this.mapWandTaskRow(row) : null;
1192
+ }
1121
1193
  createWandTask(input) {
1122
1194
  const id = crypto.randomUUID();
1123
1195
  const now = nowIso();
@@ -1126,7 +1198,7 @@ export class WandStorage {
1126
1198
  const max = this.db.prepare("SELECT COALESCE(MAX(sort_order), -1) AS value FROM wand_tasks WHERE workspace_id IS ? AND status = ?").get(input.workspaceId ?? null, status);
1127
1199
  const numberRow = this.db.prepare("SELECT COALESCE(MAX(CAST(substr(identifier, 6) AS INTEGER)), 0) AS value FROM wand_tasks WHERE identifier GLOB 'TASK-[0-9]*'").get();
1128
1200
  const identifier = `TASK-${(numberRow?.value ?? 0) + 1}`;
1129
- this.db.prepare(`INSERT INTO wand_tasks (id, identifier, workspace_id, title, description, status, priority, labels_json, due_date, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, identifier, input.workspaceId ?? null, input.title, input.description ?? "", status, priority, JSON.stringify(input.labels ?? []), input.dueDate ?? null, (max?.value ?? -1) + 1, now, now);
1201
+ this.db.prepare(`INSERT INTO wand_tasks (id, identifier, workspace_id, workspace_task_id, title, description, status, priority, labels_json, due_date, sort_order, agent_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, identifier, input.workspaceId ?? null, input.workspaceTaskId ?? null, input.title, input.description ?? "", status, priority, JSON.stringify(input.labels ?? []), input.dueDate ?? null, (max?.value ?? -1) + 1, input.agent ? JSON.stringify(input.agent) : null, now, now);
1130
1202
  return this.getWandTask(id);
1131
1203
  }
1132
1204
  updateWandTask(id, patch) {
@@ -1134,12 +1206,12 @@ export class WandStorage {
1134
1206
  if (!current)
1135
1207
  return null;
1136
1208
  const next = { ...current, ...patch, updatedAt: nowIso() };
1137
- this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, title = ?, description = ?, status = ?, priority = ?, labels_json = ?, due_date = ?, sort_order = ?, updated_at = ? WHERE id = ?`).run(next.workspaceId, next.title, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.sortOrder, next.updatedAt, id);
1209
+ this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, workspace_task_id = ?, title = ?, description = ?, status = ?, priority = ?, labels_json = ?, due_date = ?, sort_order = ?, agent_json = ?, updated_at = ? WHERE id = ?`).run(next.workspaceId, next.workspaceTaskId, next.title, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.sortOrder, next.agent ? JSON.stringify(next.agent) : null, next.updatedAt, id);
1138
1210
  return next;
1139
1211
  }
1140
1212
  deleteWandTask(id) { this.db.prepare("DELETE FROM wand_tasks WHERE id = ?").run(id); }
1141
1213
  listWandTaskSessionIds(taskId) {
1142
- const rows = this.db.prepare("SELECT session_id FROM wand_task_sessions WHERE task_id = ? ORDER BY bound_at ASC").all(taskId);
1214
+ const rows = this.db.prepare("SELECT session_id FROM wand_task_sessions WHERE task_id = ? ORDER BY rowid ASC, session_id ASC").all(taskId);
1143
1215
  return rows.map((row) => row.session_id);
1144
1216
  }
1145
1217
  bindWandTaskSession(taskId, sessionId) {
@@ -1150,18 +1222,6 @@ export class WandStorage {
1150
1222
  this.db.prepare("INSERT OR IGNORE INTO wand_task_sessions (task_id, session_id, bound_at) VALUES (?, ?, ?)").run(taskId, sessionId, nowIso());
1151
1223
  }
1152
1224
  unbindWandTaskSession(taskId, sessionId) { this.db.prepare("DELETE FROM wand_task_sessions WHERE task_id = ? AND session_id = ?").run(taskId, sessionId); }
1153
- bindTaskboardTaskSession(taskId, sessionId) {
1154
- if (!this.getSession(sessionId))
1155
- throw new Error("未找到该会话。");
1156
- this.db.prepare("INSERT OR IGNORE INTO taskboard_session_bindings (task_id, session_id, bound_at) VALUES (?, ?, ?)").run(taskId, sessionId, nowIso());
1157
- }
1158
- listTaskboardTaskSessions(taskId) {
1159
- const rows = this.db.prepare("SELECT session_id FROM taskboard_session_bindings WHERE task_id = ? ORDER BY rowid ASC").all(taskId);
1160
- return rows.map((row) => row.session_id);
1161
- }
1162
- unbindTaskboardTaskSession(taskId, sessionId) {
1163
- this.db.prepare("DELETE FROM taskboard_session_bindings WHERE task_id = ? AND session_id = ?").run(taskId, sessionId);
1164
- }
1165
1225
  listGithubIssueBindings(owner, repo, issueNumber) {
1166
1226
  const rows = this.db.prepare(`SELECT session_id, bound_at FROM github_issue_bindings
1167
1227
  WHERE owner = ? AND repo = ? AND issue_number = ? ORDER BY bound_at ASC`).all(owner, repo, issueNumber);
@@ -1208,14 +1268,23 @@ export class WandStorage {
1208
1268
  setAppSecret(value) {
1209
1269
  this.setConfigValue("appSecret", value);
1210
1270
  }
1211
- encryptStoredPassword(password) {
1212
- if (!password)
1271
+ /** Encrypt an at-rest secret with the vault key; plaintext when no key exists. */
1272
+ encryptStoredSecret(value) {
1273
+ if (!value)
1213
1274
  return null;
1214
1275
  const secret = this.getAppSecret();
1215
- return secret ? encryptVaultSecret(password, secret) : password;
1276
+ return secret ? encryptVaultSecret(value, secret) : value;
1216
1277
  }
1217
- decryptPasswordItem(item) {
1218
- return { ...item, password: decryptVaultSecret(item.password, this.getAppSecret()) };
1278
+ decryptPasswordItem(item, raw) {
1279
+ const secret = this.getAppSecret();
1280
+ const notes = decryptVaultSecret(raw.notes ?? undefined, secret);
1281
+ const fieldsJson = decryptVaultSecret(raw.fields, secret) ?? raw.fields;
1282
+ return {
1283
+ ...item,
1284
+ password: decryptVaultSecret(raw.password ?? undefined, secret),
1285
+ notes,
1286
+ fields: decodePasswordFields(fieldsJson),
1287
+ };
1219
1288
  }
1220
1289
  // ============ Browser Extension Password Vault Methods ============
1221
1290
  ensureDefaultPasswordVault() {
@@ -1260,7 +1329,7 @@ export class WandStorage {
1260
1329
  const limit = typeof filter.limit === "number" && Number.isFinite(filter.limit)
1261
1330
  ? Math.max(1, Math.min(200, Math.floor(filter.limit)))
1262
1331
  : 100;
1263
- return rows.map((row) => this.decryptPasswordItem(mapPasswordItemRow(row)))
1332
+ return rows.map((row) => this.decryptPasswordItem(mapPasswordItemRow(row), row))
1264
1333
  .filter((item) => itemMatchesFilter(item, filter)).slice(0, limit);
1265
1334
  }
1266
1335
  getPasswordItem(id) {
@@ -1270,7 +1339,7 @@ export class WandStorage {
1270
1339
  FROM password_items
1271
1340
  WHERE id = ? AND archived = 0`)
1272
1341
  .get(id);
1273
- return row ? this.decryptPasswordItem(mapPasswordItemRow(row)) : null;
1342
+ return row ? this.decryptPasswordItem(mapPasswordItemRow(row), row) : null;
1274
1343
  }
1275
1344
  createPasswordItem(input) {
1276
1345
  this.ensureDefaultPasswordVault();
@@ -1286,7 +1355,7 @@ export class WandStorage {
1286
1355
  id, vault_id, type, title, username, password, urls, notes, fields, tags, favorite, archived,
1287
1356
  created_at, updated_at, last_used_at, password_updated_at
1288
1357
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, NULL, ?)`)
1289
- .run(id, vaultId, normalized.type, normalized.title, normalized.username ?? null, this.encryptStoredPassword(normalized.password), JSON.stringify(normalized.urls), normalized.notes ?? null, JSON.stringify(normalized.fields), JSON.stringify(normalized.tags), normalized.favorite ? 1 : 0, now, now, passwordUpdatedAt ?? null);
1358
+ .run(id, vaultId, normalized.type, normalized.title, normalized.username ?? null, this.encryptStoredSecret(normalized.password), JSON.stringify(normalized.urls), this.encryptStoredSecret(normalized.notes), this.encryptStoredSecret(JSON.stringify(normalized.fields)) ?? "{}", JSON.stringify(normalized.tags), normalized.favorite ? 1 : 0, now, now, passwordUpdatedAt ?? null);
1290
1359
  return this.getPasswordItem(id);
1291
1360
  }
1292
1361
  updatePasswordItem(id, input) {
@@ -1317,7 +1386,7 @@ export class WandStorage {
1317
1386
  SET vault_id = ?, type = ?, title = ?, username = ?, password = ?, urls = ?, notes = ?,
1318
1387
  fields = ?, tags = ?, favorite = ?, updated_at = ?, password_updated_at = ?
1319
1388
  WHERE id = ? AND archived = 0`)
1320
- .run(vaultId, normalized.type, normalized.title, normalized.username ?? null, this.encryptStoredPassword(normalized.password), JSON.stringify(normalized.urls), normalized.notes ?? null, JSON.stringify(normalized.fields), JSON.stringify(normalized.tags), normalized.favorite ? 1 : 0, now, passwordUpdatedAt, id);
1389
+ .run(vaultId, normalized.type, normalized.title, normalized.username ?? null, this.encryptStoredSecret(normalized.password), JSON.stringify(normalized.urls), this.encryptStoredSecret(normalized.notes), this.encryptStoredSecret(JSON.stringify(normalized.fields)) ?? "{}", JSON.stringify(normalized.tags), normalized.favorite ? 1 : 0, now, passwordUpdatedAt, id);
1321
1390
  return this.getPasswordItem(id);
1322
1391
  }
1323
1392
  touchPasswordItem(id) {
@@ -1382,10 +1451,7 @@ export class WandStorage {
1382
1451
  return this.db.prepare("DELETE FROM connectors WHERE provider = ?").run(provider).changes > 0;
1383
1452
  }
1384
1453
  encryptConnectorToken(token) {
1385
- if (!token)
1386
- return "";
1387
- const secret = this.getAppSecret();
1388
- return secret ? encryptVaultSecret(token, secret) : token;
1454
+ return this.encryptStoredSecret(token) ?? "";
1389
1455
  }
1390
1456
  // ============ Auth Session Methods ============
1391
1457
  saveAuthSession(token, expiresAt, principal = { kind: "browser-admin", scopes: ["admin"] }) {
@@ -1670,6 +1736,20 @@ function mapPasswordVaultRow(row) {
1670
1736
  updatedAt: row.updated_at,
1671
1737
  };
1672
1738
  }
1739
+ /** Parse the (possibly encrypted) `fields` JSON column into a string map. */
1740
+ function decodePasswordFields(json) {
1741
+ if (!json)
1742
+ return {};
1743
+ try {
1744
+ const parsed = JSON.parse(json);
1745
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1746
+ return {};
1747
+ return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string"));
1748
+ }
1749
+ catch {
1750
+ return {};
1751
+ }
1752
+ }
1673
1753
  function mapPasswordItemRow(row) {
1674
1754
  return {
1675
1755
  id: row.id,
@@ -1677,10 +1757,7 @@ function mapPasswordItemRow(row) {
1677
1757
  type: row.type,
1678
1758
  title: row.title,
1679
1759
  username: row.username ?? undefined,
1680
- password: row.password ?? undefined,
1681
1760
  urls: safeJsonParse(row.urls)?.filter((item) => typeof item === "string") ?? [],
1682
- notes: row.notes ?? undefined,
1683
- fields: safeJsonParse(row.fields) ?? {},
1684
1761
  tags: safeJsonParse(row.tags)?.filter((item) => typeof item === "string") ?? [],
1685
1762
  favorite: Boolean(row.favorite),
1686
1763
  createdAt: row.created_at,
@@ -60,6 +60,17 @@ export interface StructuredExecHost {
60
60
  listRuns(): Promise<StructuredRunState[]>;
61
61
  forgetRun(runId: string): void;
62
62
  }
63
+ /**
64
+ * Stateful UTF-8 decoder for structured CLI stdout/stderr.
65
+ * Incomplete sequences are held across chunks. end() discards a trailing
66
+ * incomplete sequence instead of emitting U+FFFD replacement characters.
67
+ * Call this at the byte-stream seam, before line splitting or log append.
68
+ */
69
+ export interface Utf8TextDecoder {
70
+ write(chunk: Buffer | string): string;
71
+ end(): string;
72
+ }
73
+ export declare function createUtf8TextDecoder(): Utf8TextDecoder;
63
74
  /** Resolve a stable daemon-side key for a session's active structured run. */
64
75
  export declare function structuredRunId(sessionId: string): string;
65
76
  /** Legacy/local adapter and deterministic test adapter for the structured seam. */
@@ -67,8 +78,8 @@ export declare class InProcessStructuredExecHost implements StructuredExecHost {
67
78
  readonly persistent = false;
68
79
  private readonly processes;
69
80
  spawnStructured(request: StructuredSpawnRequest): Promise<StructuredExecProcess>;
70
- attachRun(): Promise<StructuredRunState | null>;
71
- adoptRun(): Promise<StructuredExecProcess | null>;
81
+ attachRun(_runId: string): Promise<StructuredRunState | null>;
82
+ adoptRun(_runId: string): Promise<StructuredExecProcess | null>;
72
83
  listRuns(): Promise<StructuredRunState[]>;
73
84
  forgetRun(runId: string): void;
74
85
  }