@taskforcehq/taskforce 0.3.305 → 0.3.306

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 (337) hide show
  1. package/dist/Taskforce.module.css +295 -89
  2. package/dist/TaskforceCore.js +215 -43
  3. package/dist/TaskforceCore.test.js +433 -28
  4. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  5. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  8. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  10. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  11. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  12. package/dist/compat/workspaceSyncCompat.js +46 -2
  13. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  14. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  15. package/dist/components/context/ContextAttachmentManager.js +549 -0
  16. package/dist/components/features/AgentsModule.d.ts +10 -1
  17. package/dist/components/features/AgentsModule.js +260 -15
  18. package/dist/components/features/AgentsModule.test.js +255 -5
  19. package/dist/components/features/DocumentViewer.d.ts +5 -1
  20. package/dist/components/features/DocumentViewer.js +60 -3
  21. package/dist/components/features/DocumentWorkspace.js +1 -1
  22. package/dist/components/features/DocumentWorkspace.test.js +33 -0
  23. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  24. package/dist/components/task/TaskCard.js +19 -28
  25. package/dist/components/task/TaskCard.test.js +38 -3
  26. package/dist/components/task/TaskContextUpload.js +4 -526
  27. package/dist/components/task/TaskForm.js +85 -90
  28. package/dist/components/task/TaskForm.test.js +110 -8
  29. package/dist/components/task/TaskKanban.d.ts +2 -1
  30. package/dist/components/task/TaskKanban.js +94 -13
  31. package/dist/components/task/TaskKanban.test.js +36 -0
  32. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  33. package/dist/components/ui/EditableAvatarButton.js +18 -0
  34. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  35. package/dist/components/views/PlanComparisonPage.js +70 -14
  36. package/dist/components/views/PlanComparisonPage.test.js +238 -0
  37. package/dist/components/views/PlansPage.js +127 -36
  38. package/dist/components/views/PlansPage.test.js +294 -12
  39. package/dist/components/views/StandaloneLayout.js +283 -533
  40. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  41. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  42. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  43. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  44. package/dist/components/views/planningScope.d.ts +20 -0
  45. package/dist/components/views/planningScope.js +25 -0
  46. package/dist/components/views/planningScope.test.d.ts +1 -0
  47. package/dist/components/views/planningScope.test.js +60 -0
  48. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  49. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  50. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  51. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  52. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  53. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  54. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  55. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  56. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  57. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  58. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  59. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  60. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  61. package/dist/core/AiProfileService.d.ts +39 -2
  62. package/dist/core/AiProfileService.js +639 -73
  63. package/dist/core/AiProfileService.test.js +19 -1
  64. package/dist/core/AiProfiles.test.js +566 -7
  65. package/dist/core/AttachmentLinkService.js +57 -15
  66. package/dist/core/AuthIdentityService.d.ts +107 -0
  67. package/dist/core/AuthIdentityService.js +284 -0
  68. package/dist/core/AuthTokenService.d.ts +4 -0
  69. package/dist/core/AuthTokenService.js +34 -6
  70. package/dist/core/AuthTokenService.test.js +3 -3
  71. package/dist/core/EntitlementsPolicy.test.js +2 -2
  72. package/dist/core/GlobalSettingsService.js +115 -6
  73. package/dist/core/PlanEntitlementService.d.ts +23 -2
  74. package/dist/core/PlanEntitlementService.js +343 -57
  75. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  76. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  77. package/dist/core/PlanVersionPolicy.test.js +35 -2
  78. package/dist/core/PlanningEntities.test.js +52 -0
  79. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  80. package/dist/core/SyncReconciliationService.js +2 -65
  81. package/dist/core/SystemAdmin.test.js +42 -42
  82. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  83. package/dist/core/Taskforce.d.ts +225 -14
  84. package/dist/core/Taskforce.js +765 -192
  85. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  86. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  87. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  88. package/dist/core/WorkspaceLifecycleService.js +0 -5
  89. package/dist/core/WorkspacePermissions.test.js +25 -1
  90. package/dist/core/WorkspacePlanMode.test.js +117 -16
  91. package/dist/core/shared.d.ts +1 -1
  92. package/dist/core/shared.js +5 -1
  93. package/dist/core/types.d.ts +68 -2
  94. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  95. package/dist/hooks/sync/bootstrap.js +2 -0
  96. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  97. package/dist/hooks/sync/controlPlane.js +78 -0
  98. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  99. package/dist/hooks/sync/controlPlane.test.js +121 -0
  100. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  101. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  102. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  103. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  104. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  105. package/dist/hooks/sync/orchestratorShared.js +68 -47
  106. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  107. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  108. package/dist/hooks/sync/pullLifecycle.js +24 -0
  109. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  110. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  111. package/dist/hooks/sync/recovery.d.ts +16 -2
  112. package/dist/hooks/sync/recovery.js +171 -53
  113. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  114. package/dist/hooks/sync/recovery.test.js +292 -0
  115. package/dist/hooks/sync/transfers.d.ts +15 -3
  116. package/dist/hooks/sync/transfers.js +179 -62
  117. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  118. package/dist/hooks/sync/transfers.test.js +396 -0
  119. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  120. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  121. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  122. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  123. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  124. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  125. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  126. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  127. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  128. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  129. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  130. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  131. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  132. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  133. package/dist/hooks/useSyncOrchestrator.js +359 -178
  134. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  135. package/dist/hooks/useTaskData.js +8 -3
  136. package/dist/hooks/useTaskData.test.js +45 -0
  137. package/dist/hooks/useTaskforce.d.ts +60 -2
  138. package/dist/hooks/useTaskforce.js +549 -240
  139. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  140. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  141. package/dist/hooks/useTaskforce.sync-behavior.test.js +2380 -212
  142. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  143. package/dist/hooks/useWorkspaceSyncController.js +6 -1
  144. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  145. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  146. package/dist/localization/locales/en-US.d.ts +0 -1
  147. package/dist/localization/locales/en-US.js +0 -1
  148. package/dist/localization/locales/es-419.js +0 -1
  149. package/dist/localization/locales/pt-BR.js +0 -1
  150. package/dist/mcp/documentAssetRegistrar.js +3 -3
  151. package/dist/mcp/runtime.d.ts +2 -0
  152. package/dist/mcp/runtime.js +131 -64
  153. package/dist/mcp/runtime.test.js +291 -12
  154. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  155. package/dist/migrations/taskSchemaMigrations.js +171 -61
  156. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  157. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  158. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  159. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  160. package/dist/resources/templates/workflows/plan.yaml +1 -1
  161. package/dist/resources/templates/workflows/review.yaml +2 -2
  162. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  163. package/dist/server/auth/providers/apple.d.ts +33 -0
  164. package/dist/server/auth/providers/apple.js +82 -0
  165. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  166. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  167. package/dist/server/auth/providers/github.d.ts +26 -0
  168. package/dist/server/auth/providers/github.js +86 -0
  169. package/dist/server/auth/providers/google.d.ts +21 -0
  170. package/dist/server/auth/providers/google.js +56 -0
  171. package/dist/server/auth/providers/types.d.ts +21 -0
  172. package/dist/server/auth/providers/types.js +1 -0
  173. package/dist/server/auth.d.ts +2 -0
  174. package/dist/server/auth.js +6 -3
  175. package/dist/server/documentReviewRoutes.test.js +36 -3
  176. package/dist/server/index.cookieProxy.test.js +1 -0
  177. package/dist/server/index.d.ts +12 -0
  178. package/dist/server/index.js +120 -9
  179. package/dist/server/index.rateLimit.test.js +3 -0
  180. package/dist/server/index.test.js +106 -3
  181. package/dist/server/routes/admin.d.ts +3 -0
  182. package/dist/server/routes/admin.js +272 -19
  183. package/dist/server/routes/auth.d.ts +15 -2
  184. package/dist/server/routes/auth.js +994 -57
  185. package/dist/server/routes/authSupport.d.ts +1 -1
  186. package/dist/server/routes/authSupport.js +1 -1
  187. package/dist/server/routes/billing.js +361 -35
  188. package/dist/server/routes/billing.test.js +592 -12
  189. package/dist/server/routes/documents.d.ts +1 -0
  190. package/dist/server/routes/documents.js +172 -70
  191. package/dist/server/routes/shared.d.ts +28 -2
  192. package/dist/server/routes/shared.js +69 -4
  193. package/dist/server/routes/sync.integration.test.js +437 -39
  194. package/dist/server/routes/sync.js +61 -605
  195. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  196. package/dist/server/routes/syncAuxRoutes.js +91 -0
  197. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  198. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  199. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  200. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  201. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  202. package/dist/server/routes/syncPushRoutes.js +212 -0
  203. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  204. package/dist/server/routes/syncRouteGuards.js +67 -0
  205. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  206. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  207. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  208. package/dist/server/routes/syncRouteTypes.js +1 -0
  209. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  210. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  211. package/dist/server/routes/tasks.js +43 -1
  212. package/dist/server/routes/workspaces.js +36 -5
  213. package/dist/server/routes.js +60 -3
  214. package/dist/server/routes.test.js +1824 -171
  215. package/dist/services/workflowExportSnapshots.test.js +2 -0
  216. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  217. package/dist/shared/aiProfileSeatScope.js +21 -0
  218. package/dist/shared/passwordPolicy.d.ts +13 -0
  219. package/dist/shared/passwordPolicy.js +84 -0
  220. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  221. package/dist/shared/passwordPolicy.test.js +34 -0
  222. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  223. package/dist/storage/workspaceAssetStore.js +132 -44
  224. package/dist/storage/workspaceAssetStore.test.js +93 -6
  225. package/dist/styles/fonts.css +70 -0
  226. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  227. package/dist/sync/collaborationSyncPayload.js +17 -11
  228. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  229. package/dist/sync/contentSyncState.d.ts +4 -8
  230. package/dist/sync/contentSyncState.js +17 -70
  231. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  232. package/dist/sync/syncApplyHandlers.js +28 -13
  233. package/dist/sync/syncApplyHandlers.test.js +70 -3
  234. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  235. package/dist/sync/syncFieldSemantics.js +55 -0
  236. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  237. package/dist/sync/syncResourceAdapters.js +77 -0
  238. package/dist/sync/syncService.d.ts +5 -0
  239. package/dist/sync/syncService.js +52 -0
  240. package/dist/sync/syncService.test.js +57 -3
  241. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  242. package/dist/sync/taskSyncChangeKey.js +143 -0
  243. package/dist/sync/taskSyncPayload.js +3 -8
  244. package/dist/sync/workspacePullFeed.js +4 -3
  245. package/dist/sync/workspacePullFeed.test.js +43 -0
  246. package/dist/sync/workspaceRepair.js +5 -2
  247. package/dist/sync/workspaceSyncModel.d.ts +45 -0
  248. package/dist/sync/workspaceSyncModel.js +183 -28
  249. package/dist/sync/workspaceSyncModel.test.js +291 -12
  250. package/dist/sync/workspaceSyncState.d.ts +17 -0
  251. package/dist/sync/workspaceSyncState.js +2 -1
  252. package/dist/sync/workspaceSyncSurface.js +3 -8
  253. package/dist/types.d.ts +17 -0
  254. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  255. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BG6P_GVW.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  256. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  257. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  258. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  259. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  260. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  261. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  262. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-9E6ParrY.js} +1 -1
  263. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  264. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  265. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  266. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  267. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  268. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  269. package/dist/ui/assets/{TaskSettings-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
  270. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  271. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  272. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  273. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-DuEd_65c.js} +1 -1
  274. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  275. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  276. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  277. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  278. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  279. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  280. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  281. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  282. package/dist/ui/fonts/Noir_medium.otf +0 -0
  283. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  284. package/dist/ui/fonts/Noir_regular.otf +0 -0
  285. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  286. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  287. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  288. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  289. package/dist/ui/index.html +3 -3
  290. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  291. package/dist/utils/assignees.d.ts +2 -0
  292. package/dist/utils/assignees.js +2 -0
  293. package/dist/utils/avatarUpload.d.ts +27 -0
  294. package/dist/utils/avatarUpload.js +132 -0
  295. package/dist/utils/avatarUpload.test.d.ts +1 -0
  296. package/dist/utils/avatarUpload.test.js +40 -0
  297. package/dist/utils/bootstrapTrace.d.ts +7 -0
  298. package/dist/utils/bootstrapTrace.js +29 -0
  299. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  300. package/dist/utils/contextAssetEvents.d.ts +2 -0
  301. package/dist/utils/syncEventDetails.d.ts +3 -0
  302. package/dist/utils/syncEventDetails.js +64 -0
  303. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  304. package/dist/utils/syncEventDetails.test.js +23 -0
  305. package/dist/utils/syncEventPresentation.d.ts +15 -0
  306. package/dist/utils/syncEventPresentation.js +129 -0
  307. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  308. package/dist/utils/syncEventPresentation.test.js +64 -0
  309. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  310. package/dist/utils/syncStatusPresentation.js +217 -0
  311. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  312. package/dist/utils/syncStatusPresentation.test.js +164 -0
  313. package/dist/utils/taskActivity.d.ts +2 -0
  314. package/dist/utils/taskActivity.js +228 -22
  315. package/dist/utils/taskActivity.test.js +119 -8
  316. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  317. package/dist/utils/workspaceSyncPresentation.js +68 -2
  318. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  319. package/package.json +3 -2
  320. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  321. package/scripts/run-e2e-realtime.sh +17 -3
  322. package/scripts/run-smoke.sh +17 -5
  323. package/scripts/run-soak.sh +17 -5
  324. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  325. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  326. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  327. package/src/resources/templates/workflows/plan.yaml +1 -1
  328. package/src/resources/templates/workflows/review.yaml +2 -2
  329. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  330. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  331. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  332. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  333. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  334. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  335. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  336. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  337. package/scripts/migrate-planning-model.ts +0 -233
