@co0ontty/wand 4.106.0 → 4.106.1

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 (279) hide show
  1. package/README.md +2 -2
  2. package/dist/ai-text-runner.js +24 -14
  3. package/dist/build-info.d.ts +2 -0
  4. package/dist/build-info.js +18 -0
  5. package/dist/build-info.json +3 -3
  6. package/dist/cli.js +8 -2
  7. package/dist/daemon-maintenance-state.js +1 -1
  8. package/dist/daemon-maintenance-targets.d.ts +1 -0
  9. package/dist/daemon-maintenance-targets.js +25 -9
  10. package/dist/daemon-maintenance.d.ts +3 -0
  11. package/dist/daemon-maintenance.js +60 -13
  12. package/dist/employee-im-types.d.ts +1 -1
  13. package/dist/employee-input-scheduling.d.ts +4 -0
  14. package/dist/employee-knowledge-cli.js +5 -5
  15. package/dist/employee-knowledge.d.ts +3 -1
  16. package/dist/employee-knowledge.js +24 -8
  17. package/dist/git-quick-commit.d.ts +2 -0
  18. package/dist/git-quick-commit.js +15 -2
  19. package/dist/git-worktree-batch.d.ts +26 -0
  20. package/dist/git-worktree-batch.js +139 -0
  21. package/dist/global-knowledge.d.ts +12 -0
  22. package/dist/global-knowledge.js +124 -0
  23. package/dist/iteration-log.d.ts +13 -5
  24. package/dist/iteration-log.js +120 -59
  25. package/dist/knowledge-types.d.ts +12 -1
  26. package/dist/knowledge-types.js +4 -0
  27. package/dist/middleware/api-scope.js +1 -0
  28. package/dist/model-groups.js +6 -3
  29. package/dist/models/parsers/opencode.js +31 -1
  30. package/dist/models.js +15 -14
  31. package/dist/npm-update-utils.d.ts +5 -2
  32. package/dist/npm-update-utils.js +34 -4
  33. package/dist/opencode-cli.d.ts +6 -0
  34. package/dist/opencode-cli.js +51 -0
  35. package/dist/opencode-system-fallback.d.ts +1 -0
  36. package/dist/opencode-system-fallback.js +16 -4
  37. package/dist/pi-native-event-bridge.js +55 -3
  38. package/dist/pi-native-input.d.ts +22 -0
  39. package/dist/pi-native-input.js +77 -0
  40. package/dist/pi-tmux-host.d.ts +14 -5
  41. package/dist/pi-tmux-host.js +68 -17
  42. package/dist/process-manager.js +17 -3
  43. package/dist/pty-tmux-backend.d.ts +27 -2
  44. package/dist/pty-tmux-backend.js +471 -70
  45. package/dist/pty-tmux-log.d.ts +1 -1
  46. package/dist/pty-tmux-log.js +23 -2
  47. package/dist/retention-protection.d.ts +1 -1
  48. package/dist/retention-protection.js +3 -2
  49. package/dist/retention.d.ts +5 -0
  50. package/dist/retention.js +11 -0
  51. package/dist/server-knowledge-routes.js +2 -2
  52. package/dist/server-session-input-routes.js +38 -19
  53. package/dist/server-session-lifecycle-routes.js +14 -1
  54. package/dist/server-session-worktree-routes.js +105 -12
  55. package/dist/server-task-routes.js +41 -12
  56. package/dist/server-workspace-routes.js +77 -9
  57. package/dist/server.js +67 -35
  58. package/dist/session-registry.d.ts +17 -3
  59. package/dist/session-registry.js +66 -13
  60. package/dist/session-service.js +1 -1
  61. package/dist/storage/internal.d.ts +4 -3
  62. package/dist/storage/internal.js +3 -3
  63. package/dist/storage/knowledge-repository.d.ts +5 -0
  64. package/dist/storage/knowledge-repository.js +75 -16
  65. package/dist/storage/mappers.d.ts +1 -0
  66. package/dist/storage/mappers.js +6 -1
  67. package/dist/storage/migrations.js +60 -0
  68. package/dist/storage/session-input-repository.d.ts +19 -0
  69. package/dist/storage/session-input-repository.js +35 -0
  70. package/dist/storage/task-workflow-repository.d.ts +29 -0
  71. package/dist/storage/task-workflow-repository.js +110 -0
  72. package/dist/storage/tasks-repository.d.ts +6 -1
  73. package/dist/storage/tasks-repository.js +94 -34
  74. package/dist/storage/workspaces-repository.d.ts +7 -0
  75. package/dist/storage/workspaces-repository.js +28 -2
  76. package/dist/storage-cleanup.d.ts +7 -2
  77. package/dist/storage-cleanup.js +16 -0
  78. package/dist/storage-schema.js +2 -0
  79. package/dist/storage.d.ts +5 -1
  80. package/dist/storage.js +12 -0
  81. package/dist/structured-exec-host.d.ts +4 -0
  82. package/dist/structured-exec-pump.d.ts +1 -0
  83. package/dist/structured-exec-pump.js +18 -11
  84. package/dist/structured-opencode-adapter.d.ts +2 -1
  85. package/dist/structured-opencode-adapter.js +32 -8
  86. package/dist/structured-pi-adapter.js +13 -2
  87. package/dist/structured-runner.d.ts +6 -0
  88. package/dist/structured-session-manager-replay.d.ts +2 -0
  89. package/dist/structured-session-manager-replay.js +27 -0
  90. package/dist/structured-session-manager.d.ts +32 -21
  91. package/dist/structured-session-manager.js +395 -118
  92. package/dist/structured-tmux-host.d.ts +23 -2
  93. package/dist/structured-tmux-host.js +143 -20
  94. package/dist/task-actions.js +5 -3
  95. package/dist/task-archive-policy.js +2 -1
  96. package/dist/task-comment-dispatch.d.ts +2 -1
  97. package/dist/task-comment-dispatch.js +19 -6
  98. package/dist/task-execution-queue.d.ts +2 -0
  99. package/dist/task-execution-queue.js +10 -10
  100. package/dist/task-hierarchy.js +1 -1
  101. package/dist/task-read-model.d.ts +1 -1
  102. package/dist/task-read-model.js +2 -1
  103. package/dist/task-types.d.ts +34 -3
  104. package/dist/task-types.js +21 -5
  105. package/dist/task-workflow-cli.d.ts +2 -0
  106. package/dist/task-workflow-cli.js +29 -0
  107. package/dist/task-workflow-runner.d.ts +4 -0
  108. package/dist/task-workflow-runner.js +36 -0
  109. package/dist/task-workflow.d.ts +13 -0
  110. package/dist/task-workflow.js +48 -0
  111. package/dist/terminal-daemon-server.js +12 -4
  112. package/dist/tmux-policy.d.ts +9 -0
  113. package/dist/tmux-policy.js +46 -0
  114. package/dist/tmux-terminal-host.js +5 -2
  115. package/dist/tui/commands.js +2 -1
  116. package/dist/types.d.ts +5 -0
  117. package/dist/wand-task-sync.js +5 -0
  118. package/dist/web-ui/content/ai-teams.js +9 -9
  119. package/dist/web-ui/content/module-manifest.json +1 -1
  120. package/dist/web-ui/content/modules/{52574891ef08e29b → 014fa5c6f973badf}/host.js +3 -3
  121. package/dist/web-ui/content/modules/0818b1d31a11d006/employee-profile.js +1 -0
  122. package/dist/web-ui/content/modules/{31157ceb6d23b809 → 0a58d9ec273078e4}/chunk.js +3 -3
  123. package/dist/web-ui/content/modules/{bf3ee58e0da44822 → 0a979dcd165cd0cc}/chunk.js +1 -1
  124. package/dist/web-ui/content/modules/{5f298a8f43dd0589 → 0c143928e8f96abc}/chunk.js +1 -1
  125. package/dist/web-ui/content/modules/0e9e42a319f6b8fc/workspace-window.js +1 -0
  126. package/dist/web-ui/content/modules/{b713cad51ca4c512 → 0f0d1769b732c0bd}/chunk.js +1 -1
  127. package/dist/web-ui/content/modules/{4970319a83432ec7 → 1447886e6a1e74a4}/chunk.js +1 -1
  128. package/dist/web-ui/content/modules/151c734cbca31da5/host.js +1 -0
  129. package/dist/web-ui/content/modules/{e5174201f9d918fb → 16e4ab9c5263fcfd}/chunk.js +1 -1
  130. package/dist/web-ui/content/modules/17e752fb53952753/host.js +1 -0
  131. package/dist/web-ui/content/modules/{913187c92a98a1ac → 20ed1454fc09fa6f}/chunk.js +1 -1
  132. package/dist/web-ui/content/modules/255a3baf59d2b734/home.js +6 -0
  133. package/dist/web-ui/content/modules/{2f801e53dda62770 → 296dd81886c42b45}/chunk.js +1 -1
  134. package/dist/web-ui/content/modules/{8891ca8165435ca1 → 2a01f4e525c0725e}/chunk.js +1 -1
  135. package/dist/web-ui/content/modules/2ab4409c82b44fba/markdown.js +1 -0
  136. package/dist/web-ui/content/modules/2c955eced11610c2/chunk.js +1 -0
  137. package/dist/web-ui/content/modules/2d7ee4f9e2c0aca4/panel.js +1 -0
  138. package/dist/web-ui/content/modules/{3f8830f8ad28decc → 33ae550e4a295c85}/chunk.js +1 -1
  139. package/dist/web-ui/content/modules/35f4aa4443696bd0/chunk.js +1 -0
  140. package/dist/web-ui/content/modules/388b37ffcce9d4ff/host.js +1 -0
  141. package/dist/web-ui/content/modules/39279604dcdac7c7/runtime.js +338 -0
  142. package/dist/web-ui/content/modules/{57c357d321db5df9 → 3a3cdff2a662b7d3}/chunk.js +1 -1
  143. package/dist/web-ui/content/modules/{d49f106c4d106057 → 3ab646be55514243}/chunk.js +1 -1
  144. package/dist/web-ui/content/modules/{16dab1297efd71a7 → 3ae88a5c5e1949d2}/chunk.js +1 -1
  145. package/dist/web-ui/content/modules/3e3b0e71d6ce80d8/chunk.js +1 -0
  146. package/dist/web-ui/content/modules/{ec33e17c00c1eb24 → 434aaaaf9604dc34}/chunk.js +1 -1
  147. package/dist/web-ui/content/modules/4385cd48771e1d4f/host.js +1 -0
  148. package/dist/web-ui/content/modules/{face3dbf117d63e3 → 4638129d60f549a0}/chunk.js +1 -1
  149. package/dist/web-ui/content/modules/47407209931b7011/host.js +13 -0
  150. package/dist/web-ui/content/modules/4829dd712d8b366b/chunk.js +1 -0
  151. package/dist/web-ui/content/modules/488d5b747bf374c8/chunk.js +1 -0
  152. package/dist/web-ui/content/modules/{ed7405f064944215 → 4ab085c195f7d76a}/sha256.js +1 -1
  153. package/dist/web-ui/content/modules/{f27a5d3c6c02a71c → 4c485bd44740fec7}/chunk.js +1 -1
  154. package/dist/web-ui/content/modules/{771439a41e737ee2 → 4ca0494308ab2bb3}/chunk.js +1 -1
  155. package/dist/web-ui/content/modules/4ec571218779ba3e/composer-surface-adapter.js +1 -0
  156. package/dist/web-ui/content/modules/{5933ce8965aa4004 → 5023bf0294c2e7cb}/chunk.js +1 -1
  157. package/dist/web-ui/content/modules/{e63582a3bce48f7d → 5456e3417600bf15}/chunk.js +1 -1
  158. package/dist/web-ui/content/modules/55c588b0f5b41a52/host-bridge.js +1 -0
  159. package/dist/web-ui/content/modules/{9d941d6ff39861b8 → 5c3c8237ba7d8460}/chunk.js +1 -1
  160. package/dist/web-ui/content/modules/624b858da9834757/host.js +1 -0
  161. package/dist/web-ui/content/modules/6290be936320d4b1/host.js +1 -0
  162. package/dist/web-ui/content/modules/66be408793048cc3/progress.js +1 -0
  163. package/dist/web-ui/content/modules/67a11176a563317b/chunk.js +1 -0
  164. package/dist/web-ui/content/modules/{e060508ecd50fa7e → 68ef6ec7d26d78cd}/chunk.js +1 -1
  165. package/dist/web-ui/content/modules/{41445c6589dba325 → 6ec3e41df31927d9}/host.js +2 -2
  166. package/dist/web-ui/content/modules/{97d7f267959fd385 → 6f10f369445bbcbe}/chunk.js +1 -1
  167. package/dist/web-ui/content/modules/{d47d75a78ae598e3 → 7295d9ff92d5e1e7}/chunk.js +1 -1
  168. package/dist/web-ui/content/modules/{f383d892fef73483 → 72cd613de1a5f69b}/chunk.js +1 -1
  169. package/dist/web-ui/content/modules/73aae1daafeea7e7/workspaces-panel.js +4 -0
  170. package/dist/web-ui/content/modules/{c2f09c8a472f5d83 → 73d2689f13b9a3e9}/dialog-modal.js +1 -1
  171. package/dist/web-ui/content/modules/{2f97ddba3842adaa → 75cf0c048e9b92ab}/chunk.js +1 -1
  172. package/dist/web-ui/content/modules/{4ba1e8c74bed7ef2 → 75dabda3b141296e}/chunk.js +1 -1
  173. package/dist/web-ui/content/modules/{f3cff7792ba39df8 → 76c10b58d5f92d02}/login-visual.js +1 -1
  174. package/dist/web-ui/content/modules/{b63e075f222a1266 → 783583a12f5010f8}/library-bridge.js +1 -1
  175. package/dist/web-ui/content/modules/7e13a8a4a981f2f1/group-editor.js +1 -0
  176. package/dist/web-ui/content/modules/{0d76e582a7a71b2f → 7e3034fea34c6dae}/chunk.js +1 -1
  177. package/dist/web-ui/content/modules/80bf3291f2577a68/host.js +1 -0
  178. package/dist/web-ui/content/modules/{cba6f13c0b2aec1c → 82c3e08f34a6b828}/chunk.js +1 -1
  179. package/dist/web-ui/content/modules/{dedd4a19bb70e327 → 83d1f4cf82c65f19}/page.js +3 -3
  180. package/dist/web-ui/content/modules/844f97958f25240c/shell-file-panel-drawer.js +1 -0
  181. package/dist/web-ui/content/modules/{62e0023451f428cf → 899cdc467044d418}/chunk.js +1 -1
  182. package/dist/web-ui/content/modules/{5083a5dbb5b155b7 → 8c7ca1fe72741c07}/chunk.js +1 -1
  183. package/dist/web-ui/content/modules/8dae4ca2261525e4/task-board-host.js +27 -0
  184. package/dist/web-ui/content/modules/92195c14c74929da/list-filter-panel.js +1 -0
  185. package/dist/web-ui/content/modules/{a61393bfa976d8d5 → 93793696af76bcbf}/chunk.js +1 -1
  186. package/dist/web-ui/content/modules/{db521a246e14e5e4 → 97cd6af3da5622b8}/chunk.js +4 -4
  187. package/dist/web-ui/content/modules/{8de8368570ab089d → 9d3179258249ec77}/chunk.js +1 -1
  188. package/dist/web-ui/content/modules/{579d486763d47c0d → a234c484078c0568}/chunk.js +1 -1
  189. package/dist/web-ui/content/modules/a4d215448a089a7d/host.js +1 -0
  190. package/dist/web-ui/content/modules/{c0a446100d969f3e → ae003f89901c5687}/chunk.js +1 -1
  191. package/dist/web-ui/content/modules/ae712bdd45b9917e/employee-card.js +1 -0
  192. package/dist/web-ui/content/modules/{a2caed0139df4b21 → b2a15293f7965d09}/chunk.js +1 -1
  193. package/dist/web-ui/content/modules/b4ee65e9998f0088/host.js +61 -0
  194. package/dist/web-ui/content/modules/{bd3daf04a8bf4422 → b666137d17f83be9}/chunk.js +1 -1
  195. package/dist/web-ui/content/modules/{831b6c7b43e800be → b66beb6c0185256b}/host.js +1 -1
  196. package/dist/web-ui/content/modules/{816eb9b3a000fb2c → bb03b17e877657de}/chunk.js +1 -1
  197. package/dist/web-ui/content/modules/{1eab7410d5586038 → bc343afc991db1a1}/chunk.js +1 -1
  198. package/dist/web-ui/content/modules/bca45976ff8fe2ac/host.js +69 -0
  199. package/dist/web-ui/content/modules/{d434edbb2f4672e5 → c2715dbfc9eaf510}/chunk.js +1 -1
  200. package/dist/web-ui/content/modules/{d6fffe2c2a3a738d → c30c220281f35d74}/toast-region.js +1 -1
  201. package/dist/web-ui/content/modules/c4cc46fa9b4adddc/dropdown-surface.js +1 -0
  202. package/dist/web-ui/content/modules/c6dd19ee3d3ffd59/brand-identity.js +1 -0
  203. package/dist/web-ui/content/modules/c7ea6a114e6ff8ae/host.js +1 -0
  204. package/dist/web-ui/content/modules/{a59734be368daa7e → c916d82c8acef9c8}/chunk.js +1 -1
  205. package/dist/web-ui/content/modules/{182929e188c1ab3a → cceaedda619a737a}/host.js +1 -1
  206. package/dist/web-ui/content/modules/cf67f52b076b9d2d/panel-host.js +1 -0
  207. package/dist/web-ui/content/modules/{9e163d54ff8fd1aa → d26c86558d422313}/chunk.js +1 -1
  208. package/dist/web-ui/content/modules/d2b8c6c6f3706032/queue-view-adapter.js +1 -0
  209. package/dist/web-ui/content/modules/d52ebe2ef12a9fa8/chunk.js +3 -0
  210. package/dist/web-ui/content/modules/{3fe11a75284ad8ae → d552b3bff2151eb6}/host.js +2 -2
  211. package/dist/web-ui/content/modules/d5d6b4511ac76a6e/chunk.js +3 -0
  212. package/dist/web-ui/content/modules/{bd7dc4bb46731ee7 → df42e151d625463b}/chunk.js +1 -1
  213. package/dist/web-ui/content/modules/{2a791cd2e14aa1e0 → e0a04f5ae56c68aa}/notice-view.js +1 -1
  214. package/dist/web-ui/content/modules/{438807b8bf112cd0 → e28e607c95d6f63a}/chunk.js +1 -1
  215. package/dist/web-ui/content/modules/{50da446c1377c220 → e3880df97ea62d0c}/chunk.js +1 -1
  216. package/dist/web-ui/content/modules/{87c7f50d9455d55e → e66196f1ece3e3ad}/chunk.js +1 -1
  217. package/dist/web-ui/content/modules/{9b12c2301f3a18f3 → eb76307444ac9d71}/chunk.js +1 -1
  218. package/dist/web-ui/content/modules/{c33935155289c22d → ebff7ca22cee1468}/chunk.js +1 -1
  219. package/dist/web-ui/content/modules/{4861eca9a8a87ad0 → ed1c712c569960d6}/chunk.js +1 -1
  220. package/dist/web-ui/content/modules/ee8b9c2d4bec63d4/chunk.js +1 -0
  221. package/dist/web-ui/content/modules/f458a194ce4208e2/chunk.js +1 -0
  222. package/dist/web-ui/content/modules/{11af5d14b30e57b6 → f938e826420cfe1b}/chunk.js +1 -1
  223. package/dist/web-ui/content/modules/fa7b9bf6cc84bfc1/chat-render.js +81 -0
  224. package/dist/web-ui/content/modules/{ea8fe4b9999fd2aa → fd1fb1834f5f06df}/chunk.js +1 -1
  225. package/dist/web-ui/content/modules/fd3084e562a3dbd6/host.js +1 -0
  226. package/dist/web-ui/content/styles.css +1 -1
  227. package/dist/web-ui/embedded-assets.d.ts +416 -413
  228. package/dist/web-ui/embedded-assets.js +5 -5
  229. package/dist/web-ui/page-route.js +2 -2
  230. package/dist/web-ui/react/workspaces/types.d.ts +3 -0
  231. package/dist/web-ui/thinking-efforts.d.ts +2 -0
  232. package/dist/web-ui/thinking-efforts.js +8 -0
  233. package/package.json +1 -2
  234. package/scripts/stage-render-binaries.js +0 -1
  235. package/skills/wand-knowledge/SKILL.md +5 -3
  236. package/dist/structured-bounded-keys.d.ts +0 -19
  237. package/dist/structured-bounded-keys.js +0 -47
  238. package/dist/web-ui/content/modules/00b168a19664439e/dropdown-surface.js +0 -1
  239. package/dist/web-ui/content/modules/059c5fb7f128d340/task-board-host.js +0 -27
  240. package/dist/web-ui/content/modules/07494f5a623e4471/chat-render.js +0 -76
  241. package/dist/web-ui/content/modules/0a743ad3b4db6874/chunk.js +0 -3
  242. package/dist/web-ui/content/modules/1858670c0f84fec5/employee-profile.js +0 -1
  243. package/dist/web-ui/content/modules/20c47dc0442f5a04/host.js +0 -1
  244. package/dist/web-ui/content/modules/226241b999d12008/host.js +0 -1
  245. package/dist/web-ui/content/modules/23c4b8d223ec64c5/host.js +0 -61
  246. package/dist/web-ui/content/modules/2882c97388ff4f7b/employee-card.js +0 -1
  247. package/dist/web-ui/content/modules/2c90c3be260e6cce/progress.js +0 -1
  248. package/dist/web-ui/content/modules/2d7be9935394f397/workspace-window.js +0 -1
  249. package/dist/web-ui/content/modules/389a5112332ec23a/host.js +0 -1
  250. package/dist/web-ui/content/modules/3ea13e8b3c2deaa4/panel-host.js +0 -1
  251. package/dist/web-ui/content/modules/42e5c34002fcffb7/chunk.js +0 -1
  252. package/dist/web-ui/content/modules/4a5d669c90f545cb/host.js +0 -1
  253. package/dist/web-ui/content/modules/5705d25468274534/host.js +0 -1
  254. package/dist/web-ui/content/modules/5961ab2947c86ca1/shell-file-panel-drawer.js +0 -1
  255. package/dist/web-ui/content/modules/5b6e1369df7dea69/chunk.js +0 -1
  256. package/dist/web-ui/content/modules/6787bdcf56f743a5/host.js +0 -1
  257. package/dist/web-ui/content/modules/67c39b0398186a9f/host.js +0 -1
  258. package/dist/web-ui/content/modules/6b16f35c0627bafb/chunk.js +0 -1
  259. package/dist/web-ui/content/modules/6dd4f3e2f619f7a0/chunk.js +0 -1
  260. package/dist/web-ui/content/modules/70a2e13e0ccc8549/panel.js +0 -1
  261. package/dist/web-ui/content/modules/710db590e9707f73/runtime.js +0 -338
  262. package/dist/web-ui/content/modules/7249950f8771dbeb/workspaces-panel.js +0 -4
  263. package/dist/web-ui/content/modules/87dcd516ffd9f688/composer-surface-adapter.js +0 -1
  264. package/dist/web-ui/content/modules/8b7cd7bffdfd69e7/brand-identity.js +0 -1
  265. package/dist/web-ui/content/modules/8bf9917f6c3429f6/host.js +0 -1
  266. package/dist/web-ui/content/modules/8e612d4881fa4118/home.js +0 -6
  267. package/dist/web-ui/content/modules/8e6178262ea63dec/markdown.js +0 -1
  268. package/dist/web-ui/content/modules/96f2ae970f8c576b/host.js +0 -1
  269. package/dist/web-ui/content/modules/ba89cd97627e6682/queue-view-adapter.js +0 -1
  270. package/dist/web-ui/content/modules/c351eb11e0bdec2c/sidebar-peek.js +0 -1
  271. package/dist/web-ui/content/modules/ca7ca5902a35d26e/chunk.js +0 -1
  272. package/dist/web-ui/content/modules/d5d28ec375826ef2/chunk.js +0 -3
  273. package/dist/web-ui/content/modules/d6b16dedb9011c33/chunk.js +0 -1
  274. package/dist/web-ui/content/modules/d8a7011760f61719/group-editor.js +0 -1
  275. package/dist/web-ui/content/modules/d9e2b65e9679ad39/host.js +0 -75
  276. package/dist/web-ui/content/modules/eac72f69716dba05/chunk.js +0 -1
  277. package/dist/web-ui/content/modules/ebc53dedc3945514/host.js +0 -1
  278. package/dist/web-ui/content/modules/f43da7050badf54c/host.js +0 -1
  279. package/dist/web-ui/content/modules/fb2356eced24c4cc/host-bridge.js +0 -1
