@taskforcehq/taskforce 0.3.305 → 0.3.307

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 (440) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +603 -107
  3. package/dist/TaskforceCore.js +216 -43
  4. package/dist/TaskforceCore.test.js +611 -28
  5. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  8. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  10. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  11. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  12. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  13. package/dist/compat/workspaceSyncCompat.js +46 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  16. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  17. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  18. package/dist/components/context/ContextAttachmentManager.js +561 -0
  19. package/dist/components/features/AgentsModule.d.ts +14 -1
  20. package/dist/components/features/AgentsModule.js +366 -18
  21. package/dist/components/features/AgentsModule.test.js +507 -8
  22. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  23. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  24. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  25. package/dist/components/features/DocumentIndex.d.ts +2 -2
  26. package/dist/components/features/DocumentIndex.js +3 -4
  27. package/dist/components/features/DocumentViewer.d.ts +7 -1
  28. package/dist/components/features/DocumentViewer.js +62 -4
  29. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  30. package/dist/components/features/DocumentWorkspace.js +15 -4
  31. package/dist/components/features/DocumentWorkspace.test.js +51 -1
  32. package/dist/components/features/TaskSettings.d.ts +2 -0
  33. package/dist/components/features/TaskSettings.js +83 -78
  34. package/dist/components/features/TaskSettings.test.js +171 -82
  35. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  36. package/dist/components/task/TaskActionHeader.js +11 -5
  37. package/dist/components/task/TaskActionHeader.test.js +10 -0
  38. package/dist/components/task/TaskCard.js +24 -29
  39. package/dist/components/task/TaskCard.test.js +38 -3
  40. package/dist/components/task/TaskContextUpload.js +4 -526
  41. package/dist/components/task/TaskContextUpload.test.js +3 -3
  42. package/dist/components/task/TaskForm.js +61 -116
  43. package/dist/components/task/TaskForm.test.js +110 -8
  44. package/dist/components/task/TaskKanban.d.ts +2 -1
  45. package/dist/components/task/TaskKanban.js +94 -21
  46. package/dist/components/task/TaskKanban.test.js +36 -0
  47. package/dist/components/task/TaskList.js +5 -1
  48. package/dist/components/task/TaskList.test.js +4 -0
  49. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  50. package/dist/components/ui/EditableAvatarButton.js +18 -0
  51. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  52. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  53. package/dist/components/views/AppShellHeader.d.ts +3 -1
  54. package/dist/components/views/AppShellHeader.js +27 -11
  55. package/dist/components/views/AppShellHeader.test.js +18 -1
  56. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  57. package/dist/components/views/PlanComparisonPage.js +78 -14
  58. package/dist/components/views/PlanComparisonPage.test.js +257 -0
  59. package/dist/components/views/PlansPage.js +127 -36
  60. package/dist/components/views/PlansPage.test.js +294 -12
  61. package/dist/components/views/StandaloneLayout.js +529 -592
  62. package/dist/components/views/WidgetView.js +10 -8
  63. package/dist/components/views/panels/PlanningDrawer.d.ts +25 -5
  64. package/dist/components/views/panels/PlanningDrawer.js +270 -55
  65. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  66. package/dist/components/views/panels/PlanningDrawer.test.js +529 -0
  67. package/dist/components/views/planningScope.d.ts +20 -0
  68. package/dist/components/views/planningScope.js +25 -0
  69. package/dist/components/views/planningScope.test.d.ts +1 -0
  70. package/dist/components/views/planningScope.test.js +60 -0
  71. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  72. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  73. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  74. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  75. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  76. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  77. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  78. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  79. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  80. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  81. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  82. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  83. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  84. package/dist/config/envSchema.js +1 -0
  85. package/dist/core/AiProfileService.d.ts +46 -2
  86. package/dist/core/AiProfileService.js +1073 -82
  87. package/dist/core/AiProfileService.test.js +19 -1
  88. package/dist/core/AiProfiles.test.js +1249 -44
  89. package/dist/core/AttachmentLinkService.d.ts +3 -0
  90. package/dist/core/AttachmentLinkService.js +110 -23
  91. package/dist/core/AuthIdentityService.d.ts +107 -0
  92. package/dist/core/AuthIdentityService.js +284 -0
  93. package/dist/core/AuthTokenService.d.ts +4 -0
  94. package/dist/core/AuthTokenService.js +34 -6
  95. package/dist/core/AuthTokenService.test.js +3 -3
  96. package/dist/core/EntitlementsPolicy.test.js +2 -2
  97. package/dist/core/GlobalSettingsService.js +127 -6
  98. package/dist/core/McpTokenService.d.ts +12 -0
  99. package/dist/core/McpTokenService.js +44 -1
  100. package/dist/core/PlanEntitlementService.d.ts +23 -2
  101. package/dist/core/PlanEntitlementService.js +343 -57
  102. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  103. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  104. package/dist/core/PlanVersionPolicy.test.js +35 -2
  105. package/dist/core/PlanningEntities.test.js +418 -4
  106. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  107. package/dist/core/SyncReconciliationService.js +2 -65
  108. package/dist/core/SystemAdmin.test.js +42 -42
  109. package/dist/core/TaskActivityService.d.ts +28 -1
  110. package/dist/core/TaskActivityService.js +227 -46
  111. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  112. package/dist/core/TaskActivitySync.test.js +90 -0
  113. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  114. package/dist/core/TaskAuditTimeline.test.js +96 -0
  115. package/dist/core/Taskforce.d.ts +415 -40
  116. package/dist/core/Taskforce.ids.test.js +58 -0
  117. package/dist/core/Taskforce.js +1543 -266
  118. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  119. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  120. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  121. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  122. package/dist/core/WorkspaceLifecycleService.js +0 -5
  123. package/dist/core/WorkspacePermissions.test.js +25 -1
  124. package/dist/core/WorkspacePlanMode.test.js +117 -16
  125. package/dist/core/shared.d.ts +1 -1
  126. package/dist/core/shared.js +5 -1
  127. package/dist/core/types.d.ts +75 -2
  128. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +28 -2
  129. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  130. package/dist/hooks/bootstrapTimeouts.js +8 -0
  131. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  132. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  133. package/dist/hooks/sync/bootstrap.js +2 -0
  134. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  135. package/dist/hooks/sync/controlPlane.js +78 -0
  136. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  137. package/dist/hooks/sync/controlPlane.test.js +121 -0
  138. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  139. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  140. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  141. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  142. package/dist/hooks/sync/orchestratorShared.d.ts +13 -4
  143. package/dist/hooks/sync/orchestratorShared.js +129 -48
  144. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  145. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  146. package/dist/hooks/sync/pullLifecycle.js +24 -0
  147. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  148. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  149. package/dist/hooks/sync/recovery.d.ts +16 -2
  150. package/dist/hooks/sync/recovery.js +179 -53
  151. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  152. package/dist/hooks/sync/recovery.test.js +292 -0
  153. package/dist/hooks/sync/transfers.d.ts +16 -3
  154. package/dist/hooks/sync/transfers.js +210 -66
  155. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  156. package/dist/hooks/sync/transfers.test.js +928 -0
  157. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  158. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  159. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  160. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  161. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  162. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  163. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  164. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  165. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  166. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  167. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  168. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  169. package/dist/hooks/useFilterSort.js +13 -13
  170. package/dist/hooks/useFilterSort.test.js +87 -1
  171. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  172. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  173. package/dist/hooks/useSyncOrchestrator.js +409 -183
  174. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  175. package/dist/hooks/useTaskData.js +8 -3
  176. package/dist/hooks/useTaskData.test.js +45 -0
  177. package/dist/hooks/useTaskforce.d.ts +145 -52
  178. package/dist/hooks/useTaskforce.js +592 -253
  179. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  180. package/dist/hooks/useTaskforce.runtime-routing.test.js +246 -0
  181. package/dist/hooks/useTaskforce.sync-behavior.test.js +2468 -201
  182. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  183. package/dist/hooks/useWorkspaceSyncController.js +13 -2
  184. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  185. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  186. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +328 -40
  187. package/dist/localization/locales/en-US.d.ts +0 -1
  188. package/dist/localization/locales/en-US.js +0 -1
  189. package/dist/localization/locales/es-419.js +0 -1
  190. package/dist/localization/locales/pt-BR.js +0 -1
  191. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  192. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  193. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  194. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  195. package/dist/mcp/clientIntegrations.d.ts +1 -0
  196. package/dist/mcp/clientIntegrations.js +25 -0
  197. package/dist/mcp/clientIntegrations.test.js +14 -3
  198. package/dist/mcp/clientProfileDefaults.js +3 -3
  199. package/dist/mcp/collaborationRegistrar.js +7 -7
  200. package/dist/mcp/documentAssetRegistrar.js +116 -10
  201. package/dist/mcp/runtime.d.ts +3 -0
  202. package/dist/mcp/runtime.js +1099 -185
  203. package/dist/mcp/runtime.test.js +1491 -140
  204. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  205. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  206. package/dist/mcp/toolRegistry.d.ts +56 -0
  207. package/dist/mcp/toolRegistry.js +8 -0
  208. package/dist/mcp/toolRegistry.test.js +4 -0
  209. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  210. package/dist/migrations/taskSchemaMigrations.js +192 -61
  211. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  212. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  213. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  214. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  215. package/dist/resources/templates/workflows/plan.yaml +1 -1
  216. package/dist/resources/templates/workflows/review.yaml +2 -2
  217. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  218. package/dist/server/aiProfileAvatarAssets.js +319 -0
  219. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  220. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  221. package/dist/server/annotatedAttachmentsRoutes.test.js +69 -10
  222. package/dist/server/auth/providers/apple.d.ts +33 -0
  223. package/dist/server/auth/providers/apple.js +82 -0
  224. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  225. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  226. package/dist/server/auth/providers/github.d.ts +26 -0
  227. package/dist/server/auth/providers/github.js +86 -0
  228. package/dist/server/auth/providers/google.d.ts +21 -0
  229. package/dist/server/auth/providers/google.js +56 -0
  230. package/dist/server/auth/providers/types.d.ts +21 -0
  231. package/dist/server/auth/providers/types.js +1 -0
  232. package/dist/server/auth.d.ts +2 -0
  233. package/dist/server/auth.js +6 -3
  234. package/dist/server/documentReviewRoutes.test.js +36 -3
  235. package/dist/server/index.cookieProxy.test.js +1 -0
  236. package/dist/server/index.d.ts +12 -0
  237. package/dist/server/index.js +124 -9
  238. package/dist/server/index.rateLimit.test.js +3 -0
  239. package/dist/server/index.test.js +111 -3
  240. package/dist/server/routes/admin.d.ts +3 -0
  241. package/dist/server/routes/admin.js +459 -28
  242. package/dist/server/routes/annotatedAttachments.js +51 -0
  243. package/dist/server/routes/auth.d.ts +26 -2
  244. package/dist/server/routes/auth.js +1170 -71
  245. package/dist/server/routes/authSupport.d.ts +5 -1
  246. package/dist/server/routes/authSupport.js +13 -2
  247. package/dist/server/routes/billing.js +372 -35
  248. package/dist/server/routes/billing.test.js +595 -12
  249. package/dist/server/routes/documents.d.ts +1 -0
  250. package/dist/server/routes/documents.js +182 -70
  251. package/dist/server/routes/shared.d.ts +28 -2
  252. package/dist/server/routes/shared.js +69 -14
  253. package/dist/server/routes/sync.integration.test.js +658 -39
  254. package/dist/server/routes/sync.js +74 -608
  255. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  256. package/dist/server/routes/syncAuxRoutes.js +91 -0
  257. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  258. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  259. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  260. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  261. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  262. package/dist/server/routes/syncPushRoutes.js +212 -0
  263. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  264. package/dist/server/routes/syncRouteGuards.js +67 -0
  265. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  266. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  267. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  268. package/dist/server/routes/syncRouteTypes.js +1 -0
  269. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  270. package/dist/server/routes/syncSnapshotRoutes.js +182 -0
  271. package/dist/server/routes/tasks.js +55 -10
  272. package/dist/server/routes/workspaces.js +36 -5
  273. package/dist/server/routes.js +147 -11
  274. package/dist/server/routes.test.js +2704 -183
  275. package/dist/services/workflowExportSnapshots.test.js +2 -0
  276. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  277. package/dist/shared/aiProfileSeatScope.js +21 -0
  278. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  279. package/dist/shared/aiProfileSurfaceType.js +10 -8
  280. package/dist/shared/passwordPolicy.d.ts +13 -0
  281. package/dist/shared/passwordPolicy.js +84 -0
  282. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  283. package/dist/shared/passwordPolicy.test.js +34 -0
  284. package/dist/storage/objectStorage.d.ts +1 -0
  285. package/dist/storage/objectStorage.js +2 -1
  286. package/dist/storage/objectStorageClient.d.ts +1 -0
  287. package/dist/storage/objectStorageClient.js +41 -8
  288. package/dist/storage/objectStorageClient.test.js +26 -1
  289. package/dist/storage/objectStorageConfig.js +3 -1
  290. package/dist/storage/objectStorageConfig.test.js +13 -1
  291. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  292. package/dist/storage/workspaceAssetStore.js +132 -44
  293. package/dist/storage/workspaceAssetStore.test.js +93 -6
  294. package/dist/styles/fonts.css +70 -0
  295. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  296. package/dist/sync/collaborationSyncPayload.js +19 -11
  297. package/dist/sync/collaborationSyncPayload.test.js +14 -1
  298. package/dist/sync/collaborationSyncState.d.ts +3 -3
  299. package/dist/sync/collaborationSyncState.js +19 -12
  300. package/dist/sync/contentSyncPayload.d.ts +5 -1
  301. package/dist/sync/contentSyncPayload.js +9 -0
  302. package/dist/sync/contentSyncPayload.test.js +39 -0
  303. package/dist/sync/contentSyncState.d.ts +9 -9
  304. package/dist/sync/contentSyncState.js +248 -107
  305. package/dist/sync/planningSyncPayload.js +5 -0
  306. package/dist/sync/planningSyncPayload.test.js +24 -0
  307. package/dist/sync/syncApplyHandlers.d.ts +11 -4
  308. package/dist/sync/syncApplyHandlers.js +115 -42
  309. package/dist/sync/syncApplyHandlers.test.js +251 -4
  310. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  311. package/dist/sync/syncFieldSemantics.js +55 -0
  312. package/dist/sync/syncResourceAdapters.d.ts +46 -0
  313. package/dist/sync/syncResourceAdapters.js +79 -0
  314. package/dist/sync/syncService.d.ts +6 -0
  315. package/dist/sync/syncService.js +65 -1
  316. package/dist/sync/syncService.test.js +96 -3
  317. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  318. package/dist/sync/taskSyncChangeKey.js +143 -0
  319. package/dist/sync/taskSyncPayload.js +3 -8
  320. package/dist/sync/workspacePullFeed.d.ts +4 -0
  321. package/dist/sync/workspacePullFeed.js +132 -72
  322. package/dist/sync/workspacePullFeed.test.js +92 -0
  323. package/dist/sync/workspaceRepair.js +5 -2
  324. package/dist/sync/workspaceSyncModel.d.ts +73 -6
  325. package/dist/sync/workspaceSyncModel.js +342 -73
  326. package/dist/sync/workspaceSyncModel.test.js +559 -14
  327. package/dist/sync/workspaceSyncState.d.ts +17 -0
  328. package/dist/sync/workspaceSyncState.js +2 -1
  329. package/dist/sync/workspaceSyncSurface.js +3 -8
  330. package/dist/types.d.ts +50 -2
  331. package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
  332. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
  333. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  334. package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
  335. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  336. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  337. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  338. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  339. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  340. package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
  341. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  342. package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
  343. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-D4XseTwW.js} +1 -1
  344. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  345. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  346. package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
  347. package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
  348. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  349. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  350. package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
  351. package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
  352. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-oOA9bcdK.js} +4 -4
  353. package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
  354. package/dist/ui/assets/index-607WPo_X.js +4 -0
  355. package/dist/ui/assets/index-CYyodXsg.css +1 -0
  356. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-B32hGuF2.js} +1 -1
  357. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  358. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  359. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  360. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  361. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  362. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  363. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  364. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  365. package/dist/ui/fonts/Noir_medium.otf +0 -0
  366. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  367. package/dist/ui/fonts/Noir_regular.otf +0 -0
  368. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  369. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  370. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  371. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  372. package/dist/ui/index.html +3 -3
  373. package/dist/ui/product/agent manager.png +0 -0
  374. package/dist/ui/product/agent_manager_02.png +0 -0
  375. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  376. package/dist/utils/aiProfileEvents.d.ts +7 -0
  377. package/dist/utils/aiProfileEvents.js +6 -0
  378. package/dist/utils/assignees.d.ts +8 -0
  379. package/dist/utils/assignees.js +10 -1
  380. package/dist/utils/assignees.test.d.ts +1 -0
  381. package/dist/utils/assignees.test.js +25 -0
  382. package/dist/utils/avatarDisplay.d.ts +1 -0
  383. package/dist/utils/avatarDisplay.js +15 -0
  384. package/dist/utils/avatarUpload.d.ts +27 -0
  385. package/dist/utils/avatarUpload.js +132 -0
  386. package/dist/utils/avatarUpload.test.d.ts +1 -0
  387. package/dist/utils/avatarUpload.test.js +40 -0
  388. package/dist/utils/bootstrapTrace.d.ts +7 -0
  389. package/dist/utils/bootstrapTrace.js +29 -0
  390. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  391. package/dist/utils/contextAssetEvents.d.ts +2 -0
  392. package/dist/utils/contextFiles.d.ts +3 -1
  393. package/dist/utils/contextFiles.js +2 -1
  394. package/dist/utils/syncEventDetails.d.ts +3 -0
  395. package/dist/utils/syncEventDetails.js +64 -0
  396. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  397. package/dist/utils/syncEventDetails.test.js +23 -0
  398. package/dist/utils/syncEventPresentation.d.ts +15 -0
  399. package/dist/utils/syncEventPresentation.js +129 -0
  400. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  401. package/dist/utils/syncEventPresentation.test.js +64 -0
  402. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  403. package/dist/utils/syncStatusPresentation.js +217 -0
  404. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  405. package/dist/utils/syncStatusPresentation.test.js +164 -0
  406. package/dist/utils/taskActivity.d.ts +9 -1
  407. package/dist/utils/taskActivity.js +290 -22
  408. package/dist/utils/taskActivity.test.js +141 -8
  409. package/dist/utils/taskSearch.d.ts +22 -0
  410. package/dist/utils/taskSearch.js +87 -0
  411. package/dist/utils/taskSearch.test.d.ts +1 -0
  412. package/dist/utils/taskSearch.test.js +69 -0
  413. package/dist/utils/taskUtils.js +10 -3
  414. package/dist/utils/taskUtils.test.d.ts +1 -0
  415. package/dist/utils/taskUtils.test.js +19 -0
  416. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  417. package/dist/utils/workspaceSyncPresentation.js +68 -2
  418. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  419. package/package.json +3 -2
  420. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  421. package/scripts/run-e2e-realtime.sh +17 -3
  422. package/scripts/run-smoke.sh +17 -5
  423. package/scripts/run-soak.sh +17 -5
  424. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  425. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  426. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  427. package/src/resources/templates/workflows/plan.yaml +1 -1
  428. package/src/resources/templates/workflows/review.yaml +2 -2
  429. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  430. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BG6P_GVW.js +0 -3
  431. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  432. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  433. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  434. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  435. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  436. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  437. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +0 -9
  438. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  439. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  440. package/scripts/migrate-planning-model.ts +0 -233
