@allbluecn/web-app 0.22.4 → 0.23.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 (500) hide show
  1. package/package.json +4 -14
  2. package/src/__tests__/integration/login-email-acceptance.test.ts +16 -0
  3. package/src/__tests__/story-view.test.ts +33 -1
  4. package/src/components/agents/__tests__/agent-bindings-tab.test.tsx +221 -0
  5. package/src/components/agents/__tests__/agents-page.test.tsx +284 -0
  6. package/src/components/agents/agent-bindings-tab.tsx +59 -45
  7. package/src/components/agents/agent-form-dialog.tsx +3 -3
  8. package/src/components/agents/agent-selector.tsx +2 -3
  9. package/src/components/agents/agents-page.tsx +66 -31
  10. package/src/components/agents/slash-skill-menu.tsx +2 -3
  11. package/src/components/auth/__tests__/session-guard.test.tsx +74 -0
  12. package/src/components/auth/session-guard.tsx +8 -0
  13. package/src/components/billing/login-prompt-dialog.tsx +1 -1
  14. package/src/components/billing/subscription-provider.tsx +2 -16
  15. package/src/components/bug/__tests__/peek-modes.test.tsx +8 -0
  16. package/src/components/bug/__tests__/subgroup-header.test.tsx +202 -40
  17. package/src/components/bug/peek-panel.tsx +5 -3
  18. package/src/components/bug/views/bug-list/group.tsx +12 -2
  19. package/src/components/bug/views/bug-list/index.tsx +94 -67
  20. package/src/components/command/command-palette.tsx +10 -9
  21. package/src/components/design/canvas/document/frame-custom-presets.ts +9 -1
  22. package/src/components/design/canvas/panels/toolbar.tsx +21 -22
  23. package/src/components/design/canvas/react/__tests__/canvas-context-load-echo.test.tsx +199 -0
  24. package/src/components/design/canvas/react/__tests__/hooks.test.tsx +17 -3
  25. package/src/components/design/canvas/react/canvas-context.tsx +47 -26
  26. package/src/components/design/canvas/react/hooks.ts +39 -34
  27. package/src/components/design/editor/layout/__tests__/collab-param-wiring.test.tsx +17 -0
  28. package/src/components/design/editor/layout/__tests__/context-menu.test.tsx +17 -1
  29. package/src/components/design/editor/layout/__tests__/no-page-save-toast.test.tsx +17 -0
  30. package/src/components/design/editor/layout/__tests__/persistence-restore.test.tsx +17 -1
  31. package/src/components/design/editor/layout/__tests__/ssr-localstorage-smoke.test.tsx +17 -1
  32. package/src/components/design/editor/layout/design-editor-layout.tsx +13 -2
  33. package/src/components/design/editor/layout/hooks/use-design-auto-save.ts +3 -19
  34. package/src/components/devtools/devtools-panel.tsx +56 -0
  35. package/src/components/forms/__tests__/create-app-form.test.tsx +131 -0
  36. package/src/components/forms/app-field-error.tsx +21 -1
  37. package/src/components/forms/app-field.tsx +8 -8
  38. package/src/components/forms/app-submit.tsx +12 -9
  39. package/src/components/forms/create-app-form.tsx +124 -0
  40. package/src/components/forms/index.ts +2 -3
  41. package/src/components/harness/__tests__/gallery-behavior.test.tsx +37 -1
  42. package/src/components/harness/__tests__/harness-workbench-first-send.test.tsx +4 -1
  43. package/src/components/harness/__tests__/message-actions.test.tsx +7 -6
  44. package/src/components/harness/__tests__/use-video-generation.test.tsx +165 -0
  45. package/src/components/harness/ai-chat-history/index.tsx +5 -3
  46. package/src/components/harness/gallery/gallery-card.tsx +57 -35
  47. package/src/components/harness/gallery/gallery-grid.tsx +32 -15
  48. package/src/components/harness/gallery/gallery-types.ts +19 -0
  49. package/src/components/harness/gallery/gallery-video-card.tsx +54 -0
  50. package/src/components/harness/gallery/types.ts +2 -0
  51. package/src/components/harness/gallery/use-video-generation.ts +95 -0
  52. package/src/components/harness/harness-workbench-page.tsx +2 -3
  53. package/src/components/harness/message-actions.tsx +5 -3
  54. package/src/components/hotkeys/shortcuts-panel.tsx +28 -13
  55. package/src/components/iteration/__tests__/burndown-chart.test.tsx +118 -0
  56. package/src/components/iteration/burndown-chart.tsx +54 -23
  57. package/src/components/knowledge/editor/__tests__/knowledge-doc-editor-collab.test.tsx +96 -0
  58. package/src/components/knowledge/editor/knowledge-doc-editor.tsx +56 -1
  59. package/src/components/knowledge/editor/mindmap-doc-editor.tsx +12 -0
  60. package/src/components/knowledge/editor/use-auto-save-channels.ts +3 -1
  61. package/src/components/layout/sidebar-03/__tests__/nav-workspace.test.tsx +0 -4
  62. package/src/components/layout/sidebar-03/nav-knowledge.tsx +11 -11
  63. package/src/components/layout/sidebar-03/nav-main.tsx +12 -15
  64. package/src/components/layout/sidebar-03/nav-notifications.tsx +5 -8
  65. package/src/components/layout/sidebar-03/nav-workspace.tsx +11 -9
  66. package/src/components/notifications/__tests__/settings-notifications.test.tsx +158 -0
  67. package/src/components/notifications/settings-notifications.tsx +37 -13
  68. package/src/components/notifications/team-notifications.tsx +10 -14
  69. package/src/components/settings/personal-team/account/__tests__/account-client.test.tsx +170 -0
  70. package/src/components/settings/personal-team/account/account-client.tsx +44 -7
  71. package/src/components/settings/personal-team/team/team-client.tsx +17 -19
  72. package/src/components/shared-kit/list-kit/__tests__/frozen-header.test.ts +210 -41
  73. package/src/components/shared-kit/list-kit/frozen-header.ts +134 -21
  74. package/src/components/shared-kit/list-kit/kanban/frozen-header.ts +20 -27
  75. package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +8 -0
  76. package/src/components/story/__tests__/story-list-table.test.tsx +214 -0
  77. package/src/components/story/columns.tsx +11 -2
  78. package/src/components/story/peek-panel.tsx +4 -2
  79. package/src/components/story/story-list-table.tsx +16 -1
  80. package/src/components/story/views/story-list/__tests__/frozen-headers.test.tsx +186 -0
  81. package/src/components/story/views/story-list/group.tsx +12 -1
  82. package/src/components/story/views/story-list/index.tsx +101 -68
  83. package/src/components/testcase/__tests__/case-row.test.tsx +134 -2
  84. package/src/components/testcase/__tests__/peek-panel.test.tsx +8 -0
  85. package/src/components/testcase/__tests__/step-editor.test.tsx +106 -0
  86. package/src/components/testcase/list/grouped-list.tsx +50 -22
  87. package/src/components/testcase/peek-panel.tsx +7 -4
  88. package/src/components/testcase/step-editor.tsx +196 -150
  89. package/src/components/tiptap/notion-like/editor-extensions.ts +0 -3
  90. package/src/components/tiptap/notion-like/notion-like-editor-header.tsx +1 -31
  91. package/src/components/tiptap/notion-like/notion-like-editor.tsx +32 -1
  92. package/src/components/workspace/__tests__/dialogs/create-workspace-dialog.test.tsx +2 -28
  93. package/src/components/workspace/__tests__/dialogs/edit-workspace-dialog.test.tsx +2 -28
  94. package/src/components/workspace/dialogs/create-workspace-dialog.tsx +45 -43
  95. package/src/components/workspace/dialogs/edit-workspace-dialog.tsx +38 -36
  96. package/src/design-engine/engine/engine-decor.ts +2 -0
  97. package/src/design-engine/engine/engine-path.ts +2 -0
  98. package/src/hooks/__tests__/use-unsaved-guard.test.tsx +180 -0
  99. package/src/hooks/use-notifications.ts +9 -16
  100. package/src/hooks/use-unsaved-guard.tsx +87 -0
  101. package/src/lib/__tests__/pacer.test.ts +33 -96
  102. package/src/lib/__tests__/query-defaults.test.ts +169 -0
  103. package/src/lib/__tests__/query-global-error.test.ts +256 -0
  104. package/src/lib/anyhub/anyhub-store.ts +2 -2
  105. package/src/lib/changelog/sdk-versions.ts +2 -10
  106. package/src/lib/collab/presence-store.ts +5 -5
  107. package/src/lib/collab/use-presence-list-refresh.ts +2 -2
  108. package/src/lib/collab/use-share-presence.ts +2 -2
  109. package/src/lib/collections/query-keys.ts +7 -2
  110. package/src/lib/commands/__tests__/command-palette-state.test.ts +12 -14
  111. package/src/lib/commands/__tests__/registry.test.ts +6 -3
  112. package/src/lib/commands/command-palette-state.ts +8 -31
  113. package/src/lib/hotkeys/__tests__/registry.test.ts +71 -18
  114. package/src/lib/hotkeys/registry.ts +188 -85
  115. package/src/lib/hotkeys/types.ts +1 -13
  116. package/src/lib/pacer.ts +1 -79
  117. package/src/lib/queries/README.md +67 -0
  118. package/src/lib/queries/__tests__/agents-queries.test.ts +101 -0
  119. package/src/lib/queries/__tests__/compliance-queries.test.ts +136 -0
  120. package/src/lib/queries/__tests__/notifications-queries.test.ts +175 -0
  121. package/src/lib/queries/__tests__/team-queries.test.ts +87 -0
  122. package/src/lib/queries/agents.ts +40 -0
  123. package/src/lib/queries/compliance.ts +69 -0
  124. package/src/lib/queries/notifications.ts +63 -0
  125. package/src/lib/queries/team.ts +39 -0
  126. package/src/lib/query-defaults.ts +8 -0
  127. package/src/lib/query-global-error.ts +128 -0
  128. package/src/lib/remote-auth-client.ts +16 -0
  129. package/src/paraglide/README.md +1 -1
  130. package/src/paraglide/messages/_index.d.ts +11 -0
  131. package/src/paraglide/messages/_index.js +5659 -88285
  132. package/src/paraglide/messages/common_error_generic.d.ts +16 -0
  133. package/src/paraglide/messages/common_error_generic.js +29 -0
  134. package/src/paraglide/messages/common_unsaved_body.d.ts +16 -0
  135. package/src/paraglide/messages/common_unsaved_body.js +29 -0
  136. package/src/paraglide/messages/common_unsaved_leave.d.ts +16 -0
  137. package/src/paraglide/messages/common_unsaved_leave.js +29 -0
  138. package/src/paraglide/messages/common_unsaved_stay.d.ts +16 -0
  139. package/src/paraglide/messages/common_unsaved_stay.js +29 -0
  140. package/src/paraglide/messages/common_unsaved_title.d.ts +16 -0
  141. package/src/paraglide/messages/common_unsaved_title.js +29 -0
  142. package/src/paraglide/messages/gallery_videofailed1.d.ts +17 -0
  143. package/src/paraglide/messages/gallery_videofailed1.js +30 -0
  144. package/src/paraglide/messages/gallery_videogenerating1.d.ts +17 -0
  145. package/src/paraglide/messages/gallery_videogenerating1.js +30 -0
  146. package/src/paraglide/messages/gallery_videomissingprompt2.d.ts +17 -0
  147. package/src/paraglide/messages/gallery_videomissingprompt2.js +30 -0
  148. package/src/paraglide/messages/gallery_videoprogress1.d.ts +19 -0
  149. package/src/paraglide/messages/gallery_videoprogress1.js +30 -0
  150. package/src/paraglide/messages/gallery_videostop1.d.ts +17 -0
  151. package/src/paraglide/messages/gallery_videostop1.js +30 -0
  152. package/src/paraglide/messages/story_table_resizecolumn1.d.ts +17 -0
  153. package/src/paraglide/messages/story_table_resizecolumn1.js +30 -0
  154. package/src/providers/index.tsx +5 -50
  155. package/src/routeTree.gen.ts +21 -0
  156. package/src/router.tsx +21 -8
  157. package/src/routes/__root.tsx +2 -1
  158. package/src/routes/__tests__/-edition-routing.test.tsx +6 -1
  159. package/src/routes/_login/forgot-password/route.lazy.tsx +7 -6
  160. package/src/routes/_login/login/route.lazy.tsx +10 -9
  161. package/src/routes/_login/register/route.lazy.tsx +19 -18
  162. package/src/routes/_login/reset-password/route.lazy.tsx +10 -9
  163. package/src/routes/_nav/knowledge/compliance/$id.lazy.tsx +9 -12
  164. package/src/routes/_nav/knowledge/compliance/index.lazy.tsx +16 -13
  165. package/src/routes/_nav/knowledge/laws/$id.lazy.tsx +1 -1
  166. package/src/routes/_nav/notifications/route.tsx +6 -16
  167. package/src/routes/_nav/settings/-settings-tabs.tsx +9 -12
  168. package/src/routes/api/generate/__tests__/media.test.ts +378 -0
  169. package/src/routes/api/generate/media.ts +179 -0
  170. package/src/server/__tests__/agent-client-graph.test.ts +0 -1
  171. package/src/server/__tests__/agent-crud.test.ts +37 -4
  172. package/src/server/__tests__/bug-groups-two-level.test.ts +34 -1
  173. package/src/server/__tests__/chat-conversations.test.ts +37 -4
  174. package/src/server/__tests__/chat-list-messages.test.ts +40 -7
  175. package/src/server/__tests__/csv-export.test.ts +36 -3
  176. package/src/server/__tests__/desktop-bootstrap.test.ts +189 -0
  177. package/src/server/__tests__/remote-auth.test.ts +72 -0
  178. package/src/server/__tests__/skill-crud.test.ts +37 -4
  179. package/src/server/__tests__/sso-domain.test.ts +1 -1
  180. package/src/server/__tests__/sso-rate-limit-singleton.test.ts +123 -0
  181. package/src/server/__tests__/task-serverfn-crud.test.ts +35 -2
  182. package/src/server/__tests__/task-serverfn-queries.test.ts +35 -2
  183. package/src/server/__tests__/testcase-board.test.ts +38 -5
  184. package/src/server/__tests__/testcase-bulk.test.ts +38 -5
  185. package/src/server/__tests__/testcase-list-scope.test.ts +38 -5
  186. package/src/server/ai-gateway/__tests__/ai-output-guard.test.ts +126 -2
  187. package/src/server/ai-gateway/__tests__/chat-stream-builder-code-mode.test.ts +4 -0
  188. package/src/server/ai-gateway/__tests__/chat-stream-builder-memory.test.ts +4 -0
  189. package/src/server/ai-gateway/__tests__/chat-stream-builder-sandbox.test.ts +4 -0
  190. package/src/server/ai-gateway/__tests__/chat-stream-builder-skills.test.ts +4 -0
  191. package/src/server/ai-gateway/__tests__/pgvector-memory-adapter.test.ts +30 -0
  192. package/src/server/ai-gateway/__tests__/pgvector-memory-conformance.test.ts +92 -0
  193. package/src/server/ai-gateway/__tests__/source-mode.test.ts +49 -8
  194. package/src/server/ai-gateway/ai-output-guard.ts +50 -2
  195. package/src/server/ai-gateway/chat-stream-builder.ts +3 -0
  196. package/src/server/ai-gateway/media/index.ts +36 -29
  197. package/src/server/ai-gateway/media/trim-input.ts +52 -0
  198. package/src/server/ai-gateway/memory/pgvector-adapter.ts +27 -13
  199. package/src/server/ai-gateway/sandbox/__tests__/harness-sandbox.test.ts +252 -41
  200. package/src/server/ai-gateway/sandbox/harness-sandbox.ts +6 -67
  201. package/src/server/ai-gateway/source-mode.ts +24 -8
  202. package/src/server/desktop-bootstrap.ts +74 -0
  203. package/src/server/lib/__tests__/rate-limit.test.ts +0 -1
  204. package/src/server/lib/rate-limit.ts +0 -2
  205. package/src/server/middlewares/__tests__/auth-context.test.ts +156 -0
  206. package/src/server/middlewares/auth-context.ts +62 -0
  207. package/src/server/remote-auth.ts +58 -31
  208. package/src/server/serverFns/__tests__/ai-hosted-models.test.ts +47 -15
  209. package/src/server/serverFns/__tests__/ai-source-mode.test.ts +45 -15
  210. package/src/server/serverFns/__tests__/billing.test.ts +37 -2
  211. package/src/server/serverFns/__tests__/collab-snapshot.test.ts +41 -14
  212. package/src/server/serverFns/__tests__/collab.test.ts +41 -14
  213. package/src/server/serverFns/__tests__/compliance.test.ts +33 -1
  214. package/src/server/serverFns/__tests__/email-verification.test.ts +35 -1
  215. package/src/server/serverFns/__tests__/knowledge-breadcrumb.test.ts +46 -6
  216. package/src/server/serverFns/__tests__/knowledge-delete.test.ts +42 -14
  217. package/src/server/serverFns/__tests__/knowledge-kb-projects.test.ts +46 -6
  218. package/src/server/serverFns/__tests__/knowledge-public-compliance.test.ts +36 -2
  219. package/src/server/serverFns/__tests__/knowledge-readable-kb.test.ts +42 -10
  220. package/src/server/serverFns/__tests__/knowledge-search.test.ts +45 -9
  221. package/src/server/serverFns/__tests__/legal-code-canonical.test.ts +36 -2
  222. package/src/server/serverFns/__tests__/link-preview.test.ts +38 -4
  223. package/src/server/serverFns/__tests__/notifications-list.test.ts +36 -3
  224. package/src/server/serverFns/__tests__/ownership-merge.test.ts +38 -4
  225. package/src/server/serverFns/__tests__/project-delete-transaction.test.ts +39 -2
  226. package/src/server/serverFns/__tests__/project-kb.test.ts +34 -1
  227. package/src/server/serverFns/__tests__/project-member-idor.test.ts +34 -1
  228. package/src/server/serverFns/__tests__/project-team-assign.test.ts +34 -1
  229. package/src/server/serverFns/__tests__/read-write-merge.test.ts +38 -4
  230. package/src/server/serverFns/__tests__/story-comments.test.ts +38 -4
  231. package/src/server/serverFns/__tests__/story-crud-archive-detail.test.ts +38 -4
  232. package/src/server/serverFns/__tests__/story-crud-labels-users.test.ts +37 -3
  233. package/src/server/serverFns/__tests__/story-crud.test.ts +36 -3
  234. package/src/server/serverFns/__tests__/story-links.test.ts +38 -4
  235. package/src/server/serverFns/__tests__/story-reactions.test.ts +38 -4
  236. package/src/server/serverFns/__tests__/story-tree.test.ts +38 -4
  237. package/src/server/serverFns/__tests__/team-accept.test.ts +34 -1
  238. package/src/server/serverFns/__tests__/team-invitation.test.ts +34 -1
  239. package/src/server/serverFns/__tests__/team-join-request.test.ts +34 -1
  240. package/src/server/serverFns/__tests__/team-members.test.ts +34 -1
  241. package/src/server/serverFns/__tests__/team-ownership-merge.test.ts +34 -1
  242. package/src/server/serverFns/__tests__/update-bug-guard.test.ts +37 -3
  243. package/src/server/serverFns/__tests__/update-document-guard.test.ts +38 -4
  244. package/src/server/serverFns/__tests__/update-page-guard.test.ts +40 -4
  245. package/src/server/serverFns/__tests__/update-seats.test.ts +33 -1
  246. package/src/server/serverFns/__tests__/update-story-guard.test.ts +36 -3
  247. package/src/server/serverFns/__tests__/update-testcase-guard.test.ts +37 -3
  248. package/src/server/serverFns/__tests__/workspace-kb-idor.test.ts +36 -2
  249. package/src/server/serverFns/__tests__/workspace-kb.test.ts +36 -2
  250. package/src/server/serverFns/__tests__/workspace.test.ts +34 -1
  251. package/src/server/serverFns/activity.ts +10 -14
  252. package/src/server/serverFns/agents/agent-crud.ts +16 -15
  253. package/src/server/serverFns/agents/bindings.ts +12 -13
  254. package/src/server/serverFns/agents/skill-crud.ts +16 -15
  255. package/src/server/serverFns/ai/code-mode-readiness.ts +3 -4
  256. package/src/server/serverFns/ai/search-capability.ts +3 -4
  257. package/src/server/serverFns/anyhub/app.ts +10 -14
  258. package/src/server/serverFns/anyhub/index.ts +7 -10
  259. package/src/server/serverFns/anyhub/install.ts +4 -4
  260. package/src/server/serverFns/anyhub/permissions.ts +10 -14
  261. package/src/server/serverFns/anyhub/proxy.ts +4 -6
  262. package/src/server/serverFns/auth/email-verification.ts +4 -5
  263. package/src/server/serverFns/auth/session.ts +9 -0
  264. package/src/server/serverFns/billing.ts +18 -28
  265. package/src/server/serverFns/blob-upload.ts +6 -9
  266. package/src/server/serverFns/bug/__tests__/bug-access-tokens.test.ts +34 -1
  267. package/src/server/serverFns/bug/__tests__/bug-activities.test.ts +34 -1
  268. package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +34 -1
  269. package/src/server/serverFns/bug/__tests__/bug-crud.test.ts +34 -1
  270. package/src/server/serverFns/bug/__tests__/bug-fix.test.ts +34 -1
  271. package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +34 -1
  272. package/src/server/serverFns/bug/__tests__/bug-reactions.test.ts +34 -1
  273. package/src/server/serverFns/bug/__tests__/bug-reorder.test.ts +39 -5
  274. package/src/server/serverFns/bug/__tests__/bug-repro.test.ts +34 -1
  275. package/src/server/serverFns/bug/__tests__/bug-story.test.ts +34 -1
  276. package/src/server/serverFns/bug/__tests__/bug-webhooks.test.ts +34 -1
  277. package/src/server/serverFns/bug/bug-access-tokens.ts +16 -22
  278. package/src/server/serverFns/bug/bug-activities.ts +4 -6
  279. package/src/server/serverFns/bug/bug-collect-tokens.ts +13 -18
  280. package/src/server/serverFns/bug/bug-comments.ts +13 -18
  281. package/src/server/serverFns/bug/bug-crud/detail.ts +10 -14
  282. package/src/server/serverFns/bug/bug-crud.ts +28 -38
  283. package/src/server/serverFns/bug/bug-display-options.ts +8 -11
  284. package/src/server/serverFns/bug/bug-export.ts +5 -7
  285. package/src/server/serverFns/bug/bug-fix.ts +10 -14
  286. package/src/server/serverFns/bug/bug-groups.ts +10 -14
  287. package/src/server/serverFns/bug/bug-reactions.ts +10 -14
  288. package/src/server/serverFns/bug/bug-reorder.ts +4 -6
  289. package/src/server/serverFns/bug/bug-repro.ts +8 -11
  290. package/src/server/serverFns/bug/bug-story-bulk.ts +13 -18
  291. package/src/server/serverFns/bug/bug-story.ts +10 -14
  292. package/src/server/serverFns/bug/bug-views.ts +12 -18
  293. package/src/server/serverFns/bug/bug-webhooks.ts +13 -18
  294. package/src/server/serverFns/bug/test-bug-confirm.ts +7 -10
  295. package/src/server/serverFns/changelog/__tests__/fetch.test.ts +40 -1
  296. package/src/server/serverFns/changelog/fetch.ts +4 -6
  297. package/src/server/serverFns/chat/conversations.ts +17 -28
  298. package/src/server/serverFns/chat/list-messages.ts +4 -7
  299. package/src/server/serverFns/collab-presence.ts +11 -13
  300. package/src/server/serverFns/collab-snapshot.ts +6 -7
  301. package/src/server/serverFns/collab.ts +7 -7
  302. package/src/server/serverFns/compliance.ts +8 -8
  303. package/src/server/serverFns/design/__tests__/comments.test.ts +54 -10
  304. package/src/server/serverFns/design/__tests__/designs.test.ts +39 -16
  305. package/src/server/serverFns/design/__tests__/page-versions.test.ts +52 -14
  306. package/src/server/serverFns/design/__tests__/shares-password.test.ts +44 -14
  307. package/src/server/serverFns/design/__tests__/shares.test.ts +44 -14
  308. package/src/server/serverFns/design/__tests__/versions.test.ts +44 -14
  309. package/src/server/serverFns/design/ai/__tests__/agent-conversations.test.ts +50 -21
  310. package/src/server/serverFns/design/ai/__tests__/providers.test.ts +42 -8
  311. package/src/server/serverFns/design/ai/agent-conversations.ts +19 -26
  312. package/src/server/serverFns/design/ai/fill-annotation.ts +5 -7
  313. package/src/server/serverFns/design/ai/generate-prototype.ts +5 -7
  314. package/src/server/serverFns/design/ai/hosted-models.ts +4 -5
  315. package/src/server/serverFns/design/ai/providers.ts +30 -39
  316. package/src/server/serverFns/design/ai/source-mode.ts +10 -12
  317. package/src/server/serverFns/design/comments.ts +14 -13
  318. package/src/server/serverFns/design/designs.ts +13 -19
  319. package/src/server/serverFns/design/page-versions.ts +17 -18
  320. package/src/server/serverFns/design/pages.ts +17 -18
  321. package/src/server/serverFns/design/reviews.ts +8 -9
  322. package/src/server/serverFns/design/shares-password.ts +13 -19
  323. package/src/server/serverFns/design/shares.ts +22 -31
  324. package/src/server/serverFns/design/versions.ts +8 -9
  325. package/src/server/serverFns/harness/__tests__/attach-prototype.spec.ts +52 -2
  326. package/src/server/serverFns/harness/__tests__/attach-to-kb.spec.ts +52 -2
  327. package/src/server/serverFns/harness/__tests__/attach-to-story-ssrf.spec.ts +52 -2
  328. package/src/server/serverFns/harness/__tests__/attach-to-story.spec.ts +52 -2
  329. package/src/server/serverFns/harness/__tests__/cancel-run.test.ts +40 -15
  330. package/src/server/serverFns/harness/__tests__/extract-attachment.spec.ts +52 -2
  331. package/src/server/serverFns/harness/__tests__/gallery.spec.ts +76 -12
  332. package/src/server/serverFns/harness/__tests__/github-workspace.spec.ts +38 -6
  333. package/src/server/serverFns/harness/__tests__/media-prototype.spec.ts +50 -6
  334. package/src/server/serverFns/harness/__tests__/media.spec.ts +55 -11
  335. package/src/server/serverFns/harness/__tests__/sandbox-policy.test.ts +41 -16
  336. package/src/server/serverFns/harness/__tests__/workspace-files.test.ts +41 -16
  337. package/src/server/serverFns/harness/attach-prototype.ts +4 -4
  338. package/src/server/serverFns/harness/attach-to-kb.ts +4 -4
  339. package/src/server/serverFns/harness/attach-to-story.ts +4 -4
  340. package/src/server/serverFns/harness/cancel-run.ts +4 -6
  341. package/src/server/serverFns/harness/extract-attachment.ts +3 -4
  342. package/src/server/serverFns/harness/gallery.ts +19 -23
  343. package/src/server/serverFns/harness/github-workspace.ts +14 -21
  344. package/src/server/serverFns/harness/media.ts +18 -117
  345. package/src/server/serverFns/harness/sandbox-policy.ts +11 -10
  346. package/src/server/serverFns/harness/search-mention-candidates.ts +4 -6
  347. package/src/server/serverFns/harness/workspace-files.ts +19 -26
  348. package/src/server/serverFns/iteration/__tests__/iteration-auto-schedule.test.ts +35 -3
  349. package/src/server/serverFns/iteration/__tests__/iteration-crud.test.ts +35 -3
  350. package/src/server/serverFns/iteration/__tests__/iteration-stories.test.ts +35 -3
  351. package/src/server/serverFns/iteration/__tests__/iteration-transfer.test.ts +35 -3
  352. package/src/server/serverFns/iteration/iteration-auto-schedule.ts +7 -10
  353. package/src/server/serverFns/iteration/iteration-burndown.ts +4 -6
  354. package/src/server/serverFns/iteration/iteration-crud.ts +26 -35
  355. package/src/server/serverFns/iteration/iteration-stories.ts +10 -14
  356. package/src/server/serverFns/iteration/iteration-transfer.ts +4 -6
  357. package/src/server/serverFns/knowledge/public/bookmarks/categories.ts +11 -15
  358. package/src/server/serverFns/knowledge/public/bookmarks/create.ts +4 -6
  359. package/src/server/serverFns/knowledge/public/bookmarks/delete.ts +5 -7
  360. package/src/server/serverFns/knowledge/public/bookmarks/detail.ts +4 -6
  361. package/src/server/serverFns/knowledge/public/bookmarks/favorite.ts +4 -6
  362. package/src/server/serverFns/knowledge/public/bookmarks/list.ts +4 -6
  363. package/src/server/serverFns/knowledge/public/bookmarks/refresh-favicon.ts +4 -6
  364. package/src/server/serverFns/knowledge/public/bookmarks/share.ts +4 -6
  365. package/src/server/serverFns/knowledge/public/bookmarks/sync-github.ts +5 -7
  366. package/src/server/serverFns/knowledge/public/bookmarks/unshare.ts +4 -6
  367. package/src/server/serverFns/knowledge/public/bookmarks/update.ts +6 -8
  368. package/src/server/serverFns/knowledge/public/compliance/create.ts +4 -6
  369. package/src/server/serverFns/knowledge/public/compliance/delete.ts +5 -7
  370. package/src/server/serverFns/knowledge/public/compliance/detail.ts +4 -6
  371. package/src/server/serverFns/knowledge/public/compliance/list.ts +4 -6
  372. package/src/server/serverFns/knowledge/public/compliance/share.ts +4 -6
  373. package/src/server/serverFns/knowledge/public/compliance/unshare.ts +4 -6
  374. package/src/server/serverFns/knowledge/public/compliance/update.ts +5 -7
  375. package/src/server/serverFns/knowledge/public/dictionary/create.ts +4 -6
  376. package/src/server/serverFns/knowledge/public/dictionary/delete.ts +5 -7
  377. package/src/server/serverFns/knowledge/public/dictionary/detail.ts +4 -6
  378. package/src/server/serverFns/knowledge/public/dictionary/list.ts +4 -6
  379. package/src/server/serverFns/knowledge/public/dictionary/share.ts +4 -6
  380. package/src/server/serverFns/knowledge/public/dictionary/unshare.ts +4 -6
  381. package/src/server/serverFns/knowledge/public/dictionary/update.ts +5 -7
  382. package/src/server/serverFns/knowledge/public/entity-tags.ts +4 -6
  383. package/src/server/serverFns/knowledge/public/favorite.ts +7 -10
  384. package/src/server/serverFns/knowledge/public/legal/create.ts +4 -6
  385. package/src/server/serverFns/knowledge/public/legal/delete.ts +5 -7
  386. package/src/server/serverFns/knowledge/public/legal/detail.ts +4 -6
  387. package/src/server/serverFns/knowledge/public/legal/list.ts +4 -6
  388. package/src/server/serverFns/knowledge/public/legal/share.ts +4 -6
  389. package/src/server/serverFns/knowledge/public/legal/unshare.ts +4 -6
  390. package/src/server/serverFns/knowledge/public/legal/update.ts +5 -7
  391. package/src/server/serverFns/knowledge-index.ts +12 -16
  392. package/src/server/serverFns/knowledge.ts +50 -67
  393. package/src/server/serverFns/notifications.ts +25 -34
  394. package/src/server/serverFns/plugins/harness-plugins.ts +16 -23
  395. package/src/server/serverFns/project/module-overview.ts +7 -10
  396. package/src/server/serverFns/project.ts +48 -68
  397. package/src/server/serverFns/references/create.ts +4 -6
  398. package/src/server/serverFns/references/resolve.ts +4 -6
  399. package/src/server/serverFns/references/search.ts +4 -6
  400. package/src/server/serverFns/share/design/[designId]/events.ts +4 -6
  401. package/src/server/serverFns/sso.ts +1 -1
  402. package/src/server/serverFns/story/__tests__/bulk.test.ts +34 -1
  403. package/src/server/serverFns/story/__tests__/story-assoc.test.ts +34 -1
  404. package/src/server/serverFns/story/__tests__/story-attachments.test.ts +34 -1
  405. package/src/server/serverFns/story/__tests__/story-crud-date-key.test.ts +42 -17
  406. package/src/server/serverFns/story/__tests__/story-dependencies.test.ts +34 -1
  407. package/src/server/serverFns/story/__tests__/story-gantt-deps.test.ts +34 -1
  408. package/src/server/serverFns/story/__tests__/story-groups.test.ts +34 -1
  409. package/src/server/serverFns/story/__tests__/story-read-path-authz.test.ts +40 -4
  410. package/src/server/serverFns/story/__tests__/story-search.test.ts +34 -1
  411. package/src/server/serverFns/story/__tests__/story-transfer.test.ts +34 -1
  412. package/src/server/serverFns/story/link-preview.ts +5 -7
  413. package/src/server/serverFns/story/story-assoc.ts +28 -38
  414. package/src/server/serverFns/story/story-attachments.ts +7 -10
  415. package/src/server/serverFns/story/story-children.ts +4 -6
  416. package/src/server/serverFns/story/story-comments.ts +13 -18
  417. package/src/server/serverFns/story/story-crud/activities.ts +4 -6
  418. package/src/server/serverFns/story/story-crud/archive.ts +7 -10
  419. package/src/server/serverFns/story/story-crud/bulk.ts +10 -14
  420. package/src/server/serverFns/story/story-crud/detail.ts +7 -10
  421. package/src/server/serverFns/story/story-crud/label-suggest.ts +4 -6
  422. package/src/server/serverFns/story/story-crud/labels.ts +6 -9
  423. package/src/server/serverFns/story/story-crud/user-brief.ts +4 -6
  424. package/src/server/serverFns/story/story-crud.ts +13 -18
  425. package/src/server/serverFns/story/story-dependencies.ts +16 -22
  426. package/src/server/serverFns/story/story-groups.ts +10 -14
  427. package/src/server/serverFns/story/story-links.ts +13 -18
  428. package/src/server/serverFns/story/story-reactions.ts +13 -18
  429. package/src/server/serverFns/story/story-search.ts +7 -10
  430. package/src/server/serverFns/story/story-transfer.ts +10 -14
  431. package/src/server/serverFns/story/story-tree.ts +7 -10
  432. package/src/server/serverFns/story-display-options.ts +7 -8
  433. package/src/server/serverFns/story-view.ts +12 -18
  434. package/src/server/serverFns/tasks/task-crud.ts +13 -17
  435. package/src/server/serverFns/tasks/task-queries.ts +12 -18
  436. package/src/server/serverFns/team.ts +46 -66
  437. package/src/server/serverFns/testcase/__tests__/automation.test.ts +38 -5
  438. package/src/server/serverFns/testcase/__tests__/evidence-upload.test.ts +37 -3
  439. package/src/server/serverFns/testcase/__tests__/generation.test.ts +38 -5
  440. package/src/server/serverFns/testcase/__tests__/plans.test.ts +36 -3
  441. package/src/server/serverFns/testcase/__tests__/regression.test.ts +36 -3
  442. package/src/server/serverFns/testcase/__tests__/run-cases.test.ts +36 -3
  443. package/src/server/serverFns/testcase/__tests__/runs.test.ts +36 -3
  444. package/src/server/serverFns/testcase/__tests__/stats.test.ts +36 -3
  445. package/src/server/serverFns/testcase/__tests__/testcase-board.test.ts +39 -5
  446. package/src/server/serverFns/testcase/__tests__/testcase-crud.test.ts +36 -3
  447. package/src/server/serverFns/testcase/__tests__/testcase-folders.test.ts +36 -3
  448. package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +38 -5
  449. package/src/server/serverFns/testcase/automation.ts +13 -17
  450. package/src/server/serverFns/testcase/bulk.ts +10 -14
  451. package/src/server/serverFns/testcase/evidence-upload.ts +5 -7
  452. package/src/server/serverFns/testcase/generation.ts +10 -13
  453. package/src/server/serverFns/testcase/plans.ts +23 -31
  454. package/src/server/serverFns/testcase/regression.ts +7 -10
  455. package/src/server/serverFns/testcase/run-cases.ts +13 -18
  456. package/src/server/serverFns/testcase/runs.ts +13 -18
  457. package/src/server/serverFns/testcase/stats.ts +13 -18
  458. package/src/server/serverFns/testcase/testcase-activities.ts +4 -6
  459. package/src/server/serverFns/testcase/testcase-board.ts +4 -6
  460. package/src/server/serverFns/testcase/testcase-crud.ts +20 -27
  461. package/src/server/serverFns/testcase/testcase-display-options.ts +8 -11
  462. package/src/server/serverFns/testcase/testcase-export.ts +5 -7
  463. package/src/server/serverFns/testcase/testcase-filter-views.ts +12 -18
  464. package/src/server/serverFns/testcase/testcase-folders.ts +16 -22
  465. package/src/server/serverFns/testcase/testcase-groups.ts +4 -6
  466. package/src/server/serverFns/testcase/testcase-reorder.ts +4 -6
  467. package/src/server/serverFns/translation.ts +4 -6
  468. package/src/server/serverFns/user/avatar.ts +4 -6
  469. package/src/server/serverFns/user/settings.ts +4 -6
  470. package/src/server/serverFns/user/update-locale.ts +4 -6
  471. package/src/server/serverFns/workspace-kb.ts +26 -35
  472. package/src/server/serverFns/workspace.ts +43 -59
  473. package/src/server/serverFns/ws.ts +4 -6
  474. package/src/server/serverfn-public-allowlist.ts +117 -0
  475. package/src/server/story/types.ts +0 -1
  476. package/src/server/sync/__tests__/scopes.test.ts +64 -0
  477. package/src/server/sync/scopes.ts +7 -0
  478. package/src/server/testcase/__tests__/auto-bug.test.ts +44 -12
  479. package/src/__tests__/bench/baselines/format-timestamp.json +0 -44
  480. package/src/__typeprobe__.d.ts +0 -4
  481. package/src/components/forms/app-form-dialog.tsx +0 -126
  482. package/src/components/forms/hooks/use-form-submit.ts +0 -57
  483. package/src/components/forms/types.ts +0 -34
  484. package/src/components/harness/code/workspace-picker.tsx +0 -239
  485. package/src/components/tiptap/extensions/local-file/insert-local-file.ts +0 -35
  486. package/src/components/tiptap/extensions/local-file/local-file-node.ts +0 -71
  487. package/src/devframe-docks/ai-chat-iframe.tsx +0 -29
  488. package/src/devframe-docks/session-iframe.tsx +0 -90
  489. package/src/lib/__tests__/desktop-detect.test.ts +0 -19
  490. package/src/lib/collections/index.ts +0 -22
  491. package/src/lib/collections/knowledge/index.ts +0 -18
  492. package/src/lib/collections/knowledge/knowledge-bases-collection.ts +0 -34
  493. package/src/lib/collections/reference/bookmark-categories-collection.ts +0 -31
  494. package/src/lib/collections/reference/labels-collection.ts +0 -31
  495. package/src/lib/collections/reference/projects-collection.ts +0 -31
  496. package/src/lib/collections/reference/users-brief-collection.ts +0 -35
  497. package/src/lib/collections/reference/workspaces-collection.ts +0 -34
  498. package/src/lib/desktop-detect.ts +0 -29
  499. package/src/paraglide/messages/en.js +0 -28260
  500. package/src/paraglide/messages/zh.js +0 -28258