@@ -97,6 +97,13 @@ function sortTaskDirectoryGroups(groups, order) {
97
97
  const global = Number(Boolean(right.global)) - Number(Boolean(left.global));
98
98
  if (global)
99
99
  return global;
100
+ const leftHasContent = (left.tasks?.length ?? 0) > 0 || (left.standaloneSessions?.length ?? 0) > 0;
101
+ const rightHasContent = (right.tasks?.length ?? 0) > 0 || (right.standaloneSessions?.length ?? 0) > 0;
102
+ if (leftHasContent !== rightHasContent)
103
+ return rightHasContent ? 1 : -1;
104
+ const activity = compareCreatedDesc(left.latestActivityAt ?? left.createdAt, right.latestActivityAt ?? right.createdAt);
105
+ if (activity)
106
+ return activity;
100
107
  const created = compareCreatedDesc(left.createdAt, right.createdAt);
101
108
  if (created)
102
109
  return created;
@@ -133,6 +140,8 @@ function rememberCreatedAt(target, iso) {
133
140
  return;
134
141
  if (!target.createdAt || value < target.createdAt)
135
142
  target.createdAt = value;
143
+ if (!target.latestActivityAt || value > target.latestActivityAt)
144
+ target.latestActivityAt = value;
136
145
  }
137
146
  function parseDefaultProvider(value) {
138
147
  return isSessionProvider(value) ? value : undefined;
@@ -466,7 +475,7 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
466
475
  cleanupWorktreeSync(task.worktree);
467
476
  const card = storage.getWandTaskByWorkspaceTaskId(task.id);
468
477
  if (card)
469
- storage.updateWandTask(card.id, { status: "archived", workspaceTaskId: null });
478
+ storage.archiveTaskForWorkspaceRemoval(card.id);
470
479
  }
471
480
  storage.deleteWorkspace(existing.id, { cascade });
472
481
  res.json({ ok: true });
@@ -558,6 +567,47 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
558
567
  }
