@baique/pi-web-sky 0.1.39 → 0.1.40

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 (379) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +9 -14
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/react-loadable-manifest.json +51 -51
  6. package/.next/required-server-files.js +1 -1
  7. package/.next/required-server-files.json +1 -1
  8. package/.next/routes-manifest.json +3 -45
  9. package/.next/server/app/_global-error/page.js +2 -2
  10. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/server/app/_global-error.html +1 -1
  12. package/.next/server/app/_global-error.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page.js +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +3 -3
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  22. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
  24. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  25. package/.next/server/app/api/agent/[id]/route.js +2 -2
  26. package/.next/server/app/api/agent/new/route.js +1 -1
  27. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  28. package/.next/server/app/api/agent/running/route.js +1 -1
  29. package/.next/server/app/api/app-update/route.js +1 -1
  30. package/.next/server/app/api/auth/all-providers/route.js +1 -1
  31. package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
  32. package/.next/server/app/api/auth/login/[provider]/route.js +1 -1
  33. package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
  34. package/.next/server/app/api/auth/providers/route.js +1 -1
  35. package/.next/server/app/api/boards/[id]/reconcile/route.js +1 -0
  36. package/.next/server/app/api/boards/[id]/reconcile/route_client-reference-manifest.js +1 -0
  37. package/.next/server/app/api/boards/[id]/route.js +1 -1
  38. package/.next/server/app/api/boards/purge-orphans/route.js +1 -1
  39. package/.next/server/app/api/boards/reorder/route.js +1 -1
  40. package/.next/server/app/api/boards/route.js +1 -1
  41. package/.next/server/app/api/cwd/browse/route.js +1 -1
  42. package/.next/server/app/api/cwd/validate/route.js +2 -2
  43. package/.next/server/app/api/default-cwd/route.js +2 -2
  44. package/.next/server/app/api/file-index/route.js +2 -2
  45. package/.next/server/app/api/files/[...path]/route.js +3 -3
  46. package/.next/server/app/api/files/[...path]/route.js.nft.json +1 -1
  47. package/.next/server/app/api/git/diff/route.js +1 -1
  48. package/.next/server/app/api/git/status/route.js +1 -1
  49. package/.next/server/app/api/home/route.js +1 -1
  50. package/.next/server/app/api/mcp/route.js +1 -1
  51. package/.next/server/app/api/models/route.js +2 -2
  52. package/.next/server/app/api/models-config/catalog/route.js +1 -1
  53. package/.next/server/app/api/models-config/discover/route.js +1 -1
  54. package/.next/server/app/api/models-config/route.js +1 -1
  55. package/.next/server/app/api/models-config/test/route.js +1 -1
  56. package/.next/server/app/api/plugins/route.js +2 -2
  57. package/.next/server/app/api/project-trust/route.js +2 -2
  58. package/.next/server/app/api/quota/route.js +1 -1
  59. package/.next/server/app/api/search/route.js +2 -2
  60. package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
  61. package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
  62. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
  63. package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
  64. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  65. package/.next/server/app/api/sessions/[id]/state/route.js +2 -2
  66. package/.next/server/app/api/sessions/[id]/todos/route.js +2 -2
  67. package/.next/server/app/api/sessions/route.js +2 -2
  68. package/.next/server/app/api/skills/check/route.js +1 -1
  69. package/.next/server/app/api/skills/install/route.js +2 -2
  70. package/.next/server/app/api/skills/route.js +1 -1
  71. package/.next/server/app/api/skills/search/route.js +1 -1
  72. package/.next/server/app/api/skills/update/route.js +1 -1
  73. package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
  74. package/.next/server/app/api/task-card-questions/route.js +1 -1
  75. package/.next/server/app/api/task-cards/[id]/route.js +1 -1
  76. package/.next/server/app/api/task-cards/route.js +1 -1
  77. package/.next/server/app/api/task-scheduler/status/route.js +1 -1
  78. package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
  79. package/.next/server/app/api/tasks/[id]/route.js +1 -1
  80. package/.next/server/app/api/tasks/reorder/route.js +1 -1
  81. package/.next/server/app/api/tasks/route.js +1 -1
  82. package/.next/server/app/api/terminal/[id]/events/route.js +2 -2
  83. package/.next/server/app/api/terminal/[id]/route.js +2 -2
  84. package/.next/server/app/api/terminal/route.js +2 -2
  85. package/.next/server/app/api/worktrees/route.js +2 -2
  86. package/.next/server/app/favicon.ico/route.js +1 -1
  87. package/.next/server/app/index.html +1 -1
  88. package/.next/server/app/index.rsc +5 -5
  89. package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
  90. package/.next/server/app/index.segments/_full.segment.rsc +5 -5
  91. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  92. package/.next/server/app/manifest.webmanifest/route.js +2 -2
  93. package/.next/server/app/page.js +7 -7
  94. package/.next/server/app/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app-paths-manifest.json +9 -14
  96. package/.next/server/chunks/1094.js +2 -2
  97. package/.next/server/chunks/1601.js +1 -1
  98. package/.next/server/chunks/1860.js +1 -1
  99. package/.next/server/chunks/2075.js +1 -1
  100. package/.next/server/chunks/2169.js +1 -1
  101. package/.next/server/chunks/2404.js +1 -1
  102. package/.next/server/chunks/2524.js +1 -0
  103. package/.next/server/chunks/3012.js +1 -1
  104. package/.next/server/chunks/{5760.js → 3379.js} +1 -1
  105. package/.next/server/chunks/3500.js +1 -1
  106. package/.next/server/chunks/4226.js +5 -2
  107. package/.next/server/chunks/4267.js +1 -1
  108. package/.next/server/chunks/4563.js +1 -1
  109. package/.next/server/chunks/5318.js +1 -1
  110. package/.next/server/chunks/542.js +3 -44
  111. package/.next/server/chunks/5794.js +1 -1
  112. package/.next/server/chunks/6052.js +1 -1
  113. package/.next/server/chunks/6253.js +1 -1
  114. package/.next/server/chunks/6288.js +1 -1
  115. package/.next/server/chunks/6366.js +1 -1
  116. package/.next/server/chunks/673.js +1 -1
  117. package/.next/server/chunks/6916.js +2 -2
  118. package/.next/server/chunks/7128.js +43 -0
  119. package/.next/server/chunks/7265.js +2 -2
  120. package/.next/server/chunks/7799.js +1 -1
  121. package/.next/server/chunks/8106.js +1 -1
  122. package/.next/server/chunks/8333.js +1 -1
  123. package/.next/server/chunks/8604.js +1 -1
  124. package/.next/server/chunks/861.js +1 -0
  125. package/.next/server/chunks/8713.js +1 -1
  126. package/.next/server/chunks/8782.js +2 -2
  127. package/.next/server/chunks/898.js +1 -1
  128. package/.next/server/chunks/922.js +2 -2
  129. package/.next/server/chunks/9280.js +6 -6
  130. package/.next/server/chunks/9510.js +1 -1
  131. package/.next/server/instrumentation.js +1 -1
  132. package/.next/server/instrumentation.js.nft.json +1 -1
  133. package/.next/server/middleware-build-manifest.js +1 -1
  134. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  135. package/.next/server/pages/404.html +1 -1
  136. package/.next/server/pages/500.html +1 -1
  137. package/.next/server/server-reference-manifest.json +1 -1
  138. package/.next/static/chunks/{103.23142a67b02246a8.js → 103.6c91a059aa01d92c.js} +2 -2
  139. package/.next/static/chunks/{1032.12b382a9096ab2a4.js → 1032.9e8f793d46f463f8.js} +1 -1
  140. package/.next/static/chunks/{1063.775c442bdbeef08b.js → 1063.470df8a037dc8409.js} +1 -1
  141. package/.next/static/chunks/1a258343.f4fc2c133917ded8.js +2 -0
  142. package/.next/static/chunks/{1ba63609.b2ae90f43319a29e.js → 1ba63609.569928c7234f832c.js} +1 -1
  143. package/.next/static/chunks/{2181.ba21239d3ac39f81.js → 2181.b8dda21843ef5e74.js} +1 -1
  144. package/.next/static/chunks/{2325.23caf22075e51aa5.js → 2325.23e538886331cf64.js} +1 -1
  145. package/.next/static/chunks/{2356.523277bb8c6bf72a.js → 2356.b7312589c345edec.js} +1 -1
  146. package/.next/static/chunks/{268.72794ee2773c2060.js → 268.a1d90aca268be18a.js} +1 -1
  147. package/.next/static/chunks/{2695.6f49435bae1e6f33.js → 2695.5624aa3aae540e87.js} +1 -1
  148. package/.next/static/chunks/{2703.495a65ed14b2f094.js → 2703.097bc88286733740.js} +1 -1
  149. package/.next/static/chunks/{2821.934bcf60fbdc28c6.js → 2821.d2392a93d192fe92.js} +1 -1
  150. package/.next/static/chunks/3308.2cd6f20e6c0f4e92.js +73 -0
  151. package/.next/static/chunks/{3336.a76f9485fe7cb762.js → 3336.9b1dc9ec3507d64f.js} +1 -1
  152. package/.next/static/chunks/3546.779d2c7646aa77cc.js +1 -0
  153. package/.next/static/chunks/{370.8e8f3cf8eb0e29dd.js → 370.cde2a84caa84054f.js} +1 -1
  154. package/.next/static/chunks/{3716.72bf4c9d7325debd.js → 3716.5bac496cea32ca8f.js} +1 -1
  155. package/.next/static/chunks/{3995.eca0f5bef1fc3ae5.js → 3995.cd112debcd64c610.js} +1 -1
  156. package/.next/static/chunks/{4004.74b9bcf5478b1663.js → 4004.a8f79ef62f32aa30.js} +2 -2
  157. package/.next/static/chunks/{4202.8440832386f023c7.js → 4202.96f5b1beb8b3e3f5.js} +1 -1
  158. package/.next/static/chunks/{4238.6f102dcbcf2ae339.js → 4238.28efbf9aa0c82d90.js} +1 -1
  159. package/.next/static/chunks/{4405.7a19458960a1058e.js → 4405.cda46a596a02aef7.js} +1 -1
  160. package/.next/static/chunks/54a60aa6.5e4421e7afde4de8.js +78 -0
  161. package/.next/static/chunks/{6121.eb9023b1cbc78ee2.js → 6121.5e650e714b402b6f.js} +1 -1
  162. package/.next/static/chunks/{6195.1153369f5f8b13a2.js → 6195.1b7f9dc127381bbc.js} +1 -1
  163. package/.next/static/chunks/6380.d148a8e2c701ad78.js +68 -0
  164. package/.next/static/chunks/6582.efef0ed269bacae2.js +1 -0
  165. package/.next/static/chunks/6be7e44c.00d0163f409986f0.js +1 -0
  166. package/.next/static/chunks/{7331.2add3c4e125a2dba.js → 7331.4afa66d6353c8e07.js} +1 -1
  167. package/.next/static/chunks/752.f41a9dcb0e6548eb.js +1 -0
  168. package/.next/static/chunks/{8200.a36b9f8c983c6b9e.js → 8200.350d650649b977eb.js} +1 -1
  169. package/.next/static/chunks/{8885.cf60cac98bf8b70c.js → 8885.2c060cb45c85547b.js} +1 -1
  170. package/.next/static/chunks/{918.6a9bbe564e4a2d0f.js → 918.593eb466d35cb8ad.js} +1 -1
  171. package/.next/static/chunks/{9698.4d09a99b9d99f3e5.js → 9698.758755d0387e0e22.js} +2 -2
  172. package/.next/static/chunks/97.fb73c551e14b3612.js +2 -0
  173. package/.next/static/chunks/{993e5495.8797001fcfcf2889.js → 993e5495.12cdfbc3af5c79e9.js} +1 -1
  174. package/.next/static/chunks/{9974.9a0f79beca65e8c4.js → 9974.5ee982e074ad047e.js} +1 -1
  175. package/.next/static/chunks/app/_global-error/page-bb8f79f321e26750.js +1 -0
  176. package/.next/static/chunks/app/api/agent/[id]/bash-output/route-bb8f79f321e26750.js +1 -0
  177. package/.next/static/chunks/app/api/agent/[id]/events/route-bb8f79f321e26750.js +1 -0
  178. package/.next/static/chunks/app/api/agent/[id]/route-bb8f79f321e26750.js +1 -0
  179. package/.next/static/chunks/app/api/agent/new/route-bb8f79f321e26750.js +1 -0
  180. package/.next/static/chunks/app/api/agent/running/route-bb8f79f321e26750.js +1 -0
  181. package/.next/static/chunks/app/api/app-update/route-bb8f79f321e26750.js +1 -0
  182. package/.next/static/chunks/app/api/auth/all-providers/route-bb8f79f321e26750.js +1 -0
  183. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-bb8f79f321e26750.js +1 -0
  184. package/.next/static/chunks/app/api/auth/login/[provider]/route-bb8f79f321e26750.js +1 -0
  185. package/.next/static/chunks/app/api/auth/logout/[provider]/route-bb8f79f321e26750.js +1 -0
  186. package/.next/static/chunks/app/api/auth/providers/route-bb8f79f321e26750.js +1 -0
  187. package/.next/static/chunks/app/api/boards/[id]/reconcile/route-bb8f79f321e26750.js +1 -0
  188. package/.next/static/chunks/app/api/boards/[id]/route-bb8f79f321e26750.js +1 -0
  189. package/.next/static/chunks/app/api/boards/purge-orphans/route-bb8f79f321e26750.js +1 -0
  190. package/.next/static/chunks/app/api/boards/reorder/route-bb8f79f321e26750.js +1 -0
  191. package/.next/static/chunks/app/api/boards/route-bb8f79f321e26750.js +1 -0
  192. package/.next/static/chunks/app/api/cwd/browse/route-bb8f79f321e26750.js +1 -0
  193. package/.next/static/chunks/app/api/cwd/validate/route-bb8f79f321e26750.js +1 -0
  194. package/.next/static/chunks/app/api/default-cwd/route-bb8f79f321e26750.js +1 -0
  195. package/.next/static/chunks/app/api/file-index/route-bb8f79f321e26750.js +1 -0
  196. package/.next/static/chunks/app/api/files/[...path]/route-bb8f79f321e26750.js +1 -0
  197. package/.next/static/chunks/app/api/git/diff/route-bb8f79f321e26750.js +1 -0
  198. package/.next/static/chunks/app/api/git/status/route-bb8f79f321e26750.js +1 -0
  199. package/.next/static/chunks/app/api/home/route-bb8f79f321e26750.js +1 -0
  200. package/.next/static/chunks/app/api/mcp/route-bb8f79f321e26750.js +1 -0
  201. package/.next/static/chunks/app/api/models/route-bb8f79f321e26750.js +1 -0
  202. package/.next/static/chunks/app/api/models-config/catalog/route-bb8f79f321e26750.js +1 -0
  203. package/.next/static/chunks/app/api/models-config/discover/route-bb8f79f321e26750.js +1 -0
  204. package/.next/static/chunks/app/api/models-config/route-bb8f79f321e26750.js +1 -0
  205. package/.next/static/chunks/app/api/models-config/test/route-bb8f79f321e26750.js +1 -0
  206. package/.next/static/chunks/app/api/plugins/route-bb8f79f321e26750.js +1 -0
  207. package/.next/static/chunks/app/api/project-trust/route-bb8f79f321e26750.js +1 -0
  208. package/.next/static/chunks/app/api/quota/route-bb8f79f321e26750.js +1 -0
  209. package/.next/static/chunks/app/api/search/route-bb8f79f321e26750.js +1 -0
  210. package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-bb8f79f321e26750.js +1 -0
  211. package/.next/static/chunks/app/api/sessions/[id]/context/route-bb8f79f321e26750.js +1 -0
  212. package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-bb8f79f321e26750.js +1 -0
  213. package/.next/static/chunks/app/api/sessions/[id]/export/route-bb8f79f321e26750.js +1 -0
  214. package/.next/static/chunks/app/api/sessions/[id]/route-bb8f79f321e26750.js +1 -0
  215. package/.next/static/chunks/app/api/sessions/[id]/state/route-bb8f79f321e26750.js +1 -0
  216. package/.next/static/chunks/app/api/sessions/[id]/todos/route-bb8f79f321e26750.js +1 -0
  217. package/.next/static/chunks/app/api/sessions/route-bb8f79f321e26750.js +1 -0
  218. package/.next/static/chunks/app/api/skills/check/route-bb8f79f321e26750.js +1 -0
  219. package/.next/static/chunks/app/api/skills/install/route-bb8f79f321e26750.js +1 -0
  220. package/.next/static/chunks/app/api/skills/route-bb8f79f321e26750.js +1 -0
  221. package/.next/static/chunks/app/api/skills/search/route-bb8f79f321e26750.js +1 -0
  222. package/.next/static/chunks/app/api/skills/update/route-bb8f79f321e26750.js +1 -0
  223. package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-bb8f79f321e26750.js +1 -0
  224. package/.next/static/chunks/app/api/task-card-questions/route-bb8f79f321e26750.js +1 -0
  225. package/.next/static/chunks/app/api/task-cards/[id]/route-bb8f79f321e26750.js +1 -0
  226. package/.next/static/chunks/app/api/task-cards/route-bb8f79f321e26750.js +1 -0
  227. package/.next/static/chunks/app/api/task-scheduler/status/route-bb8f79f321e26750.js +1 -0
  228. package/.next/static/chunks/app/api/tasks/[id]/board/route-bb8f79f321e26750.js +1 -0
  229. package/.next/static/chunks/app/api/tasks/[id]/route-bb8f79f321e26750.js +1 -0
  230. package/.next/static/chunks/app/api/tasks/reorder/route-bb8f79f321e26750.js +1 -0
  231. package/.next/static/chunks/app/api/tasks/route-bb8f79f321e26750.js +1 -0
  232. package/.next/static/chunks/app/api/terminal/[id]/events/route-bb8f79f321e26750.js +1 -0
  233. package/.next/static/chunks/app/api/terminal/[id]/route-bb8f79f321e26750.js +1 -0
  234. package/.next/static/chunks/app/api/terminal/route-bb8f79f321e26750.js +1 -0
  235. package/.next/static/chunks/app/api/worktrees/route-bb8f79f321e26750.js +1 -0
  236. package/.next/static/chunks/app/{layout-7761e430ed68b347.js → layout-702d3c97d297c42d.js} +1 -1
  237. package/.next/static/chunks/app/manifest.webmanifest/route-bb8f79f321e26750.js +1 -0
  238. package/.next/static/chunks/app/page-fd18462c3c937a50.js +89 -0
  239. package/.next/static/chunks/next/dist/client/components/builtin/app-error-bb8f79f321e26750.js +1 -0
  240. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-bb8f79f321e26750.js +1 -0
  241. package/.next/static/chunks/next/dist/client/components/builtin/not-found-bb8f79f321e26750.js +1 -0
  242. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-bb8f79f321e26750.js +1 -0
  243. package/.next/static/chunks/webpack-d17b4dde91b96c63.js +1 -0
  244. package/.next/static/css/0c7244f7214af81f.css +1 -0
  245. package/.next/static/css/12456255969ac8de.css +31 -0
  246. package/.next/trace +43 -49
  247. package/.next/trace-build +1 -1
  248. package/.next/types/app/api/boards/[id]/{reconcile-task-edges → reconcile}/route.ts +3 -3
  249. package/.next/types/routes.d.ts +2 -7
  250. package/.next/types/validator.ts +3 -48
  251. package/README.md +6 -9
  252. package/THIRD_PARTY_NOTICES.md +10 -82
  253. package/bin/pi-web-sky.js +3 -3
  254. package/lib/board-reconcile-scheduler.ts +56 -0
  255. package/lib/board-reconcile.test.mjs +252 -0
  256. package/lib/board-reconcile.ts +422 -0
  257. package/lib/board-store.test.mjs +26 -4
  258. package/lib/board-store.ts +44 -6
  259. package/lib/i18n/messages/en.ts +1 -1
  260. package/lib/i18n/messages/zh-CN.ts +1 -1
  261. package/lib/session-delete.ts +5 -0
  262. package/lib/sqlite-db.ts +13 -1
  263. package/lib/task-card-store.ts +26 -1
  264. package/lib/task-scheduler.test.mjs +79 -0
  265. package/lib/task-scheduler.ts +195 -21
  266. package/lib/yjs-room-server.mjs +139 -0
  267. package/package.json +12 -9
  268. package/server.mjs +7 -7
  269. package/.next/server/app/api/boards/[id]/canvas/route.js +0 -1
  270. package/.next/server/app/api/boards/[id]/canvas/route_client-reference-manifest.js +0 -1
  271. package/.next/server/app/api/boards/[id]/edges/[eid]/route.js +0 -1
  272. package/.next/server/app/api/boards/[id]/edges/[eid]/route.js.nft.json +0 -1
  273. package/.next/server/app/api/boards/[id]/edges/[eid]/route_client-reference-manifest.js +0 -1
  274. package/.next/server/app/api/boards/[id]/edges/route.js +0 -1
  275. package/.next/server/app/api/boards/[id]/edges/route.js.nft.json +0 -1
  276. package/.next/server/app/api/boards/[id]/edges/route_client-reference-manifest.js +0 -1
  277. package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js +0 -1
  278. package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js.nft.json +0 -1
  279. package/.next/server/app/api/boards/[id]/nodes/[nid]/route_client-reference-manifest.js +0 -1
  280. package/.next/server/app/api/boards/[id]/nodes/route.js +0 -1
  281. package/.next/server/app/api/boards/[id]/nodes/route.js.nft.json +0 -1
  282. package/.next/server/app/api/boards/[id]/nodes/route_client-reference-manifest.js +0 -1
  283. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js +0 -1
  284. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js.nft.json +0 -1
  285. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route_client-reference-manifest.js +0 -1
  286. package/.next/static/chunks/3308.857c554428b3294e.js +0 -73
  287. package/.next/static/chunks/333.bf0651c789b2eb33.js +0 -1
  288. package/.next/static/chunks/5315.dba7515ae6e139ae.js +0 -4
  289. package/.next/static/chunks/54a60aa6.0e43cde7298ebcb7.js +0 -78
  290. package/.next/static/chunks/7999.879d5a20e8641752.js +0 -259
  291. package/.next/static/chunks/app/_global-error/page-d6f52627eddf9119.js +0 -1
  292. package/.next/static/chunks/app/api/agent/[id]/bash-output/route-d6f52627eddf9119.js +0 -1
  293. package/.next/static/chunks/app/api/agent/[id]/events/route-d6f52627eddf9119.js +0 -1
  294. package/.next/static/chunks/app/api/agent/[id]/route-d6f52627eddf9119.js +0 -1
  295. package/.next/static/chunks/app/api/agent/new/route-d6f52627eddf9119.js +0 -1
  296. package/.next/static/chunks/app/api/agent/running/route-d6f52627eddf9119.js +0 -1
  297. package/.next/static/chunks/app/api/app-update/route-d6f52627eddf9119.js +0 -1
  298. package/.next/static/chunks/app/api/auth/all-providers/route-d6f52627eddf9119.js +0 -1
  299. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-d6f52627eddf9119.js +0 -1
  300. package/.next/static/chunks/app/api/auth/login/[provider]/route-d6f52627eddf9119.js +0 -1
  301. package/.next/static/chunks/app/api/auth/logout/[provider]/route-d6f52627eddf9119.js +0 -1
  302. package/.next/static/chunks/app/api/auth/providers/route-d6f52627eddf9119.js +0 -1
  303. package/.next/static/chunks/app/api/boards/[id]/canvas/route-d6f52627eddf9119.js +0 -1
  304. package/.next/static/chunks/app/api/boards/[id]/edges/[eid]/route-d6f52627eddf9119.js +0 -1
  305. package/.next/static/chunks/app/api/boards/[id]/edges/route-d6f52627eddf9119.js +0 -1
  306. package/.next/static/chunks/app/api/boards/[id]/nodes/[nid]/route-d6f52627eddf9119.js +0 -1
  307. package/.next/static/chunks/app/api/boards/[id]/nodes/route-d6f52627eddf9119.js +0 -1
  308. package/.next/static/chunks/app/api/boards/[id]/reconcile-task-edges/route-d6f52627eddf9119.js +0 -1
  309. package/.next/static/chunks/app/api/boards/[id]/route-d6f52627eddf9119.js +0 -1
  310. package/.next/static/chunks/app/api/boards/purge-orphans/route-d6f52627eddf9119.js +0 -1
  311. package/.next/static/chunks/app/api/boards/reorder/route-d6f52627eddf9119.js +0 -1
  312. package/.next/static/chunks/app/api/boards/route-d6f52627eddf9119.js +0 -1
  313. package/.next/static/chunks/app/api/cwd/browse/route-d6f52627eddf9119.js +0 -1
  314. package/.next/static/chunks/app/api/cwd/validate/route-d6f52627eddf9119.js +0 -1
  315. package/.next/static/chunks/app/api/default-cwd/route-d6f52627eddf9119.js +0 -1
  316. package/.next/static/chunks/app/api/file-index/route-d6f52627eddf9119.js +0 -1
  317. package/.next/static/chunks/app/api/files/[...path]/route-d6f52627eddf9119.js +0 -1
  318. package/.next/static/chunks/app/api/git/diff/route-d6f52627eddf9119.js +0 -1
  319. package/.next/static/chunks/app/api/git/status/route-d6f52627eddf9119.js +0 -1
  320. package/.next/static/chunks/app/api/home/route-d6f52627eddf9119.js +0 -1
  321. package/.next/static/chunks/app/api/mcp/route-d6f52627eddf9119.js +0 -1
  322. package/.next/static/chunks/app/api/models/route-d6f52627eddf9119.js +0 -1
  323. package/.next/static/chunks/app/api/models-config/catalog/route-d6f52627eddf9119.js +0 -1
  324. package/.next/static/chunks/app/api/models-config/discover/route-d6f52627eddf9119.js +0 -1
  325. package/.next/static/chunks/app/api/models-config/route-d6f52627eddf9119.js +0 -1
  326. package/.next/static/chunks/app/api/models-config/test/route-d6f52627eddf9119.js +0 -1
  327. package/.next/static/chunks/app/api/plugins/route-d6f52627eddf9119.js +0 -1
  328. package/.next/static/chunks/app/api/project-trust/route-d6f52627eddf9119.js +0 -1
  329. package/.next/static/chunks/app/api/quota/route-d6f52627eddf9119.js +0 -1
  330. package/.next/static/chunks/app/api/search/route-d6f52627eddf9119.js +0 -1
  331. package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-d6f52627eddf9119.js +0 -1
  332. package/.next/static/chunks/app/api/sessions/[id]/context/route-d6f52627eddf9119.js +0 -1
  333. package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-d6f52627eddf9119.js +0 -1
  334. package/.next/static/chunks/app/api/sessions/[id]/export/route-d6f52627eddf9119.js +0 -1
  335. package/.next/static/chunks/app/api/sessions/[id]/route-d6f52627eddf9119.js +0 -1
  336. package/.next/static/chunks/app/api/sessions/[id]/state/route-d6f52627eddf9119.js +0 -1
  337. package/.next/static/chunks/app/api/sessions/[id]/todos/route-d6f52627eddf9119.js +0 -1
  338. package/.next/static/chunks/app/api/sessions/route-d6f52627eddf9119.js +0 -1
  339. package/.next/static/chunks/app/api/skills/check/route-d6f52627eddf9119.js +0 -1
  340. package/.next/static/chunks/app/api/skills/install/route-d6f52627eddf9119.js +0 -1
  341. package/.next/static/chunks/app/api/skills/route-d6f52627eddf9119.js +0 -1
  342. package/.next/static/chunks/app/api/skills/search/route-d6f52627eddf9119.js +0 -1
  343. package/.next/static/chunks/app/api/skills/update/route-d6f52627eddf9119.js +0 -1
  344. package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-d6f52627eddf9119.js +0 -1
  345. package/.next/static/chunks/app/api/task-card-questions/route-d6f52627eddf9119.js +0 -1
  346. package/.next/static/chunks/app/api/task-cards/[id]/route-d6f52627eddf9119.js +0 -1
  347. package/.next/static/chunks/app/api/task-cards/route-d6f52627eddf9119.js +0 -1
  348. package/.next/static/chunks/app/api/task-scheduler/status/route-d6f52627eddf9119.js +0 -1
  349. package/.next/static/chunks/app/api/tasks/[id]/board/route-d6f52627eddf9119.js +0 -1
  350. package/.next/static/chunks/app/api/tasks/[id]/route-d6f52627eddf9119.js +0 -1
  351. package/.next/static/chunks/app/api/tasks/reorder/route-d6f52627eddf9119.js +0 -1
  352. package/.next/static/chunks/app/api/tasks/route-d6f52627eddf9119.js +0 -1
  353. package/.next/static/chunks/app/api/terminal/[id]/events/route-d6f52627eddf9119.js +0 -1
  354. package/.next/static/chunks/app/api/terminal/[id]/route-d6f52627eddf9119.js +0 -1
  355. package/.next/static/chunks/app/api/terminal/route-d6f52627eddf9119.js +0 -1
  356. package/.next/static/chunks/app/api/worktrees/route-d6f52627eddf9119.js +0 -1
  357. package/.next/static/chunks/app/manifest.webmanifest/route-d6f52627eddf9119.js +0 -1
  358. package/.next/static/chunks/app/page-2c87c41e7f848f45.js +0 -89
  359. package/.next/static/chunks/e3e7ddb2.a35db430b3564999.js +0 -1
  360. package/.next/static/chunks/next/dist/client/components/builtin/app-error-d6f52627eddf9119.js +0 -1
  361. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-d6f52627eddf9119.js +0 -1
  362. package/.next/static/chunks/next/dist/client/components/builtin/not-found-d6f52627eddf9119.js +0 -1
  363. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d6f52627eddf9119.js +0 -1
  364. package/.next/static/chunks/webpack-4bbf04891e910713.js +0 -1
  365. package/.next/static/css/80c818c5781fbadc.css +0 -4
  366. package/.next/static/css/d3a2b5077b52c727.css +0 -1
  367. package/.next/types/app/api/boards/[id]/canvas/route.ts +0 -353
  368. package/.next/types/app/api/boards/[id]/edges/[eid]/route.ts +0 -353
  369. package/.next/types/app/api/boards/[id]/edges/route.ts +0 -353
  370. package/.next/types/app/api/boards/[id]/nodes/[nid]/route.ts +0 -353
  371. package/.next/types/app/api/boards/[id]/nodes/route.ts +0 -353
  372. package/lib/board-search.test.mjs +0 -110
  373. package/lib/board-search.ts +0 -63
  374. package/lib/file-explorer-state.test.mjs +0 -63
  375. package/lib/file-explorer-state.ts +0 -36
  376. package/lib/sync-room-server.mjs +0 -137
  377. /package/.next/server/app/api/boards/[id]/{canvas → reconcile}/route.js.nft.json +0 -0
  378. /package/.next/static/{IzmOG04jJst1VpUvr_fr5 → 12RFLqhiUZG8Sj49T1KKQ}/_buildManifest.js +0 -0
  379. /package/.next/static/{IzmOG04jJst1VpUvr_fr5 → 12RFLqhiUZG8Sj49T1KKQ}/_ssgManifest.js +0 -0
