@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
@@ -1,4 +1,5 @@
1
1
  import crypto from "node:crypto";
2
+ import { normalizeProjectCwd } from "../workspace-binding.js";
2
3
  import { assertTaskHierarchy } from "../task-hierarchy.js";
3
4
  import { TASK_RETENTION_ARCHIVE_VERSION } from "../task-retention.js";
4
5
  import { firstLayoutTabId } from "../layout-tree.js";
@@ -6,7 +7,7 @@ import { nowIso } from "../password-manager.js";
6
7
  import { isSessionProvider } from "../session-provider.js";
7
8
  import { isSystemAiCall } from "../system-ai-call.js";
8
9
  import { isThinkingEffort } from "../structured-provider-common.js";
9
- import { DEFAULT_ITERATION_NAME, DEFAULT_WAND_TASK_PRIORITY, isTaskExecutionSubject, normalizeWandTaskAgentMode } from "../task-types.js";
10
+ import { DEFAULT_ITERATION_NAME, DEFAULT_WAND_TASK_PRIORITY, isTaskExecutionSubject, isWandTaskStatus, normalizeWandTaskAgentMode, taskAiVerified, taskHumanAccepted } from "../task-types.js";
10
11
  import { GLOBAL_WORKSPACE_ID } from "../types.js";
11
12
  import { safeJsonParse, mapWandMilestoneRow, mapIterationPromptRow } from "./mappers.js";
12
13
  import { parseWandTaskAgent } from "./mappers.js";