559
568
  res.json(result);
560
569
  }));
570
+ // Sections are layout containers, independent of task-board metadata.
571
+ app.post("/api/workspace-sections", (req, res) => {
572
+ try {
573
+ const name = typeof req.body?.name === "string" ? req.body.name.trim() : "";
574
+ if (!name)
575
+ throw badRequest("请输入分区名称。");
576
+ const cwd = resolveWorkspaceCwd(req.body?.cwd);
577
+ const workspaceId = typeof req.body?.workspaceId === "string" ? req.body.workspaceId : "";
578
+ const workspace = workspaceId ? storage.getWorkspace(workspaceId)
579
+ : storage.listWorkspaces().find(candidate => !isGlobalWorkspace(candidate)
580
+ && normalizeProjectCwd(candidate.cwd) === normalizeProjectCwd(cwd)) ?? storage.ensureGlobalWorkspace();
581
+ if (!workspace)
582
+ throw badRequest("未找到工作区。");
583
+ if (!isGlobalWorkspace(workspace) && normalizeProjectCwd(workspace.cwd) !== normalizeProjectCwd(cwd)) {
584
+ throw badRequest("分区目录与工作区不一致。");
585
+ }
586
+ const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId.trim() : undefined;
587
+ const section = storage.createWorkspaceSection({ workspaceId: workspace.id, name, cwd, sessionId });
588
+ if (sessionId)
589
+ sessions?.refreshSessionWorkspace(sessionId);
590
+ res.status(201).json({ ...section, sessions: [], isolated: false });
591
+ }
592
+ catch (error) {
593
+ sendRouteError(res, error, "无法创建分区。");
594
+ }
595
+ });
596
+ app.post("/api/workspace-sections/unassign", (req, res) => {
597
+ try {
598
+ const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId.trim() : "";
599
+ if (!storage.getSessionSlim(sessionId)) {
600
+ res.status(404).json({ error: "未找到会话。" });
601
+ return;
602
+ }
603
+ storage.moveSessionToWorkspaceTask(sessionId, null);
604
+ sessions?.refreshSessionWorkspace(sessionId);
605
+ res.json({ ok: true });
606
+ }
607
+ catch (error) {
608
+ sendRouteError(res, error, "无法移回工作区。");
609
+ }
610
+ });
561
611
  // 两个创建入口只在项目来源上不同;独立任务挂到隐藏全局空间。