@@ -64,6 +64,18 @@ vi.mock("@allbluecn/database", () => ({
64
64
 
65
65
  import { InMemoryRunStore } from "@tanstack/ai"
66
66
  import { InMemoryLockStore } from "@tanstack/ai/locks"
67
+ import {
68
+ InMemorySandboxCheckpointStore,
69
+ SandboxCheckpointConflictError,
70
+ SandboxCheckpointDuplicateIdError,
71
+ SandboxCheckpointInvalidEntryError,
72
+ SandboxCheckpointInvalidIdError,
73
+ SandboxCheckpointNotHeadError,
74
+ SandboxCheckpointParentMismatchError,
75
+ SandboxCheckpointWriterConflictError,
76
+ SandboxCheckpointWriterLostError,
77
+ type SandboxCheckpoint,
78
+ } from "@tanstack/ai-sandbox"
67
79
  import { buildSandboxMiddleware } from "../harness-sandbox"
68
80
 
69
81
  const RUNS = new InMemoryRunStore()
@@ -362,14 +374,7 @@ describe("snapshots messages.loadThread 与 checkpoint store(审计 A3x-2)",
362
374
  function snapshotStoresWithCheckpoints() {
363
375
  return (mocks.withSandbox.mock.calls[0][1] as {
364
376
  snapshots?: {
365
- checkpoints?: {
366
- get: (id: string) => Promise<unknown>
367
- list: (threadId: string) => Promise<unknown[]>
368
- getHead: (threadId: string) => Promise<string | null>
369
- append: (input: { checkpoint: unknown; writer: { threadId: string } }) => Promise<{ headId: string }>
370
- deleteHead: (input: { threadId: string; checkpointId: string }) => Promise<void>
371
- acquireWriter: (threadId: string) => Promise<{ ownerToken: string; fence: number }>
372
- }
377
+ checkpoints?: InMemorySandboxCheckpointStore
373
378
  persistence?: { stores?: { messages?: { loadThread: (threadId: string) => Promise<unknown[]> } } }
374
379
  }
375
380
  }).snapshots!
@@ -409,56 +414,262 @@ describe("snapshots messages.loadThread 与 checkpoint store(审计 A3x-2)",
409
414
  expect.objectContaining({ where: { threadId: "thread-1" } }),
410
415
  )
411
416
  })
417
+ })
418
+
419
+ /**
420
+ * 官方 InMemorySandboxCheckpointStore 语义(任务 14:替换自建 99 行占位)。
421
+ *
422
+ * 占位版吞掉的官方错误语义在此逐条锁定:expectedHead 乐观并发(STALE_HEAD)、
423
+ * 重复 id、parent 链路一致、写者租约(conflict / lost)、deleteHead 非 head、
424
+ * id/entry 校验、blob 引用计数。checkpoint store 实例随 buildSandboxMiddleware
425
+ * 每次新建,用例间天然隔离;占位版的 200 条 FIFO 淘汰随占位一起移除——官方实现
426
+ * 不设上限,增长由 thread 生命周期(keepAlive 30m reap)约束。
427
+ */
428
+ describe("官方 InMemorySandboxCheckpointStore 语义(任务 14)", () => {
429
+ beforeEach(() => {
430
+ vi.clearAllMocks()
431
+ vi.unstubAllEnvs()
432
+ delete process.env.DEPLOY_TARGET
433
+ mocks.resolveSandboxProvider.mockResolvedValue({ provider: { name: "mock" } })
434
+ mocks.defineSandbox.mockImplementation((config: unknown) => config)
435
+ mocks.prismaInstanceStore.mockReturnValue({ store: "prisma" })
436
+ mocks.pgLockStore.mockReturnValue({ lock: "pg" })
437
+ mocks.sandboxPolicyFor.mockImplementation((level: string) => ({ policy: level }))
438
+ mocks.withSandbox.mockImplementation((_sandbox: unknown, options: unknown) => options)
439
+ })
440
+
441
+ function store(): InMemorySandboxCheckpointStore {
442
+ return (mocks.withSandbox.mock.calls[0][1] as {
443
+ snapshots?: { checkpoints?: InMemorySandboxCheckpointStore }
444
+ }).snapshots!.checkpoints!
445
+ }
446
+
447
+ const HEX64 = "a".repeat(64)
448
+
449
+ function checkpoint(overrides: {
450
+ id: string
451
+ threadId: string
452
+ parentCheckpointId?: string | null
453
+ createdAt?: number
454
+ files?: SandboxCheckpoint["files"]
455
+ }): SandboxCheckpoint {
456
+ return {
457
+ id: overrides.id,
458
+ threadId: overrides.threadId,
459
+ parentCheckpointId: overrides.parentCheckpointId ?? null,
460
+ createdAt: overrides.createdAt ?? 1_000,
461
+ reason: "automatic",
462
+ files: overrides.files ?? [],
463
+ conversation: [],
464
+ artifacts: [],
465
+ }
466
+ }
467
+
468
+ /** 以合法租约把 checkpoint 追加为当前 head(parent/expectedHead 取实时 head),成功后释放租约。 */
469
+ async function appendAsHead(
470
+ s: InMemorySandboxCheckpointStore,
471
+ id: string,
472
+ threadId: string,
473
+ createdAt?: number,
474
+ ): Promise<string> {
475
+ const writer = await s.acquireWriter(threadId)
476
+ const parent = await s.getHead(threadId)
477
+ const { headId } = await s.append({
478
+ checkpoint: checkpoint({ id, threadId, createdAt, parentCheckpointId: parent }),
479
+ expectedHeadId: parent,
480
+ writer,
481
+ })
482
+ await writer.release()
483
+ return headId
484
+ }
485
+
486
+ it("装配的是官方 InMemorySandboxCheckpointStore 实例(占位实现已移除)", async () => {
487
+ await buildSandboxMiddleware(baseParams())
488
+ expect(store()).toBeInstanceOf(InMemorySandboxCheckpointStore)
489
+ })
490
+
491
+ it("append → getHead/get 回放;list 按 createdAt 排序;deleteHead 真删并回退 head 到 parent", async () => {
492
+ await buildSandboxMiddleware(baseParams())
493
+ const s = store()
494
+
495
+ await appendAsHead(s, "cp-1", "t-1")
496
+ await appendAsHead(s, "cp-2", "t-1", 2_000)
497
+ expect(await s.getHead("t-1")).toBe("cp-2")
498
+
499
+ const listed = await s.list("t-1")
500
+ expect(listed.map((c) => c.id)).toEqual(["cp-1", "cp-2"])
501
+
502
+ const writer = await s.acquireWriter("t-1")
503
+ await s.deleteHead({ threadId: "t-1", checkpointId: "cp-2", writer })
504
+ expect(await s.get("cp-2")).toBeNull()
505
+ // head 回退到被删 checkpoint 的 parent(cp-1),而非清空——与 append 链式 parent 对齐
506
+ expect(await s.getHead("t-1")).toBe("cp-1")
507
+ expect(await s.get("cp-1")).toEqual(checkpoint({ id: "cp-1", threadId: "t-1" }))
508
+ })
412
509
 
413
- it("checkpoint store:append 后 getHead 可回放,deleteHead 真删(非 no-op)", async () => {
510
+ it("append expectedHead 过期 → SandboxCheckpointConflictError(占位版吞掉的 STALE_HEAD 语义)", async () => {
414
511
  await buildSandboxMiddleware(baseParams())
415
- const cp = snapshotStoresWithCheckpoints().checkpoints!
512
+ const s = store()
513
+ await appendAsHead(s, "cp-1", "t-1")
514
+ const writer = await s.acquireWriter("t-1")
515
+
516
+ await expect(
517
+ s.append({
518
+ checkpoint: checkpoint({ id: "cp-2", threadId: "t-1", parentCheckpointId: "stale" }),
519
+ expectedHeadId: "stale",
520
+ writer,
521
+ }),
522
+ ).rejects.toBeInstanceOf(SandboxCheckpointConflictError)
523
+ })
416
524
 
417
- const { headId } = await cp.append({ checkpoint: { n: 1 }, writer: { threadId: "t-1" } })
418
- expect(await cp.getHead("t-1")).toBe(headId)
419
- expect(await cp.get(headId)).toEqual({ n: 1 })
525
+ it("append 重复 id → SandboxCheckpointDuplicateIdError(先于 head 校验拦截)", async () => {
526
+ await buildSandboxMiddleware(baseParams())
527
+ const s = store()
528
+ await appendAsHead(s, "cp-1", "t-1")
529
+ const writer = await s.acquireWriter("t-1")
530
+
531
+ await expect(
532
+ s.append({
533
+ checkpoint: checkpoint({ id: "cp-1", threadId: "t-1", parentCheckpointId: "cp-1" }),
534
+ expectedHeadId: "cp-1",
535
+ writer,
536
+ }),
537
+ ).rejects.toBeInstanceOf(SandboxCheckpointDuplicateIdError)
538
+ })
420
539
 
421
- await cp.deleteHead({ threadId: "t-1", checkpointId: headId })
422
- expect(await cp.getHead("t-1")).toBeNull()
423
- expect(await cp.get(headId)).toBeNull()
540
+ it("append parent 与 expectedHead 不一致 → SandboxCheckpointParentMismatchError", async () => {
541
+ await buildSandboxMiddleware(baseParams())
542
+ const s = store()
543
+ await appendAsHead(s, "cp-1", "t-1")
544
+ const writer = await s.acquireWriter("t-1")
545
+
546
+ // expectedHead 与真实 head 一致,但 checkpoint.parent 指向别处 → 链路断裂即拒
547
+ await expect(
548
+ s.append({
549
+ checkpoint: checkpoint({ id: "cp-2", threadId: "t-1", parentCheckpointId: null }),
550
+ expectedHeadId: "cp-1",
551
+ writer,
552
+ }),
553
+ ).rejects.toBeInstanceOf(SandboxCheckpointParentMismatchError)
424
554
  })
425
555
 
426
- it("checkpoint store:deleteHead 仅当 checkpointId 是当前 head 才删(防误删新 head)", async () => {
556
+ it("append 未持有效写者租约 → SandboxCheckpointWriterLostError(占位版完全忽略 writer)", async () => {
427
557
  await buildSandboxMiddleware(baseParams())
428
- const cp = snapshotStoresWithCheckpoints().checkpoints!
558
+ const s = store()
559
+ await appendAsHead(s, "cp-1", "t-1")
560
+
561
+ await expect(
562
+ s.append({
563
+ checkpoint: checkpoint({ id: "cp-2", threadId: "t-1", parentCheckpointId: "cp-1" }),
564
+ expectedHeadId: "cp-1",
565
+ // 伪造 writer(从未 acquire):ownerToken/fence 均不匹配
566
+ writer: { threadId: "t-1", ownerToken: "bogus", fence: 999 },
567
+ }),
568
+ ).rejects.toBeInstanceOf(SandboxCheckpointWriterLostError)
569
+ })
429
570
 
430
- const first = await cp.append({ checkpoint: { n: 1 }, writer: { threadId: "t-2" } })
431
- const second = await cp.append({ checkpoint: { n: 2 }, writer: { threadId: "t-2" } })
571
+ it("release 后 renew 抛 WriterLostError(租约真正失效,占位版 renew 恒成功)", async () => {
572
+ await buildSandboxMiddleware(baseParams())
573
+ const s = store()
574
+ const writer = await s.acquireWriter("t-1")
575
+ await writer.release()
432
576
 
433
- // 用旧 head 调 deleteHead → 新 head 不受影响
434
- await cp.deleteHead({ threadId: "t-2", checkpointId: first.headId })
435
- expect(await cp.getHead("t-2")).toBe(second.headId)
577
+ await expect(writer.renew()).rejects.toBeInstanceOf(SandboxCheckpointWriterLostError)
436
578
  })
437
579
 
438
- it("checkpoint store:ownerToken 唯一(randomUUID)且 fence 单调递增(写者栅栏生效)", async () => {
580
+ it("并发 acquireWriter → WriterConflictError;release 后可再取且 ownerToken 唯一 fence 单调", async () => {
439
581
  await buildSandboxMiddleware(baseParams())
440
- const cp = snapshotStoresWithCheckpoints().checkpoints!
582
+ const s = store()
441
583
 
442
- const w1 = await cp.acquireWriter("t-3")
443
- const w2 = await cp.acquireWriter("t-3")
444
- expect(w1.ownerToken).not.toBe(w2.ownerToken)
584
+ const w1 = await s.acquireWriter("t-1")
585
+ await expect(s.acquireWriter("t-1")).rejects.toBeInstanceOf(
586
+ SandboxCheckpointWriterConflictError,
587
+ )
588
+
589
+ await w1.release()
590
+ const w2 = await s.acquireWriter("t-1")
591
+ expect(w2.ownerToken).not.toBe(w1.ownerToken)
445
592
  expect(w2.fence).toBeGreaterThan(w1.fence)
446
593
  })
447
594
 
448
- it("checkpoint store:超过 200 条 FIFO 淘汰(长生命周期进程内存不失控)", async () => {
595
+ it("deleteHead 非 head → SandboxCheckpointNotHeadError(占位版静默 no-op 的语义收紧)", async () => {
596
+ await buildSandboxMiddleware(baseParams())
597
+ const s = store()
598
+ const first = await appendAsHead(s, "cp-1", "t-1")
599
+ await appendAsHead(s, "cp-2", "t-1", 2_000)
600
+ const writer = await s.acquireWriter("t-1")
601
+
602
+ // 用旧 head 调 deleteHead → 抛 NotHeadError,新 head 不受影响
603
+ await expect(
604
+ s.deleteHead({ threadId: "t-1", checkpointId: first, writer }),
605
+ ).rejects.toBeInstanceOf(SandboxCheckpointNotHeadError)
606
+ expect(await s.getHead("t-1")).toBe("cp-2")
607
+ })
608
+
609
+ it("无效 checkpoint id → SandboxCheckpointInvalidIdError(get/append 同门槛)", async () => {
449
610
  await buildSandboxMiddleware(baseParams())
450
- const cp = snapshotStoresWithCheckpoints().checkpoints!
611
+ const s = store()
612
+
613
+ await expect(s.get("")).rejects.toBeInstanceOf(SandboxCheckpointInvalidIdError)
614
+ const writer = await s.acquireWriter("t-1")
615
+ await expect(
616
+ s.append({
617
+ checkpoint: checkpoint({ id: "", threadId: "t-1" }),
618
+ expectedHeadId: null,
619
+ writer,
620
+ }),
621
+ ).rejects.toBeInstanceOf(SandboxCheckpointInvalidIdError)
622
+ })
451
623
 
452
- const headIds: string[] = []
453
- for (let i = 0; i < 210; i++) {
454
- const r = await cp.append({ checkpoint: { i }, writer: { threadId: "t-fifo" } })
455
- headIds.push(r.headId)
456
- }
457
- // 最老的 10 条被淘汰
458
- expect(await cp.get(headIds[0])).toBeNull()
459
- expect(await cp.get(headIds[9])).toBeNull()
460
- // 新条目仍在
461
- expect(await cp.get(headIds[209])).toEqual({ i: 209 })
462
- expect(await cp.getHead("t-fifo")).toBe(headIds[209])
624
+ it("files blobKey 不符内容寻址格式 → SandboxCheckpointInvalidEntryError(占位版来者不拒)", async () => {
625
+ await buildSandboxMiddleware(baseParams())
626
+ const s = store()
627
+ const writer = await s.acquireWriter("t-1")
628
+
629
+ await expect(
630
+ s.append({
631
+ checkpoint: checkpoint({
632
+ id: "cp-bad",
633
+ threadId: "t-1",
634
+ files: [{ path: "src/a.txt", kind: "file", blobKey: "run-no-artifact", size: 3 }],
635
+ }),
636
+ expectedHeadId: null,
637
+ writer,
638
+ }),
639
+ ).rejects.toBeInstanceOf(SandboxCheckpointInvalidEntryError)
640
+ })
641
+
642
+ it("listBlobReferences 按 blobKey 计数引用,deleteHead 递减(blob GC 依据)", async () => {
643
+ await buildSandboxMiddleware(baseParams())
644
+ const s = store()
645
+ const blobKey = `sandbox-files/sha256/${HEX64}`
646
+ const files: SandboxCheckpoint["files"] = [
647
+ { path: "src/a.txt", kind: "file", blobKey, size: 3 },
648
+ ]
649
+
650
+ const w1 = await s.acquireWriter("t-1")
651
+ await s.append({
652
+ checkpoint: checkpoint({ id: "cp-1", threadId: "t-1", files }),
653
+ expectedHeadId: null,
654
+ writer: w1,
655
+ })
656
+ await w1.release()
657
+ const w2 = await s.acquireWriter("t-1")
658
+ await s.append({
659
+ checkpoint: checkpoint({
660
+ id: "cp-2",
661
+ threadId: "t-1",
662
+ parentCheckpointId: "cp-1",
663
+ createdAt: 2_000,
664
+ files,
665
+ }),
666
+ expectedHeadId: "cp-1",
667
+ writer: w2,
668
+ })
669
+
670
+ expect(await s.listBlobReferences()).toEqual([{ key: blobKey, references: 2 }])
671
+
672
+ await s.deleteHead({ threadId: "t-1", checkpointId: "cp-2", writer: w2 })
673
+ expect(await s.listBlobReferences()).toEqual([{ key: blobKey, references: 1 }])
463
674
  })
464
675
  })
@@ -19,11 +19,11 @@ import type { RunStore, AnyTextAdapter } from "@tanstack/ai"
19
19
  import {
20
20
  defineSandbox,
21
21
  withSandbox,
22
+ InMemorySandboxCheckpointStore,
22
23
  type WorkspaceDefinition,
23
24
  createSecrets,
24
25
  localSource,
25
26
  } from "@tanstack/ai-sandbox"
26
- import { randomUUID } from "node:crypto"
27
27
  import { InMemoryLockStore } from "@tanstack/ai/locks"
28
28
  import type { PgStreamDurabilityFactory } from "../durability/pg-stream"
29
29
  import { pgLockStore } from "../locks/pg-lock"
@@ -39,70 +39,6 @@ import {
39
39
  import { codexText, CODEX_MODELS } from "@tanstack/ai-codex"
40
40
  import { acpCompatibleText } from "@tanstack/ai-acp"
41
41
  import { opencodeText } from "@tanstack/ai-opencode"
42
- import type { SandboxCheckpointStore } from "@tanstack/ai-sandbox"
43
-
44
- /**
45
- * 内存 checkpoint store 骨架——任务 14 占位,后续接 DB 持久化。
46
- *
47
- * 占位不等于放任(审计 2026-10-25 🟡):append 只增不删会让长生命周期进程
48
- * 内存持续增长,故设条目上限 FIFO 淘汰;deleteHead 真删 head 指向条目;
49
- * ownerToken 用 randomUUID(Date.now() 同毫秒并发即重复);fence 单调递增
50
- * (恒 0 使写者栅栏失效)。
51
- */
52
- const MEMORY_CHECKPOINT_MAX_ENTRIES = 200
53
-
54
- function memoryCheckpointStore(): SandboxCheckpointStore {
55
- const checkpoints = new Map<string, unknown>()
56
- const heads = new Map<string, string>()
57
- let fenceCounter = 0
58
- return {
59
- get: async (_id: string) => (checkpoints.get(_id) ?? null) as never,
60
- list: async (threadId: string) => {
61
- const prefix = `thread:${threadId}:`
62
- return Array.from(checkpoints.entries())
63
- .filter(([id]) => id.startsWith(prefix))
64
- .map(([, v]) => v as never)
65
- },
66
- getHead: async (threadId: string) => heads.get(`thread:${threadId}`) ?? null,
67
- append: async (input: any) => {
68
- const tid = input?.writer?.threadId ?? "unknown"
69
- const id = `thread:${tid}:${Date.now()}-${randomUUID().slice(0, 8)}`
70
- checkpoints.set(id, input.checkpoint)
71
- heads.set(`thread:${tid}`, id)
72
- // FIFO 淘汰:Map 迭代按插入序,删到上限以内(head 指向的条目若被淘汰,
73
- // getHead 返回的 id 在 get 中自然落空——占位实现的已知边界)。
74
- while (checkpoints.size > MEMORY_CHECKPOINT_MAX_ENTRIES) {
75
- const oldest = checkpoints.keys().next().value
76
- if (oldest === undefined) break
77
- checkpoints.delete(oldest)
78
- }
79
- return { headId: id } as any
80
- },
81
- deleteHead: async (input: {
82
- threadId: string
83
- checkpointId: string
84
- }) => {
85
- // 仅当 input.checkpointId 仍是当前 head 时才删(防误删新 head)。
86
- // heads 的 key 与 getHead 同前缀(`thread:<threadId>`),value 是完整
87
- // checkpoint id(`thread:<threadId>:<ts>-<uuid>`)。
88
- const headKey = `thread:${input.threadId}`
89
- const headId = heads.get(headKey)
90
- if (headId === undefined || headId !== input.checkpointId) return
91
- heads.delete(headKey)
92
- checkpoints.delete(headId)
93
- },
94
- acquireWriter: async (threadId: string) => ({
95
- threadId,
96
- expiresAt: Date.now() + 60_000,
97
- renewAfterMs: 30_000,
98
- renew: () => Promise.resolve({ expiresAt: Date.now() + 60_000 } as any),
99
- ownerToken: `token-${threadId}-${randomUUID()}`,
100
- fence: ++fenceCounter,
101
- release: () => Promise.resolve(),
102
- }) as any,
103
- listBlobReferences: async () => [],
104
- } as never
105
- }
106
42
 
107
43
  /** 快照恢复的消息上限——与 persistence/message-store.ts 的 LOAD_THREAD_MAX_MESSAGES 同口径。 */
108
44
  const SANDBOX_LOAD_THREAD_MAX_MESSAGES = 100
@@ -239,9 +175,12 @@ export async function buildSandboxMiddleware(
239
175
  },
240
176
  }
241
177
  : {}),
242
- // snapshots.blobs(artifacts + blobs store)P3b 任务 14 接线收口
178
+ // snapshots.blobs(artifacts + blobs store)P3b 任务 14 接线收口。
179
+ // checkpoint store 用官方 InMemorySandboxCheckpointStore(任务 14 替换自建
180
+ // 占位):完整写者租约 + expectedHead 乐观并发 + 9 个错误类语义;后续接
181
+ // DB 持久化时同接口平移。
243
182
  snapshots: {
244
- checkpoints: memoryCheckpointStore(),
183
+ checkpoints: new InMemorySandboxCheckpointStore(),
245
184
  persistence: {
246
185
  stores: {
247
186
  messages: {
@@ -18,6 +18,7 @@
18
18
  import { prisma } from '@allbluecn/database'
19
19
  import type { AiSourceMode } from '@allbluecn/kernel'
20
20
  import { AI_SOURCE_MODES } from '@allbluecn/kernel'
21
+ import { GUEST_USER_ID } from '../desktop-guest'
21
22
  import { getPorts } from './runtime'
22
23
 
23
24
  export interface SourceModeSubject {
@@ -25,16 +26,30 @@ export interface SourceModeSubject {
25
26
  isGuest: boolean
26
27
  }
27
28
 
29
+ /** guest 隔离键:aiSourceMode:local-guest(GUEST_USER_ID 统一从 desktop-guest 引用) */
30
+ const GUEST_SOURCE_MODE_KEY = `aiSourceMode:${GUEST_USER_ID}`
31
+ /** 旧全局键:仅迁移读,不回写、不删除 */
32
+ const LEGACY_SOURCE_MODE_KEY = 'aiSourceMode'
33
+
28
34
  /** 读用户当前模型来源模式;null = 未选择(按 byok 现状链路由) */
29
35
  export async function resolveSourceMode(subject: SourceModeSubject): Promise<AiSourceMode | null> {
30
36
  try {
31
37
  if (subject.isGuest) {
32
- // ⚠ desktop-only 前置条件(审计 🟡):aiSourceMode 是全局共享的
33
- // desktopSetting key,不按用户隔离。仅 desktop 单 guest(GUEST_USER_ID)
34
- // 场景成立;若未来 web 侧出现多 guest/多实例共享该 key,任意 guest 的
35
- // 写入会覆盖所有 guest——届时必须给 key 加实例/用户维度后缀。
36
- const row = await prisma.desktopSetting.findUnique({ where: { key: 'aiSourceMode' } })
37
- const raw = row?.value
38
+ // guest 键隔离:读写均走 aiSourceMode:${GUEST_USER_ID},不再共享全局键,
39
+ // 多 guest/多实例写入互不覆盖。旧全局键仅做迁移读:新键缺失时回落旧值,
40
+ // 下次写入自然 upsert 落新键;旧键保留不删(桌面单机幂等,无害残留)。
41
+ const row = await prisma.desktopSetting.findUnique({ where: { key: GUEST_SOURCE_MODE_KEY } })
42
+ if (!row) {
43
+ const legacy = await prisma.desktopSetting.findUnique({
44
+ where: { key: LEGACY_SOURCE_MODE_KEY },
45
+ })
46
+ const legacyRaw = legacy?.value
47
+ if (typeof legacyRaw === 'string' && AI_SOURCE_MODES.includes(legacyRaw as AiSourceMode)) {
48
+ return legacyRaw as AiSourceMode
49
+ }
50
+ return null
51
+ }
52
+ const raw = row.value
38
53
  if (typeof raw !== 'string' || !AI_SOURCE_MODES.includes(raw as AiSourceMode)) return null
39
54
  return raw as AiSourceMode
40
55
  }
@@ -55,9 +70,10 @@ export async function resolveSourceMode(subject: SourceModeSubject): Promise<AiS
55
70
 
56
71
  export async function setSourceMode(subject: SourceModeSubject, mode: AiSourceMode): Promise<void> {
57
72
  if (subject.isGuest) {
73
+ // 写隔离键;旧键不删除(迁移读后首次写入自然落新键)
58
74
  await prisma.desktopSetting.upsert({
59
- where: { key: 'aiSourceMode' },
60
- create: { key: 'aiSourceMode', value: mode },
75
+ where: { key: GUEST_SOURCE_MODE_KEY },
76
+ create: { key: GUEST_SOURCE_MODE_KEY, value: mode },
61
77
  update: { value: mode },
62
78
  })
63
79
  return
@@ -0,0 +1,74 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ // 桌面首启本地影子用户(本地优先改造 P1,规格 §3.1 懒 seed)。
19
+ // 背景:桌面端(Electron + PGlite)免登录发放 guest 会话(desktop-guest.ts),但
20
+ // users 表无行 → assertOwned / assertProjectMembership / requireUserInDb 与各表
21
+ // FK 在 guest 写入时全部失败。解法:首启为 local-guest 建真实影子行(users 一行
22
+ // + 内置「独立需求」项目 + owner member),业务链零改动自然通过。
23
+ // 挂点:serverFns/auth/session.ts getSessionFn 桌面 guest 分支(失败仅 warn 不
24
+ // 阻断会话发放——首启体验优先)。
25
+
26
+ import { randomUUID } from 'node:crypto'
27
+ import { prisma } from '@allbluecn/database'
28
+ import { ensureBuiltinIndependentProject } from '@/server/builtin-project'
29
+ import {
30
+ GUEST_EMAIL,
31
+ GUEST_USER_ID,
32
+ GUEST_USERNAME,
33
+ isDesktopServer,
34
+ } from '@/server/desktop-guest'
35
+ import { defaultAvatarConfig, presetBgColors } from '@/lib/avatar/avatar-config'
36
+
37
+ // 与 desktop-guest.ts 的 guestBgColor 同式(未导出,为不扩散导出面在此重复;两处
38
+ // seed 同为 GUEST_USERNAME,影子行与内存 guest 会话的头像底色恒一致)
39
+ function shadowBgColor(seed: string): string {
40
+ const sum = [...seed].reduce((acc, ch) => acc + ch.charCodeAt(0), 0)
41
+ return presetBgColors[sum % presetBgColors.length] as string
42
+ }
43
+
44
+ /**
45
+ * 幂等落库本地影子用户(id=local-guest)并复用内置独立项目,返回 GUEST_USER_ID。
46
+ *
47
+ * - 非 desktop 部署:防御性早退不触库(正常调用方只在桌面分支触达)。
48
+ * - upsert 的 update 分支留空:不覆盖用户后续改名/改头像(幂等)。
49
+ * - password 为随机占位(非 bcrypt 哈希):guest 不走密码登录,凭据比对必失败,
50
+ * 杜绝影子行被密码登录复用。
51
+ * - 头像三件(avatarConfig/bgShape/bgColor)与 buildGuestSession 对齐,经 DB 读
52
+ * 取用户的路径(成员列表等)与内存 guest 会话渲染一致。
53
+ */
54
+ export async function ensureLocalShadowUser(): Promise<string> {
55
+ if (!isDesktopServer()) return GUEST_USER_ID
56
+
57
+ await prisma.user.upsert({
58
+ where: { id: GUEST_USER_ID },
59
+ create: {
60
+ id: GUEST_USER_ID,
61
+ email: GUEST_EMAIL,
62
+ username: GUEST_USERNAME,
63
+ password: randomUUID(),
64
+ avatarConfig: defaultAvatarConfig as object,
65
+ bgShape: 'square',
66
+ bgColor: shadowBgColor(GUEST_USERNAME),
67
+ status: 'ACTIVE',
68
+ },
69
+ update: {},
70
+ })
71
+
72
+ await ensureBuiltinIndependentProject(GUEST_USER_ID)
73
+ return GUEST_USER_ID
74
+ }
@@ -31,7 +31,6 @@ describe('限流注册表', () => {
31
31
  expect(AI_RATE_LIMIT.mediaVideo.max).toBeGreaterThan(0)
32
32
  expect(AI_RATE_LIMIT.mediaSpeech.max).toBeGreaterThan(0)
33
33
  expect(AI_RATE_LIMIT.mediaTranscription.max).toBeGreaterThan(0)
34
- expect(AI_RATE_LIMIT.mediaPoll.max).toBeGreaterThanOrEqual(AI_RATE_LIMIT.mediaImage.max)
35
34
  expect(AI_RATE_LIMIT.mediaPrototype.max).toBeGreaterThan(0)
36
35
  })
37
36
 
@@ -47,8 +47,6 @@ export const AI_RATE_LIMIT = {
47
47
  mediaVideo: { max: 5, windowMs: 60_000 },
48
48
  mediaSpeech: { max: 10, windowMs: 60_000 },
49
49
  mediaTranscription: { max: 10, windowMs: 60_000 },
50
- // 轮询端点:任务进行期间会被周期调用,放宽阈值防误伤(仍封死无任务空转滥用)
51
- mediaPoll: { max: 60, windowMs: 60_000 },
52
50
  mediaPrototype: { max: 5, windowMs: 60_000 },
53
51
  aiTask: { max: 5, windowMs: 60_000 },
54
52
  // 审计 A2x-serverfns-4 补口:知识库向量索引(embedding 计费调用,单文档建索引;