@@ -88,6 +89,8 @@ export class TaskRepository {
88
89
  mapWandTaskRow(row) {
89
90
  return {
90
91
  acceptance: safeJsonParse(String(row.acceptance_json ?? "null")),
92
+ verification: safeJsonParse(String(row.verification_json ?? "null")),
93
+ deliveryRevision: Number(row.delivery_revision) || 0,
91
94
  id: String(row.id),
92
95
  identifier: typeof row.identifier === "string" && row.identifier ? row.identifier : `TASK-${String(row.id).slice(0, 6)}`,
93
96
  workspaceId: typeof row.workspace_id === "string" ? row.workspace_id : null,
@@ -97,7 +100,7 @@ export class TaskRepository {
97
100
  titleSource: row.title_source === "auto" ? "auto" : "user",
98
101
  autoTitleSignature: typeof row.auto_title_signature === "string" && row.auto_title_signature ? row.auto_title_signature : null,
99
102
  description: String(row.description ?? ""),
100
- status: row.status === "backlog" || row.status === "doing" || row.status === "done" || row.status === "archived" ? row.status : "todo",
103
+ status: isWandTaskStatus(row.status) ? row.status : "todo",
101
104
  priority: row.priority === "low" || row.priority === "medium" || row.priority === "high" || row.priority === "urgent" ? row.priority : "none",
102
105
  labels: (() => { const parsed = safeJsonParse(String(row.labels_json ?? "[]")); return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : []; })(),
103
106
  dueDate: typeof row.due_date === "string" ? row.due_date : null,
@@ -143,10 +146,13 @@ export class TaskRepository {
143
146
  .all();
144
147
  return new Map(rows.map(row => [row.task_id, Number(row.count) || 0]));
145
148
  }
149
+ isTaskReviewComment(id) {
150
+ return Boolean(this.db.prepare("SELECT 1 FROM wand_task_comment_dispatches WHERE comment_id = ? AND purpose = 'review' LIMIT 1").get(id));
151
+ }
146
152
  getWandTaskComment(id) {
147
153
  const row = this.db.prepare(`SELECT c.*, d.request_id, d.source_comment_id, d.employee_id, d.employee_name,
148
154
  d.instruction, d.task_version, d.state, d.session_id, d.error, d.execution_status, d.referenced_task_ids,
149
- d.result_start_index, d.result_end_index, d.result_request_id, d.result_outcome, d.result_fingerprint, d.result_start_fingerprint FROM wand_task_comments c
155
+ d.result_start_index, d.result_end_index, d.result_request_id, d.result_outcome, d.result_fingerprint, d.result_start_fingerprint, d.purpose, d.review_revision FROM wand_task_comments c
150
156
  LEFT JOIN wand_task_comment_dispatches d ON d.comment_id = c.id WHERE c.id = ?`).get(id);
151
157
  return row ? this.mapWandTaskCommentRow(row) : null;
152
158
  }
@@ -158,7 +164,7 @@ export class TaskRepository {
158
164
  const placeholders = boundedIds.map(() => "?").join(", ");
159
165
  const rows = this.db.prepare(`SELECT c.*, d.request_id, d.source_comment_id, d.employee_id, d.employee_name,
160
166
  d.instruction, d.task_version, d.state, d.session_id, d.error, d.execution_status, d.referenced_task_ids,
161
- d.result_start_index, d.result_end_index, d.result_request_id, d.result_outcome, d.result_fingerprint, d.result_start_fingerprint
167
+ d.result_start_index, d.result_end_index, d.result_request_id, d.result_outcome, d.result_fingerprint, d.result_start_fingerprint, d.purpose, d.review_revision
162
168
  FROM wand_task_comments c LEFT JOIN wand_task_comment_dispatches d ON d.comment_id = c.id
163
169
  WHERE c.task_id = ? AND c.id IN (${placeholders})`).all(taskId, ...boundedIds);
164
170
  const byId = new Map(rows.map(row => [row.id, this.mapWandTaskCommentRow(row)]));
@@ -167,6 +173,7 @@ export class TaskRepository {
167
173
  mapWandTaskCommentRow(row) {
168
174
  const referencedTaskIds = parseReferencedTaskIds(row.referenced_task_ids);
169
175
  return { id: row.id, taskId: row.task_id, authorName: row.author_name, body: row.body, createdAt: row.created_at,
176
+ purpose: row.purpose === "review" ? "review" : "execution", reviewRevision: row.review_revision == null ? null : Number(row.review_revision),
170
177
  ...(row.request_id ? { requestId: row.request_id, sourceCommentId: row.source_comment_id, instruction: row.instruction, taskVersion: row.task_version,
171
178
  ...(referencedTaskIds.length ? { referencedTaskIds } : {}),
172
179
  dispatch: row.employee_id ? { employeeId: row.employee_id, employeeName: row.employee_name,
@@ -190,8 +197,8 @@ export class TaskRepository {
190
197
  this.db.prepare(`INSERT INTO wand_task_comments(id, task_id, author_name, body, created_at) VALUES (?, ?, ?, ?, ?)`)
191
198
  .run(id, input.taskId, "用户", input.body, nowIso());
192
199
  this.db.prepare(`INSERT INTO wand_task_comment_dispatches(comment_id, task_id, request_id, signature, source_comment_id,
193
- employee_id, employee_name, instruction, task_version, state, referenced_task_ids, agent_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
194
- .run(id, input.taskId, input.requestId, input.signature, input.sourceCommentId, input.employeeId, input.employeeName, input.instruction, input.taskVersion, input.employeeId ? input.queued ? "queued" : "pending" : "accepted", JSON.stringify(input.referencedTaskIds ?? []), input.agent ? JSON.stringify(input.agent) : null);
200
+ employee_id, employee_name, instruction, task_version, state, referenced_task_ids, agent_json, purpose, review_revision) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
201
+ .run(id, input.taskId, input.requestId, input.signature, input.sourceCommentId, input.employeeId, input.employeeName, input.instruction, input.taskVersion, input.employeeId ? input.queued ? "queued" : "pending" : "accepted", JSON.stringify(input.referencedTaskIds ?? []), input.agent ? JSON.stringify(input.agent) : null, input.purpose ?? "execution", input.reviewRevision ?? null);
195
202
  return this.getWandTaskComment(id);
196
203
  });
197
204
  }
@@ -456,8 +463,8 @@ export class TaskRepository {
456
463
  FROM wand_tasks WHERE identifier GLOB 'TASK-[0-9]*'`).get();
457
464
  try {
458
465
  this.db.prepare(`INSERT INTO wand_tasks (${WAND_TASK_FIELDS})
459
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
460
- .run(id, `TASK-${number.value + 1}`, input.workspaceId ?? null, input.workspaceTaskId ?? null, input.parentTaskId ?? null, input.title, input.titleSource ?? "user", null, input.description ?? "", status, input.priority ?? DEFAULT_WAND_TASK_PRIORITY, JSON.stringify(input.labels ?? []), input.dueDate ?? null, input.milestoneId ?? null, max.value + 1, input.agent ? JSON.stringify(input.agent) : null, input.executionSubject ? JSON.stringify(input.executionSubject) : null, archivedAt, createdAtValue, createdAtValue, input.acceptance ? JSON.stringify(input.acceptance) : null);
466
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
467
+ .run(id, `TASK-${number.value + 1}`, input.workspaceId ?? null, input.workspaceTaskId ?? null, input.parentTaskId ?? null, input.title, input.titleSource ?? "user", null, input.description ?? "", status, input.priority ?? DEFAULT_WAND_TASK_PRIORITY, JSON.stringify(input.labels ?? []), input.dueDate ?? null, input.milestoneId ?? null, max.value + 1, input.agent ? JSON.stringify(input.agent) : null, input.executionSubject ? JSON.stringify(input.executionSubject) : null, archivedAt, createdAtValue, createdAtValue, input.acceptance ? JSON.stringify(input.acceptance) : null, null, 0);
461
468
  }
462
469
  catch (error) {
463
470
  if (input.workspaceTaskId && this.getWandTaskByWorkspaceTaskId(input.workspaceTaskId)) {
@@ -471,8 +478,12 @@ export class TaskRepository {
471
478
  }
472
479
  if (input.parentTaskId) {
473
480
  const parent = this.getWandTask(input.parentTaskId);
474
- if (parent?.acceptance?.acceptedAt)
475
- this.writeWandTask(parent, { acceptance: { ...parent.acceptance, acceptedAt: null } });
481
+ if (parent && (parent.verification || parent.acceptance?.acceptedAt))
482
+ this.writeWandTask(parent, {
483
+ deliveryRevision: (parent.deliveryRevision ?? 0) + 1, verification: null,
484
+ ...(parent.acceptance ? { acceptance: { ...parent.acceptance, acceptedAt: null, confirmedRevision: undefined } } : {}),
485
+ ...(parent.status === "ready" || parent.status === "review" ? { status: "doing" } : {}),
486
+ }, true);
476
487
  }
477
488
  this.s.notifyTaskTitleChange();
478
489
  return this.getWandTask(id);
@@ -480,12 +491,20 @@ export class TaskRepository {
480
491
  createWandTask(input) {
481
492
  return this.s.transaction(() => {
482
493
  const linked = input.workspaceTaskId ? this.s.getWorkspaceTask(input.workspaceTaskId) : null;
483
- const workspaceId = input.workspaceId === undefined && linked
494
+ let workspaceId = input.workspaceId === undefined && linked
484
495
  ? linked.workspaceId === GLOBAL_WORKSPACE_ID ? null : linked.workspaceId
485
496
  : input.workspaceId ?? null;
486
- const workspace = workspaceId ? this.s.getWorkspace(workspaceId) : this.s.ensureGlobalWorkspace();
497
+ let workspace = workspaceId ? this.s.getWorkspace(workspaceId) : this.s.ensureGlobalWorkspace();
487
498
  if (!workspace)
488
499
  throw new Error("任务所属工作区不存在。");
500
+ if (input.cwd && (!workspaceId || workspace.id === GLOBAL_WORKSPACE_ID)) {
501
+ const normalized = normalizeProjectCwd(input.cwd);
502
+ const match = this.s.listWorkspaces().find(w => w.id !== GLOBAL_WORKSPACE_ID && normalizeProjectCwd(w.cwd) === normalized);
503
+ if (match) {
504
+ workspace = match;
505
+ workspaceId = match.id;
506
+ }
507
+ }
489
508
  const milestoneId = this.s.taskMilestoneForWrite(input.milestoneId, workspaceId);
490
509
  if (input.workspaceTaskId) {
491
510
  const container = this.s.getWorkspaceTask(input.workspaceTaskId);
@@ -498,23 +517,41 @@ export class TaskRepository {
498
517
  }
499
518
  const workspaceTaskId = input.workspaceTaskId ?? this.s.insertWorkspaceTask({
500
519
  workspaceId: workspace.id, name: input.title,
501
- status: input.status === "done" || input.status === "archived" ? "done" : "active",
520
+ status: input.status === "archived" ? "done" : "active",
502
521
  milestoneId,
522
+ cwd: input.cwd ?? null,
503
523
  });
504
524
  return this.insertWandTask({ ...input, workspaceId, workspaceTaskId, milestoneId });
505
525
  });
506
526
  }
507
527
  /** Private canonical writer; callers own the transaction, never a reverse writer. */
508
- writeWandTask(current, patch) {
509
- const next = { ...current, ...patch, updatedAt: nowIso() };
528
+ writeWandTask(current, patch, workflow = false) {
529
+ const next = { ...current, ...patch, updatedAt: new Date(Math.max(Date.now(), (Date.parse(current.updatedAt) || 0) + 1)).toISOString() };
530
+ const deliveryChanged = patch.description !== undefined && patch.description !== current.description
531
+ || patch.parentTaskId !== undefined && patch.parentTaskId !== current.parentTaskId
532
+ || patch.workspaceId !== undefined && patch.workspaceId !== current.workspaceId
533
+ || patch.acceptance !== undefined && (patch.acceptance?.evidence !== current.acceptance?.evidence || patch.acceptance?.blocker !== current.acceptance?.blocker)
534
+ || patch.status !== undefined && ["backlog", "todo", "doing"].includes(patch.status) && patch.status !== current.status && ["ready", "review", "done", "archived"].includes(current.status);
535
+ if (!workflow && deliveryChanged) {
536
+ next.deliveryRevision = (current.deliveryRevision ?? 0) + 1;
537
+ next.verification = null;
538
+ if (next.acceptance)
539
+ next.acceptance = { ...next.acceptance, acceptedAt: null, confirmedRevision: undefined };
540
+ if (next.status === "ready" || next.status === "review")
541
+ next.status = "doing";
542
+ }
543
+ if (next.status === "ready" && !taskAiVerified(next))
544
+ throw new Error("待归档需要当前交付的 AI 验收记录,请走验收工作流。");
545
+ if (!workflow && next.status !== current.status && next.status === "archived" && !taskHumanAccepted(current))
546
+ throw new Error("请先经 AI 验收,再由人工确认并归档。");
547
+ if (!workflow && patch.acceptance?.acceptedAt && patch.acceptance.acceptedAt !== current.acceptance?.acceptedAt)
548
+ throw new Error("人工确认只能通过验收工作流写入。");
510
549
  assertTaskHierarchy(this.listWandTasks(), current.id, next.parentTaskId, next.workspaceId, current);
511
- if (next.acceptance?.acceptedAt) {
512
- if (patch.description !== undefined && patch.description !== current.description)
513
- next.acceptance = { ...next.acceptance, acceptedAt: null };
514
- else if (!next.acceptance.evidence.trim() || next.acceptance.blocker.trim())
515
- throw new Error("验收通过需要交付证据,并先解除阻塞。");
516
- else if (this.listWandTasks().some(child => child.parentTaskId === current.id && !child.acceptance?.acceptedAt))
517
- throw new Error("仍有子任务未验收通过,不能通过主目标整体验收。");
550
+ if (taskHumanAccepted(next)) {
551
+ if (!next.acceptance.evidence.trim() || next.acceptance.blocker.trim())
552
+ throw new Error("人工确认需要交付证据,并先解除阻塞。");
553
+ if (this.listWandTaskChildren(current.id).some(child => !taskHumanAccepted(child)))
554
+ throw new Error("仍有子任务未由人工确认。");
518
555
  }
519
556
  // 归档时间只在进入归档时写一次;取消归档/重新打开时清空,恢复“未动多久”重新计时。
520
557
  if (next.status === "archived") {
@@ -544,15 +581,15 @@ export class TaskRepository {
544
581
  throw new Error("任务所属工作区不存在。");
545
582
  next.workspaceTaskId = this.s.insertWorkspaceTask({
546
583
  workspaceId: workspace.id, name: next.title, milestoneId: next.milestoneId,
547
- status: next.status === "done" ? "done" : "active", createdAt: next.createdAt,
584
+ status: "active", createdAt: next.createdAt,
548
585
  });
549
586
  }
550
587
  this.db.prepare(`UPDATE wand_tasks SET workspace_id = ?, workspace_task_id = ?, parent_task_id = ?,
551
588
  title = ?, title_source = ?, auto_title_signature = ?, description = ?, status = ?, priority = ?,
552
589
  labels_json = ?, due_date = ?, milestone_id = ?, sort_order = ?, agent_json = ?, execution_subject_json = ?,
553
- archived_at = ?, updated_at = ?, acceptance_json = ?
590
+ archived_at = ?, updated_at = ?, acceptance_json = ?, verification_json = ?, delivery_revision = ?
554
591
  WHERE id = ?`)
555
- .run(next.workspaceId, next.workspaceTaskId, next.parentTaskId, next.title, next.titleSource, next.autoTitleSignature ?? null, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.milestoneId ?? null, next.sortOrder, next.agent ? JSON.stringify(next.agent) : null, next.executionSubject ? JSON.stringify(next.executionSubject) : null, next.archivedAt, next.updatedAt, next.acceptance ? JSON.stringify(next.acceptance) : null, next.id);
592
+ .run(next.workspaceId, next.workspaceTaskId, next.parentTaskId, next.title, next.titleSource, next.autoTitleSignature ?? null, next.description, next.status, next.priority, JSON.stringify(next.labels), next.dueDate, next.milestoneId ?? null, next.sortOrder, next.agent ? JSON.stringify(next.agent) : null, next.executionSubject ? JSON.stringify(next.executionSubject) : null, next.archivedAt, next.updatedAt, next.acceptance ? JSON.stringify(next.acceptance) : null, next.verification ? JSON.stringify(next.verification) : null, next.deliveryRevision ?? 0, next.id);
556
593
  // Only a real transition may opt into the new policy. Editing/backfilling an old archive must not do so.
557
594
  if (next.status !== current.status) {
558
595
  this.db.prepare("UPDATE wand_tasks SET retention_archive_version = ? WHERE id = ?")
@@ -566,11 +603,18 @@ export class TaskRepository {
566
603
  this.db.prepare("UPDATE command_sessions SET workspace_id = ? WHERE workspace_task_id = ?")
567
604
  .run(workspaceId, next.workspaceTaskId);
568
605
  }
569
- if ((patch.acceptance !== undefined || patch.description !== undefined || patch.parentTaskId !== undefined) && next.parentTaskId) {
570
- const parent = this.getWandTask(next.parentTaskId);
571
- if (parent?.acceptance?.acceptedAt)
572
- this.writeWandTask(parent, { acceptance: { ...parent.acceptance, acceptedAt: null } });
573
- }
606
+ if (deliveryChanged || patch.parentTaskId !== undefined && patch.parentTaskId !== current.parentTaskId || patch.deliveryRevision !== undefined && patch.deliveryRevision !== current.deliveryRevision
607
+ || patch.verification !== undefined && !taskAiVerified(next)
608
+ || patch.acceptance !== undefined && taskHumanAccepted(current) && !taskHumanAccepted(next))
609
+ for (const parentId of new Set([current.parentTaskId, next.parentTaskId].filter((id) => Boolean(id)))) {
610
+ const parent = this.getWandTask(parentId);
611
+ if (parent && (parent.verification || parent.acceptance?.acceptedAt))
612
+ this.writeWandTask(parent, {
613
+ deliveryRevision: (parent.deliveryRevision ?? 0) + 1, verification: null,
614
+ ...(parent.acceptance ? { acceptance: { ...parent.acceptance, acceptedAt: null, confirmedRevision: undefined } } : {}),
615
+ ...(parent.status === "ready" || parent.status === "review" ? { status: "doing" } : {}),
616
+ }, true);
617
+ }
574
618
  this.s.notifyTaskTitleChange();
575
619
  return next;
576
620
  }
@@ -582,6 +626,18 @@ export class TaskRepository {
582
626
  return this.writeWandTask(current, patch);
583
627
  });
584
628
  }
629
+ /** Explicit destructive project removal collects history, never claims delivery acceptance. */
630
+ archiveTaskForWorkspaceRemoval(id) {
631
+ return this.s.transaction(() => {
632
+ const current = this.getWandTask(id);
633
+ if (!current)
634
+ return null;
635
+ const task = this.writeWandTask(current, { status: "archived", workspaceTaskId: null }, true);
636
+ this.db.prepare("INSERT INTO wand_task_comments(id, task_id, author_name, body, created_at) VALUES (?, ?, ?, ?, ?)")
637
+ .run(crypto.randomUUID(), id, "项目移除", "项目被明确删除,仅收纳原任务与历史;这不是 AI 验收或人工接受交付。", task.updatedAt);
638
+ return task;
639
+ });
640
+ }
585
641
  /** Commit completion archives only the selected completed cards, atomically. */
586
642
  archiveCompletedWandTasks(ids) {
587
643
  return this.archiveWandTasks(ids, { completedOnly: true });
@@ -597,7 +653,9 @@ export class TaskRepository {
597
653
  const task = this.getWandTask(id);
598
654
  if (!task || task.status === "archived")
599
655
  continue;
600
- if (options.completedOnly && task.status !== "done")
656
+ if (options.completedOnly && !taskHumanAccepted(task))
657
+ continue;
658
+ if (!taskHumanAccepted(task))
601
659
  continue;
602
660
  this.writeWandTask(task, { status: "archived" });
603
661
  archived.push(id);
@@ -851,6 +909,8 @@ export class TaskRepository {
851
909
  const target = taskId ? this.s.getWorkspaceTask(taskId) : null;
852
910
  if (taskId && !target)
853
911
  throw new Error("未找到目标任务。");
912
+ if (target?.archived)
913
+ throw new Error("目标已归档,请先恢复后再移入会话。");
854
914
  if (target && !this.s.getWorkspace(target.workspaceId))
855
915
  throw new Error("目标工作区不存在。");
856
916
  const source = session.workspaceTaskId ? this.s.getWorkspaceTask(session.workspaceTaskId) : null;
@@ -862,16 +922,16 @@ export class TaskRepository {
862
922
  this.s.saveWorkspaceTaskLayout(source.id, { ...source.layout, windows });
863
923
  }
864
924
  const targetCard = target ? this.getWandTaskByWorkspaceTaskId(target.id) : null;
865
- if (target && !targetCard)
925
+ if (target && target.kind !== "section" && !targetCard)
866
926
  throw new Error("未找到目标任务。");
867
927
  this.db.prepare("DELETE FROM wand_task_sessions WHERE session_id = ? AND (? IS NULL OR task_id != ?)")
868
928
  .run(sessionId, targetCard?.id ?? null, targetCard?.id ?? null);
869
929
  this.db.prepare("UPDATE command_sessions SET workspace_task_id = ?, workspace_id = ? WHERE id = ?")
870
930
  .run(target?.id ?? null, target?.workspaceId ?? session.workspaceId ?? null, sessionId);
871
- if (target) {
931
+ if (targetCard) {
872
932
  this.db.prepare("INSERT OR IGNORE INTO wand_task_sessions (task_id, session_id, bound_at) VALUES (?, ?, ?)")
873
933
  .run(targetCard.id, sessionId, nowIso());
874
- if (source?.id !== target.id)
934
+ if (source?.id !== target?.id)
875
935
  this.recordTaskSession(this.s.getSessionSlim(sessionId));
876
936
  }
877
937
  });
@@ -62,6 +62,13 @@ export declare class WorkspaceRepository {
62
62
  createdAt?: string;
63
63
  }): string;
64
64
  taskMilestoneForWrite(id: string | null | undefined, workspaceId: string | null): string;
65
+ /** A user-named session container; deliberately has no task-board card. */
66
+ createWorkspaceSection(input: {
67
+ workspaceId: string;
68
+ name: string;
69
+ cwd?: string;
70
+ sessionId?: string;
71
+ }): WorkspaceTask;
65
72
  createWorkspaceTask(input: {
66
73
  workspaceId: string;
67
74
  name: string;
@@ -242,6 +242,21 @@ export class WorkspaceRepository {
242
242
  return workspaceId && milestone.workspaceId && milestone.workspaceId !== workspaceId
243
243
  ? this.s.ensureDefaultWandMilestone().id : id;
244
244
  }
245
+ /** A user-named session container; deliberately has no task-board card. */
246
+ createWorkspaceSection(input) {
247
+ return this.s.transaction(() => {
248
+ if (!this.getWorkspace(input.workspaceId))
249
+ throw new Error("分区所属工作区不存在。");
250
+ const name = input.name.trim();
251
+ if (!name || name.length > 80)
252
+ throw new Error("请输入 1–80 字的分区名称。");
253
+ const id = this.insertWorkspaceTask({ ...input, name });
254
+ this.db.prepare("UPDATE workspace_tasks SET container_kind = 'section' WHERE id = ?").run(id);
255
+ if (input.sessionId)
256
+ this.s.moveSessionToWorkspaceTask(input.sessionId, id);
257
+ return this.getWorkspaceTask(id);
258
+ });
259
+ }
245
260
  createWorkspaceTask(input) {
246
261
  return this.s.transaction(() => {
247
262
  const workspace = this.getWorkspace(input.workspaceId);
@@ -263,8 +278,16 @@ export class WorkspaceRepository {
263
278
  updateWorkspaceTask(id, patch) {
264
279
  this.s.transaction(() => {
265
280
  const card = this.s.getWandTaskByWorkspaceTaskId(id);
266
- if (!card)
267
- throw new Error("未找到该任务。");
281
+ if (!card) {
282
+ const container = this.getWorkspaceTask(id);
283
+ if (container?.kind !== "section")
284
+ throw new Error("未找到该任务。");
285
+ if (patch.name !== undefined)
286
+ this.db.prepare("UPDATE workspace_tasks SET name = ? WHERE id = ?").run(patch.name, id);
287
+ if (patch.status !== undefined)
288
+ this.db.prepare("UPDATE workspace_tasks SET status = ? WHERE id = ?").run(patch.status, id);
289
+ return;
290
+ }
268
291
  if (patch.worktree !== undefined) {
269
292
  this.db.prepare("UPDATE workspace_tasks SET worktree_json = ? WHERE id = ?")
270
293
  .run(patch.worktree ? JSON.stringify(patch.worktree) : null, id);
@@ -286,6 +309,9 @@ export class WorkspaceRepository {
286
309
  }
287
310
  archiveWorkspaceTask(id) {
288
311
  const card = this.s.getWandTaskByWorkspaceTaskId(id);
312
+ if (!card && this.getWorkspaceTask(id)?.kind === "section") {
313
+ throw new Error("分区不需要归档,请将会话移回工作区后删除空分区。");
314
+ }
289
315
  return card ? this.s.updateWandTask(card.id, { status: "archived" }) : null;
290
316
  }
291
317
  saveWorkspaceTaskLayout(id, layout, expectedRevision) {
@@ -1,5 +1,6 @@
1
1
  import { type TempKind } from "./managed-temp.js";
2
2
  import type { UploadRepository } from "./storage/uploads-repository.js";
3
+ import type { PtyTmuxGcReport } from "./pty-tmux-backend.js";
3
4
  export declare const STORAGE_CLEANUP_RETENTION_DAYS = 7;
4
5
  export interface CleanupReport {
5
6
  applied: boolean;
@@ -11,7 +12,7 @@ export interface CleanupReport {
11
12
  allocatedBytes: number;
12
13
  skipped: number;
13
14
  issues: string[];
14
- byKind: Partial<Record<TempKind | "upload" | "diagnostic-log" | "distribution-package" | "native-run" | "migration-snapshot", number>>;
15
+ byKind: Partial<Record<TempKind | "upload" | "diagnostic-log" | "distribution-package" | "native-run" | "migration-snapshot" | "pty-tmux", number>>;
15
16
  }
16
17
  /** pi-native 回收结果(与 PiTmuxHost.collectGarbage 的返回一致)。 */
17
18
  export interface PiNativeGcReport {
@@ -52,6 +53,10 @@ interface CleanupOptions {
52
53
  deleted: string[];
53
54
  freedBytes: number;
54
55
  };
56
+ /**
57
+ * 回收 pty-tmux 下废弃的会话记录与 /tmp 遗留的失效 socket。
58
+ */
59
+ collectPtyTmuxArtifacts?: () => PtyTmuxGcReport | null;
55
60
  }
56
61
  /** Runtime-only, explicit ownership. User history, downloads and rollback inputs are never candidates. */
57
62
  export declare class StorageCleanup {
@@ -81,5 +86,5 @@ export declare class StorageCleanup {
81
86
  private sweepDistributionArtifacts;
82
87
  private sweepTemporary;
83
88
  }
84
- export declare function createStorageCleanup(configDir: string, options?: Pick<CleanupOptions, "uploads" | "uploadsActive" | "onUploadRemoved" | "pruneDistributionArtifacts" | "collectNativeArtifacts" | "pruneMigrationSnapshots">): StorageCleanup;
89
+ export declare function createStorageCleanup(configDir: string, options?: Pick<CleanupOptions, "uploads" | "uploadsActive" | "onUploadRemoved" | "pruneDistributionArtifacts" | "collectNativeArtifacts" | "pruneMigrationSnapshots" | "collectPtyTmuxArtifacts">): StorageCleanup;
85
90
  export {};
@@ -268,6 +268,22 @@ export class StorageCleanup {
268
268
  report.issues.push("Migration snapshot cleanup failed; snapshots preserved.");
269
269
  }
270
270
  }
271
+ if (this.options.collectPtyTmuxArtifacts) {
272
+ try {
273
+ const ptyReport = this.options.collectPtyTmuxArtifacts();
274
+ const removed = (ptyReport?.removedRecords ?? 0) + (ptyReport?.removedSockets ?? 0);
275
+ if (ptyReport && removed > 0) {
276
+ report.removed += removed;
277
+ report.logicalBytes += ptyReport.freedBytes;
278
+ report.byKind["pty-tmux"] = removed;
279
+ detail.push(`PTY tmux 产物 ${removed} 项(${(ptyReport.freedBytes / 1024 / 1024).toFixed(1)} MB)`
280
+ + `:记录 ${ptyReport.removedRecords} 条 / socket ${ptyReport.removedSockets} 个`);
281
+ }
282
+ }
283
+ catch {
284
+ report.issues.push("PTY tmux artifact cleanup failed; artifacts preserved.");
285
+ }
286
+ }
271
287
  // 后台删除必须留痕迹:这些路径不是用户主动触发,没有日志就无法事后追溯删了什么。
272
288
  if (detail.length > 0)
273
289
  process.stdout.write(`[storage-cleanup] 已回收 ${detail.join(";")}\n`);
@@ -259,6 +259,8 @@ const INIT_SQL = `
259
259
  agent_json TEXT,
260
260
  execution_subject_json TEXT,
261
261
  acceptance_json TEXT,
262
+ verification_json TEXT,
263
+ delivery_revision INTEGER NOT NULL DEFAULT 0,
262
264
  workspace_task_id TEXT,
263
265
  parent_task_id TEXT,
264
266
  title_source TEXT,
package/dist/storage.d.ts CHANGED
@@ -10,6 +10,7 @@ import { ConversationOrganizationRepository } from "./storage/conversation-organ
10
10
  import { PreferencesRepository } from "./storage/preferences-repository.js";
11
11
  import { WorkspaceRepository } from "./storage/workspaces-repository.js";
12
12
  import { TaskRepository } from "./storage/tasks-repository.js";
13
+ import { TaskWorkflowRepository } from "./storage/task-workflow-repository.js";
13
14
  import { SecretsRepository } from "./storage/secrets-repository.js";
14
15
  import { KnowledgeRepository } from "./storage/knowledge-repository.js";
15
16
  import { EmployeeRepository } from "./storage/employees-repository.js";
@@ -17,6 +18,7 @@ import { UserMemoryRepository } from "./storage/user-memory-repository.js";
17
18
  import { AiTeamRepository } from "./storage/ai-teams-repository.js";
18
19
  import { MissionRepository } from "./storage/missions-repository.js";
19
20
  import { SessionRepository } from "./storage/sessions-repository.js";
21
+ import { SessionInputRepository } from "./storage/session-input-repository.js";
20
22
  import { UploadRepository } from "./storage/uploads-repository.js";
21
23
  import { FeedbackRepository } from "./storage/feedback-repository.js";
22
24
  export { parseWandTaskAgent };
@@ -26,7 +28,7 @@ export { ensureDatabaseFile } from "./storage-schema.js";
26
28
  type RepositoryMethods<T> = Pick<T, {
27
29
  [K in keyof T]: T[K] extends (...args: never[]) => unknown ? K : never;
28
30
  }[keyof T]>;
29
- export interface WandStorage extends RepositoryMethods<DingTalkRepository>, RepositoryMethods<ConversationRepository>, RepositoryMethods<ConversationOrganizationRepository>, RepositoryMethods<PreferencesRepository>, RepositoryMethods<WorkspaceRepository>, RepositoryMethods<TaskRepository>, RepositoryMethods<SecretsRepository>, RepositoryMethods<EmployeeRepository>, RepositoryMethods<KnowledgeRepository>, RepositoryMethods<UserMemoryRepository>, RepositoryMethods<AiTeamRepository>, RepositoryMethods<MissionRepository>, RepositoryMethods<SessionRepository>, RepositoryMethods<UploadRepository>, RepositoryMethods<FeedbackRepository> {
31
+ export interface WandStorage extends RepositoryMethods<DingTalkRepository>, RepositoryMethods<ConversationRepository>, RepositoryMethods<ConversationOrganizationRepository>, RepositoryMethods<PreferencesRepository>, RepositoryMethods<WorkspaceRepository>, RepositoryMethods<TaskRepository>, RepositoryMethods<TaskWorkflowRepository>, RepositoryMethods<SecretsRepository>, RepositoryMethods<EmployeeRepository>, RepositoryMethods<KnowledgeRepository>, RepositoryMethods<UserMemoryRepository>, RepositoryMethods<AiTeamRepository>, RepositoryMethods<MissionRepository>, RepositoryMethods<SessionRepository>, RepositoryMethods<SessionInputRepository>, RepositoryMethods<UploadRepository>, RepositoryMethods<FeedbackRepository> {
30
32
  }
31
33
  export declare class WandStorage {
32
34
  readonly dingtalk: DingTalkRepository;
@@ -37,6 +39,7 @@ export declare class WandStorage {
37
39
  readonly preferences: PreferencesRepository;
38
40
  readonly workspaces: WorkspaceRepository;
39
41
  readonly tasks: TaskRepository;
42
+ readonly taskWorkflow: TaskWorkflowRepository;
40
43
  readonly secrets: SecretsRepository;
41
44
  readonly employees: EmployeeRepository;
42
45
  readonly knowledge: KnowledgeRepository;
@@ -44,6 +47,7 @@ export declare class WandStorage {
44
47
  readonly aiTeams: AiTeamRepository;
45
48
  readonly missions: MissionRepository;
46
49
  readonly sessions: SessionRepository;
50
+ readonly sessionInputs: SessionInputRepository;
47
51
  readonly uploads: UploadRepository;
48
52
  readonly feedback: FeedbackRepository;
49
53
  private readonly closeListeners;
package/dist/storage.js CHANGED
@@ -13,6 +13,7 @@ import { ConversationOrganizationRepository } from "./storage/conversation-organ
13
13
  import { PreferencesRepository } from "./storage/preferences-repository.js";
14
14
  import { WorkspaceRepository } from "./storage/workspaces-repository.js";
15
15
  import { TaskRepository } from "./storage/tasks-repository.js";
16
+ import { TaskWorkflowRepository } from "./storage/task-workflow-repository.js";
16
17
  import { SecretsRepository } from "./storage/secrets-repository.js";
17
18
  import { KnowledgeRepository } from "./storage/knowledge-repository.js";
18
19
  import { EmployeeRepository } from "./storage/employees-repository.js";
@@ -20,6 +21,7 @@ import { UserMemoryRepository } from "./storage/user-memory-repository.js";
20
21
  import { AiTeamRepository } from "./storage/ai-teams-repository.js";
21
22
  import { MissionRepository } from "./storage/missions-repository.js";
22
23
  import { SessionRepository } from "./storage/sessions-repository.js";
24
+ import { SessionInputRepository } from "./storage/session-input-repository.js";
23
25
  import { UploadRepository } from "./storage/uploads-repository.js";
24
26
  import { FeedbackRepository } from "./storage/feedback-repository.js";
25
27
  export { parseWandTaskAgent };
@@ -33,6 +35,7 @@ export class WandStorage {
33
35
  preferences;
34
36
  workspaces;
35
37
  tasks;
38
+ taskWorkflow;
36
39
  secrets;
37
40
  employees;
38
41
  knowledge;
@@ -40,6 +43,7 @@ export class WandStorage {
40
43
  aiTeams;
41
44
  missions;
42
45
  sessions;
46
+ sessionInputs;
43
47
  uploads;
44
48
  feedback;
45
49
  closeListeners = new Set();
@@ -54,6 +58,10 @@ export class WandStorage {
54
58
  return `${this.projectionEpoch}:${own.revision}:${external.data_version}`;
55
59
  }
56
60
  onClose(listener) {
61
+ if (this.closed) {
62
+ listener();
63
+ return () => { };
64
+ }
57
65
  this.closeListeners.add(listener);
58
66
  return () => { this.closeListeners.delete(listener); };
59
67
  }
@@ -100,6 +108,7 @@ export class WandStorage {
100
108
  this.preferences = new PreferencesRepository(this.db, this);
101
109
  this.workspaces = new WorkspaceRepository(this.db, this);
102
110
  this.tasks = new TaskRepository(this.db, this);
111
+ this.taskWorkflow = new TaskWorkflowRepository(this.db, this);
103
112
  this.secrets = new SecretsRepository(this.db, this);
104
113
  this.employees = new EmployeeRepository(this.db, this);
105
114
  this.knowledge = new KnowledgeRepository(this.db, this);
@@ -107,6 +116,7 @@ export class WandStorage {
107
116
  this.aiTeams = new AiTeamRepository(this.db, this);
108
117
  this.missions = new MissionRepository(this.db, this);
109
118
  this.sessions = new SessionRepository(this.db, this);
119
+ this.sessionInputs = new SessionInputRepository(this.db, this);
110
120
  this.uploads = new UploadRepository(this.db, this);
111
121
  this.feedback = new FeedbackRepository(this.db, this);
112
122
  this.schedules = new ScheduledTaskRepository(this.db, action => this.transaction(action));
@@ -256,6 +266,7 @@ const REPOSITORY_FORWARDS = [
256
266
  ["preferences", PreferencesRepository],
257
267
  ["workspaces", WorkspaceRepository],
258
268
  ["tasks", TaskRepository],
269
+ ["taskWorkflow", TaskWorkflowRepository],
259
270
  ["secrets", SecretsRepository],
260
271
  ["employees", EmployeeRepository],
261
272
  ["knowledge", KnowledgeRepository],
@@ -263,6 +274,7 @@ const REPOSITORY_FORWARDS = [
263
274
  ["aiTeams", AiTeamRepository],
264
275
  ["missions", MissionRepository],
265
276
  ["sessions", SessionRepository],
277
+ ["sessionInputs", SessionInputRepository],
266
278
  ["uploads", UploadRepository],
267
279
  ["feedback", FeedbackRepository],
268
280
  ];
@@ -16,6 +16,7 @@ export interface StructuredSpawnRequest {
16
16
  stdinData?: string;
17
17
  /** Internal opt-in: a real Pi native TUI in an owned tmux pane. Never supplied by HTTP command text. */
18
18
  nativePi?: boolean;
19
+ nativeInputMessageId?: string;
19
20
  requestId?: string;
20
21
  /** Native launch cancellation before input has been accepted; not sent to daemon RPCs. */
21
22
  launchSignal?: AbortSignal;
@@ -70,8 +71,11 @@ export interface StructuredExecProcess {
70
71
  export interface StructuredExecHost {
71
72
  readonly persistent: boolean;
72
73
  readonly piNativeTui?: boolean;
74
+ offerPiInput?(runId: string, boundaryId: string, take: (identity: import("./pi-native-input.js").PiInputHandoff) => string | null): boolean;
75
+ resolvePiInput?(runId: string, handoff: import("./pi-native-input.js").PiInputHandoff): import("./pi-native-input.js").PiInputResolution;
73
76
  collectGarbage?(options: {
74
77
  liveSessionIds: ReadonlySet<string>;
78
+ activeSessionIds?: ReadonlySet<string>;
75
79
  retentionMs: number;
76
80
  now?: number;
77
81
  }): {
@@ -31,6 +31,7 @@ export interface StructuredCliPumpOptions<S extends StructuredRunnerTurnState> {
31
31
  /** Pi opts in: stdout is captured to a private file; only file reads feed processLine. */
32
32
  stdoutFile?: PiOutputFileOptions;
33
33
  nativePi?: boolean;
34
+ nativeInputMessageId?: string;
34
35
  requestId?: string;
35
36
  /** Provider-owned streaming projection; defaults to the strict shared line framer. */
36
37
  createProtocolReader?(onLine: (line: string) => void): {