562
612
  // 默认只创建逻辑分组,仅显式 worktree:true 才创建隔离目录。
563
613
  app.post(["/api/tasks", "/api/workspaces/:id/tasks"], asyncRoute(async (req, res) => {
@@ -601,7 +651,7 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
601
651
  const snapshots = sessions?.listSlim();
602
652
  const revision = crypto.createHash("sha256").update(JSON.stringify({
603
653
  storage: storage.projectionRevision(),
604
- projection: "workspace-work-sessions-v2-queued",
654
+ projection: "workspace-work-sessions-v3-task-status",
605
655
  query: { includeArchived, workspaceFilter, taskLimit, sessionLimit },
606
656
  runtime: snapshots,
607
657
  })).digest("base64url");
@@ -682,17 +732,19 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
682
732
  target.tasks.push({
683
733
  ...task,
684
734
  boardTaskId: board?.id ?? null,
735
+ boardTaskStatus: board?.status ?? null,
685
736
  ...(board ? model.execution(board) : taskExecutionProjection(visibleSessions)),
686
737
  parentWorkspaceTaskId: parent?.workspaceTaskId ?? null,
687
- canArchive: (() => { const card = model.cardsByContainer.get(task.id); return card ? model.canArchive(card) : true; })(),
738
+ canArchive: task.kind !== "section" && (() => { const card = model.cardsByContainer.get(task.id); return card ? model.canArchive(card) : true; })(),
688
739
  ...model.milestone(task.milestoneId),
689
740
  cwd: taskRuntimeCwd(task, workspace),
690
741
  isolated: task.worktree !== null,
691
742
  sessions,
692
743
  totalSessions: visibleSessions.length,
693
744
  });
694
- if (target.synthetic)
695
- rememberCreatedAt(target, task.createdAt);
745
+ rememberCreatedAt(target, task.createdAt);
746
+ for (const session of visibleSessions)
747
+ rememberCreatedAt(target, session.startedAt);
696
748
  };
697
749
  for (const workspace of visibleWorkspaces) {
698
750
  const base = groups.get(workspace.id);
@@ -788,8 +840,17 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
788
840
  group.standaloneSessions.push(summarize(session, {
789
841
  workspaceName: group.workspaceName,
790
842
  }));
791
- if (group.synthetic)
792
- rememberCreatedAt(group, session.startedAt);
843
+ rememberCreatedAt(group, session.startedAt);
844
+ }
845
+ // 目录内的任务与会话排序:新创建的必须在上面(严格按创建/启动时间倒序)。
846
+ for (const group of groups.values()) {
847
+ group.tasks.sort((left, right) => compareCreatedDesc(left.createdAt, right.createdAt));
848
+ for (const task of group.tasks) {
849
+ if (Array.isArray(task.sessions)) {
850
+ task.sessions.sort((left, right) => compareCreatedDesc(left.startedAt, right.startedAt));
851
+ }
852
+ }
853
+ group.standaloneSessions.sort((left, right) => compareCreatedDesc(left.startedAt, right.startedAt));
793
854
  }
794
855
  const orderedGroups = [...groups.values()]
795
856
  .filter((group) => !group.global || group.tasks.length > 0 || group.standaloneSessions.length > 0);
@@ -993,6 +1054,10 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
993
1054
  res.status(404).json({ error: "未找到该任务。" });
994
1055
  return;
995
1056
  }
1057
+ if (existing.kind === "section") {
1058
+ res.status(409).json({ error: "分区不需要归档,可删除分区并保留会话。" });
1059
+ return;
1060
+ }
996
1061
  const card = storage.getWandTaskByWorkspaceTaskId(existing.id);
997
1062
  if (card && !canArchiveTask(storage, sessions, card.id)) {
998
1063
  res.status(409).json({ error: "任务尚未完成或仍有执行,完成后才能归档。" });
@@ -1018,16 +1083,19 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
1018
1083
  }
1019
1084
  // 删除隔离任务必然会移除其 cwd,因此即使调用方没有显式传 cascade,
1020
1085
  // 也必须同步删除会话;非隔离任务仍保留旧的“默认解绑”API 语义。
1021
- const cascade = req.query.cascade === "1"
1086
+ const cascade = existing.kind !== "section" && (req.query.cascade === "1"
1022
1087
  || req.query.cascade === "true"
1023
- || existing.worktree !== null;
1088
+ || existing.worktree !== null);
1024
1089
  if (cascade) {
1025
1090
  deleteSessions(storage, sessions, storage.listSessionsByWorkspaceTaskSlim(existing.id).map((session) => session.id));
1026
1091
  }
1027
1092
  // 尽力清理 worktree 与分支;失败不阻塞删除任务行。
1028
1093
  if (cascade)
1029
1094
  cleanupWorktreeSync(existing.worktree);
1095
+ const retained = !cascade ? storage.listSessionsByWorkspaceTaskSlim(existing.id).map(session => session.id) : [];
1030
1096
  storage.deleteWorkspaceTask(existing.id, { cascade });
1097
+ for (const id of retained)
1098
+ sessions?.refreshSessionWorkspace(id);
1031
1099
  res.json({ ok: true });
1032
1100
  });