@@ -2,29 +2,61 @@ import { useCallback, useEffect } from 'react';
2
2
  import { createDefaultAssigneeOptions, createWorkspaceAssigneeOptions, mergeAssigneeOptions } from '../../utils/assignees';
3
3
  import { hasWorkspaceTaxonomyStatePayload, normalizeWorkspaceTaxonomyStatePayload } from '../../shared/taxonomyState.js';
4
4
  import { logBootstrapDebug, readPersistedWorkspaceId } from './workspaceLocalState';
5
+ import { resolveBootstrapRequestTimeoutMs } from '../bootstrapTimeouts';
6
+ function getBootstrapTimestamp() {
7
+ return typeof performance !== 'undefined' ? performance.now() : Date.now();
8
+ }
5
9
  export function useTaskforceWorkspaceBootstrap(args) {
6
- const { shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls, getWorkspaceRequestState, isWorkspaceRequestStale, isWorkspaceEpochStale, isAbortError, isBootstrapTimeoutError, fetchWithTimeout, handleUnauthorized, applyBootstrapConfig, loadPersistedUiState, fetchBuildInfo, markBootstrapPhase, markBootstrapStalled, fetchWorkflows, fetchInitiativeTemplates, loadAvailableEnvironments, setReferenceDataLoaded, setCustomCategories, setCustomTypes, setPriorities, setTaxonomies, setTaxonomyDisplayLabels, setConfigLoaded, hasLoadedUiStateRef, currentWorkspaceIdRef, referenceDataRequestRef, referenceDataAbortRef, bootstrapConfigAbortRef, workspaceListAbortRef, assigneeOptionsAbortRef, workspaceListHydratedRef, setAvailableWorkspaces, runtimeMode, applyResolvedWorkspaceId, workspaceSwitchingEnabled, authSessionResolved, isAuthenticated, authUserId, setHydratedWorkspaceRole, setAssigneeOptions, setAssigneeOptionsLoaded, commitCurrentWorkspaceId, currentWorkspaceId, workspaceSelectionScope, abortWorkspaceScopedRequests, setWorkspaceBootstrapPending, checkAuthSession, lastConfigRefreshKeyRef, lastAssigneeOptionsLoadKeyRef, lastWorkspaceSyncStateLoadKeyRef, lastTaskSurfaceLoadKeyRef, lastSettingsSurfaceLoadKeyRef, isOpen, activeTab, showArchive, taskScope, settingsSection, fetchTasks, fetchArchive, fetchPlanningEntities, refreshTaskCollections, loadWorkspaceSyncState, archiveBootstrapTimerRef, hasBootstrappedDataRef } = args;
10
+ const { shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls, getWorkspaceRequestState, isWorkspaceRequestStale, isWorkspaceEpochStale, isAbortError, isBootstrapTimeoutError, fetchWithTimeout, handleUnauthorized, applyBootstrapConfig, loadPersistedUiState, fetchBuildInfo, markBootstrapPhase, markBootstrapStalled, fetchWorkflows, fetchInitiativeTemplates, loadAvailableEnvironments, setReferenceDataLoaded, setCustomCategories, setCustomTypes, setPriorities, setTaxonomies, setTaxonomyDisplayLabels, setConfigLoaded, hasLoadedUiStateRef, currentWorkspaceIdRef, referenceDataRequestRef, referenceDataAbortRef, bootstrapConfigAbortRef, workspaceListAbortRef, assigneeOptionsAbortRef, workspaceListHydratedRef, setAvailableWorkspaces, runtimeMode, applyResolvedWorkspaceId, workspaceSwitchingEnabled, authSessionResolved, isAuthenticated, authUserId, authUserAvatarUrl, setHydratedWorkspaceRole, setAssigneeOptions, setAssigneeOptionsLoaded, commitCurrentWorkspaceId, currentWorkspaceId, workspaceSelectionScope, abortWorkspaceScopedRequests, setWorkspaceBootstrapPending, checkAuthSession, lastConfigRefreshKeyRef, lastAssigneeOptionsLoadKeyRef, lastWorkspaceSyncStateLoadKeyRef, lastTaskSurfaceLoadKeyRef, lastSettingsSurfaceLoadKeyRef, isOpen, activeTab, showArchive, taskScope, settingsSection, workspaceBootstrapPending, fetchTasks, fetchArchive, fetchPlanningEntities, refreshTaskCollections, loadWorkspaceSyncState, archiveBootstrapTimerRef, hasBootstrappedDataRef } = args;
7
11
  const fetchReferenceData = useCallback(async (options) => {
8
12
  const ignoreAuthGuard = options?.ignoreAuthGuard === true;
9
13
  const force = options?.force === true;
10
- if (!ignoreAuthGuard && (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls))
14
+ if (!ignoreAuthGuard && (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls)) {
15
+ logBootstrapDebug('bootstrap_reference_data_skipped', {
16
+ reason: shouldBlockProtectedApiCalls ? 'auth_blocked' : 'auth_deferred',
17
+ runtimeMode,
18
+ workspaceId: String(getWorkspaceRequestState().workspaceId || '').trim() || 'default',
19
+ force
20
+ });
11
21
  return;
22
+ }
12
23
  const requestState = getWorkspaceRequestState();
24
+ const normalizedWorkspaceId = String(requestState.workspaceId || '').trim() || 'default';
25
+ const cloudWorkspaceResolved = runtimeMode !== 'cloud'
26
+ || !authSessionResolved
27
+ || !isAuthenticated
28
+ || (normalizedWorkspaceId !== '' && normalizedWorkspaceId !== 'default');
29
+ if (!cloudWorkspaceResolved) {
30
+ logBootstrapDebug('bootstrap_reference_data_skipped', {
31
+ reason: 'workspace_unresolved',
32
+ runtimeMode,
33
+ workspaceId: normalizedWorkspaceId,
34
+ force
35
+ });
36
+ return;
37
+ }
13
38
  if (!force && referenceDataRequestRef.current?.workspaceId === requestState.workspaceId) {
39
+ logBootstrapDebug('bootstrap_reference_data_reused', {
40
+ workspaceId: normalizedWorkspaceId,
41
+ force
42
+ });
14
43
  await referenceDataRequestRef.current.promise;
15
44
  return;
16
45
  }
17
46
  setReferenceDataLoaded(false);
47
+ const startedAt = getBootstrapTimestamp();
18
48
  const abortController = new AbortController();
19
49
  referenceDataAbortRef.current = abortController;
20
50
  const request = (async () => {
21
51
  await Promise.allSettled([
22
52
  (async () => {
53
+ let source = 'empty-taxonomy-state';
23
54
  const taxonomyRes = await fetch('/api/taskforce/taxonomy-state', { signal: abortController.signal });
24
55
  if (!taxonomyRes.ok)
25
56
  return;
26
57
  const taxonomyData = await taxonomyRes.json().catch(() => ({}));
27
58
  if (!hasWorkspaceTaxonomyStatePayload(taxonomyData)) {
59
+ source = 'fallback-endpoints';
28
60
  const [catRes, typeRes, priorityRes, taxRes] = await Promise.all([
29
61
  fetch('/api/taskforce/categories', { signal: abortController.signal }),
30
62
  fetch('/api/taskforce/types', { signal: abortController.signal }),
@@ -42,9 +74,17 @@ export function useTaskforceWorkspaceBootstrap(args) {
42
74
  taxonomyData.priorities = priorityData.priorities;
43
75
  taxonomyData.taxonomies = taxData.taxonomies;
44
76
  }
77
+ else {
78
+ source = 'taxonomy-state';
79
+ }
45
80
  const normalized = normalizeWorkspaceTaxonomyStatePayload(taxonomyData);
46
- if (isWorkspaceRequestStale(requestState))
81
+ if (isWorkspaceRequestStale(requestState)) {
82
+ logBootstrapDebug('bootstrap_reference_data_stale_drop', {
83
+ workspaceId: normalizedWorkspaceId,
84
+ source
85
+ });
47
86
  return;
87
+ }
48
88
  if (normalized.categories.length > 0)
49
89
  setCustomCategories(normalized.categories);
50
90
  if (normalized.types.length > 0)
@@ -53,6 +93,14 @@ export function useTaskforceWorkspaceBootstrap(args) {
53
93
  setPriorities(normalized.priorities);
54
94
  setTaxonomies(normalized.taxonomies);
55
95
  setTaxonomyDisplayLabels(normalized.displayLabels || {});
96
+ logBootstrapDebug('bootstrap_reference_data_source', {
97
+ workspaceId: normalizedWorkspaceId,
98
+ source,
99
+ categories: normalized.categories.length,
100
+ types: normalized.types.length,
101
+ priorities: normalized.priorities.length,
102
+ taxonomies: normalized.taxonomies.length
103
+ });
56
104
  })(),
57
105
  fetchWorkflows(),
58
106
  fetchInitiativeTemplates(),
@@ -61,6 +109,11 @@ export function useTaskforceWorkspaceBootstrap(args) {
61
109
  if (isWorkspaceRequestStale(requestState))
62
110
  return;
63
111
  setReferenceDataLoaded(true);
112
+ logBootstrapDebug('bootstrap_reference_data_loaded', {
113
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
114
+ workspaceId: normalizedWorkspaceId,
115
+ force
116
+ });
64
117
  })();
65
118
  referenceDataRequestRef.current = { workspaceId: requestState.workspaceId, promise: request };
66
119
  try {
@@ -72,17 +125,18 @@ export function useTaskforceWorkspaceBootstrap(args) {
72
125
  if (referenceDataAbortRef.current === abortController)
73
126
  referenceDataAbortRef.current = null;
74
127
  }
75
- }, [fetchInitiativeTemplates, fetchWorkflows, getWorkspaceRequestState, isWorkspaceRequestStale, loadAvailableEnvironments, referenceDataAbortRef, referenceDataRequestRef, setCustomCategories, setCustomTypes, setPriorities, setReferenceDataLoaded, setTaxonomies, setTaxonomyDisplayLabels, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
128
+ }, [authSessionResolved, fetchInitiativeTemplates, fetchWorkflows, getWorkspaceRequestState, isAuthenticated, isWorkspaceRequestStale, loadAvailableEnvironments, referenceDataAbortRef, referenceDataRequestRef, runtimeMode, setCustomCategories, setCustomTypes, setPriorities, setReferenceDataLoaded, setTaxonomies, setTaxonomyDisplayLabels, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
76
129
  const fetchBootstrapConfig = useCallback(async (options) => {
77
130
  const ignoreAuthGuard = options?.ignoreAuthGuard === true;
78
131
  if (!ignoreAuthGuard && (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls))
79
132
  return;
80
133
  markBootstrapPhase('config');
81
134
  const requestState = getWorkspaceRequestState();
135
+ const startedAt = getBootstrapTimestamp();
82
136
  const abortController = new AbortController();
83
137
  bootstrapConfigAbortRef.current = abortController;
84
138
  try {
85
- const res = await fetchWithTimeout('/api/taskforce/config', undefined, 12000, { signal: abortController.signal });
139
+ const res = await fetchWithTimeout('/api/taskforce/config', undefined, resolveBootstrapRequestTimeoutMs(runtimeMode), { signal: abortController.signal });
86
140
  if (isWorkspaceEpochStale(requestState))
87
141
  return;
88
142
  if (res.status === 401) {
@@ -114,6 +168,11 @@ export function useTaskforceWorkspaceBootstrap(args) {
114
168
  }
115
169
  setConfigLoaded(true);
116
170
  markBootstrapPhase('ready');
171
+ logBootstrapDebug('bootstrap_config_loaded', {
172
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
173
+ status: res.status,
174
+ workspaceId: configResolvedWorkspaceId || requestState.workspaceId
175
+ });
117
176
  void fetchBuildInfo();
118
177
  }
119
178
  catch (error) {
@@ -125,16 +184,31 @@ export function useTaskforceWorkspaceBootstrap(args) {
125
184
  }
126
185
  markBootstrapStalled(isBootstrapTimeoutError(error) ? 'Startup checks timed out.' : 'Unable to finish startup checks.');
127
186
  setConfigLoaded(true);
187
+ logBootstrapDebug('bootstrap_config_failed', {
188
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
189
+ workspaceId: requestState.workspaceId,
190
+ error: error instanceof Error ? error.message : String(error)
191
+ });
128
192
  }
129
193
  finally {
130
194
  if (bootstrapConfigAbortRef.current === abortController)
131
195
  bootstrapConfigAbortRef.current = null;
132
196
  }
133
- }, [applyBootstrapConfig, bootstrapConfigAbortRef, currentWorkspaceIdRef, fetchBuildInfo, fetchWithTimeout, getWorkspaceRequestState, handleUnauthorized, hasLoadedUiStateRef, isAbortError, isBootstrapTimeoutError, isWorkspaceEpochStale, loadPersistedUiState, markBootstrapPhase, markBootstrapStalled, setConfigLoaded, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
197
+ }, [applyBootstrapConfig, bootstrapConfigAbortRef, currentWorkspaceIdRef, fetchBuildInfo, fetchWithTimeout, getWorkspaceRequestState, handleUnauthorized, hasLoadedUiStateRef, isAbortError, isBootstrapTimeoutError, isWorkspaceEpochStale, loadPersistedUiState, markBootstrapPhase, markBootstrapStalled, runtimeMode, setConfigLoaded, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
134
198
  const fetchConfig = useCallback(async (options) => {
135
199
  await fetchBootstrapConfig(options);
136
200
  await fetchReferenceData(options);
137
201
  }, [fetchBootstrapConfig, fetchReferenceData]);
202
+ const scheduleDeferredArchiveBootstrap = useCallback(() => {
203
+ if (typeof window === 'undefined')
204
+ return;
205
+ if (archiveBootstrapTimerRef.current !== null)
206
+ window.clearTimeout(archiveBootstrapTimerRef.current);
207
+ archiveBootstrapTimerRef.current = window.setTimeout(() => {
208
+ archiveBootstrapTimerRef.current = null;
209
+ void fetchArchive(true).then(() => logBootstrapDebug('bootstrap_archive_loaded_deferred', {}));
210
+ }, 1500);
211
+ }, [archiveBootstrapTimerRef, fetchArchive]);
138
212
  const fetchWorkspaces = useCallback(async () => {
139
213
  if (!workspaceSwitchingEnabled) {
140
214
  setAvailableWorkspaces([]);
@@ -142,10 +216,11 @@ export function useTaskforceWorkspaceBootstrap(args) {
142
216
  return { success: true, workspaces: [] };
143
217
  }
144
218
  const requestState = getWorkspaceRequestState();
219
+ const startedAt = getBootstrapTimestamp();
145
220
  const abortController = new AbortController();
146
221
  workspaceListAbortRef.current = abortController;
147
222
  try {
148
- const res = await fetchWithTimeout('/api/taskforce/workspaces', { method: 'GET', credentials: 'include' }, 12000, { signal: abortController.signal });
223
+ const res = await fetchWithTimeout('/api/taskforce/workspaces', { method: 'GET', credentials: 'include' }, resolveBootstrapRequestTimeoutMs(runtimeMode), { signal: abortController.signal });
149
224
  if (isWorkspaceEpochStale(requestState))
150
225
  return { success: false, error: 'Workspace changed while loading workspaces.' };
151
226
  if (res.status === 401) {
@@ -177,6 +252,12 @@ export function useTaskforceWorkspaceBootstrap(args) {
177
252
  if (runtimeMode !== 'local' && typeof data?.currentWorkspaceId === 'string' && data.currentWorkspaceId.trim().length > 0) {
178
253
  applyResolvedWorkspaceId(data.currentWorkspaceId.trim(), { preserveNonDefaultDefault: true });
179
254
  }
255
+ logBootstrapDebug('bootstrap_workspaces_loaded', {
256
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
257
+ workspaceId: requestState.workspaceId,
258
+ count: workspaces.length,
259
+ status: res.status
260
+ });
180
261
  return { success: true, workspaces };
181
262
  }
182
263
  catch (error) {
@@ -184,6 +265,11 @@ export function useTaskforceWorkspaceBootstrap(args) {
184
265
  markBootstrapStalled('Startup checks timed out.');
185
266
  setAvailableWorkspaces([]);
186
267
  workspaceListHydratedRef.current = false;
268
+ logBootstrapDebug('bootstrap_workspaces_failed', {
269
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
270
+ workspaceId: requestState.workspaceId,
271
+ error: error instanceof Error ? error.message : String(error)
272
+ });
187
273
  return { success: false, error: isAbortError(error) ? 'Workspace list request aborted.' : 'Failed to load workspaces.' };
188
274
  }
189
275
  finally {
@@ -193,14 +279,15 @@ export function useTaskforceWorkspaceBootstrap(args) {
193
279
  }, [applyResolvedWorkspaceId, fetchWithTimeout, getWorkspaceRequestState, isAbortError, isBootstrapTimeoutError, isWorkspaceEpochStale, markBootstrapStalled, runtimeMode, setAvailableWorkspaces, workspaceListAbortRef, workspaceListHydratedRef, workspaceSwitchingEnabled]);
194
280
  const fetchAssigneeOptions = useCallback(async () => {
195
281
  const requestState = getWorkspaceRequestState();
282
+ const startedAt = getBootstrapTimestamp();
196
283
  const abortController = new AbortController();
197
284
  assigneeOptionsAbortRef.current = abortController;
198
285
  setAssigneeOptionsLoaded(false);
199
286
  try {
200
287
  const [assigneeResult, workspaceMembersResult] = await Promise.all([
201
- fetchWithTimeout('/api/taskforce/workspace/assignee-options', { method: 'GET', credentials: 'include' }, 12000, { signal: abortController.signal }),
288
+ fetchWithTimeout('/api/taskforce/workspace/assignee-options', { method: 'GET', credentials: 'include' }, resolveBootstrapRequestTimeoutMs(runtimeMode), { signal: abortController.signal }),
202
289
  (authSessionResolved && isAuthenticated)
203
- ? fetchWithTimeout('/api/taskforce/auth/workspace-members', { method: 'GET', credentials: 'include' }, 12000, { signal: abortController.signal }).catch(() => null)
290
+ ? fetchWithTimeout('/api/taskforce/auth/workspace-members', { method: 'GET', credentials: 'include' }, resolveBootstrapRequestTimeoutMs(runtimeMode), { signal: abortController.signal }).catch(() => null)
204
291
  : Promise.resolve(null)
205
292
  ]);
206
293
  if (isWorkspaceRequestStale(requestState))
@@ -218,6 +305,19 @@ export function useTaskforceWorkspaceBootstrap(args) {
218
305
  setAssigneeOptionsLoaded(true);
219
306
  return { success: false, error: data?.error || `Failed to load assignee options (${assigneeResult.status})` };
220
307
  }
308
+ const normalizedAuthUserId = String(authUserId || '').trim();
309
+ const currentUserAvatarUrl = String(authUserAvatarUrl || '').trim();
310
+ const resolveMemberAvatarUrl = (candidate, fallbackValue) => {
311
+ const candidateAvatarUrl = typeof candidate?.avatarUrl === 'string' && candidate.avatarUrl.trim().length > 0
312
+ ? candidate.avatarUrl.trim()
313
+ : '';
314
+ if (candidateAvatarUrl)
315
+ return candidateAvatarUrl;
316
+ const candidateUserId = String(candidate?.userId || fallbackValue || '').trim();
317
+ return normalizedAuthUserId && candidateUserId === normalizedAuthUserId && currentUserAvatarUrl
318
+ ? currentUserAvatarUrl
319
+ : null;
320
+ };
221
321
  const assignees = Array.isArray(data?.assignees) ? data.assignees : [];
222
322
  const directOptions = assignees.map((entry) => {
223
323
  const value = String(entry?.value || '').trim();
@@ -225,21 +325,41 @@ export function useTaskforceWorkspaceBootstrap(args) {
225
325
  if (!value)
226
326
  return null;
227
327
  if (kindRaw === 'agent' || kindRaw === 'ai')
228
- return { value, label: String(entry?.label || value).trim() || value, icon: String(entry?.icon || 'Bot'), color: String(entry?.color || '#8b5cf6'), kind: 'agent' };
328
+ return {
329
+ value,
330
+ label: String(entry?.label || value).trim() || value,
331
+ icon: String(entry?.icon || 'Bot'),
332
+ color: String(entry?.color || '#8b5cf6'),
333
+ kind: 'agent',
334
+ avatarUrl: typeof entry?.avatarUrl === 'string' && entry.avatarUrl.trim().length > 0 ? entry.avatarUrl.trim() : null,
335
+ avatarRevision: Number.isFinite(Number(entry?.avatarRevision)) ? Math.max(0, Math.floor(Number(entry.avatarRevision))) : null,
336
+ avatarUpdatedAt: typeof entry?.avatarUpdatedAt === 'string' && entry.avatarUpdatedAt.trim().length > 0 ? entry.avatarUpdatedAt.trim() : null,
337
+ username: typeof entry?.username === 'string' && entry.username.trim().length > 0 ? entry.username.trim() : null,
338
+ archivedAt: typeof entry?.archivedAt === 'string' && entry.archivedAt.trim().length > 0 ? entry.archivedAt.trim() : null,
339
+ createdAt: typeof entry?.createdAt === 'string' && entry.createdAt.trim().length > 0 ? entry.createdAt.trim() : null,
340
+ updatedAt: typeof entry?.updatedAt === 'string' && entry.updatedAt.trim().length > 0 ? entry.updatedAt.trim() : null
341
+ };
229
342
  if (kindRaw === 'member' || kindRaw === 'human')
230
- return { value, label: String(entry?.label || value).trim() || value, icon: String(entry?.icon || 'User'), color: String(entry?.color || '#22c55e'), kind: 'member' };
343
+ return {
344
+ value,
345
+ label: String(entry?.label || value).trim() || value,
346
+ icon: String(entry?.icon || 'User'),
347
+ color: String(entry?.color || '#22c55e'),
348
+ kind: 'member',
349
+ avatarUrl: resolveMemberAvatarUrl(entry, value)
350
+ };
231
351
  return null;
232
352
  }).filter(Boolean);
233
353
  const fallbackCandidates = assignees.map((candidate) => ({
234
354
  userId: String(candidate?.userId || '').trim(),
235
355
  email: String(candidate?.email || '').trim().toLowerCase(),
236
- displayName: typeof candidate?.displayName === 'string' ? candidate.displayName : null
356
+ displayName: typeof candidate?.displayName === 'string' ? candidate.displayName : null,
357
+ avatarUrl: resolveMemberAvatarUrl(candidate)
237
358
  })).filter((candidate) => candidate.userId);
238
359
  let memberCandidates = fallbackCandidates;
239
360
  if (workspaceMembersResult?.ok) {
240
361
  const workspaceMembersData = await workspaceMembersResult.json().catch(() => ({}));
241
362
  const users = Array.isArray(workspaceMembersData?.users) ? workspaceMembersData.users : [];
242
- const normalizedAuthUserId = String(authUserId || '').trim();
243
363
  const currentMember = users.find((candidate) => String(candidate?.userId || '').trim() === normalizedAuthUserId);
244
364
  const currentMemberRole = String(currentMember?.role || '').trim().toLowerCase();
245
365
  if (currentMemberRole === 'owner' || currentMemberRole === 'admin' || currentMemberRole === 'member' || currentMemberRole === 'read-only') {
@@ -251,7 +371,8 @@ export function useTaskforceWorkspaceBootstrap(args) {
251
371
  const workspaceMemberCandidates = users.map((candidate) => ({
252
372
  userId: String(candidate?.userId || '').trim(),
253
373
  email: String(candidate?.email || '').trim().toLowerCase(),
254
- displayName: typeof candidate?.displayName === 'string' ? candidate.displayName : null
374
+ displayName: typeof candidate?.displayName === 'string' ? candidate.displayName : null,
375
+ avatarUrl: resolveMemberAvatarUrl(candidate)
255
376
  })).filter((candidate) => candidate.userId);
256
377
  memberCandidates = Array.from(new Map([...fallbackCandidates, ...workspaceMemberCandidates].map((candidate) => [candidate.userId, candidate])).values());
257
378
  }
@@ -263,6 +384,11 @@ export function useTaskforceWorkspaceBootstrap(args) {
263
384
  return { success: false, error: 'Workspace changed while loading assignee options.' };
264
385
  setAssigneeOptions(merged);
265
386
  setAssigneeOptionsLoaded(true);
387
+ logBootstrapDebug('bootstrap_assignee_options_loaded', {
388
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
389
+ workspaceId: requestState.workspaceId,
390
+ count: merged.length
391
+ });
266
392
  return { success: true };
267
393
  }
268
394
  catch (error) {
@@ -270,20 +396,32 @@ export function useTaskforceWorkspaceBootstrap(args) {
270
396
  setAssigneeOptions(createDefaultAssigneeOptions());
271
397
  setAssigneeOptionsLoaded(true);
272
398
  }
399
+ logBootstrapDebug('bootstrap_assignee_options_failed', {
400
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
401
+ workspaceId: requestState.workspaceId,
402
+ error: error instanceof Error ? error.message : String(error)
403
+ });
273
404
  return { success: false, error: isAbortError(error) ? 'Assignee options request aborted.' : 'Failed to load assignee options.' };
274
405
  }
275
406
  finally {
276
407
  if (assigneeOptionsAbortRef.current === abortController)
277
408
  assigneeOptionsAbortRef.current = null;
278
409
  }
279
- }, [assigneeOptionsAbortRef, authSessionResolved, authUserId, fetchWithTimeout, getWorkspaceRequestState, isAbortError, isAuthenticated, isWorkspaceRequestStale, setAssigneeOptions, setAssigneeOptionsLoaded, setHydratedWorkspaceRole, workspaceSwitchingEnabled]);
410
+ }, [assigneeOptionsAbortRef, authSessionResolved, authUserAvatarUrl, authUserId, fetchWithTimeout, getWorkspaceRequestState, isAbortError, isAuthenticated, isWorkspaceRequestStale, runtimeMode, setAssigneeOptions, setAssigneeOptionsLoaded, setHydratedWorkspaceRole, workspaceSwitchingEnabled]);
280
411
  const switchWorkspace = useCallback(async (workspaceId, options) => {
281
412
  if (!workspaceSwitchingEnabled)
282
413
  return { success: false, error: 'Workspace switching is unavailable in local mode.', code: 'WORKSPACE_SWITCHING_DISABLED' };
283
414
  const normalizedWorkspaceId = String(workspaceId || '').trim();
284
415
  if (!normalizedWorkspaceId)
285
416
  return { success: false, error: 'workspaceId is required.' };
417
+ const previousWorkspaceId = String(currentWorkspaceIdRef.current || '').trim() || 'default';
418
+ const startedAt = getBootstrapTimestamp();
286
419
  try {
420
+ logBootstrapDebug('bootstrap_workspace_switch_started', {
421
+ fromWorkspaceId: previousWorkspaceId,
422
+ toWorkspaceId: normalizedWorkspaceId,
423
+ hydrate: options?.hydrate !== false
424
+ });
287
425
  const res = await fetch('/api/taskforce/session/workspace', {
288
426
  method: 'POST',
289
427
  headers: { 'Content-Type': 'application/json' },
@@ -309,17 +447,36 @@ export function useTaskforceWorkspaceBootstrap(args) {
309
447
  lastTaskSurfaceLoadKeyRef.current = `${normalizedWorkspaceId}:${activeTab}:${showArchive ? 'archive' : 'active'}:${taskScope}`;
310
448
  if (isOpen && activeTab === 'settings')
311
449
  lastSettingsSurfaceLoadKeyRef.current = `${normalizedWorkspaceId}:${activeTab}:${settingsSection}`;
312
- await Promise.all([fetchConfig(), fetchWorkspaces(), refreshTaskCollections({ isSilent: false }), fetchAssigneeOptions(), loadWorkspaceSyncState()]);
450
+ await Promise.all([
451
+ fetchConfig(),
452
+ fetchWorkspaces(),
453
+ refreshTaskCollections({ isSilent: false }),
454
+ loadWorkspaceSyncState()
455
+ ]);
456
+ void fetchPlanningEntities({ ignoreAuthGuard: true });
313
457
  }
458
+ logBootstrapDebug('bootstrap_workspace_switch_completed', {
459
+ fromWorkspaceId: previousWorkspaceId,
460
+ toWorkspaceId: normalizedWorkspaceId,
461
+ hydrate: options?.hydrate !== false,
462
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt))
463
+ });
314
464
  return { success: true };
315
465
  }
316
- catch {
466
+ catch (error) {
467
+ logBootstrapDebug('bootstrap_workspace_switch_failed', {
468
+ fromWorkspaceId: previousWorkspaceId,
469
+ toWorkspaceId: normalizedWorkspaceId,
470
+ hydrate: options?.hydrate !== false,
471
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
472
+ error: error instanceof Error ? error.message : String(error)
473
+ });
317
474
  return { success: false, error: 'Failed to switch workspace.' };
318
475
  }
319
476
  finally {
320
477
  setWorkspaceBootstrapPending(false);
321
478
  }
322
- }, [abortWorkspaceScopedRequests, activeTab, checkAuthSession, commitCurrentWorkspaceId, fetchAssigneeOptions, fetchConfig, fetchWorkspaces, isOpen, lastAssigneeOptionsLoadKeyRef, lastConfigRefreshKeyRef, lastSettingsSurfaceLoadKeyRef, lastTaskSurfaceLoadKeyRef, lastWorkspaceSyncStateLoadKeyRef, loadWorkspaceSyncState, markBootstrapPhase, refreshTaskCollections, runtimeMode, settingsSection, setWorkspaceBootstrapPending, showArchive, taskScope, workspaceSelectionScope, workspaceSwitchingEnabled]);
479
+ }, [abortWorkspaceScopedRequests, activeTab, checkAuthSession, commitCurrentWorkspaceId, currentWorkspaceIdRef, fetchConfig, fetchPlanningEntities, fetchWorkspaces, isOpen, lastAssigneeOptionsLoadKeyRef, lastConfigRefreshKeyRef, lastSettingsSurfaceLoadKeyRef, lastTaskSurfaceLoadKeyRef, lastWorkspaceSyncStateLoadKeyRef, loadWorkspaceSyncState, markBootstrapPhase, refreshTaskCollections, runtimeMode, settingsSection, setWorkspaceBootstrapPending, showArchive, taskScope, workspaceSelectionScope, workspaceSwitchingEnabled]);
323
480
  const resolveWorkspaceAfterAuth = useCallback(async (options) => {
324
481
  markBootstrapPhase('workspace');
325
482
  setWorkspaceBootstrapPending(true);
@@ -366,59 +523,188 @@ export function useTaskforceWorkspaceBootstrap(args) {
366
523
  }, [commitCurrentWorkspaceId, currentWorkspaceId, currentWorkspaceIdRef, fetchWorkspaces, markBootstrapPhase, markBootstrapStalled, setWorkspaceBootstrapPending, switchWorkspace, workspaceSelectionScope, workspaceSwitchingEnabled]);
367
524
  const retryBootstrapChecks = useCallback(async () => {
368
525
  markBootstrapPhase('auth');
526
+ const startedAt = getBootstrapTimestamp();
527
+ logBootstrapDebug('bootstrap_retry_started', {
528
+ workspaceId: String(currentWorkspaceIdRef.current || 'default').trim() || 'default'
529
+ });
369
530
  const authenticated = await checkAuthSession({ force: true });
531
+ let workspaceSetupRequired = false;
370
532
  if (workspaceSwitchingEnabled && authenticated) {
371
533
  const workspaceResolution = await resolveWorkspaceAfterAuth();
372
534
  if (!workspaceResolution.success || workspaceResolution.workspaceSetupRequired) {
373
535
  await fetchConfig({ ignoreAuthGuard: true });
536
+ logBootstrapDebug('bootstrap_retry_completed', {
537
+ workspaceId: String(currentWorkspaceIdRef.current || 'default').trim() || 'default',
538
+ authenticated,
539
+ workspaceSetupRequired: workspaceResolution.workspaceSetupRequired === true,
540
+ success: workspaceResolution.success,
541
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt))
542
+ });
374
543
  return;
375
544
  }
376
545
  }
377
- await fetchConfig({ ignoreAuthGuard: true });
378
- }, [checkAuthSession, fetchConfig, markBootstrapPhase, resolveWorkspaceAfterAuth, workspaceSwitchingEnabled]);
546
+ setWorkspaceBootstrapPending(true);
547
+ try {
548
+ await fetchConfig({ ignoreAuthGuard: true });
549
+ if (authenticated && !workspaceSetupRequired) {
550
+ await Promise.all([
551
+ fetchTasks(true, { ignoreAuthGuard: true }),
552
+ fetchReferenceData({ ignoreAuthGuard: true, force: true })
553
+ ]);
554
+ void fetchPlanningEntities({ ignoreAuthGuard: true });
555
+ }
556
+ logBootstrapDebug('bootstrap_retry_completed', {
557
+ workspaceId: String(currentWorkspaceIdRef.current || 'default').trim() || 'default',
558
+ authenticated,
559
+ workspaceSetupRequired,
560
+ success: true,
561
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt))
562
+ });
563
+ }
564
+ finally {
565
+ setWorkspaceBootstrapPending(false);
566
+ }
567
+ }, [checkAuthSession, currentWorkspaceIdRef, fetchConfig, fetchPlanningEntities, fetchReferenceData, fetchTasks, markBootstrapPhase, resolveWorkspaceAfterAuth, setWorkspaceBootstrapPending, workspaceSwitchingEnabled]);
568
+ const runCloudBootstrap = useCallback(async (options) => {
569
+ const reason = options?.reason || 'initial-load';
570
+ const ignoreAuthGuard = options?.ignoreAuthGuard === true;
571
+ const tasksSilent = options?.tasksSilent !== false;
572
+ const includeDeferredArchive = options?.includeDeferredArchive !== false;
573
+ const startedAt = getBootstrapTimestamp();
574
+ markBootstrapPhase('auth');
575
+ const authenticated = await checkAuthSession({ force: true });
576
+ if (!authenticated) {
577
+ await fetchConfig({ ignoreAuthGuard: true });
578
+ logBootstrapDebug('cloud_bootstrap_completed', {
579
+ reason,
580
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
581
+ authenticated: false,
582
+ workspaceSetupRequired: false,
583
+ success: false
584
+ });
585
+ return { success: false, authenticated: false, workspaceSetupRequired: false, error: 'Sign in required.', code: 'AUTH_REQUIRED' };
586
+ }
587
+ if (workspaceSwitchingEnabled) {
588
+ const workspaceResolution = await resolveWorkspaceAfterAuth();
589
+ if (!workspaceResolution.success) {
590
+ await fetchConfig({ ignoreAuthGuard: true });
591
+ logBootstrapDebug('cloud_bootstrap_completed', {
592
+ reason,
593
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
594
+ authenticated: true,
595
+ workspaceSetupRequired: false,
596
+ success: false,
597
+ error: workspaceResolution.error || 'Workspace resolution failed.'
598
+ });
599
+ return {
600
+ success: false,
601
+ authenticated: true,
602
+ workspaceSetupRequired: false,
603
+ error: workspaceResolution.error || 'Unable to resolve workspace access.',
604
+ code: workspaceResolution.code
605
+ };
606
+ }
607
+ if (workspaceResolution.workspaceSetupRequired) {
608
+ await fetchConfig({ ignoreAuthGuard: true });
609
+ logBootstrapDebug('cloud_bootstrap_completed', {
610
+ reason,
611
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
612
+ authenticated: true,
613
+ workspaceSetupRequired: true,
614
+ success: true
615
+ });
616
+ return { success: true, authenticated: true, workspaceSetupRequired: true };
617
+ }
618
+ }
619
+ setWorkspaceBootstrapPending(true);
620
+ try {
621
+ await fetchBootstrapConfig({ ignoreAuthGuard });
622
+ await Promise.all([
623
+ fetchTasks(tasksSilent, { ignoreAuthGuard }),
624
+ fetchReferenceData({ ignoreAuthGuard })
625
+ ]);
626
+ void fetchPlanningEntities({ ignoreAuthGuard });
627
+ if (includeDeferredArchive) {
628
+ scheduleDeferredArchiveBootstrap();
629
+ }
630
+ logBootstrapDebug('cloud_bootstrap_completed', {
631
+ reason,
632
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
633
+ authenticated: true,
634
+ workspaceSetupRequired: false,
635
+ success: true,
636
+ workspaceId: String(currentWorkspaceIdRef.current || 'default').trim() || 'default'
637
+ });
638
+ return { success: true, authenticated: true, workspaceSetupRequired: false };
639
+ }
640
+ finally {
641
+ setWorkspaceBootstrapPending(false);
642
+ }
643
+ }, [
644
+ checkAuthSession,
645
+ currentWorkspaceIdRef,
646
+ fetchBootstrapConfig,
647
+ fetchPlanningEntities,
648
+ fetchReferenceData,
649
+ fetchTasks,
650
+ markBootstrapPhase,
651
+ resolveWorkspaceAfterAuth,
652
+ scheduleDeferredArchiveBootstrap,
653
+ setWorkspaceBootstrapPending,
654
+ workspaceSwitchingEnabled
655
+ ]);
379
656
  useEffect(() => {
380
657
  if (!shouldDeferProtectedApiCalls && !shouldBlockProtectedApiCalls && !hasBootstrappedDataRef.current) {
381
658
  hasBootstrappedDataRef.current = true;
382
659
  void (async () => {
660
+ const startedAt = getBootstrapTimestamp();
383
661
  if (runtimeMode === 'cloud' && authSessionResolved && isAuthenticated) {
384
- const workspaceResolution = await resolveWorkspaceAfterAuth();
385
- if (!workspaceResolution.success || workspaceResolution.workspaceSetupRequired) {
386
- await fetchBootstrapConfig();
387
- void fetchReferenceData();
662
+ const result = await runCloudBootstrap({
663
+ reason: 'initial-load',
664
+ tasksSilent: false,
665
+ includeDeferredArchive: true
666
+ });
667
+ if (!result.success || result.workspaceSetupRequired) {
388
668
  return;
389
669
  }
390
670
  }
391
- await fetchBootstrapConfig();
392
- await Promise.all([fetchTasks(false), fetchReferenceData(), fetchPlanningEntities()]);
393
- if (typeof window !== 'undefined') {
394
- if (archiveBootstrapTimerRef.current !== null)
395
- window.clearTimeout(archiveBootstrapTimerRef.current);
396
- archiveBootstrapTimerRef.current = window.setTimeout(() => {
397
- archiveBootstrapTimerRef.current = null;
398
- void fetchArchive(true).then(() => logBootstrapDebug('bootstrap_archive_loaded_deferred', {}));
399
- }, 1500);
671
+ else {
672
+ await Promise.all([
673
+ fetchBootstrapConfig(),
674
+ fetchTasks(false),
675
+ fetchReferenceData()
676
+ ]);
677
+ void fetchPlanningEntities();
678
+ }
679
+ if (runtimeMode !== 'cloud') {
680
+ scheduleDeferredArchiveBootstrap();
400
681
  }
682
+ logBootstrapDebug('bootstrap_initial_load_completed', {
683
+ durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - startedAt)),
684
+ runtimeMode,
685
+ workspaceId: String(currentWorkspaceIdRef.current || 'default').trim() || 'default'
686
+ });
401
687
  })();
402
688
  }
403
- }, [archiveBootstrapTimerRef, authSessionResolved, fetchArchive, fetchBootstrapConfig, fetchPlanningEntities, fetchReferenceData, fetchTasks, hasBootstrappedDataRef, isAuthenticated, resolveWorkspaceAfterAuth, runtimeMode, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
689
+ }, [authSessionResolved, currentWorkspaceIdRef, fetchBootstrapConfig, fetchPlanningEntities, fetchReferenceData, fetchTasks, hasBootstrappedDataRef, isAuthenticated, runCloudBootstrap, runtimeMode, scheduleDeferredArchiveBootstrap, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
404
690
  useEffect(() => {
405
- if (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls || workspaceSelectionScope === 'bootstrap' || !hasBootstrappedDataRef.current)
691
+ if (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls || workspaceBootstrapPending || workspaceSelectionScope === 'bootstrap' || !hasBootstrappedDataRef.current)
406
692
  return;
407
693
  const loadKey = `${runtimeMode}:${currentWorkspaceId}:${workspaceSelectionScope}`;
408
694
  if (lastConfigRefreshKeyRef.current === loadKey)
409
695
  return;
410
696
  lastConfigRefreshKeyRef.current = loadKey;
411
697
  void fetchConfig();
412
- }, [currentWorkspaceId, fetchConfig, hasBootstrappedDataRef, lastConfigRefreshKeyRef, runtimeMode, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls, workspaceSelectionScope]);
698
+ }, [currentWorkspaceId, fetchConfig, hasBootstrappedDataRef, lastConfigRefreshKeyRef, runtimeMode, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls, workspaceBootstrapPending, workspaceSelectionScope]);
413
699
  useEffect(() => {
414
- if (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls)
700
+ if (shouldDeferProtectedApiCalls || shouldBlockProtectedApiCalls || workspaceBootstrapPending)
415
701
  return;
416
702
  const loadKey = currentWorkspaceId;
417
703
  if (lastWorkspaceSyncStateLoadKeyRef.current === loadKey)
418
704
  return;
419
705
  lastWorkspaceSyncStateLoadKeyRef.current = loadKey;
420
706
  void loadWorkspaceSyncState();
421
- }, [currentWorkspaceId, lastWorkspaceSyncStateLoadKeyRef, loadWorkspaceSyncState, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls]);
707
+ }, [currentWorkspaceId, lastWorkspaceSyncStateLoadKeyRef, loadWorkspaceSyncState, shouldBlockProtectedApiCalls, shouldDeferProtectedApiCalls, workspaceBootstrapPending]);
422
708
  useEffect(() => {
423
709
  if (runtimeMode !== 'cloud' || !authSessionResolved || !isAuthenticated) {
424
710
  if (runtimeMode !== 'cloud')
@@ -426,9 +712,11 @@ export function useTaskforceWorkspaceBootstrap(args) {
426
712
  workspaceListHydratedRef.current = false;
427
713
  return;
428
714
  }
715
+ if (workspaceBootstrapPending)
716
+ return;
429
717
  if (!hasBootstrappedDataRef.current || workspaceListHydratedRef.current)
430
718
  return;
431
719
  void fetchWorkspaces();
432
- }, [authSessionResolved, fetchWorkspaces, hasBootstrappedDataRef, isAuthenticated, runtimeMode, setAvailableWorkspaces, workspaceListHydratedRef]);
433
- return { fetchReferenceData, fetchBootstrapConfig, fetchConfig, fetchWorkspaces, fetchAssigneeOptions, switchWorkspace, resolveWorkspaceAfterAuth, retryBootstrapChecks };
720
+ }, [authSessionResolved, fetchWorkspaces, hasBootstrappedDataRef, isAuthenticated, runtimeMode, setAvailableWorkspaces, workspaceBootstrapPending, workspaceListHydratedRef]);
721
+ return { fetchReferenceData, fetchBootstrapConfig, fetchConfig, fetchWorkspaces, fetchAssigneeOptions, switchWorkspace, resolveWorkspaceAfterAuth, retryBootstrapChecks, runCloudBootstrap };
434
722
  }
@@ -12,7 +12,6 @@ export interface MessageCatalog {
12
12
  updatedLabel: string;
13
13
  completedLabel: string;
14
14
  emptyValue: string;
15
- parentCannotSchedule: string;
16
15
  dueBeforeScheduled: string;
17
16
  overdue: string;
18
17
  };
@@ -12,7 +12,6 @@ export const enUS = {
12
12
  updatedLabel: 'Updated:',
13
13
  completedLabel: 'Completed:',
14
14
  emptyValue: '—',
15
- parentCannotSchedule: 'Parent tasks cannot be scheduled. Schedule leaf tasks instead.',
16
15
  dueBeforeScheduled: 'Due date is before scheduled date.',
17
16
  overdue: 'This task is overdue.'
18
17
  },
@@ -12,7 +12,6 @@ export const es419 = {
12
12
  updatedLabel: 'Actualizado:',
13
13
  completedLabel: 'Completado:',
14
14
  emptyValue: '—',
15
- parentCannotSchedule: 'Las tareas padre no se pueden programar. Programa tareas hoja en su lugar.',
16
15
  dueBeforeScheduled: 'La fecha de vencimiento es anterior a la fecha programada.',
17
16
  overdue: 'Esta tarea esta vencida.'
18
17
  },