@@ -34,58 +34,6 @@ export function ensureTenantSchema(db) {
34
34
  CREATE INDEX IF NOT EXISTS idx_comments_tenant_task ON comments(tenant_id, task_id);
35
35
  `);
36
36
  }
37
- export function ensureParentTaskSchema(db) {
38
- const columns = db.prepare(`PRAGMA table_info(tasks)`).all();
39
- const hasParentColumn = columns.some((c) => c.name === 'parent_task_id');
40
- const hasChildDisplayOrderColumn = columns.some((c) => c.name === 'child_display_order');
41
- if (!hasParentColumn) {
42
- db.exec(`ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`);
43
- }
44
- if (!hasChildDisplayOrderColumn) {
45
- db.exec(`ALTER TABLE tasks ADD COLUMN child_display_order INTEGER`);
46
- }
47
- db.exec(`
48
- CREATE INDEX IF NOT EXISTS idx_tasks_parent_task_id ON tasks(parent_task_id);
49
- CREATE INDEX IF NOT EXISTS idx_tasks_parent_child_order ON tasks(parent_task_id, child_display_order);
50
- `);
51
- // SQLite-only runtime guard triggers. Postgres path enforces constraints in core logic.
52
- if (db.provider === 'sqlite') {
53
- db.exec(`
54
- CREATE TRIGGER IF NOT EXISTS trg_tasks_parent_guard_insert
55
- BEFORE INSERT ON tasks
56
- FOR EACH ROW
57
- WHEN NEW.parent_task_id IS NOT NULL AND TRIM(NEW.parent_task_id) <> ''
58
- BEGIN
59
- SELECT CASE
60
- WHEN NEW.parent_task_id = NEW.id THEN RAISE(ABORT, 'Task cannot parent itself')
61
- WHEN (SELECT id FROM tasks WHERE id = NEW.parent_task_id) IS NULL THEN RAISE(ABORT, 'Parent task not found')
62
- END;
63
- END;
64
- `);
65
- db.exec(`
66
- CREATE TRIGGER IF NOT EXISTS trg_tasks_parent_guard_update
67
- BEFORE UPDATE OF parent_task_id ON tasks
68
- FOR EACH ROW
69
- WHEN NEW.parent_task_id IS NOT NULL AND TRIM(NEW.parent_task_id) <> ''
70
- BEGIN
71
- SELECT CASE
72
- WHEN NEW.parent_task_id = NEW.id THEN RAISE(ABORT, 'Task cannot parent itself')
73
- WHEN (SELECT id FROM tasks WHERE id = NEW.parent_task_id) IS NULL THEN RAISE(ABORT, 'Parent task not found')
74
- END;
75
- END;
76
- `);
77
- // Block deletion of parent tasks while children still point to them.
78
- db.exec(`
79
- CREATE TRIGGER IF NOT EXISTS trg_tasks_block_delete_with_children
80
- BEFORE DELETE ON tasks
81
- FOR EACH ROW
82
- WHEN EXISTS (SELECT 1 FROM tasks WHERE parent_task_id = OLD.id)
83
- BEGIN
84
- SELECT RAISE(ABORT, 'Cannot delete parent task with children');
85
- END;
86
- `);
87
- }
88
- }
89
37
  export function ensureScheduleTaskSchema(db) {
90
38
  const columns = db.prepare(`PRAGMA table_info(tasks)`).all();
91
39
  const hasScheduledDateColumn = columns.some((c) => c.name === 'scheduled_date');
@@ -226,9 +174,6 @@ export function ensureWorkspaceSchema(db) {
226
174
  email TEXT NOT NULL,
227
175
  display_name TEXT,
228
176
  avatar_url TEXT,
229
- auth_provider TEXT,
230
- external_auth_id TEXT,
231
- password_hash TEXT,
232
177
  email_verified_at TEXT,
233
178
  beta_access BOOLEAN NOT NULL DEFAULT TRUE,
234
179
  is_disabled BOOLEAN NOT NULL DEFAULT FALSE,
@@ -319,9 +264,6 @@ export function ensureWorkspaceSchema(db) {
319
264
  const postgresUserRepairs = [
320
265
  `ALTER TABLE users ADD COLUMN IF NOT EXISTS display_name TEXT`,
321
266
  `ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_url TEXT`,
322
- `ALTER TABLE users ADD COLUMN IF NOT EXISTS auth_provider TEXT`,
323
- `ALTER TABLE users ADD COLUMN IF NOT EXISTS external_auth_id TEXT`,
324
- `ALTER TABLE users ADD COLUMN IF NOT EXISTS password_hash TEXT`,
325
267
  `ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified_at TEXT`,
326
268
  `ALTER TABLE users ADD COLUMN IF NOT EXISTS beta_access BOOLEAN NOT NULL DEFAULT TRUE`,
327
269
  `ALTER TABLE users ADD COLUMN IF NOT EXISTS is_disabled BOOLEAN NOT NULL DEFAULT FALSE`,
@@ -357,9 +299,6 @@ export function ensureWorkspaceSchema(db) {
357
299
  const sqliteUserRepairs = [
358
300
  { name: 'display_name', sql: `ALTER TABLE users ADD COLUMN display_name TEXT` },
359
301
  { name: 'avatar_url', sql: `ALTER TABLE users ADD COLUMN avatar_url TEXT` },
360
- { name: 'auth_provider', sql: `ALTER TABLE users ADD COLUMN auth_provider TEXT` },
361
- { name: 'external_auth_id', sql: `ALTER TABLE users ADD COLUMN external_auth_id TEXT` },
362
- { name: 'password_hash', sql: `ALTER TABLE users ADD COLUMN password_hash TEXT` },
363
302
  { name: 'email_verified_at', sql: `ALTER TABLE users ADD COLUMN email_verified_at TEXT` },
364
303
  { name: 'beta_access', sql: `ALTER TABLE users ADD COLUMN beta_access BOOLEAN NOT NULL DEFAULT TRUE` },
365
304
  { name: 'is_disabled', sql: `ALTER TABLE users ADD COLUMN is_disabled BOOLEAN NOT NULL DEFAULT FALSE` },
@@ -697,6 +636,7 @@ export function ensureEntitlementsSchema(db) {
697
636
  plan_id TEXT PRIMARY KEY,
698
637
  tenant_id TEXT NOT NULL DEFAULT 'default',
699
638
  display_name TEXT NOT NULL,
639
+ description TEXT,
700
640
  status TEXT NOT NULL DEFAULT 'active',
701
641
  created_at TEXT NOT NULL,
702
642
  updated_at TEXT NOT NULL
@@ -727,6 +667,17 @@ export function ensureEntitlementsSchema(db) {
727
667
  FOREIGN KEY (plan_version_id) REFERENCES plan_versions(plan_version_id) ON DELETE CASCADE
728
668
  );
729
669
 
670
+ CREATE TABLE IF NOT EXISTS plan_feature_catalog_overrides (
671
+ tenant_id TEXT NOT NULL DEFAULT 'default',
672
+ feature_key TEXT NOT NULL,
673
+ public_label TEXT,
674
+ public_description TEXT,
675
+ public_display_order INTEGER,
676
+ created_at TEXT NOT NULL,
677
+ updated_at TEXT NOT NULL,
678
+ PRIMARY KEY (tenant_id, feature_key)
679
+ );
680
+
730
681
  CREATE TABLE IF NOT EXISTS account_entitlements (
731
682
  account_id TEXT PRIMARY KEY,
732
683
  tenant_id TEXT NOT NULL DEFAULT 'default',
@@ -763,6 +714,8 @@ export function ensureEntitlementsSchema(db) {
763
714
  ON plan_versions(tenant_id, plan_id, is_default);
764
715
  CREATE INDEX IF NOT EXISTS idx_plan_feature_matrix_plan_version
765
716
  ON plan_feature_matrix(tenant_id, plan_version_id);
717
+ CREATE INDEX IF NOT EXISTS idx_plan_feature_catalog_overrides_tenant
718
+ ON plan_feature_catalog_overrides(tenant_id, updated_at);
766
719
  CREATE INDEX IF NOT EXISTS idx_account_entitlements_state
767
720
  ON account_entitlements(tenant_id, entitlement_state);
768
721
  CREATE INDEX IF NOT EXISTS idx_account_entitlements_plan_version
@@ -774,6 +727,15 @@ export function ensureEntitlementsSchema(db) {
774
727
  CREATE INDEX IF NOT EXISTS idx_entitlement_audit_log_actor
775
728
  ON entitlement_audit_log(tenant_id, actor_user_id, created_at);
776
729
  `);