1033
1101
  // 保存任务的标签 / 分屏布局
package/dist/server.js CHANGED
@@ -16,6 +16,7 @@ import { AuthService, principalHasScope, } from "./auth.js";
16
16
  import { authenticateBearerAppToken, createRequireAuth, getEffectivePassword, requireAdmin as requireAdminScope, requireAdminOrSessionPreferences, requireScope, } from "./middleware/auth.js";
17
17
  import { createApiScopeMiddleware } from "./middleware/api-scope.js";
18
18
  import { createExtensionCors } from "./middleware/cors.js";
19
+ import { RUNNING_BUILD_INFO as BUILD_INFO } from "./build-info.js";
19
20
  import { ensureCertificates } from "./cert.js";
20
21
  import { getDefaultModelForProvider, getProviderDefaultModels, resolveConfigDir, writePreferenceToStorage, } from "./config.js";
21
22
  import { ModelCatalogService } from "./models.js";
@@ -83,6 +84,7 @@ import { registerClaudeHistoryRoutes, registerSessionRoutes } from "./server-ses
83
84
  import { registerWorkspaceRoutes } from "./server-workspace-routes.js";
84
85
  import { backfillSessionWorkspaces } from "./workspace-binding.js";
85
86
  import { getErrorMessage } from "./error-utils.js";
87
+ import { stopIterationPrompts, whenIterationPromptsSettled } from "./iteration-log.js";
86
88
  import { asyncRoute, jsonErrorHandler } from "./express-async.js";
87
89
  import { FeedbackReceiver, FeedbackReporter } from "./feedback.js";
88
90
  import { feedbackFeatureForApiPath } from "./feedback-feature-map.js";
@@ -111,8 +113,10 @@ import { registerDaemonMaintenanceRoutes } from "./server-daemon-maintenance-rou
111
113
  import { terminalDaemonBuildIsCurrent } from "./terminal-daemon-build.js";
112
114
  import { createUpgradeAwareStructuredHost } from "./render-structured-host.js";
113
115
  import { PiTmuxHost } from "./pi-tmux-host.js";
116
+ import { PtyTmuxBackend } from "./pty-tmux-backend.js";
114
117
  import { StructuredTmuxHost } from "./structured-tmux-host.js";
115
- import { tmuxEnabled } from "./tmux-policy.js";
118
+ import { logInfo } from "./runtime-log.js";
119
+ import { probeTmuxSupport, tmuxEnabled } from "./tmux-policy.js";
116
120
  import { structuredHostMaintenance } from "./structured-host-maintenance.js";
117
121
  import { createTmuxTerminalHost } from "./tmux-terminal-host.js";
118
122
  import { updateProviderClis, verifyProviderCliUpdateResults, } from "./provider-cli-updater.js";
@@ -140,31 +144,13 @@ async function checkLatestPackageVersion(channel, forceRefresh = false) {
140
144
  }
141
145
  // The immutable running build is also what the UI reports. Shared package metadata may
142
146
  // belong to a different installer; it must not make a local Beta look like an older stable.
143
- const info = await checkPackageUpdateAsync(DISPLAY_VERSION, channel);
147
+ const info = await checkPackageUpdateAsync(DISPLAY_VERSION, channel, undefined, BUILD_INFO);
144
148
  if (info.latest) {
145
149
  packageUpdateCache.set(channel, { info, timestamp: now });
146
150
  }
147
151
  return info;
148
152
  }
149
153
  // ── Build info (构建时打入的 commit SHA) + Beta 通道 ──
150
- /** 读取 dist/build-info.json(由 scripts/stamp-build-info.js 在 build 时生成)。 */
151
- function readBuildInfo() {
152
- try {
153
- const raw = readFileSync(path.join(SERVER_MODULE_DIR, "build-info.json"), "utf8");
154
- const j = JSON.parse(raw);
155
- return {
156
- commit: typeof j.commit === "string" && j.commit ? j.commit : null,
157
- builtAt: typeof j.builtAt === "string" && j.builtAt ? j.builtAt : null,
158
- version: typeof j.version === "string" && j.version ? j.version : null,
159
- channel: typeof j.channel === "string" && j.channel ? j.channel : null,
160
- };
161
- }
162
- catch {
163
- // dev(tsx 跑 src/)或老版本(无此文件)时降级为全 null。
164
- return { commit: null, builtAt: null, version: null, channel: null };
165
- }
166
- }
167
- const BUILD_INFO = readBuildInfo();
168
154
  const DISPLAY_VERSION = BUILD_INFO.version || PKG_VERSION;
169
155
  const SERVER_INSTANCE_ID = crypto.randomUUID();
170
156
  // ── Startup error handling ──
@@ -308,8 +294,11 @@ export async function startServer(config, configPath, options = {}) {
308
294
  const ensured = formatManagedSkillsEnsureLog(await ensureManagedSkills(configPath));
309
295
  if (ensured)
310
296
  process.stdout.write(ensured);
297
+ storage.syncGlobalKnowledge();
311
298
  }
312
299
  let notifyFreeDefaultChanged = (_employeeId) => { };