@@ -0,0 +1,422 @@
1
+ // ============================================================================
2
+ // 看板派生 reconcile(后端权威)—— 替代前端 useBoardCanvas 的 reconcile
3
+ //
4
+ // 设计(方案 rev2 核心):
5
+ // - 业务表(tasks / task_cards / task_card_links / session_meta)= 唯一真相源,后端写
6
+ // - 派生元素(会话卡存在性 / exec 线 / 依赖线 / 孤儿删)= 后端读业务表 → 增量写 Y.Doc
7
+ // - 确定性 id 幂等:会话卡 `session-<sid>`、exec 线 `exec-<cardId>-<sessionId>`、
8
+ // 依赖线 `link-<fromShapeId>-<toShapeId>-<kind>` → 缺补多删,绝不整表覆盖
9
+ // - 用户内容(布局/尺寸/便笺文本)由前端写 Y.Doc,本模块只动派生元素,不碰用户布局
10
+ // - 孤儿删只删「业务表确认不存在的会话卡」,跳过新会话卡(cwd 非空 = 会话尚未创建)
11
+ //
12
+ // 触发:调度器写业务表后 / 任务归属变化 / 建卡删卡 / 定时兜底(见 board-reconcile-scheduler)
13
+ // ============================================================================
14
+ import { getBoard, listAllBoards } from "./board-store";
15
+ import { listTaskSessionIds } from "./task-store";
16
+ import { listCards, listLinks } from "./task-card-store";
17
+
18
+ // ---- yjs maps 类型 + mutateBoard(由 server.mjs 通过 globalThis 注入,避免 Next 打包 node:sqlite)----
19
+ interface BoardMaps {
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- yjs Map 泛型在服务端宽松处理(避免过度类型工程)
21
+ nodes: import("yjs").Map<any>;
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ edges: import("yjs").Map<any>;
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ view: import("yjs").Map<any>;
26
+ ydoc: import("yjs").Doc;
27
+ }
28
+
29
+ declare global {
30
+ var __yjsBoard: {
31
+ mutateBoard: (boardId: string, transaction: (maps: BoardMaps) => void) => Promise<unknown>;
32
+ destroyBoardDocument: (boardId: string) => Promise<void>;
33
+ } | undefined;
34
+ }
35
+
36
+ const mutateBoard: (boardId: string, transaction: (maps: BoardMaps) => void) => Promise<unknown> =
37
+ ((boardId: string, transaction: (maps: BoardMaps) => void) => {
38
+ const yjs = globalThis.__yjsBoard;
39
+ if (!yjs) return Promise.resolve(); // server.mjs 未加载(独立构建/测试环境)→ 跳过
40
+ return yjs.mutateBoard(boardId, transaction);
41
+ });
42
+
43
+ /** Y.Doc 节点(RF Node 形状的宽松类型) */
44
+ interface DocNode {
45
+ id: string;
46
+ type?: string;
47
+ position?: { x: number; y: number };
48
+ style?: { width?: number; height?: number };
49
+ data?: Record<string, unknown>;
50
+ }
51
+
52
+
53
+ // ---- 卡片尺寸(与前端组件对齐,作补卡摆位锚点)----
54
+ export const CARD_W = 340;
55
+ export const CARD_H = 160;
56
+ const FORM_W = 380;
57
+ const COLLAPSED_MIN_H = 240;
58
+
59
+ /** 会话卡默认尺寸(收合态) */
60
+ export const SESSION_CARD_W = CARD_W;
61
+ export const SESSION_CARD_H = CARD_H;
62
+
63
+ /** 任务卡 shape 尺寸(未建卡占位 / 已建卡) */
64
+ const TASK_CARD_W = FORM_W;
65
+ const TASK_CARD_H = COLLAPSED_MIN_H;
66
+
67
+ /** 判断 Y.Doc 中某节点是否为「新会话卡」(cwd 非空 = 会话尚未创建,跳过补卡/孤儿删) */
68
+ function isPendingNewSession(node: DocNode | undefined): boolean {
69
+ const cwd = node?.data?.cwd;
70
+ return Boolean(node && typeof cwd === "string" && cwd.length > 0);
71
+ }
72
+
73
+ /** 找空闲落点(4 列布局:每行 4 卡,逐行找空位)。与前端 findFreeSpot 同构。 */
74
+ function findFreeSpot(nodes: DocNode[], width = CARD_W, height = CARD_H) {
75
+ const STEP = 24;
76
+ const PER_ROW = 4;
77
+ const occupied = nodes
78
+ .filter((n) => n?.type === "session-card")
79
+ .map((n) => ({
80
+ x: n.position?.x ?? 0,
81
+ y: n.position?.y ?? 0,
82
+ w: n.style?.width ?? width,
83
+ h: n.style?.height ?? height,
84
+ }));
85
+ const overlaps = (x: number, y: number) =>
86
+ occupied.some((o) => x < o.x + o.w + STEP && x + width + STEP > o.x && y < o.y + o.h + STEP && y + height + STEP > o.y);
87
+ let y = 60;
88
+ let guard = 0;
89
+ while (guard < 2000) {
90
+ for (let col = 0; col < PER_ROW; col += 1) {
91
+ const x = 60 + col * (width + STEP);
92
+ if (!overlaps(x, y)) return { x, y };
93
+ }
94
+ y += height + STEP;
95
+ guard += 1;
96
+ }
97
+ return { x: 60, y: 60 };
98
+ }
99
+
100
+ /**
101
+ * 对单个看板执行派生 reconcile(幂等)。
102
+ * 读业务表 → mutate Y.Doc:补/清会话卡、补/清 exec 线、补/清依赖线。
103
+ * 仅任务看板(board.taskId 非空)有派生元素;普通看板跳过。
104
+ */
105
+ export async function reconcileBoard(boardId: string): Promise<void> {
106
+ const board = getBoard(boardId);
107
+ if (!board?.taskId) return; // 普通看板:会话卡由用户拖入/新建管理,无派生
108
+ const taskId = board.taskId;
109
+
110
+ const sessionIds = listTaskSessionIds(taskId);
111
+ const cards = listCards(boardId);
112
+ // 全部会话 id(任务会话 + 任务卡执行会话)——执行会话卡必然存在(先有会话才有关联)
113
+ const allSessionIds = new Set(sessionIds);
114
+ for (const c of cards) if (c.sessionId) allSessionIds.add(c.sessionId);
115
+
116
+ await mutateBoard(boardId, (maps) => {
117
+ const nodesMap = maps.nodes;
118
+ const edgesMap = maps.edges;
119
+ const nodes = Array.from(nodesMap.values()) as unknown as DocNode[];
120
+
121
+ // ---- 1) 会话卡:缺补、孤儿删 ----
122
+ const existingSessionBySid = new Map(); // sid -> node(转正卡)
123
+ const pendingSids = new Set(); // 新建中占位卡(cwd 非空)的 sid——视为「该 sid 已有卡,不补、不孤儿删」
124
+ for (const n of nodes) {
125
+ if (n?.type !== "session-card") continue;
126
+ const sid = n.data?.sessionId;
127
+ if (!sid) continue;
128
+ // 新建中占位卡(cwd 非空):不参与转正卡 map,也不做孤儿删;
129
+ // 但它的 sid 要进 pendingSids——窗口期(服务端已 assign、前端尚未转正清 cwd)
130
+ // 补卡循环应把该 sid 视为「已覆盖」,否则会补出第二张确定性 session-<sid> 卡。
131
+ if (isPendingNewSession(n)) {
132
+ pendingSids.add(sid);
133
+ continue;
134
+ }
135
+ existingSessionBySid.set(sid, n);
136
+ }
137
+ // 孤儿删:画布有、业务表没有的会话卡(非新会话卡)→ 删
138
+ // 例外:卡声明归属本板任务(data.taskId === 本板 taskId,拖入已存在会话时前端先落卡
139
+ // 异步归属,归属完成前本板 reconcile 不能把它当孤儿删——否则用户刚拖的卡消失)
140
+ const orphanIds = [];
141
+ for (const [sid, n] of existingSessionBySid) {
142
+ if (allSessionIds.has(sid)) continue;
143
+ if (n.data?.taskId === taskId) continue;
144
+ orphanIds.push(n.id);
145
+ }
146
+ for (const id of orphanIds) {
147
+ nodesMap.delete(id);
148
+ // 级联删以它为端点的边(exec/依赖/手绘线)
149
+ for (const e of Array.from(edgesMap.values())) {
150
+ if (e.source === id || e.target === id) edgesMap.delete(e.id);
151
+ }
152
+ }
153
+ // 缺卡补:业务表有、画布没有的会话卡 → 补(确定性 id,4 列布局落点)
154
+ // 补全 allSessionIds(任务根会话 + 任务卡执行会话)——exec 线目标会话卡必须有节点。
155
+ // 注意:不能因画布存在「新建中占位卡」(pendingNew,cwd 非空)就跳过补卡——
156
+ // 那会让任务卡执行会话卡永远不补、exec 线断(历史 bug)。补卡 id 为确定性 session-<sid>,
157
+ // 与随机 id 的占位卡不冲突、幂等。
158
+ {
159
+ const remaining = Array.from(nodesMap.values());
160
+ for (const sid of allSessionIds) {
161
+ // 已有转正卡 或 存在同 sid 新建中占位卡(窗口期视为已覆盖)→ 不补
162
+ if (existingSessionBySid.has(sid) || pendingSids.has(sid)) continue;
163
+ const id = `session-${sid}`;
164
+ const spot = findFreeSpot(remaining);
165
+ nodesMap.set(id, {
166
+ id,
167
+ type: "session-card",
168
+ position: { x: spot.x, y: spot.y },
169
+ style: { width: SESSION_CARD_W, height: SESSION_CARD_H },
170
+ data: {
171
+ sessionId: sid,
172
+ title: "",
173
+ projectName: "",
174
+ messageCount: 0,
175
+ lastReply: "",
176
+ phase: "idle",
177
+ runningMs: 0,
178
+ endedAt: 0,
179
+ lastActivityAt: 0,
180
+ stale: false,
181
+ expanded: false,
182
+ cwd: "",
183
+ taskId: "",
184
+ w: SESSION_CARD_W,
185
+ h: SESSION_CARD_H,
186
+ expandedW: 0,
187
+ expandedH: 0,
188
+ collapsedW: 0,
189
+ collapsedH: 0,
190
+ },
191
+ });
192
+ remaining.push(nodesMap.get(id));
193
+ }
194
+ }
195
+
196
+ // ---- 1.5) 任务卡节点补齐:业务表存在、画布无对应节点 → 补(exec 线锚点)----
197
+ // 任务卡由用户从工具栏拖出创建;业务表已存在的卡(外部建卡/历史数据)自动入板。
198
+ // 确定性 id(task-<cardId>)→ 幂等;孤儿任务卡节点(业务表已删)→ 删。
199
+ const existingCardByCardId = new Map(); // cardId -> node
200
+ for (const n of nodes) {
201
+ if (n?.type === "task-card" && n.data?.cardId) existingCardByCardId.set(n.data.cardId, n);
202
+ }
203
+ // 孤儿任务卡:画布有 cardId 但业务表没有 → 删节点(级联删边)
204
+ const knownCardIds = new Set(cards.map((c) => c.id));
205
+ const orphanCardIds = [];
206
+ for (const [cid, n] of existingCardByCardId) {
207
+ if (!knownCardIds.has(cid)) orphanCardIds.push(n.id);
208
+ }
209
+ for (const id of orphanCardIds) {
210
+ nodesMap.delete(id);
211
+ for (const e of Array.from(edgesMap.values())) {
212
+ if (e.source === id || e.target === id) edgesMap.delete(e.id);
213
+ }
214
+ }
215
+ // 缺卡补:业务表有、画布没有 → 补(form 尺寸,4 列布局)
216
+ {
217
+ const remaining = Array.from(nodesMap.values()) as unknown as DocNode[];
218
+ for (const card of cards) {
219
+ if (existingCardByCardId.has(card.id)) continue;
220
+ const id = `task-${card.id}`;
221
+ const spot = findFreeSpot(remaining, TASK_CARD_W, TASK_CARD_H);
222
+ nodesMap.set(id, {
223
+ id,
224
+ type: "task-card",
225
+ position: { x: spot.x, y: spot.y },
226
+ style: { width: TASK_CARD_W, height: TASK_CARD_H },
227
+ data: {
228
+ cardId: card.id,
229
+ number: card.number,
230
+ name: card.name,
231
+ description: card.description,
232
+ readyStatus: card.readyStatus,
233
+ execStatus: card.execStatus,
234
+ priority: card.priority,
235
+ due: card.due ?? undefined,
236
+ expanded: false,
237
+ w: TASK_CARD_W,
238
+ h: TASK_CARD_H,
239
+ expandedW: 0,
240
+ expandedH: 0,
241
+ collapsedW: 0,
242
+ collapsedH: 0,
243
+ },
244
+ });
245
+ remaining.push(nodesMap.get(id));
246
+ }
247
+ }
248
+
249
+ // ---- 2) exec 线:任务卡 sessionId → 卡节点 + 会话节点 → 建线 ----
250
+ // 卡 shape:cardId 映射到节点
251
+ const cardShapes = new Map(); // cardId -> node
252
+ for (const n of Array.from(nodesMap.values()) as unknown as DocNode[]) {
253
+ if (n?.type === "task-card" && n.data?.cardId) cardShapes.set(n.data.cardId, n);
254
+ }
255
+ const sessionShapes = new Map(); // sid -> node(含刚补的)
256
+ for (const n of Array.from(nodesMap.values())) {
257
+ if (n?.type === "session-card" && n.data?.sessionId) sessionShapes.set(n.data.sessionId, n);
258
+ }
259
+ // 现有 exec 线按 from 卡节点分组
260
+ const execByCard = new Map<string, Array<{ to: string | null; edgeId: string }>>(); // fromShapeId -> [{ to, edgeId }]
261
+ for (const e of Array.from(edgesMap.values())) {
262
+ if (!e.data?.execLink) continue;
263
+ const from = e.source;
264
+ const list = execByCard.get(from) ?? [];
265
+ list.push({ to: e.target, edgeId: e.id });
266
+ execByCard.set(from, list);
267
+ }
268
+ for (const card of cards) {
269
+ const cardShape = cardShapes.get(card.id);
270
+ if (!cardShape) continue;
271
+ const wanted = card.sessionId ? sessionShapes.get(card.sessionId) : null;
272
+ const existing = execByCard.get(cardShape.id) ?? [];
273
+ // 删端点不匹配的旧线(卡 sessionId 变了 / 已清空)
274
+ for (const e of existing) {
275
+ if (e.to !== (wanted?.id ?? null)) edgesMap.delete(e.edgeId);
276
+ }
277
+ if (!wanted || existing.some((e) => e.to === wanted.id)) continue;
278
+ const id = `exec-${card.id}-${card.sessionId}`;
279
+ edgesMap.set(id, {
280
+ id,
281
+ source: cardShape.id,
282
+ target: wanted.id,
283
+ type: "default",
284
+ data: { execLink: true, cardId: card.id, sessionId: card.sessionId },
285
+ markerEnd: { type: "arrowclosed" },
286
+ style: { strokeWidth: 1.5, stroke: "#3184f8", strokeDasharray: "6 4" },
287
+ });
288
+ }
289
+
290
+ // ---- 3) 依赖线:task_card_links → 建线(缺补多删)----
291
+ const existingLinks = new Map(); // "<from>-><to>:<kind>" -> edgeId
292
+ for (const e of Array.from(edgesMap.values())) {
293
+ if (!e.data?.taskLink) continue;
294
+ const from = e.source;
295
+ const to = e.target;
296
+ const kind = e.data.taskLink;
297
+ if (from && to && kind) existingLinks.set(`${from}->${to}:${kind}`, e.id);
298
+ }
299
+ const wantLinks = new Set();
300
+ for (const card of cards) {
301
+ const fromShape = cardShapes.get(card.id);
302
+ if (!fromShape) continue;
303
+ for (const link of listLinks(card.id)) {
304
+ const toShape = cardShapes.get(link.targetCardId);
305
+ if (!toShape) continue;
306
+ const key = `${fromShape.id}->${toShape.id}:${link.kind}`;
307
+ wantLinks.add(key);
308
+ if (!existingLinks.has(key)) {
309
+ const id = `link-${fromShape.id}-${toShape.id}-${link.kind}`;
310
+ edgesMap.set(id, {
311
+ id,
312
+ source: fromShape.id,
313
+ target: toShape.id,
314
+ type: "default",
315
+ data: { taskLink: link.kind },
316
+ markerEnd: { type: "arrowclosed" },
317
+ style: { strokeWidth: 1.5, stroke: "#f59e0b" },
318
+ });
319
+ }
320
+ }
321
+ }
322
+ for (const [key, edgeId] of existingLinks) {
323
+ if (!wantLinks.has(key)) edgesMap.delete(edgeId);
324
+ }
325
+ });
326
+ }
327
+
328
+ /**
329
+ * 任务下新建的会话入板(需求 2:外部/本页新建会话自动加入任务看板)。
330
+ * 调度器 assignSessionToTask 后调用:补一张会话卡到对应任务看板。
331
+ * 幂等(确定性 id);看板不存在(任务看板未创建)时静默跳过。
332
+ */
333
+ export async function ensureTaskSessionCard(boardId: string, sessionId: string): Promise<void> {
334
+ const board = getBoard(boardId);
335
+ if (!board?.taskId) return;
336
+ await mutateBoard(boardId, (maps) => {
337
+ const nodesMap = maps.nodes;
338
+ const nodes = Array.from(nodesMap.values());
339
+ const id = `session-${sessionId}`;
340
+ if (nodesMap.has(id)) return;
341
+ const spot = findFreeSpot(nodes);
342
+ nodesMap.set(id, {
343
+ id,
344
+ type: "session-card",
345
+ position: { x: spot.x, y: spot.y },
346
+ style: { width: SESSION_CARD_W, height: SESSION_CARD_H },
347
+ data: {
348
+ sessionId,
349
+ title: "",
350
+ projectName: "",
351
+ messageCount: 0,
352
+ lastReply: "",
353
+ phase: "idle",
354
+ runningMs: 0,
355
+ endedAt: 0,
356
+ lastActivityAt: 0,
357
+ stale: false,
358
+ expanded: false,
359
+ cwd: "",
360
+ taskId: "",
361
+ w: SESSION_CARD_W,
362
+ h: SESSION_CARD_H,
363
+ expandedW: 0,
364
+ expandedH: 0,
365
+ collapsedW: 0,
366
+ collapsedH: 0,
367
+ },
368
+ });
369
+ });
370
+ }
371
+
372
+ /**
373
+ * 从所有 RF 看板的 yjs 文档中删除指定会话的会话卡(含占位卡)并级联删边。
374
+ *
375
+ * 背景:会话删除(单删 / 删任务整树)只走 removeSessionFromBoards 清 tldraw 遗留的
376
+ * board_nodes 废弃表,对 RF 画布(yjs Y.Doc)无效——任务看板由 10s reconcile 兜底删,
377
+ * 普通看板 reconcileBoard 直接 return,会话卡永久残留(幽灵卡)。
378
+ *
379
+ * 定位:会话卡落在哪块看板没有反向索引,只能枚举全部看板逐个扫(低频操作可接受;
380
+ * 维护反向索引属过度设计)。幂等:板上没有匹配卡时无变更。
381
+ */
382
+ export async function removeSessionsFromYjsBoards(sessionIds: string[]): Promise<void> {
383
+ if (sessionIds.length === 0) return;
384
+ const yjs = globalThis.__yjsBoard;
385
+ if (!yjs) return; // server.mjs 未加载(独立构建/测试环境)→ 跳过
386
+ const targets = new Set(sessionIds);
387
+ for (const board of listAllBoards()) {
388
+ try {
389
+ await yjs.mutateBoard(board.id, (maps) => {
390
+ const nodesMap = maps.nodes;
391
+ const edgesMap = maps.edges;
392
+ const hitIds: string[] = [];
393
+ for (const n of Array.from(nodesMap.values()) as unknown as DocNode[]) {
394
+ if (n?.type === "session-card" && n.data?.sessionId && targets.has(String(n.data.sessionId))) {
395
+ hitIds.push(n.id);
396
+ }
397
+ }
398
+ if (hitIds.length === 0) return;
399
+ for (const id of hitIds) {
400
+ nodesMap.delete(id);
401
+ for (const e of Array.from(edgesMap.values())) {
402
+ if (e.source === id || e.target === id) edgesMap.delete(e.id);
403
+ }
404
+ }
405
+ });
406
+ } catch (e) {
407
+ // 单板清理失败不中断其余板(也绝不拖垮会话删除主流程)
408
+ console.warn(`[board] 会话删除清理 yjs 板 ${board.id} 异常:`, e instanceof Error ? e.message : e);
409
+ }
410
+ }
411
+ }
412
+
413
+ /**
414
+ * 删除看板的 yjs 文档(业务行删完后调用)。
415
+ * deleteBoard/deleteTask 只清 boards/board_nodes 等业务表,yjs_documents 行残留会导致
416
+ * 看板 id 复用时旧文档复活;此处同时断开该看板的活动连接。
417
+ */
418
+ export async function destroyBoardYjsDocument(boardId: string): Promise<void> {
419
+ const yjs = globalThis.__yjsBoard;
420
+ if (!yjs) return;
421
+ await yjs.destroyBoardDocument(boardId);
422
+ }
@@ -8,6 +8,7 @@ const { setDbForTesting, SCHEMA_VERSION } = await jiti.import("./sqlite-db.ts");
8
8
  const {
9
9
  getBoard,
10
10
  listBoards,
11
+ listAllBoards,
11
12
  createBoard,
12
13
  renameBoard,
13
14
  deleteBoard,
@@ -25,6 +26,7 @@ const {
25
26
  deleteEdge,
26
27
  getSystemRunningBoard,
27
28
  reorderBoards,
29
+ reorderAllBoards,
28
30
  syncCardEdges,
29
31
  syncExecEdge,
30
32
  removeSessionFromBoards,
@@ -56,9 +58,9 @@ test("schema version reaches latest with board tables", () => {
56
58
  .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'boards'")
57
59
  .all();
58
60
  assert.equal(boards.length, 1);
59
- assert.equal(SCHEMA_VERSION, 8);
61
+ assert.equal(SCHEMA_VERSION, 9);
60
62
  const row = db.prepare("PRAGMA user_version").get();
61
- assert.equal(row.user_version, 8);
63
+ assert.equal(row.user_version, 9);
62
64
  // v5 迁移:boards.task_id 列存在
63
65
  const cols = db.prepare("PRAGMA table_info(boards)").all().map((c) => c.name);
64
66
  assert.ok(cols.includes("task_id"));
@@ -87,11 +89,31 @@ test("board CRUD: create / rename / list / delete", () => {
87
89
  assert.equal(listBoards(PROJECT).length, 0);
88
90
  });
89
91
 
90
- test("empty projectKey / name rejected", () => {
91
- assert.throws(() => createBoard("", "x"), /projectKey is required/);
92
+ test("empty projectKey creates global board / empty name rejected", () => {
93
+ // projectKey 现为全局共享看板(跨项目聚合,与系统看板同层语义)
94
+ const g = createBoard("", "全局板");
95
+ assert.equal(g.projectKey, "");
96
+ assert.equal(g.isSystem, false);
97
+ assert.equal(g.taskId, null);
92
98
  assert.throws(() => createBoard(PROJECT, " "), /name must not be empty/);
93
99
  });
94
100
 
101
+ test("global boards: listAllBoards aggregates across projects + reorderAllBoards", () => {
102
+ const pa = createBoard("proj-a", "A");
103
+ const pb = createBoard("proj-b", "B");
104
+ const g = createBoard("", "G");
105
+ // 全局列表含所有项目 + 全局看板(新板置顶:G 取全局最小 -1 最前,A/B 同项目内 sort 0 按 created)
106
+ assert.deepEqual(listAllBoards().map((x) => x.id), [g.id, pa.id, pb.id]);
107
+
108
+ // 全局排序跨项目
109
+ const reordered = reorderAllBoards([pa.id, g.id, pb.id]);
110
+ assert.deepEqual(reordered.map((x) => x.id), [pa.id, g.id, pb.id]);
111
+
112
+ // 外来 id(含任务型/系统)→ 回滚保持原序
113
+ assert.throws(() => reorderAllBoards([pa.id, "foreign"]));
114
+ assert.deepEqual(listAllBoards().map((x) => x.id), [pa.id, g.id, pb.id]);
115
+ });
116
+
95
117
  test("system running board: always present, not deletable/renamable", () => {
96
118
  const sys = getSystemRunningBoard();
97
119
  assert.equal(sys.id, SYSTEM_RUNNING_BOARD_ID);
@@ -182,6 +182,19 @@ export function listBoards(projectKey: string): BoardInfo[] {
182
182
  return rows.map((row) => rowToBoard(row, countNodes(row.id)));
183
183
  }
184
184
 
185
+ /**
186
+ * 全部看板(不含系统看板,系统看板由调用方前置插入)。全局共享视图:
187
+ * 不看 project_key,所有手动看板 + 任务型看板一起返回(展示层自行过滤 taskId)。
188
+ */
189
+ export function listAllBoards(): BoardInfo[] {
190
+ const rows = getDb()
191
+ .prepare(
192
+ "SELECT id, project_key AS projectKey, name, is_system AS isSystem, task_id AS taskId, sort_order AS sortOrder, created, updated FROM boards WHERE is_system = 0 ORDER BY sort_order, created, rowid",
193
+ )
194
+ .all() as unknown as BoardRow[];
195
+ return rows.map((row) => rowToBoard(row, countNodes(row.id)));
196
+ }
197
+
185
198
  /**
186
199
  * 项目内看板批量排序(完整有序 id 列表)。id 必须都属于该项目,否则事务回滚。
187
200
  */
@@ -205,17 +218,42 @@ export function reorderBoards(projectKey: string, orderedIds: string[]): BoardIn
205
218
  return listBoards(projectKey);
206
219
  }
207
220
 
208
- /** 创建看板。空 projectKey / 空名抛错。taskId 可选:非空时看板为任务型(id = taskId)。 */
221
+ /**
222
+ * 全局看板批量排序(完整有序 id 列表,手动看板范围:非系统、非任务型)。
223
+ * id 必须都存在且属于手动看板,否则事务回滚。
224
+ */
225
+ export function reorderAllBoards(orderedIds: string[]): BoardInfo[] {
226
+ const db = getDb();
227
+ if (orderedIds.length === 0) return listAllBoards();
228
+ db.exec("BEGIN");
229
+ try {
230
+ const stmt = db.prepare("UPDATE boards SET sort_order = ?, updated = ? WHERE id = ? AND is_system = 0 AND task_id IS NULL");
231
+ orderedIds.forEach((id, index) => {
232
+ const res = stmt.run(index, now(), id);
233
+ if (res.changes === 0) {
234
+ throw new Error(`board ${id} does not belong to global manual scope`);
235
+ }
236
+ });
237
+ db.exec("COMMIT");
238
+ } catch (error) {
239
+ db.exec("ROLLBACK");
240
+ throw error;
241
+ }
242
+ return listAllBoards();
243
+ }
244
+
245
+ /** 创建看板。空 projectKey 表示全局共享看板(跨项目聚合,与系统看板同层)。
246
+ * 空名抛错。taskId 可选:非空时看板为任务型(id = taskId)。 */
209
247
  export function createBoard(projectKey: string, name: string, taskId?: string): BoardInfo {
210
248
  const trimmed = name.trim();
211
- if (!projectKey) throw new Error("projectKey is required");
212
249
  if (!trimmed) throw new Error("name must not be empty");
213
250
  const id = taskId ?? randomUUID();
214
251
  const ts = now();
215
- // 新看板置顶:sort_order 取当前项目最小值 - 1
216
- const minRow = getDb()
217
- .prepare("SELECT MIN(sort_order) AS minOrder FROM boards WHERE project_key = ?")
218
- .get(projectKey) as { minOrder: number | null };
252
+ const global = !projectKey;
253
+ // 新看板置顶:全局看板取全部手动看板最小 - 1;项目看板取项目内最小 - 1
254
+ const minRow = global
255
+ ? (getDb().prepare("SELECT MIN(sort_order) AS minOrder FROM boards WHERE is_system = 0 AND task_id IS NULL").get() as { minOrder: number | null })
256
+ : (getDb().prepare("SELECT MIN(sort_order) AS minOrder FROM boards WHERE project_key = ?").get(projectKey) as { minOrder: number | null });
219
257
  const sortOrder = minRow.minOrder === null ? 0 : minRow.minOrder - 1;
220
258
  getDb()
221
259
  .prepare("INSERT INTO boards (id, project_key, name, is_system, task_id, sort_order, created, updated) VALUES (?, ?, ?, 0, ?, ?, ?, ?)")
@@ -162,7 +162,7 @@ export const enLocale: LocalePlugin = {
162
162
  "boards.system": "System",
163
163
  "boards.title": "Boards",
164
164
  "boards.dragToAdd": "Drag sessions from the sidebar to add",
165
- "boards.dropToAdd": "Drop to add session",
165
+ "boards.dropToAdd": "Drop to add to board",
166
166
  "boards.loadingSession": "Loading session…",
167
167
  "boards.sessionMissing": "Session no longer exists",
168
168
  "boards.collapse": "Collapse",
@@ -163,7 +163,7 @@ export const zhCNLocale: LocalePlugin = {
163
163
  "boards.system": "系统",
164
164
  "boards.title": "看板",
165
165
  "boards.dragToAdd": "从侧栏拖入会话以添加",
166
- "boards.dropToAdd": "松手添加会话",
166
+ "boards.dropToAdd": "松手添加到看板",
167
167
  "boards.loadingSession": "正在加载会话…",
168
168
  "boards.sessionMissing": "会话已不存在",
169
169
  "boards.collapse": "收合",
@@ -15,6 +15,7 @@ import { sessionPathKey } from "./session-path";
15
15
  import { getRpcSession } from "./rpc-manager";
16
16
  import { unassignSession } from "./task-store";
17
17
  import { removeSessionFromBoards } from "./board-store";
18
+ import { removeSessionsFromYjsBoards } from "./board-reconcile";
18
19
 
19
20
  /** 递归收集一个会话的全部 fork 后代 id(含自身)。 */
20
21
  export async function collectSessionDescendants(rootId: string): Promise<string[]> {
@@ -82,5 +83,9 @@ export async function deleteSessionTrees(rootIds: string[]): Promise<string[]> {
82
83
  } catch { /* keep deleting the rest */ }
83
84
  }
84
85
  }
86
+ // RF 画布(yjs)清理:从所有普通看板移除这些会话的卡(含占位卡)。
87
+ // 任务看板由删除方(deleteTask)整体销毁文档,这里一并处理也无害;
88
+ // 无 __yjsBoard(测试/独立构建)时为空操作。
89
+ await removeSessionsFromYjsBoards(deleted);
85
90
  return deleted;
86
91
  }
package/lib/sqlite-db.ts CHANGED
@@ -88,7 +88,7 @@ export function initSchema(db: DatabaseSync): void {
88
88
  * 老库打开时自动按序补齐缺失的迁移(每个迁移一个事务,成功后推进版本号),
89
89
  * 新库建表后从 v0 一路迁到 SCHEMA_VERSION。重复打开不再执行已完成的迁移。
90
90
  */
91
- export const SCHEMA_VERSION = 8;
91
+ export const SCHEMA_VERSION = 9;
92
92
 
93
93
  interface Migration {
94
94
  version: number;
@@ -175,6 +175,18 @@ const MIGRATIONS: Migration[] = [
175
175
  "ALTER TABLE task_cards ADD COLUMN dispatch_token TEXT NULL;",
176
176
  ],
177
177
  },
178
+ {
179
+ version: 9,
180
+ name: "task_cards.owner + heartbeat (状态判定实例归属)",
181
+ statements: [
182
+ // 多实例共库时,「会话是否在跑」是本进程私有(getRunningRpcSessionIds),
183
+ // 非派发实例会误把别人在跑的卡翻成 review(#27)。给 running 卡标 owner
184
+ // + heartbeat,非 owner 且 owner 心跳新鲜 → 跳过,只有 owner(或 owner
185
+ // 心跳过期被接管)才判定状态,使「谁判定某张卡」确定。
186
+ "ALTER TABLE task_cards ADD COLUMN owner TEXT NULL;",
187
+ "ALTER TABLE task_cards ADD COLUMN heartbeat INTEGER NOT NULL DEFAULT 0;",
188
+ ],
189
+ },
178
190
  ];
179
191
 
180
192
  function migrate(db: DatabaseSync): void {