730
+ const planCatalogColumns = db.prepare(`PRAGMA table_info(plan_catalog)`).all();
731
+ const hasDescriptionColumn = planCatalogColumns.some((c) => c.name === 'description');
732
+ if (!hasDescriptionColumn) {
733
+ db.exec(`ALTER TABLE plan_catalog ADD COLUMN description TEXT`);
734
+ }
735
+ const planFeatureCatalogOverrideColumns = db.prepare(`PRAGMA table_info(plan_feature_catalog_overrides)`).all();
736
+ if (!planFeatureCatalogOverrideColumns.some((column) => column.name === 'public_display_order')) {
737
+ db.exec(`ALTER TABLE plan_feature_catalog_overrides ADD COLUMN public_display_order INTEGER`);
738
+ }
777
739
  }
778
740
  export function ensureTaskAssetsSchema(db) {
779
741
  db.exec(`
@@ -853,6 +815,7 @@ export function ensureWorkspaceAssetsSchema(db) {
853
815
  tenant_id TEXT NOT NULL DEFAULT 'default',
854
816
  workspace_id TEXT NOT NULL,
855
817
  asset_id TEXT NOT NULL,
818
+ target_type TEXT NOT NULL DEFAULT 'task',
856
819
  task_id TEXT NOT NULL,
857
820
  role TEXT NOT NULL,
858
821
  display_name TEXT,
@@ -873,6 +836,15 @@ export function ensureWorkspaceAssetsSchema(db) {
873
836
  if (!hasDisplayNameColumn) {
874
837
  db.exec(`ALTER TABLE workspace_asset_links ADD COLUMN display_name TEXT`);
875
838
  }
839
+ const hasTargetTypeColumn = linkColumns.some((c) => c.name === 'target_type');
840
+ if (!hasTargetTypeColumn) {
841
+ db.exec(`ALTER TABLE workspace_asset_links ADD COLUMN target_type TEXT NOT NULL DEFAULT 'task'`);
842
+ db.exec(`UPDATE workspace_asset_links SET target_type = 'task' WHERE target_type IS NULL OR TRIM(target_type) = ''`);
843
+ }
844
+ db.exec(`
845
+ CREATE INDEX IF NOT EXISTS idx_workspace_asset_links_target
846
+ ON workspace_asset_links(tenant_id, workspace_id, target_type, task_id, deleted_at, updated_at)
847
+ `);
876
848
  }
877
849
  export function ensureDocumentReferenceSchema(db) {
878
850
  const columns = db.prepare(`PRAGMA table_info(workspace_assets)`).all();
@@ -1160,3 +1132,141 @@ export function ensureDocumentReviewSessionsSchema(db) {
1160
1132
  }
1161
1133
  }
1162
1134
  }
1135
+ export function ensureAuthIdentitySchema(db) {
1136
+ db.exec(`
1137
+ CREATE TABLE IF NOT EXISTS user_auth_identities (
1138
+ id TEXT PRIMARY KEY,
1139
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1140
+ user_id TEXT NOT NULL,
1141
+ provider TEXT NOT NULL,
1142
+ provider_subject TEXT,
1143
+ provider_email TEXT,
1144
+ provider_email_verified INTEGER,
1145
+ password_hash TEXT,
1146
+ display_name_snapshot TEXT,
1147
+ avatar_url_snapshot TEXT,
1148
+ metadata_json TEXT,
1149
+ created_at TEXT NOT NULL,
1150
+ updated_at TEXT NOT NULL,
1151
+ last_login_at TEXT,
1152
+ linked_at TEXT,
1153
+ disabled_at TEXT,
1154
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
1155
+ );
1156
+
1157
+ CREATE TABLE IF NOT EXISTS auth_oauth_states (
1158
+ state TEXT PRIMARY KEY,
1159
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1160
+ provider TEXT NOT NULL,
1161
+ flow_mode TEXT NOT NULL,
1162
+ user_id TEXT,
1163
+ invite_token TEXT,
1164
+ return_to TEXT,
1165
+ pkce_verifier TEXT,
1166
+ nonce TEXT,
1167
+ expires_at TEXT NOT NULL,
1168
+ consumed_at TEXT,
1169
+ created_at TEXT NOT NULL,
1170
+ created_ip TEXT
1171
+ );
1172
+ `);
1173
+ if (db.provider === 'postgres') {
1174
+ try {
1175
+ db.exec(`
1176
+ ALTER TABLE user_auth_identities
1177
+ ADD CONSTRAINT uq_auth_identity_provider_subject
1178
+ UNIQUE (tenant_id, provider, provider_subject)
1179
+ `);
1180
+ }
1181
+ catch { /* already exists */ }
1182
+ try {
1183
+ db.exec(`
1184
+ ALTER TABLE user_auth_identities
1185
+ ADD CONSTRAINT uq_auth_identity_user_provider
1186
+ UNIQUE (tenant_id, user_id, provider)
1187
+ `);
1188
+ }
1189
+ catch { /* already exists */ }
1190
+ }
1191
+ else {
1192
+ // SQLite: create unique indexes if they don't already exist
1193
+ db.exec(`
1194
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_provider_subject
1195
+ ON user_auth_identities(tenant_id, provider, provider_subject)
1196
+ WHERE provider_subject IS NOT NULL;
1197
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_user_provider
1198
+ ON user_auth_identities(tenant_id, user_id, provider);
1199
+ `);
1200
+ }
1201
+ db.exec(`
1202
+ CREATE INDEX IF NOT EXISTS idx_auth_identities_user
1203
+ ON user_auth_identities(tenant_id, user_id);
1204
+ CREATE INDEX IF NOT EXISTS idx_auth_oauth_states_expires
1205
+ ON auth_oauth_states(expires_at);
1206
+ `);
1207
+ // ── Backfill: migrate any remaining users.password_hash rows into user_auth_identities.
1208
+ // Guarded: wrapped in try/catch — if the legacy column was already dropped (T-837),
1209
+ // the SELECT will throw and we skip cleanly. Idempotent via unique index.
1210
+ try {
1211
+ const now = new Date().toISOString();
1212
+ const usersWithPassword = db.prepare(`
1213
+ SELECT id, tenant_id, password_hash
1214
+ FROM users
1215
+ WHERE password_hash IS NOT NULL
1216
+ AND TRIM(COALESCE(password_hash, '')) != ''
1217
+ `).all();
1218
+ const hasIdentity = db.prepare(`
1219
+ SELECT id FROM user_auth_identities
1220
+ WHERE tenant_id = ? AND user_id = ? AND provider = 'password'
1221
+ LIMIT 1
1222
+ `);
1223
+ const insertIdentity = db.prepare(`
1224
+ INSERT INTO user_auth_identities
1225
+ (id, tenant_id, user_id, provider, password_hash, created_at, updated_at, linked_at)
1226
+ VALUES (?, ?, ?, 'password', ?, ?, ?, ?)
1227
+ `);
1228
+ for (const user of usersWithPassword) {
1229
+ const existing = hasIdentity.get(user.tenant_id, user.id);
1230
+ if (existing?.id)
1231
+ continue;
1232
+ const identityId = `identity-${user.id}-pwd`;
1233
+ try {
1234
+ insertIdentity.run(identityId, user.tenant_id, user.id, user.password_hash, now, now, now);
1235
+ }
1236
+ catch {
1237
+ // Concurrent startup or duplicate — safe to skip.
1238
+ }
1239
+ }
1240
+ }
1241
+ catch {
1242
+ // users.password_hash column already absent — backfill not needed.
1243
+ }
1244
+ // ── Drop legacy auth columns from users (T-837).
1245
+ // Safe to run repeatedly: column-existence checks prevent double-drop errors.
1246
+ if (db.provider === 'postgres') {
1247
+ // PostgreSQL: DROP COLUMN IF EXISTS is a single atomic statement.
1248
+ try {
1249
+ db.exec(`
1250
+ ALTER TABLE users
1251
+ DROP COLUMN IF EXISTS auth_provider,
1252
+ DROP COLUMN IF EXISTS external_auth_id,
1253
+ DROP COLUMN IF EXISTS password_hash
1254
+ `);
1255
+ }
1256
+ catch { /* best-effort — ignore if already absent */ }
1257
+ }
1258
+ else {
1259
+ // SQLite: DROP COLUMN requires 3.35.0+ (2021-03-12).
1260
+ // better-sqlite3 ships its own SQLite which satisfies this requirement.
1261
+ // Guard each drop individually to tolerate partial completion.
1262
+ const currentUserCols = new Set(db.prepare(`PRAGMA table_info(users)`).all().map(c => c.name));
1263
+ for (const col of ['auth_provider', 'external_auth_id', 'password_hash']) {
1264
+ if (currentUserCols.has(col)) {
1265
+ try {
1266
+ db.exec(`ALTER TABLE users DROP COLUMN ${col}`);
1267
+ }
1268
+ catch { /* ignore — concurrent drop or already gone */ }
1269
+ }
1270
+ }
1271
+ }
1272
+ }
@@ -1,4 +1,6 @@
1
1
  import Database from 'better-sqlite3';
2
+ import fs from 'fs';
3
+ import path from 'path';
2
4
  import { afterEach, describe, expect, it } from 'vitest';
3
5
  import { ensureAnnotatedAttachmentSessionsSchema, ensureDocumentReviewSessionsSchema, ensureWorkspaceSchema } from './taskSchemaMigrations';
4
6
  const openDbs = [];
@@ -208,3 +210,16 @@ describe('ensureDocumentReviewSessionsSchema', () => {
208
210
  expect(row?.author_actor_id).toBe('user-1');
209
211
  });
210
212
  });
213
+ describe('postgres bootstrap parity', () => {
214
+ it('includes workspace asset tables and target_type indexes', () => {
215
+ const sqlPath = path.resolve(process.cwd(), 'src/migrations/postgres/001_init.sql');
216
+ const sql = fs.readFileSync(sqlPath, 'utf8');
217
+ expect(sql).toContain('CREATE TABLE IF NOT EXISTS workspace_assets');
218
+ expect(sql).toContain('CREATE TABLE IF NOT EXISTS workspace_asset_links');
219
+ expect(sql).toContain('target_type TEXT NOT NULL DEFAULT \'task\'');
220
+ expect(sql).toContain('display_name TEXT');
221
+ expect(sql).toContain('reference_number INTEGER');
222
+ expect(sql).toContain('CREATE INDEX IF NOT EXISTS idx_workspace_asset_links_target');
223
+ expect(sql).toContain('CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_assets_workspace_kind_reference');
224
+ });
225
+ });
@@ -284,7 +284,7 @@ export const workflowDocs = [
284
284
  ' - `### Next Action`',
285
285
  '6. Ask whether to save as `{artifacts.implementationPlan.path}`.',
286
286
  '7. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "implementation-plan.md"`.',
287
- ' - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).',
287
+ ' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
288
288
  '8. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.',
289
289
  '9. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
290
290
  '10. Print `prompts.handoffPrompt` and stop.'
@@ -358,7 +358,7 @@ export const workflowDocs = [
358
358
  ' - `### Next Step`',
359
359
  '4. Ask whether to save as `{artifacts.collaborationBlueprint.path}`.',
360
360
  '5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "collaboration-blueprint.md"`.',
361
- ' - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).',
361
+ ' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
362
362
  '6. Ask concise clarification questions only if required fields above are missing.',
363
363
  '7. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.',
364
364
  '8. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
@@ -435,14 +435,14 @@ export const workflowDocs = [
435
435
  ' - `### Next Step`',
436
436
  '4. Ask whether to save as `{artifacts.evaluationReport.path}`.',
437
437
  '5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "evaluation.md"`.',
438
- ' - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).',
438
+ ' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
439
439
  '6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
440
440
  '',
441
441
  '## 3. Optional Report Artifact',
442
442
  '',
443
443
  '1. Use report files only when user opts in.',
444
444
  '2. Save to `task attachment "evaluation.md"` and attach to task context when approved.',
445
- ' - If the document is not task-specific, store it under `docs/` and optionally link it with `{{MCP_PREFIX}}link_task_context`.',
445
+ ' - If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.',
446
446
  '3. Stop.'
447
447
  ].join('\n')
448
448
  }
@@ -513,7 +513,7 @@ export const workflowDocs = [
513
513
  ' - `### Next Status`',
514
514
  '3. Ask whether to save as `{artifacts.reviewReport.path}`.',
515
515
  '4. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "review.md"`.',
516
- ' - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).',
516
+ ' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
517
517
  '5. Keep status within allowed statuses (`task`, `on-hold`, `in-progress`, `review`, `done`) unless user explicitly instructs otherwise.',
518
518
  ' - If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.',
519
519
  '6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
@@ -522,7 +522,7 @@ export const workflowDocs = [
522
522
  '',
523
523
  '1. Use report files only when user opts in.',
524
524
  '2. Save to `task attachment "review.md"` and attach to task context when approved.',
525
- ' - If the document is not task-specific, store it under `docs/` and optionally link it with `{{MCP_PREFIX}}link_task_context`.',
525
+ ' - If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.',
526
526
  '3. Stop.'
527
527
  ].join('\n')
528
528
  }
@@ -102,7 +102,7 @@ content:
102
102
  - `### Next Step`
103
103
  4. Ask whether to save as `{artifacts.collaborationBlueprint.path}`.
104
104
  5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "collaboration-blueprint.md"`.
105
- - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).
105
+ - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
106
106
  6. Ask concise clarification questions only if required fields above are missing.
107
107
  7. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.
108
108
  8. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
@@ -106,12 +106,12 @@ content:
106
106
  - `### Next Step`
107
107
  4. Ask whether to save as `{artifacts.evaluationReport.path}`.
108
108
  5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "evaluation.md"`.
109
- - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).
109
+ - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
110
110
  6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
111
111
 
112
112
  ## 3. Optional Report Artifact
113
113
 
114
114
  1. Use report files only when user opts in.
115
115
  2. Save to `task attachment "evaluation.md"` and attach to task context when approved.
116
- - If the document is not task-specific, store it under `docs/` and optionally link it with `{{MCP_PREFIX}}link_task_context`.
116
+ - If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.
117
117
  3. Stop.
@@ -113,7 +113,7 @@ content:
113
113
  - `### Next Action`
114
114
  6. Ask whether to save as `{artifacts.implementationPlan.path}`.
115
115
  7. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "implementation-plan.md"`.
116
- - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).
116
+ - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
117
117
  8. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.
118
118
  9. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
119
119
  10. Print `prompts.handoffPrompt` and stop.
@@ -102,7 +102,7 @@ content:
102
102
  - `### Next Status`
103
103
  3. Ask whether to save as `{artifacts.reviewReport.path}`.
104
104
  4. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "review.md"`.
105
- - Use `{{MCP_PREFIX}}link_task_context` only for linking existing files/URLs (for example, shared docs under `docs/`).
105
+ - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
106
106
  5. Keep status within allowed statuses (`task`, `on-hold`, `in-progress`, `review`, `done`) unless user explicitly instructs otherwise.
107
107
  - If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.
108
108
  6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
@@ -111,5 +111,5 @@ content:
111
111
 
112
112
  1. Use report files only when user opts in.
113
113
  2. Save to `task attachment "review.md"` and attach to task context when approved.
114
- - If the document is not task-specific, store it under `docs/` and optionally link it with `{{MCP_PREFIX}}link_task_context`.
114
+ - If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.
115
115
  3. Stop.
@@ -209,9 +209,9 @@ describe('annotated attachment routes', () => {
209
209
  core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
210
210
  const now = new Date().toISOString();
211
211
  core.db.prepare(`
212
- INSERT INTO users (id, tenant_id, email, display_name, auth_provider, external_auth_id, beta_access, is_disabled, created_at, updated_at)
213
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
214
- `).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User', 'local', 'user-local-1', 1, 0, now, now);
212
+ INSERT INTO users (id, tenant_id, email, display_name, beta_access, is_disabled, created_at, updated_at)
213
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
214
+ `).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User', 1, 0, now, now);
215
215
  core.db.prepare(`
216
216
  INSERT INTO workspace_memberships (id, tenant_id, workspace_id, user_id, role, status, created_at, updated_at)
217
217
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Apple Sign In adapter.
3
+ *
4
+ * Flow:
5
+ * 1. Redirect user to buildAppleAuthUrl() with response_mode=form_post.
6
+ * 2. Apple POSTs `code`, `state`, `id_token` and optional `user` JSON to the callback.
7
+ * 3. Call exchangeAppleCode() which mints the client-secret JWT, exchanges the code,
8
+ * and decodes the id_token payload for the canonical profile.
9
+ *
10
+ * Notes:
11
+ * - Apple never re-sends the user's name after the very first authorisation.
12
+ * If the user JSON arrives on first login it is captured; subsequent logins
13
+ * use the snapshot stored in user_auth_identities.
14
+ * - Private-relay email addresses contain `.privaterelay.appleid.com`; the
15
+ * `is_private_email` claim is set to `true` in that case.
16
+ */
17
+ import type { NormalizedOAuthProfile } from './types.js';
18
+ export declare function buildAppleAuthUrl(input: {
19
+ servicesId: string;
20
+ redirectUri: string;
21
+ state: string;
22
+ nonce: string;
23
+ }): string;
24
+ export declare function exchangeAppleCode(input: {
25
+ code: string;
26
+ redirectUri: string;
27
+ servicesId: string;
28
+ teamId: string;
29
+ keyId: string;
30
+ privateKeyPem: string;
31
+ /** Optional: JSON string Apple sends on first authorisation containing { name }. */
32
+ userJson?: string | null;
33
+ }): Promise<NormalizedOAuthProfile>;
@@ -0,0 +1,82 @@
1
+ import { getAppleClientSecret } from './appleClientSecret.js';
2
+ export function buildAppleAuthUrl(input) {
3
+ const params = new URLSearchParams({
4
+ client_id: input.servicesId,
5
+ redirect_uri: input.redirectUri,
6
+ response_type: 'code id_token',
7
+ scope: 'name email',
8
+ state: input.state,
9
+ nonce: input.nonce,
10
+ response_mode: 'form_post',
11
+ });
12
+ return `https://appleid.apple.com/auth/authorize?${params.toString()}`;
13
+ }
14
+ export async function exchangeAppleCode(input) {
15
+ const clientSecret = getAppleClientSecret({
16
+ servicesId: input.servicesId,
17
+ teamId: input.teamId,
18
+ keyId: input.keyId,
19
+ privateKeyPem: input.privateKeyPem,
20
+ });
21
+ // Step 1: exchange authorisation code for tokens
22
+ const tokenRes = await fetch('https://appleid.apple.com/auth/token', {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
25
+ body: new URLSearchParams({
26
+ grant_type: 'authorization_code',
27
+ code: input.code,
28
+ redirect_uri: input.redirectUri,
29
+ client_id: input.servicesId,
30
+ client_secret: clientSecret,
31
+ }).toString(),
32
+ });
33
+ if (!tokenRes.ok) {
34
+ const body = await tokenRes.text().catch(() => '');
35
+ throw new Error(`Apple token exchange failed (${tokenRes.status}): ${body}`);
36
+ }
37
+ const tokens = await tokenRes.json();
38
+ const idToken = String(tokens.id_token || '').trim();
39
+ if (!idToken)
40
+ throw new Error('Apple token exchange: missing id_token');
41
+ // Step 2: decode id_token payload (trust Apple — skip signature verification here)
42
+ const parts = idToken.split('.');
43
+ if (parts.length !== 3)
44
+ throw new Error('Apple id_token: not a valid JWT');
45
+ let payload;
46
+ try {
47
+ payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
48
+ }
49
+ catch {
50
+ throw new Error('Apple id_token: could not decode payload');
51
+ }
52
+ const sub = String(payload.sub || '').trim();
53
+ if (!sub)
54
+ throw new Error('Apple id_token: missing sub');
55
+ const email = String(payload.email || '').trim().toLowerCase() || null;
56
+ const emailVerified = payload.email_verified === true || payload.email_verified === 'true';
57
+ const isPrivateEmail = payload.is_private_email === true || payload.is_private_email === 'true';
58
+ // Step 3: extract display name from one-time `user` JSON (first-login only)
59
+ let displayName = null;
60
+ if (input.userJson) {
61
+ try {
62
+ const userObj = JSON.parse(input.userJson);
63
+ const firstName = String(userObj?.name?.firstName || '').trim();
64
+ const lastName = String(userObj?.name?.lastName || '').trim();
65
+ const full = [firstName, lastName].filter(Boolean).join(' ');
66
+ if (full)
67
+ displayName = full;
68
+ }
69
+ catch {
70
+ // malformed — ignore
71
+ }
72
+ }
73
+ return {
74
+ provider: 'apple',
75
+ providerSubject: sub,
76
+ email,
77
+ emailVerified,
78
+ displayName,
79
+ avatarUrl: null, // Apple never provides an avatar
80
+ isPrivateEmail,
81
+ };
82
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Apple OAuth client-secret JWT minter.
3
+ *
4
+ * Apple Sign In requires a short-lived JWT as the OAuth `client_secret` rather
5
+ * than a static secret. The JWT must be:
6
+ * - Signed with ES256 (ECDSA P-256) using the private key registered in App Store Connect
7
+ * - Valid for at most 6 months (Apple enforces this; we use 10 min for freshness)
8
+ * - Cached so callers don't re-sign on every request
9
+ *
10
+ * Reference: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
11
+ */
12
+ export interface AppleClientSecretConfig {
13
+ /** 10-character Apple Team ID */
14
+ teamId: string;
15
+ /** Services ID (bundle ID) registered in App Store Connect */
16
+ servicesId: string;
17
+ /** Key ID of the registered Sign In with Apple key */
18
+ keyId: string;
19
+ /** PEM-encoded EC P-256 private key downloaded from App Store Connect */
20
+ privateKeyPem: string;
21
+ }
22
+ /**
23
+ * Returns a cached client-secret JWT for the given Apple config, minting a
24
+ * fresh one if the cache entry is absent or within 60 s of expiry.
25
+ */
26
+ export declare function getAppleClientSecret(cfg: AppleClientSecretConfig): string;
27
+ /**
28
+ * Force-mints a fresh JWT (bypasses cache) for health-check purposes.
29
+ * Returns success/error without updating the cache.
30
+ */
31
+ export declare function testMintAppleClientSecret(cfg: AppleClientSecretConfig): {
32
+ ok: true;
33
+ token: string;
34
+ } | {
35
+ ok: false;
36
+ error: string;
37
+ };