300
+ let collectNativeRunArtifacts = null;
301
+ let collectPtyTmuxArtifacts = null;
313
302
  const systemFallback = testMode || process.env.WAND_DISABLE_OPENCODE_FALLBACK === "1" ? undefined : new OpenCodeFreeModelsService({ configDir,
314
303
  storage, inheritEnv: config.inheritEnv, onLog: line => process.stdout.write(`[wand] ${line}\n`),
315
304
  onModelsChanged(models) {
@@ -352,11 +341,11 @@ export async function startServer(config, configPath, options = {}) {
352
341
  freedBytes: results.reduce((total, result) => total + result.freedBytes, 0),
353
342
  };
354
343
  },
355
- // Pi 原生回合目录(pi-native/)按回合数增长,同样交给同一轮定时回收。
356
- collectNativeArtifacts: () => structuredSessions.collectNativeGarbage({
357
- liveSessionIds: new Set(sessionRegistry.listSlim().map((session) => session.id)),
358
- retentionMs: STORAGE_CLEANUP_RETENTION_DAYS * 24 * 60 * 60 * 1000,
359
- }),
344
+ // Pi 原生回合目录(pi-native/)和 structured-tmux 按回合数增长,交给同一轮定时回收。
345
+ // 只保护未归档会话。已删除或已归档的 slot 在 ack 完成、进程退出后整槽删除。
346
+ // 宿主在下面才包好,这里先留空,避免清理闭包引用尚未赋值的变量。
347
+ collectNativeArtifacts: () => collectNativeRunArtifacts?.() ?? null,
348
+ collectPtyTmuxArtifacts: () => collectPtyTmuxArtifacts?.() ?? null,
360
349
  // 旧版本遗留的迁移前快照:无上限堆积。迁移成功路径已在 storage-schema 修剪。
361
350
  pruneMigrationSnapshots: () => pruneMigrationBackups(databasePath, { dryRun: false }) });
362
351
  const localModels = new LocalModelSetupService({ configDir, decisions,
@@ -391,6 +380,15 @@ export async function startServer(config, configPath, options = {}) {
391
380
  const tmuxTerminals = options.terminalHost
392
381
  ? { host: ptyHosts.host, client: null, configPath, maintenanceTarget: undefined }
393
382
  : await createTmuxTerminalHost(configPath, ptyHosts.host);
383
+ if (tmuxEnabled()) {
384
+ const probe = probeTmuxSupport();
385
+ if (probe.ok) {
386
+ logInfo(`tmux runtime active (${probe.version}, ${probe.path}); terminal and structured CLI runs hosted.`);
387
+ }
388
+ else {
389
+ wandWarn(`tmux 执行底座检查未通过: ${probe.error}`);
390
+ }
391
+ }
394
392
  const daemonAdmission = new DaemonAdmission();
395
393
  const terminalHost = daemonAdmission.terminal(tmuxTerminals.host);
396
394
  let decisionRuntime = null;
@@ -404,17 +402,43 @@ export async function startServer(config, configPath, options = {}) {
404
402
  ?? (ptyHosts.host instanceof TerminalDaemonClient ? ptyHosts.host : null);
405
403
  const structuredHosts = await createUpgradeAwareStructuredHost(configPath, legacyStructuredHost, config.structured?.processHost);
406
404
  let structuredHost = structuredHosts.host;
405
+ let structuredTmuxHost = null;
407
406
  if (structuredHost?.persistent) {
408
407
  const enabled = tmuxEnabled();
409
- const tmuxHost = new StructuredTmuxHost(configDir, structuredHost, enabled);
408
+ structuredTmuxHost = new StructuredTmuxHost(configDir, structuredHost, enabled);
410
409
  // Keep both routes available after disabling new tmux launches: existing
411
410
  // native and generic runs still belong to their original persistent owner.
412
- structuredHost = new PiTmuxHost(configDir, tmuxHost, enabled && process.env.WAND_PI_NATIVE_TMUX !== "0");
411
+ structuredHost = new PiTmuxHost(configDir, structuredTmuxHost, enabled && process.env.WAND_PI_NATIVE_TMUX !== "0");
413
412
  }
414
413
  const structuredSessions = new StructuredSessionManager(storage, config, structuredLogger, {}, structuredHost ? daemonAdmission.structured(structuredHost) : undefined, () => decisionRuntime, openRouter, () => autoAssignEvaluate, webLoginTickets);
415
414
  // No SDK prewarm: Pi defaults to CLI. Only an explicit Wand Agent selection
416
415
  // probes the in-process harness; CLI startup/settings must not depend on it.
417
416
  const sessionRegistry = new SessionRegistry(processes, structuredSessions, storage);
417
+ collectNativeRunArtifacts = () => {
418
+ const activeSessionIds = new Set(sessionRegistry.listSlim().filter((session) => !session.archived).map((session) => session.id));
419
+ const retentionMs = STORAGE_CLEANUP_RETENTION_DAYS * 24 * 60 * 60 * 1000;
420
+ const hosts = [structuredHost, structuredTmuxHost].filter((host) => Boolean(host?.collectGarbage));
421
+ let removedSlots = 0;
422
+ let removedTurns = 0;
423
+ let freedBytes = 0;
424
+ let ran = false;
425
+ for (const host of hosts) {
426
+ const part = host.collectGarbage?.({ liveSessionIds: activeSessionIds, activeSessionIds, retentionMs });
427
+ if (!part)
428
+ continue;
429
+ ran = true;
430
+ removedSlots += part.removedSlots;
431
+ removedTurns += part.removedTurns;
432
+ freedBytes += part.freedBytes;
433
+ }
434
+ const report = ran ? { removedSlots, removedTurns, freedBytes } : null;
435
+ return report;
436
+ };
437
+ collectPtyTmuxArtifacts = () => {
438
+ const activeSessionIds = new Set(sessionRegistry.listSlim().filter((session) => !session.archived).map((session) => session.id));
439
+ const retentionMs = STORAGE_CLEANUP_RETENTION_DAYS * 24 * 60 * 60 * 1000;
440
+ return PtyTmuxBackend.collectGarbage(configDir, { activeSessionIds, retentionMs });
441
+ };
418
442
  // 员工任务并发闸门:所有员工派发共用同一实例,排队状态随存储持久化。
419
443
  const taskExecutionQueue = new TaskExecutionQueue({
420
444
  storage, config, structured: structuredSessions, processes, sessions: sessionRegistry,
@@ -912,14 +936,14 @@ export async function startServer(config, configPath, options = {}) {
912
936
  while (structuredSessions.getCoreTurnStatus().hasActiveTurns) {
913
937
  await new Promise((resolve) => setTimeout(resolve, 1000));
914
938
  }
915
- // Do not dispose tools/services or arm the force-exit timer while a native
916
- // turn is still running. The same barrier covers manual and auto restart.
939
+ // Do not dispose tools/services while a native turn is still running.
940
+ // The same barrier covers manual and auto restart.
917
941
  const plan = computeRelaunch({
918
942
  serviceInstalled: safeServiceInstalled(),
919
943
  globalCli: resolveGlobalWandCli(),
920
944
  });
921
- const forceExitTimer = setTimeout(() => process.exit(0), 5000);
922
- forceExitTimer.unref?.();
945
+ // Accepted iteration records can need the bounded Git compatibility retry;
946
+ // a force-exit timer here would discard them before close() drains the queue.
923
947
  await close();
924
948
  if (plan.mode === "spawn") {
925
949
  spawn(process.execPath, [plan.bin ?? "", ...(plan.args ?? [])], {
@@ -964,6 +988,7 @@ export async function startServer(config, configPath, options = {}) {
964
988
  await new Promise((resolve, reject) => {
965
989
  const cleanupFailedListen = async () => {
966
990
  shuttingDown = true;
991
+ stopIterationPrompts(storage);
967
992
  dingtalk.dispose();
968
993
  dingtalkDws.dispose();
969
994
  employeeImLifecycle.dispose();
@@ -1008,17 +1033,20 @@ export async function startServer(config, configPath, options = {}) {
1008
1033
  authService.dispose();
1009
1034
  taskExecutionQueue.dispose();
1010
1035
  try {
1036
+ await whenIterationPromptsSettled(storage);
1037
+ }
1038
+ finally {
1011
1039
  storage.close();
1012
1040
  }
1013
- catch { /* noop */ }
1014
1041
  };
1015
1042
  const onListenError = (err) => {
1016
1043
  feedback.capture("startup_failed", err);
1017
1044
  feedback.stop();
1018
1045
  server.off("error", onListenError);
1046
+ const startupError = err.code === "EADDRINUSE" ? new PortInUseError(config.port, config.host) : err;
1019
1047
  void cleanupFailedListen().then(() => {
1020
- reject(err.code === "EADDRINUSE" ? new PortInUseError(config.port, config.host) : err);
1021
- }, reject);
1048
+ reject(startupError);
1049
+ }, cleanupError => reject(new AggregateError([startupError, cleanupError], "Server startup rollback failed")));
1022
1050
  };
1023
1051
  server.once("error", onListenError);
1024
1052
  server.listen(config.port, config.host, () => {
@@ -1082,6 +1110,7 @@ export async function startServer(config, configPath, options = {}) {
1082
1110
  storage,
1083
1111
  sessions: sessionRegistry,
1084
1112
  taskRetention: () => config.taskRetention,
1113
+ releaseArchivedProviderHistory: (sessionIds) => sessionRegistry.releaseArchivedProviderHistory(sessionIds),
1085
1114
  };
1086
1115
  const sweepRetention = createRetentionSweep(retentionDeps);
1087
1116
  runTaskRetentionSweep = () => {
@@ -1244,6 +1273,7 @@ export async function startServer(config, configPath, options = {}) {
1244
1273
  return closePromise;
1245
1274
  closePromise = (async () => {
1246
1275
  shuttingDown = true;
1276
+ stopIterationPrompts(storage);
1247
1277
  feedback.stop();
1248
1278
  if (activeFeedback === feedback)
1249
1279
  activeFeedback = null;
@@ -1343,9 +1373,11 @@ export async function startServer(config, configPath, options = {}) {
1343
1373
  // can never retain or call a closed storage instance.
1344
1374
  authService.dispose();
1345
1375
  try {
1376
+ await whenIterationPromptsSettled(storage);
1377
+ }
1378
+ finally {
1346
1379
  storage.close();
1347
1380
  }
1348
- catch { /* ignore */ }
1349
1381
  }
1350
1382
  })();
1351
1383
  return closePromise;
@@ -53,13 +53,27 @@ export declare class SessionRegistry {
53
53
  setSessionThinkingEffort(id: string, effort: SessionSnapshot["thinkingEffort"]): SessionSnapshot | null;
54
54
  setSessionMode(id: string, mode: ExecutionMode): SessionSnapshot | null;
55
55
  setSessionTopic(id: string, title: string, description: string): SessionSnapshot | null;
56
+ /** 停掉空闲 PTY 壳。保留期只对「进程还在、但没有正在处理的回合」调用。 */
57
+ stop(id: string): SessionSnapshot | null;
56
58
  /**
57
- * 归档 / 取消归档一个会话:只写标记,不杀进程、不删历史;真正的清理留给保留期扫描。
59
+ * 归档 / 取消归档一个会话:不杀进程。归档后这次对话不再续接,
60
+ * 立刻删掉 CLI 原生历史,避免 ~/.pi、~/.codex 这类目录继续堆积;
61
+ * 取消归档时把原生 id 移出隐藏名单,已有的 CLI 历史可以继续恢复。
58
62
  * 会话可能只存在于 storage(重启后未加载),因此 storage-only 也要能改。
59
63
  */
60
- /** 停掉空闲 PTY 壳。保留期只对「进程还在、但没有正在处理的回合」调用。 */
61
- stop(id: string): SessionSnapshot | null;
62
64
  setArchived(id: string, archived: boolean): SessionSnapshot | null;
65
+ /**
66
+ * 归档与 CLI 原生历史的对应关系。别的未归档会话还在用同一个原生 id 时不动。
67
+ * 删文件失败只记日志:归档标记已经写上,不能因为清理失败把请求打回去。
68
+ */
69
+ private syncArchivedProviderHistory;
70
+ /**
71
+ * 清掉这些已归档会话独占的 CLI 原生历史,并记入隐藏名单。
72
+ * 同一个原生 id 仍被未归档会话使用时跳过。重复调用只是再删一次已不存在的文件。
73
+ */
74
+ releaseArchivedProviderHistory(sessionIds: readonly string[]): void;
75
+ /** 另一个未归档会话是否还拿着同一个 CLI 原生 id。 */
76
+ private activeNativeOwner;
63
77
  updateWorktreeState(id: string, status: SessionSnapshot["worktreeMergeStatus"], info: SessionSnapshot["worktreeMergeInfo"]): SessionSnapshot | null;
64
78
  private deleteManagedSession;
65
79
  /** All destructive paths, including retention, remove native session artifacts. */
@@ -259,16 +259,18 @@ export class SessionRegistry {
259
259
  return this.processes.setSessionTopic(id, title, description);
260
260
  return this.updateStored(id, (snapshot) => ({ ...snapshot, title, description, summary: description }));
261
261
  }
262
- /**
263
- * 归档 / 取消归档一个会话:只写标记,不杀进程、不删历史;真正的清理留给保留期扫描。
264
- * 会话可能只存在于 storage(重启后未加载),因此 storage-only 也要能改。
265
- */
266
262
  /** 停掉空闲 PTY 壳。保留期只对「进程还在、但没有正在处理的回合」调用。 */
267
263
  stop(id) {
268
264
  if (this.ownerOf(id) !== "pty")
269
265
  return this.get(id);
270
266
  return this.processes.stop(id);
271
267
  }
268
+ /**
269
+ * 归档 / 取消归档一个会话:不杀进程。归档后这次对话不再续接,
270
+ * 立刻删掉 CLI 原生历史,避免 ~/.pi、~/.codex 这类目录继续堆积;
271
+ * 取消归档时把原生 id 移出隐藏名单,已有的 CLI 历史可以继续恢复。
272
+ * 会话可能只存在于 storage(重启后未加载),因此 storage-only 也要能改。
273
+ */
272
274
  setArchived(id, archived) {
273
275
  const current = this.getLatest(id);
274
276
  if (current && !!current.archived === archived && (!archived || current.archivedAt))
@@ -277,15 +279,66 @@ export class SessionRegistry {
277
279
  throw Object.assign(new Error("会话仍在执行或等待处理,完成后才能归档。"), { statusCode: 409 });
278
280
  }
279
281
  const owner = this.ownerOf(id);
280
- if (owner === "structured")
281
- return this.structured.setSessionArchived(id, archived);
282
- if (owner === "pty")
283
- return this.processes.setSessionArchived(id, archived);
284
- return this.updateStored(id, (snapshot) => ({
285
- ...snapshot,
286
- archived,
287
- archivedAt: archived ? new Date().toISOString() : null,
288
- }));
282
+ const updated = owner === "structured"
283
+ ? this.structured.setSessionArchived(id, archived)
284
+ : owner === "pty"
285
+ ? this.processes.setSessionArchived(id, archived)
286
+ : this.updateStored(id, (snapshot) => ({
287
+ ...snapshot,
288
+ archived,
289
+ archivedAt: archived ? new Date().toISOString() : null,
290
+ }));
291
+ if (updated)
292
+ this.syncArchivedProviderHistory(updated);
293
+ return updated;
294
+ }
295
+ /**
296
+ * 归档与 CLI 原生历史的对应关系。别的未归档会话还在用同一个原生 id 时不动。
297
+ * 删文件失败只记日志:归档标记已经写上,不能因为清理失败把请求打回去。
298
+ */
299
+ syncArchivedProviderHistory(snapshot) {
300
+ if (!snapshot.archived) {
301
+ const providerSessionId = snapshot.claudeSessionId?.trim();
302
+ if (!providerSessionId)
303
+ return;
304
+ const provider = sessionProvider(snapshot);
305
+ const shared = this.activeNativeOwner(snapshot.id, provider, providerSessionId);
306
+ if (!shared)
307
+ removeHiddenSessionIds(this.storage, [providerSessionId]);
308
+ return;
309
+ }
310
+ this.releaseArchivedProviderHistory([snapshot.id]);
311
+ }
312
+ /**
313
+ * 清掉这些已归档会话独占的 CLI 原生历史,并记入隐藏名单。
314
+ * 同一个原生 id 仍被未归档会话使用时跳过。重复调用只是再删一次已不存在的文件。
315
+ */
316
+ releaseArchivedProviderHistory(sessionIds) {
317
+ const wanted = new Set(sessionIds);
318
+ for (const snapshot of this.listSlim()) {
319
+ if (!wanted.has(snapshot.id) || !snapshot.archived)
320
+ continue;
321
+ const providerSessionId = snapshot.claudeSessionId?.trim();
322
+ if (!providerSessionId)
323
+ continue;
324
+ const provider = sessionProvider(snapshot);
325
+ if (this.activeNativeOwner(snapshot.id, provider, providerSessionId))
326
+ continue;
327
+ addHiddenSessionIds(this.storage, [providerSessionId]);
328
+ try {
329
+ deleteProviderNativeHistory(this.processes, provider, providerSessionId, snapshot.cwd);
330
+ }
331
+ catch {
332
+ logInfo("[SessionCleanup] Archived CLI session cleanup could not complete; artifacts were preserved.");
333
+ }
334
+ }
335
+ }
336
+ /** 另一个未归档会话是否还拿着同一个 CLI 原生 id。 */
337
+ activeNativeOwner(sessionId, provider, providerSessionId) {
338
+ return this.listSlim().some((other) => other.id !== sessionId
339
+ && !other.archived
340
+ && other.claudeSessionId?.trim() === providerSessionId
341
+ && sessionProvider(other) === provider);
289
342
  }
290
343
  updateWorktreeState(id, status, info) {
291
344
  const owner = this.ownerOf(id);
@@ -55,7 +55,7 @@ export function getInputErrorResponse(error, sessionId) {
55
55
  };
56
56
  }
57
57
  const errorCode = error?.code;
58
- if (errorCode === "duplicate_queued_message" || errorCode === "duplicate_idempotency_key") {
58
+ if (errorCode === "duplicate_queued_message" || errorCode === "duplicate_idempotency_key" || errorCode === "idempotency_key_conflict") {
59
59
  return {
60
60
  statusCode: 409,
61
61
  payload: {
@@ -44,13 +44,14 @@ export interface CreateWandTaskInput {
44
44
  priority?: WandTask["priority"];
45
45
  labels?: string[];
46
46
  dueDate?: string | null;
47
+ cwd?: string | null;
47
48
  milestoneId?: string | null;
48
49
  agent?: WandTaskAgent | null;
49
50
  executionSubject?: WandTask["executionSubject"];
50
51
  }
51
- export type WandTaskPatch = Partial<Pick<WandTask, "workspaceId" | "workspaceTaskId" | "parentTaskId" | "title" | "titleSource" | "autoTitleSignature" | "description" | "status" | "priority" | "labels" | "dueDate" | "milestoneId" | "sortOrder" | "agent" | "executionSubject" | "acceptance">>;
52
- export declare const WAND_TASK_FIELDS = "id, identifier, workspace_id, workspace_task_id, parent_task_id,\n title, title_source, auto_title_signature, description, status, priority, labels_json,\n due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json";
53
- export declare const WORKSPACE_TASK_PROJECTION = "SELECT wt.id,\n CASE WHEN card.id IS NULL THEN wt.workspace_id\n ELSE COALESCE(card.workspace_id, 'wand-global') END AS workspace_id,\n CASE WHEN card.id IS NULL THEN wt.name ELSE card.title END AS name,\n CASE WHEN card.id IS NULL THEN wt.status\n WHEN card.status IN ('done', 'archived') THEN 'done' ELSE 'active' END AS status,\n CASE WHEN card.id IS NULL THEN wt.milestone_id ELSE card.milestone_id END AS milestone_id,\n wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,\n card.status AS card_status, wt.rowid AS _created_order\n FROM workspace_tasks wt LEFT JOIN wand_tasks card ON card.id = (\n SELECT id FROM wand_tasks WHERE workspace_task_id = wt.id\n ORDER BY updated_at DESC, rowid DESC LIMIT 1\n )";
52
+ export type WandTaskPatch = Partial<Pick<WandTask, "workspaceId" | "workspaceTaskId" | "parentTaskId" | "title" | "titleSource" | "autoTitleSignature" | "description" | "status" | "priority" | "labels" | "dueDate" | "milestoneId" | "sortOrder" | "agent" | "executionSubject" | "acceptance" | "verification" | "deliveryRevision">>;
53
+ export declare const WAND_TASK_FIELDS = "id, identifier, workspace_id, workspace_task_id, parent_task_id,\n title, title_source, auto_title_signature, description, status, priority, labels_json,\n due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json, verification_json, delivery_revision";
54
+ export declare const WORKSPACE_TASK_PROJECTION = "SELECT wt.id,\n CASE WHEN card.id IS NULL THEN wt.workspace_id\n ELSE COALESCE(card.workspace_id, 'wand-global') END AS workspace_id,\n CASE WHEN card.id IS NULL THEN wt.name ELSE card.title END AS name,\n CASE WHEN card.id IS NULL THEN wt.status\n WHEN card.status = 'archived' THEN 'done' ELSE 'active' END AS status,\n CASE WHEN card.id IS NULL THEN wt.milestone_id ELSE card.milestone_id END AS milestone_id,\n wt.container_kind, wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,\n card.status AS card_status, wt.rowid AS _created_order\n FROM workspace_tasks wt LEFT JOIN wand_tasks card ON card.id = (\n SELECT id FROM wand_tasks WHERE workspace_task_id = wt.id\n ORDER BY updated_at DESC, rowid DESC LIMIT 1\n )";
54
55
  export declare function withoutSessionTab(node: LayoutNode, sessionId: string): LayoutNode;
55
56
  export declare function mapSiliconEmployeeRow(row: Record<string, unknown>): SiliconEmployee;
56
57
  export declare function mapAiTeamRow(row: Record<string, unknown>): AiTeam;
@@ -17,7 +17,7 @@ export function resolveDatabasePath(configPath) {
17
17
  }
18
18
  export const WAND_TASK_FIELDS = `id, identifier, workspace_id, workspace_task_id, parent_task_id,
19
19
  title, title_source, auto_title_signature, description, status, priority, labels_json,
20
- due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json`;
20
+ due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json, verification_json, delivery_revision`;
21
21
  // Shared metadata comes from the latest explicitly linked card, including meaningful NULLs.
22
22
  // The old container columns remain readable for legacy rows until the startup migration.
23
23
  export const WORKSPACE_TASK_PROJECTION = `SELECT wt.id,
@@ -25,9 +25,9 @@ export const WORKSPACE_TASK_PROJECTION = `SELECT wt.id,
25
25
  ELSE COALESCE(card.workspace_id, '${GLOBAL_WORKSPACE_ID}') END AS workspace_id,
26
26
  CASE WHEN card.id IS NULL THEN wt.name ELSE card.title END AS name,
27
27
  CASE WHEN card.id IS NULL THEN wt.status
28
- WHEN card.status IN ('done', 'archived') THEN 'done' ELSE 'active' END AS status,
28
+ WHEN card.status = 'archived' THEN 'done' ELSE 'active' END AS status,
29
29
  CASE WHEN card.id IS NULL THEN wt.milestone_id ELSE card.milestone_id END AS milestone_id,
30
- wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,
30
+ wt.container_kind, wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,
31
31
  card.status AS card_status, wt.rowid AS _created_order
32
32
  FROM workspace_tasks wt LEFT JOIN wand_tasks card ON card.id = (
33
33
  SELECT id FROM wand_tasks WHERE workspace_task_id = wt.id
@@ -7,6 +7,10 @@ export declare class KnowledgeRepository {
7
7
  private readonly s;
8
8
  constructor(db: DatabaseSync, s: WandStorage);
9
9
  knowledgeOwners(): {
10
+ global: {
11
+ id: string;
12
+ name: string;
13
+ };
10
14
  employees: Array<{
11
15
  id: string;
12
16
  name: string;
@@ -19,6 +23,7 @@ export declare class KnowledgeRepository {
19
23
  };
20
24
  readKnowledgeLibrary(scope: KnowledgeScope, ownerId: string, query?: string, accessToken?: string): KnowledgeLibrary;
21
25
  getKnowledgeDocument(scope: KnowledgeScope, ownerId: string, id: string, accessToken?: string): KnowledgeDocument;
26
+ syncGlobalKnowledge(): void;
22
27
  createKnowledgeDocument(scope: KnowledgeScope, ownerId: string, input: KnowledgeDocumentInput, accessToken?: string): {
23
28
  entry: KnowledgeDocument;
24
29
  created: boolean;