@taskforcehq/taskforce 0.3.168

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 (492) hide show
  1. package/README.md +241 -0
  2. package/dist/Taskforce.d.ts +3 -0
  3. package/dist/Taskforce.js +3 -0
  4. package/dist/Taskforce.module.css +7587 -0
  5. package/dist/TaskforceCore.CategoryFallback.test.d.ts +1 -0
  6. package/dist/TaskforceCore.CategoryFallback.test.js +98 -0
  7. package/dist/TaskforceCore.d.ts +12 -0
  8. package/dist/TaskforceCore.js +729 -0
  9. package/dist/TaskforceCore.test.d.ts +1 -0
  10. package/dist/TaskforceCore.test.js +2132 -0
  11. package/dist/TaskforceScreenshot.d.ts +11 -0
  12. package/dist/TaskforceScreenshot.js +113 -0
  13. package/dist/TaskforceWidget.d.ts +6 -0
  14. package/dist/TaskforceWidget.js +101 -0
  15. package/dist/TaskforceWidget.test.d.ts +1 -0
  16. package/dist/TaskforceWidget.test.js +92 -0
  17. package/dist/UnsavedChangesRelaxed.test.d.ts +1 -0
  18. package/dist/UnsavedChangesRelaxed.test.js +16 -0
  19. package/dist/__tests__/localizationCatalogs.test.d.ts +1 -0
  20. package/dist/__tests__/localizationCatalogs.test.js +37 -0
  21. package/dist/__tests__/planParser.test.d.ts +1 -0
  22. package/dist/__tests__/planParser.test.js +183 -0
  23. package/dist/__tests__/releaseVersion.test.d.ts +1 -0
  24. package/dist/__tests__/releaseVersion.test.js +32 -0
  25. package/dist/__tests__/structuredCommentValidation.test.d.ts +1 -0
  26. package/dist/__tests__/structuredCommentValidation.test.js +31 -0
  27. package/dist/__tests__/structuredDocValidation.test.d.ts +1 -0
  28. package/dist/__tests__/structuredDocValidation.test.js +30 -0
  29. package/dist/__tests__/templateEngine.test.d.ts +1 -0
  30. package/dist/__tests__/templateEngine.test.js +150 -0
  31. package/dist/__tests__/templates.test.d.ts +1 -0
  32. package/dist/__tests__/templates.test.js +45 -0
  33. package/dist/__tests__/workflowDocumentFixtures.test.d.ts +1 -0
  34. package/dist/__tests__/workflowDocumentFixtures.test.js +78 -0
  35. package/dist/__tests__/workflowExportMatrix.test.d.ts +1 -0
  36. package/dist/__tests__/workflowExportMatrix.test.js +113 -0
  37. package/dist/__tests__/workflowExportSnapshots.test.d.ts +1 -0
  38. package/dist/__tests__/workflowExportSnapshots.test.js +28 -0
  39. package/dist/__tests__/workflowGeneration.test.d.ts +1 -0
  40. package/dist/__tests__/workflowGeneration.test.js +12 -0
  41. package/dist/__tests__/workflowSimulation.test.d.ts +1 -0
  42. package/dist/__tests__/workflowSimulation.test.js +113 -0
  43. package/dist/__tests__/workflowV2Policy.test.d.ts +1 -0
  44. package/dist/__tests__/workflowV2Policy.test.js +80 -0
  45. package/dist/api.d.ts +30 -0
  46. package/dist/api.js +85 -0
  47. package/dist/cli.d.ts +2 -0
  48. package/dist/cli.js +217 -0
  49. package/dist/compat/priorityCompat.d.ts +2 -0
  50. package/dist/compat/priorityCompat.js +24 -0
  51. package/dist/components/features/DocumentGenerateModal.d.ts +9 -0
  52. package/dist/components/features/DocumentGenerateModal.js +114 -0
  53. package/dist/components/features/DocumentIndex.d.ts +19 -0
  54. package/dist/components/features/DocumentIndex.js +51 -0
  55. package/dist/components/features/DocumentViewer.d.ts +9 -0
  56. package/dist/components/features/DocumentViewer.js +253 -0
  57. package/dist/components/features/DocumentWorkspace.d.ts +21 -0
  58. package/dist/components/features/DocumentWorkspace.js +88 -0
  59. package/dist/components/features/TaskSettings.d.ts +209 -0
  60. package/dist/components/features/TaskSettings.js +500 -0
  61. package/dist/components/features/TaskSettings.test.d.ts +1 -0
  62. package/dist/components/features/TaskSettings.test.js +308 -0
  63. package/dist/components/features/settings/CategoryManager.d.ts +19 -0
  64. package/dist/components/features/settings/CategoryManager.js +113 -0
  65. package/dist/components/features/settings/TaxonomyEditor.d.ts +8 -0
  66. package/dist/components/features/settings/TaxonomyEditor.js +98 -0
  67. package/dist/components/features/settings/TaxonomyManager 2.d.ts +52 -0
  68. package/dist/components/features/settings/TaxonomyManager 2.js +75 -0
  69. package/dist/components/features/settings/TaxonomyManager.d.ts +54 -0
  70. package/dist/components/features/settings/TaxonomyManager.js +77 -0
  71. package/dist/components/features/settings/TypeManager.d.ts +10 -0
  72. package/dist/components/features/settings/TypeManager.js +50 -0
  73. package/dist/components/features/settings/WorkflowEditorPrototype.d.ts +15 -0
  74. package/dist/components/features/settings/WorkflowEditorPrototype.js +245 -0
  75. package/dist/components/task/TaskActionHeader.d.ts +20 -0
  76. package/dist/components/task/TaskActionHeader.js +13 -0
  77. package/dist/components/task/TaskCard.d.ts +47 -0
  78. package/dist/components/task/TaskCard.js +326 -0
  79. package/dist/components/task/TaskCard.test.d.ts +1 -0
  80. package/dist/components/task/TaskCard.test.js +95 -0
  81. package/dist/components/task/TaskContextUpload.d.ts +12 -0
  82. package/dist/components/task/TaskContextUpload.js +393 -0
  83. package/dist/components/task/TaskContextUpload.test.d.ts +1 -0
  84. package/dist/components/task/TaskContextUpload.test.js +189 -0
  85. package/dist/components/task/TaskForm.d.ts +105 -0
  86. package/dist/components/task/TaskForm.js +241 -0
  87. package/dist/components/task/TaskForm.test.d.ts +1 -0
  88. package/dist/components/task/TaskForm.test.js +288 -0
  89. package/dist/components/task/TaskKanban 2.d.ts +76 -0
  90. package/dist/components/task/TaskKanban 2.js +1004 -0
  91. package/dist/components/task/TaskKanban.d.ts +76 -0
  92. package/dist/components/task/TaskKanban.empty-column-drop.test.d.ts +1 -0
  93. package/dist/components/task/TaskKanban.empty-column-drop.test.js +66 -0
  94. package/dist/components/task/TaskKanban.js +1026 -0
  95. package/dist/components/task/TaskKanban.test.d.ts +1 -0
  96. package/dist/components/task/TaskKanban.test.js +46 -0
  97. package/dist/components/task/TaskList.d.ts +74 -0
  98. package/dist/components/task/TaskList.js +38 -0
  99. package/dist/components/task/TaskList.test.d.ts +1 -0
  100. package/dist/components/task/TaskList.test.js +270 -0
  101. package/dist/components/task/TaskScreenshotSection.d.ts +15 -0
  102. package/dist/components/task/TaskScreenshotSection.js +22 -0
  103. package/dist/components/ui/LevelSelector.d.ts +13 -0
  104. package/dist/components/ui/LevelSelector.js +25 -0
  105. package/dist/components/ui/LevelSelector.test.d.ts +1 -0
  106. package/dist/components/ui/LevelSelector.test.js +55 -0
  107. package/dist/components/ui/Markdown.d.ts +6 -0
  108. package/dist/components/ui/Markdown.js +94 -0
  109. package/dist/components/ui/Markdown.test.d.ts +1 -0
  110. package/dist/components/ui/Markdown.test.js +38 -0
  111. package/dist/components/ui/Modal.d.ts +16 -0
  112. package/dist/components/ui/Modal.js +49 -0
  113. package/dist/components/ui/MultiSelectFilter.d.ts +10 -0
  114. package/dist/components/ui/MultiSelectFilter.js +71 -0
  115. package/dist/components/ui/MultiSelectFilter.test.d.ts +1 -0
  116. package/dist/components/ui/MultiSelectFilter.test.js +74 -0
  117. package/dist/components/ui/TaxonomyDropdown.d.ts +17 -0
  118. package/dist/components/ui/TaxonomyDropdown.js +145 -0
  119. package/dist/components/ui/TaxonomyDropdown.test.d.ts +1 -0
  120. package/dist/components/ui/TaxonomyDropdown.test.js +130 -0
  121. package/dist/components/views/StandaloneLayout 2.d.ts +8 -0
  122. package/dist/components/views/StandaloneLayout 2.js +1660 -0
  123. package/dist/components/views/StandaloneLayout.d.ts +8 -0
  124. package/dist/components/views/StandaloneLayout.js +2117 -0
  125. package/dist/components/views/WidgetView.d.ts +8 -0
  126. package/dist/components/views/WidgetView.js +221 -0
  127. package/dist/config/envSchema.d.ts +28 -0
  128. package/dist/config/envSchema.js +374 -0
  129. package/dist/config/envSchema.test.d.ts +1 -0
  130. package/dist/config/envSchema.test.js +97 -0
  131. package/dist/config/surfaceBase.d.ts +2 -0
  132. package/dist/config/surfaceBase.js +8 -0
  133. package/dist/core/BulkUpdateFieldsPreview.test.d.ts +1 -0
  134. package/dist/core/BulkUpdateFieldsPreview.test.js +62 -0
  135. package/dist/core/CompletedAt.test.d.ts +1 -0
  136. package/dist/core/CompletedAt.test.js +44 -0
  137. package/dist/core/CreatedBy.test.d.ts +1 -0
  138. package/dist/core/CreatedBy.test.js +60 -0
  139. package/dist/core/EntitlementsPolicy.test.d.ts +1 -0
  140. package/dist/core/EntitlementsPolicy.test.js +145 -0
  141. package/dist/core/InitiativeTemplates.test.d.ts +1 -0
  142. package/dist/core/InitiativeTemplates.test.js +50 -0
  143. package/dist/core/ParentLifecycle.test.d.ts +1 -0
  144. package/dist/core/ParentLifecycle.test.js +98 -0
  145. package/dist/core/ScheduleFields.test.d.ts +1 -0
  146. package/dist/core/ScheduleFields.test.js +197 -0
  147. package/dist/core/SetupState.test.d.ts +1 -0
  148. package/dist/core/SetupState.test.js +113 -0
  149. package/dist/core/SystemAdmin.test.d.ts +1 -0
  150. package/dist/core/SystemAdmin.test.js +295 -0
  151. package/dist/core/TaskTaxonomyValidation.test.d.ts +1 -0
  152. package/dist/core/TaskTaxonomyValidation.test.js +64 -0
  153. package/dist/core/Taskforce.d.ts +1063 -0
  154. package/dist/core/Taskforce.ids.test.d.ts +1 -0
  155. package/dist/core/Taskforce.ids.test.js +168 -0
  156. package/dist/core/Taskforce.js +5981 -0
  157. package/dist/core/Taskforce.realtimeMutation.test.d.ts +1 -0
  158. package/dist/core/Taskforce.realtimeMutation.test.js +76 -0
  159. package/dist/core/WorkspaceDelete.test.d.ts +1 -0
  160. package/dist/core/WorkspaceDelete.test.js +140 -0
  161. package/dist/core/WorkspacePermissions.test.d.ts +1 -0
  162. package/dist/core/WorkspacePermissions.test.js +151 -0
  163. package/dist/core/WorkspacePlanMode.test.d.ts +1 -0
  164. package/dist/core/WorkspacePlanMode.test.js +104 -0
  165. package/dist/hooks/authGuardPolicy.d.ts +26 -0
  166. package/dist/hooks/authGuardPolicy.js +32 -0
  167. package/dist/hooks/authGuardPolicy.test.d.ts +1 -0
  168. package/dist/hooks/authGuardPolicy.test.js +76 -0
  169. package/dist/hooks/useDraggable.d.ts +12 -0
  170. package/dist/hooks/useDraggable.js +65 -0
  171. package/dist/hooks/useRealtimeSync.d.ts +24 -0
  172. package/dist/hooks/useRealtimeSync.js +275 -0
  173. package/dist/hooks/useRealtimeSync.test.d.ts +1 -0
  174. package/dist/hooks/useRealtimeSync.test.js +284 -0
  175. package/dist/hooks/useSyncOrchestrator.d.ts +164 -0
  176. package/dist/hooks/useSyncOrchestrator.js +1427 -0
  177. package/dist/hooks/useSyncOrchestrator.retry-closure.test.d.ts +1 -0
  178. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +367 -0
  179. package/dist/hooks/useTaskforce.d.ts +727 -0
  180. package/dist/hooks/useTaskforce.js +4396 -0
  181. package/dist/hooks/useTaskforce.priority-filters.test.d.ts +1 -0
  182. package/dist/hooks/useTaskforce.priority-filters.test.js +33 -0
  183. package/dist/hooks/useWorkspaceSyncController.d.ts +80 -0
  184. package/dist/hooks/useWorkspaceSyncController.js +598 -0
  185. package/dist/hooks/useWorkspaceSyncController.test.d.ts +1 -0
  186. package/dist/hooks/useWorkspaceSyncController.test.js +236 -0
  187. package/dist/index.d.ts +12 -0
  188. package/dist/index.js +11 -0
  189. package/dist/localization/index.d.ts +8 -0
  190. package/dist/localization/index.js +81 -0
  191. package/dist/localization/locales/en-US.d.ts +151 -0
  192. package/dist/localization/locales/en-US.js +150 -0
  193. package/dist/localization/locales/es-419.d.ts +2 -0
  194. package/dist/localization/locales/es-419.js +150 -0
  195. package/dist/localization/locales/pt-BR.d.ts +2 -0
  196. package/dist/localization/locales/pt-BR.js +150 -0
  197. package/dist/main.d.ts +1 -0
  198. package/dist/main.js +33 -0
  199. package/dist/mcp/optionResolution.d.ts +14 -0
  200. package/dist/mcp/optionResolution.js +126 -0
  201. package/dist/mcp/optionResolution.test.d.ts +1 -0
  202. package/dist/mcp/optionResolution.test.js +39 -0
  203. package/dist/mcp/server.d.ts +1 -0
  204. package/dist/mcp/server.js +1793 -0
  205. package/dist/mcp/structuredCommentValidation.d.ts +6 -0
  206. package/dist/mcp/structuredCommentValidation.js +56 -0
  207. package/dist/mcp/structuredDocValidation.d.ts +6 -0
  208. package/dist/mcp/structuredDocValidation.js +75 -0
  209. package/dist/migrations/billingSchemaParity.test.d.ts +1 -0
  210. package/dist/migrations/billingSchemaParity.test.js +46 -0
  211. package/dist/migrations/clientMigrations.d.ts +9 -0
  212. package/dist/migrations/clientMigrations.js +74 -0
  213. package/dist/migrations/flags.d.ts +6 -0
  214. package/dist/migrations/flags.js +8 -0
  215. package/dist/migrations/taskMigrations.d.ts +71 -0
  216. package/dist/migrations/taskMigrations.js +236 -0
  217. package/dist/migrations/taskMigrations.test.d.ts +1 -0
  218. package/dist/migrations/taskMigrations.test.js +34 -0
  219. package/dist/migrations/taskSchemaMigrations.d.ts +11 -0
  220. package/dist/migrations/taskSchemaMigrations.js +453 -0
  221. package/dist/plugins/index.d.ts +10 -0
  222. package/dist/plugins/index.js +10 -0
  223. package/dist/plugins/vite.d.ts +19 -0
  224. package/dist/plugins/vite.js +271 -0
  225. package/dist/public/android-chrome-192x192.png +0 -0
  226. package/dist/public/android-chrome-512x512.png +0 -0
  227. package/dist/public/apple-touch-icon.png +0 -0
  228. package/dist/public/favicon-16x16.png +0 -0
  229. package/dist/public/favicon-32x32.png +0 -0
  230. package/dist/public/favicon.ico +0 -0
  231. package/dist/public/favicon.png +0 -0
  232. package/dist/public/site.webmanifest +1 -0
  233. package/dist/public/taskforce.png +0 -0
  234. package/dist/resources/templates/WORKFLOW_V2_POLICY.md +55 -0
  235. package/dist/resources/templates/environments/aider.yaml +14 -0
  236. package/dist/resources/templates/environments/antigravity.yaml +10 -0
  237. package/dist/resources/templates/environments/claude.yaml +10 -0
  238. package/dist/resources/templates/environments/cline.yaml +10 -0
  239. package/dist/resources/templates/environments/codex.yaml +13 -0
  240. package/dist/resources/templates/environments/copilot.yaml +10 -0
  241. package/dist/resources/templates/environments/cursor.yaml +9 -0
  242. package/dist/resources/templates/environments/gemini.yaml +9 -0
  243. package/dist/resources/templates/environments/windsurf.yaml +10 -0
  244. package/dist/resources/templates/workflow-sources/README.md +9 -0
  245. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +520 -0
  246. package/dist/resources/templates/workflow-sources/workflowEditorOverrides.json +1 -0
  247. package/dist/resources/templates/workflows/collaborate.yaml +104 -0
  248. package/dist/resources/templates/workflows/do.yaml +82 -0
  249. package/dist/resources/templates/workflows/evaluate.yaml +111 -0
  250. package/dist/resources/templates/workflows/execute.yaml +142 -0
  251. package/dist/resources/templates/workflows/plan.yaml +112 -0
  252. package/dist/resources/templates/workflows/review.yaml +109 -0
  253. package/dist/runtime/capabilities.d.ts +8 -0
  254. package/dist/runtime/capabilities.js +16 -0
  255. package/dist/security/virusScan.d.ts +16 -0
  256. package/dist/security/virusScan.js +83 -0
  257. package/dist/server/auth.d.ts +51 -0
  258. package/dist/server/auth.js +266 -0
  259. package/dist/server/auth.test.d.ts +1 -0
  260. package/dist/server/auth.test.js +111 -0
  261. package/dist/server/buildInfo.d.ts +7 -0
  262. package/dist/server/buildInfo.js +100 -0
  263. package/dist/server/buildInfo.test.d.ts +1 -0
  264. package/dist/server/buildInfo.test.js +29 -0
  265. package/dist/server/cors.d.ts +7 -0
  266. package/dist/server/cors.js +76 -0
  267. package/dist/server/cors.test.d.ts +1 -0
  268. package/dist/server/cors.test.js +71 -0
  269. package/dist/server/email.d.ts +28 -0
  270. package/dist/server/email.js +138 -0
  271. package/dist/server/email.test.d.ts +1 -0
  272. package/dist/server/email.test.js +113 -0
  273. package/dist/server/index.cookieProxy.test.d.ts +1 -0
  274. package/dist/server/index.cookieProxy.test.js +19 -0
  275. package/dist/server/index.d.ts +17 -0
  276. package/dist/server/index.js +757 -0
  277. package/dist/server/index.rateLimit.test.d.ts +1 -0
  278. package/dist/server/index.rateLimit.test.js +13 -0
  279. package/dist/server/index.test.d.ts +1 -0
  280. package/dist/server/index.test.js +19 -0
  281. package/dist/server/rateLimit.d.ts +29 -0
  282. package/dist/server/rateLimit.js +99 -0
  283. package/dist/server/rateLimit.test.d.ts +1 -0
  284. package/dist/server/rateLimit.test.js +107 -0
  285. package/dist/server/realtimeSync.auth.test.d.ts +1 -0
  286. package/dist/server/realtimeSync.auth.test.js +62 -0
  287. package/dist/server/realtimeSync.replay.test.d.ts +1 -0
  288. package/dist/server/realtimeSync.replay.test.js +192 -0
  289. package/dist/server/realtimeSync.workspaceIsolation.test.d.ts +1 -0
  290. package/dist/server/realtimeSync.workspaceIsolation.test.js +101 -0
  291. package/dist/server/realtimeSyncWs.d.ts +30 -0
  292. package/dist/server/realtimeSyncWs.js +257 -0
  293. package/dist/server/routes.billing.d.ts +15 -0
  294. package/dist/server/routes.billing.js +991 -0
  295. package/dist/server/routes.billing.test.d.ts +1 -0
  296. package/dist/server/routes.billing.test.js +387 -0
  297. package/dist/server/routes.d.ts +72 -0
  298. package/dist/server/routes.js +4820 -0
  299. package/dist/server/routes.sync.d.ts +17 -0
  300. package/dist/server/routes.sync.integration.test.d.ts +1 -0
  301. package/dist/server/routes.sync.integration.test.js +470 -0
  302. package/dist/server/routes.sync.js +1141 -0
  303. package/dist/server/routes.test.d.ts +1 -0
  304. package/dist/server/routes.test.js +4945 -0
  305. package/dist/server/runtimeMode.d.ts +4 -0
  306. package/dist/server/runtimeMode.js +25 -0
  307. package/dist/server/runtimeMode.test.d.ts +1 -0
  308. package/dist/server/runtimeMode.test.js +26 -0
  309. package/dist/server/securityHeaders.d.ts +3 -0
  310. package/dist/server/securityHeaders.js +25 -0
  311. package/dist/server/securityHeaders.test.d.ts +1 -0
  312. package/dist/server/securityHeaders.test.js +32 -0
  313. package/dist/server/smtpTransport.d.ts +21 -0
  314. package/dist/server/smtpTransport.js +11 -0
  315. package/dist/server/start.d.ts +1 -0
  316. package/dist/server/start.js +17 -0
  317. package/dist/services/formatTransformers.d.ts +29 -0
  318. package/dist/services/formatTransformers.js +56 -0
  319. package/dist/services/templateEngine.d.ts +46 -0
  320. package/dist/services/templateEngine.js +148 -0
  321. package/dist/shared/runtimeContract.d.ts +12 -0
  322. package/dist/shared/runtimeContract.js +23 -0
  323. package/dist/shared/runtimeContract.test.d.ts +1 -0
  324. package/dist/shared/runtimeContract.test.js +23 -0
  325. package/dist/soak/preflight.cli.d.ts +1 -0
  326. package/dist/soak/preflight.cli.js +28 -0
  327. package/dist/soak/preflight.d.ts +26 -0
  328. package/dist/soak/preflight.js +113 -0
  329. package/dist/soak/preflight.test.d.ts +1 -0
  330. package/dist/soak/preflight.test.js +39 -0
  331. package/dist/soak/syncSoakHarness.d.ts +6 -0
  332. package/dist/soak/syncSoakHarness.js +10 -0
  333. package/dist/storage/createDatabaseAdapter.d.ts +2 -0
  334. package/dist/storage/createDatabaseAdapter.js +9 -0
  335. package/dist/storage/databaseAdapter.d.ts +22 -0
  336. package/dist/storage/databaseAdapter.js +1 -0
  337. package/dist/storage/documentIntegrity.d.ts +28 -0
  338. package/dist/storage/documentIntegrity.js +103 -0
  339. package/dist/storage/documentPurge.d.ts +14 -0
  340. package/dist/storage/documentPurge.js +46 -0
  341. package/dist/storage/documentRegistry.d.ts +39 -0
  342. package/dist/storage/documentRegistry.js +115 -0
  343. package/dist/storage/integritySchedule.d.ts +17 -0
  344. package/dist/storage/integritySchedule.js +62 -0
  345. package/dist/storage/objectStorage.d.ts +20 -0
  346. package/dist/storage/objectStorage.js +15 -0
  347. package/dist/storage/objectStorageClient.d.ts +18 -0
  348. package/dist/storage/objectStorageClient.js +301 -0
  349. package/dist/storage/objectStorageConfig.d.ts +2 -0
  350. package/dist/storage/objectStorageConfig.js +45 -0
  351. package/dist/storage/postgresAdapter.d.ts +20 -0
  352. package/dist/storage/postgresAdapter.js +204 -0
  353. package/dist/storage/postgresWorker.d.ts +1 -0
  354. package/dist/storage/postgresWorker.js +110 -0
  355. package/dist/storage/providerConfig.d.ts +6 -0
  356. package/dist/storage/providerConfig.js +11 -0
  357. package/dist/storage/sqliteAdapter.d.ts +14 -0
  358. package/dist/storage/sqliteAdapter.js +28 -0
  359. package/dist/storage/taskAssetStore.d.ts +56 -0
  360. package/dist/storage/taskAssetStore.js +297 -0
  361. package/dist/sync/cloudSyncApi.d.ts +47 -0
  362. package/dist/sync/cloudSyncApi.js +138 -0
  363. package/dist/sync/cloudSyncApi.test.d.ts +1 -0
  364. package/dist/sync/cloudSyncApi.test.js +54 -0
  365. package/dist/sync/encryptionService.d.ts +11 -0
  366. package/dist/sync/encryptionService.js +61 -0
  367. package/dist/sync/encryptionService.test.d.ts +1 -0
  368. package/dist/sync/encryptionService.test.js +52 -0
  369. package/dist/sync/realtimeFeature.d.ts +6 -0
  370. package/dist/sync/realtimeFeature.js +20 -0
  371. package/dist/sync/realtimeFeature.test.d.ts +1 -0
  372. package/dist/sync/realtimeFeature.test.js +30 -0
  373. package/dist/sync/realtimeReconcile.d.ts +30 -0
  374. package/dist/sync/realtimeReconcile.js +90 -0
  375. package/dist/sync/realtimeReconcile.test.d.ts +1 -0
  376. package/dist/sync/realtimeReconcile.test.js +43 -0
  377. package/dist/sync/syncService.d.ts +96 -0
  378. package/dist/sync/syncService.js +423 -0
  379. package/dist/sync/syncService.test.d.ts +1 -0
  380. package/dist/sync/syncService.test.js +484 -0
  381. package/dist/sync/workspaceSyncModel.d.ts +61 -0
  382. package/dist/sync/workspaceSyncModel.js +136 -0
  383. package/dist/sync/workspaceSyncModel.test.d.ts +1 -0
  384. package/dist/sync/workspaceSyncModel.test.js +73 -0
  385. package/dist/test/setup.d.ts +1 -0
  386. package/dist/test/setup.js +15 -0
  387. package/dist/types.d.ts +157 -0
  388. package/dist/types.js +41 -0
  389. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  390. package/dist/ui/assets/index-0W5vyCpn.css +1 -0
  391. package/dist/ui/assets/index-MCZuNNfn.js +23 -0
  392. package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
  393. package/dist/ui/assets/taskforce-BxLPokNB.png +0 -0
  394. package/dist/ui/assets/vendor-dnd-BI1K77C1.js +5 -0
  395. package/dist/ui/assets/vendor-icons-ZFG3SGwo.js +1 -0
  396. package/dist/ui/assets/vendor-markdown-CKYkQHfj.js +29 -0
  397. package/dist/ui/assets/vendor-react-DdIDsh7f.js +9 -0
  398. package/dist/ui/assets/vendor-router-BtiNU7Dl.js +3 -0
  399. package/dist/ui/branding/logos/logo_black_01.png +0 -0
  400. package/dist/ui/branding/logos/logo_black_02.png +0 -0
  401. package/dist/ui/branding/logos/logo_white_01.png +0 -0
  402. package/dist/ui/branding/logos/logo_white_02.png +0 -0
  403. package/dist/ui/favicon/android-chrome-192x192.png +0 -0
  404. package/dist/ui/favicon/android-chrome-512x512.png +0 -0
  405. package/dist/ui/favicon/apple-touch-icon.png +0 -0
  406. package/dist/ui/favicon/favicon-16x16.png +0 -0
  407. package/dist/ui/favicon/favicon-32x32.png +0 -0
  408. package/dist/ui/favicon/favicon.ico +0 -0
  409. package/dist/ui/favicon/favicon.png +0 -0
  410. package/dist/ui/favicon/site.webmanifest +1 -0
  411. package/dist/ui/fonts/RussoOne-Regular.ttf +0 -0
  412. package/dist/ui/images/taskforce.png +0 -0
  413. package/dist/ui/index.html +19 -0
  414. package/dist/utils/checklistMarkdown.d.ts +9 -0
  415. package/dist/utils/checklistMarkdown.js +46 -0
  416. package/dist/utils/constants.d.ts +21 -0
  417. package/dist/utils/constants.icons.test.d.ts +1 -0
  418. package/dist/utils/constants.icons.test.js +40 -0
  419. package/dist/utils/constants.js +100 -0
  420. package/dist/utils/contextFiles.d.ts +8 -0
  421. package/dist/utils/contextFiles.js +54 -0
  422. package/dist/utils/formatting.d.ts +5 -0
  423. package/dist/utils/formatting.js +30 -0
  424. package/dist/utils/planParser.d.ts +22 -0
  425. package/dist/utils/planParser.js +151 -0
  426. package/dist/utils/taskDragZones.d.ts +8 -0
  427. package/dist/utils/taskDragZones.js +14 -0
  428. package/dist/utils/taskDragZones.test.d.ts +1 -0
  429. package/dist/utils/taskDragZones.test.js +14 -0
  430. package/dist/utils/taskHierarchy.d.ts +4 -0
  431. package/dist/utils/taskHierarchy.js +69 -0
  432. package/dist/utils/taskHierarchy.test.d.ts +1 -0
  433. package/dist/utils/taskHierarchy.test.js +38 -0
  434. package/dist/utils/taskParenting.d.ts +3 -0
  435. package/dist/utils/taskParenting.js +24 -0
  436. package/dist/utils/taskParenting.test.d.ts +1 -0
  437. package/dist/utils/taskParenting.test.js +21 -0
  438. package/dist/utils/taskRelations.d.ts +6 -0
  439. package/dist/utils/taskRelations.js +24 -0
  440. package/dist/utils/taskRelations.test.d.ts +1 -0
  441. package/dist/utils/taskRelations.test.js +34 -0
  442. package/dist/utils/taskUtils.d.ts +2 -0
  443. package/dist/utils/taskUtils.js +20 -0
  444. package/dist/utils/taskforceApiClient.d.ts +7 -0
  445. package/dist/utils/taskforceApiClient.js +42 -0
  446. package/dist/utils/workspaceIdentity.d.ts +8 -0
  447. package/dist/utils/workspaceIdentity.js +47 -0
  448. package/dist/utils/workspaceIdentity.test.d.ts +1 -0
  449. package/dist/utils/workspaceIdentity.test.js +32 -0
  450. package/dist/utils/workspaceLifecycle.d.ts +26 -0
  451. package/dist/utils/workspaceLifecycle.js +69 -0
  452. package/dist/utils/workspaceLifecycle.test.d.ts +1 -0
  453. package/dist/utils/workspaceLifecycle.test.js +64 -0
  454. package/package.json +133 -0
  455. package/scripts/check-compat-boundaries.js +48 -0
  456. package/scripts/check-env.ts +24 -0
  457. package/scripts/check-localization-strings.mjs +49 -0
  458. package/scripts/check-runtime-boundaries.mjs +44 -0
  459. package/scripts/debug-global.mjs +34 -0
  460. package/scripts/debug-server.mjs +36 -0
  461. package/scripts/export-sqlite-to-postgres.mjs +125 -0
  462. package/scripts/generate-env-example.ts +8 -0
  463. package/scripts/generate-workflow-templates.mjs +134 -0
  464. package/scripts/mcp-stdio-adapter.mjs +160 -0
  465. package/scripts/postinstall.js +60 -0
  466. package/scripts/release-version-lib.mjs +88 -0
  467. package/scripts/release-version.mjs +72 -0
  468. package/scripts/run-dev-proxy.sh +49 -0
  469. package/scripts/run-e2e-realtime.sh +16 -0
  470. package/scripts/run-smoke.sh +23 -0
  471. package/scripts/run-soak.sh +29 -0
  472. package/scripts/test-global.js +143 -0
  473. package/scripts/update-homebrew-formula.sh +38 -0
  474. package/src/resources/templates/WORKFLOW_V2_POLICY.md +55 -0
  475. package/src/resources/templates/environments/aider.yaml +14 -0
  476. package/src/resources/templates/environments/antigravity.yaml +10 -0
  477. package/src/resources/templates/environments/claude.yaml +10 -0
  478. package/src/resources/templates/environments/cline.yaml +10 -0
  479. package/src/resources/templates/environments/codex.yaml +13 -0
  480. package/src/resources/templates/environments/copilot.yaml +10 -0
  481. package/src/resources/templates/environments/cursor.yaml +9 -0
  482. package/src/resources/templates/environments/gemini.yaml +9 -0
  483. package/src/resources/templates/environments/windsurf.yaml +10 -0
  484. package/src/resources/templates/workflow-sources/README.md +9 -0
  485. package/src/resources/templates/workflow-sources/workflowDocs.mjs +520 -0
  486. package/src/resources/templates/workflow-sources/workflowEditorOverrides.json +1 -0
  487. package/src/resources/templates/workflows/collaborate.yaml +104 -0
  488. package/src/resources/templates/workflows/do.yaml +82 -0
  489. package/src/resources/templates/workflows/evaluate.yaml +111 -0
  490. package/src/resources/templates/workflows/execute.yaml +142 -0
  491. package/src/resources/templates/workflows/plan.yaml +112 -0
  492. package/src/resources/templates/workflows/review.yaml +109 -0
@@ -0,0 +1,598 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { calculateWorkspaceRetryDelayMs } from '../sync/syncService';
3
+ import { bootstrapPushWorkspaceSnapshot } from '../sync/cloudSyncApi';
4
+ const WORKSPACE_PULL_LEASE_KEY_PREFIX = 'taskforce.sync.pullLease.v1';
5
+ const WORKSPACE_PULL_COOLDOWN_KEY_PREFIX = 'taskforce.sync.pullCooldown.v1';
6
+ const WORKSPACE_PULL_LEASE_MS = 30_000;
7
+ const WORKSPACE_PULL_MIN_INTERVAL_MS = 10_000;
8
+ const SYNC_DEBUG_LOG_KEY = 'taskforce.syncDebug.v1';
9
+ function isSyncDebugLoggingEnabled() {
10
+ if (typeof window === 'undefined')
11
+ return false;
12
+ try {
13
+ const raw = String(window.localStorage.getItem(SYNC_DEBUG_LOG_KEY) || '').trim().toLowerCase();
14
+ return raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes';
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ function logSyncDebug(event, details) {
21
+ if (!isSyncDebugLoggingEnabled())
22
+ return;
23
+ const payload = details && typeof details === 'object' ? details : {};
24
+ console.info('[Taskforce Sync]', event, payload);
25
+ }
26
+ function getWorkspacePullLeaseStorageKey(workspaceId) {
27
+ return `${WORKSPACE_PULL_LEASE_KEY_PREFIX}.${workspaceId}`;
28
+ }
29
+ function getWorkspacePullCooldownStorageKey(workspaceId) {
30
+ return `${WORKSPACE_PULL_COOLDOWN_KEY_PREFIX}.${workspaceId}`;
31
+ }
32
+ function isValidWorkspaceKey(workspaceId) {
33
+ const workspaceKey = String(workspaceId || '').trim();
34
+ return workspaceKey.length > 0 && workspaceKey.toLowerCase() !== 'default';
35
+ }
36
+ export function useWorkspaceSyncController(input) {
37
+ const { currentWorkspaceId, setWorkspaceSyncReady, setWorkspaceCloudHydrated, setWorkspaceCloudSyncEnabled, setWorkspaceSyncSourceOfTruth, setWorkspaceSyncOnboardingCompleted, setUserGlobalLastSyncedAt, setWorkspaceLastPullAt, setWorkspaceLastPushAt } = input;
38
+ const [workspaceRetryAt, setWorkspaceRetryAt] = useState(null);
39
+ const [workspaceBootstrapSyncInProgress, setWorkspaceBootstrapSyncInProgress] = useState(false);
40
+ const [workspaceBootstrapSyncPages, setWorkspaceBootstrapSyncPages] = useState(0);
41
+ const [workspaceBootstrapSyncAppliedChanges, setWorkspaceBootstrapSyncAppliedChanges] = useState(0);
42
+ const [workspaceSyncFailureCount, setWorkspaceSyncFailureCount] = useState(0);
43
+ const [workspaceSyncRetryScheduledCount, setWorkspaceSyncRetryScheduledCount] = useState(0);
44
+ const [workspaceSyncAuthFailureCount, setWorkspaceSyncAuthFailureCount] = useState(0);
45
+ const [workspaceSyncLastFailureSource, setWorkspaceSyncLastFailureSource] = useState(null);
46
+ const [workspaceSyncLastFailureStatusCode, setWorkspaceSyncLastFailureStatusCode] = useState(null);
47
+ const workspacePushInFlightRef = useRef(false);
48
+ const workspacePullInFlightRef = useRef(false);
49
+ const workspaceRetryTimerRef = useRef(null);
50
+ const workspaceRetryAtMsRef = useRef(null);
51
+ const workspaceRetryAttemptRef = useRef(0);
52
+ const workspaceLastPushedSignatureRef = useRef('');
53
+ const workspacePendingSignatureRef = useRef('');
54
+ const workspaceLastPushedTaskIdsRef = useRef(new Set());
55
+ const workspaceDeletedTaskIdsRef = useRef(new Set());
56
+ const workspacePullCursorRef = useRef(null);
57
+ const workspacePullBootstrapDoneRef = useRef(false);
58
+ const workspaceBackfillForcedRef = useRef(new Set());
59
+ const workspaceSyncReadyRef = useRef(new Set());
60
+ const workspaceCloudHydratedRef = useRef(new Set());
61
+ const workspaceSyncStateReadCountRef = useRef(0);
62
+ const workspacePullLeaseOwnerRef = useRef('');
63
+ const activeLockWorkspaceIdRef = useRef(null);
64
+ const activeLockAbortControllerRef = useRef(null);
65
+ const onFailureRecordedRef = useRef(input.onFailureRecorded);
66
+ useEffect(() => {
67
+ onFailureRecordedRef.current = input.onFailureRecorded;
68
+ }, [input.onFailureRecorded]);
69
+ if (!workspacePullLeaseOwnerRef.current) {
70
+ const now = Date.now().toString(36);
71
+ const rand = Math.random().toString(36).slice(2, 10);
72
+ workspacePullLeaseOwnerRef.current = `pull-${now}-${rand}`;
73
+ }
74
+ const clearWorkspaceRetry = useCallback(() => {
75
+ workspaceRetryAttemptRef.current = 0;
76
+ workspaceRetryAtMsRef.current = null;
77
+ if (workspaceRetryTimerRef.current !== null && typeof window !== 'undefined') {
78
+ window.clearTimeout(workspaceRetryTimerRef.current);
79
+ workspaceRetryTimerRef.current = null;
80
+ }
81
+ setWorkspaceRetryAt(null);
82
+ }, []);
83
+ const scheduleWorkspacePullRetry = useCallback((invokePull, options) => {
84
+ if (typeof window === 'undefined')
85
+ return;
86
+ setWorkspaceSyncRetryScheduledCount((prev) => prev + 1);
87
+ const attempt = workspaceRetryAttemptRef.current + 1;
88
+ workspaceRetryAttemptRef.current = attempt;
89
+ const computedDelayMs = calculateWorkspaceRetryDelayMs(attempt);
90
+ const explicitDelayMs = Number(options?.minDelayMs);
91
+ const delayMs = Number.isFinite(explicitDelayMs) && explicitDelayMs > 0
92
+ ? Math.max(computedDelayMs, Math.floor(explicitDelayMs))
93
+ : computedDelayMs;
94
+ const retryAtMs = Date.now() + delayMs;
95
+ const retryAtIso = new Date(Date.now() + delayMs).toISOString();
96
+ const workspaceKey = String(options?.workspaceId || '').trim();
97
+ if (workspaceKey) {
98
+ try {
99
+ const cooldownKey = getWorkspacePullCooldownStorageKey(workspaceKey);
100
+ const raw = window.localStorage.getItem(cooldownKey);
101
+ const parsed = raw ? JSON.parse(raw) : null;
102
+ const existingNextAt = Number(parsed?.nextAllowedAt || 0);
103
+ const nextAllowedAt = Math.max(existingNextAt, retryAtMs);
104
+ window.localStorage.setItem(cooldownKey, JSON.stringify({ nextAllowedAt }));
105
+ }
106
+ catch {
107
+ // best-effort only
108
+ }
109
+ }
110
+ workspaceRetryAtMsRef.current = retryAtMs;
111
+ setWorkspaceRetryAt(retryAtIso);
112
+ if (workspaceRetryTimerRef.current !== null) {
113
+ window.clearTimeout(workspaceRetryTimerRef.current);
114
+ }
115
+ workspaceRetryTimerRef.current = window.setTimeout(() => {
116
+ workspaceRetryTimerRef.current = null;
117
+ workspaceRetryAtMsRef.current = null;
118
+ setWorkspaceRetryAt(null);
119
+ if (workspaceKey) {
120
+ try {
121
+ const cooldownKey = getWorkspacePullCooldownStorageKey(workspaceKey);
122
+ const raw = window.localStorage.getItem(cooldownKey);
123
+ const parsed = raw ? JSON.parse(raw) : null;
124
+ const nextAllowedAt = Number(parsed?.nextAllowedAt || 0);
125
+ if (Number.isFinite(nextAllowedAt) && nextAllowedAt <= retryAtMs) {
126
+ window.localStorage.setItem(cooldownKey, JSON.stringify({ nextAllowedAt: Date.now() - 1 }));
127
+ }
128
+ }
129
+ catch {
130
+ // best-effort only
131
+ }
132
+ }
133
+ void invokePull();
134
+ }, delayMs);
135
+ }, []);
136
+ const isWorkspacePullRetryPending = useCallback(() => {
137
+ const retryAtMs = workspaceRetryAtMsRef.current;
138
+ return typeof retryAtMs === 'number' && Number.isFinite(retryAtMs) && retryAtMs > Date.now();
139
+ }, []);
140
+ const isWorkspacePullCooldownActive = useCallback((workspaceId) => {
141
+ if (typeof window === 'undefined')
142
+ return false;
143
+ const workspaceKey = String(workspaceId || '').trim();
144
+ if (!workspaceKey)
145
+ return false;
146
+ try {
147
+ const raw = window.localStorage.getItem(getWorkspacePullCooldownStorageKey(workspaceKey));
148
+ const parsed = raw ? JSON.parse(raw) : null;
149
+ const nextAllowedAt = Number(parsed?.nextAllowedAt || 0);
150
+ return Number.isFinite(nextAllowedAt) && nextAllowedAt > Date.now();
151
+ }
152
+ catch {
153
+ return false;
154
+ }
155
+ }, []);
156
+ const markWorkspacePullCooldown = useCallback((workspaceId, minDelayMs) => {
157
+ if (typeof window === 'undefined')
158
+ return;
159
+ const workspaceKey = String(workspaceId || '').trim();
160
+ if (!workspaceKey)
161
+ return;
162
+ try {
163
+ const cooldownKey = getWorkspacePullCooldownStorageKey(workspaceKey);
164
+ const raw = window.localStorage.getItem(cooldownKey);
165
+ const parsed = raw ? JSON.parse(raw) : null;
166
+ const existingNextAt = Number(parsed?.nextAllowedAt || 0);
167
+ const explicitDelayMs = Number(minDelayMs);
168
+ const baselineDelay = Number.isFinite(explicitDelayMs) && explicitDelayMs > 0
169
+ ? Math.max(WORKSPACE_PULL_MIN_INTERVAL_MS, Math.floor(explicitDelayMs))
170
+ : WORKSPACE_PULL_MIN_INTERVAL_MS;
171
+ const nextAllowedAt = Math.max(existingNextAt, Date.now() + baselineDelay);
172
+ window.localStorage.setItem(cooldownKey, JSON.stringify({ nextAllowedAt }));
173
+ }
174
+ catch {
175
+ // best-effort only
176
+ }
177
+ }, []);
178
+ const tryAcquireWorkspacePullLease = useCallback(async (workspaceId) => {
179
+ const workspaceKey = String(workspaceId || '').trim();
180
+ if (!workspaceKey)
181
+ return true;
182
+ const fallbackAcquire = () => {
183
+ if (typeof window === 'undefined')
184
+ return true;
185
+ try {
186
+ const leaseKey = getWorkspacePullLeaseStorageKey(workspaceKey);
187
+ const ownerId = workspacePullLeaseOwnerRef.current;
188
+ const now = Date.now();
189
+ const raw = window.localStorage.getItem(leaseKey);
190
+ const parsed = raw ? JSON.parse(raw) : null;
191
+ const existingOwner = typeof parsed?.owner === 'string' ? parsed.owner : '';
192
+ const existingExpiresAt = Number(parsed?.expiresAt || 0);
193
+ const leaseActive = Number.isFinite(existingExpiresAt) && existingExpiresAt > now;
194
+ if (leaseActive && existingOwner && existingOwner !== ownerId)
195
+ return false;
196
+ const nextLease = {
197
+ owner: ownerId,
198
+ acquiredAt: now,
199
+ expiresAt: now + WORKSPACE_PULL_LEASE_MS
200
+ };
201
+ window.localStorage.setItem(leaseKey, JSON.stringify(nextLease));
202
+ const verifyRaw = window.localStorage.getItem(leaseKey);
203
+ const verify = verifyRaw ? JSON.parse(verifyRaw) : null;
204
+ return verify?.owner === ownerId;
205
+ }
206
+ catch {
207
+ return true;
208
+ }
209
+ };
210
+ if (typeof window === 'undefined' || typeof navigator === 'undefined' || !navigator.locks) {
211
+ return fallbackAcquire();
212
+ }
213
+ if (activeLockAbortControllerRef.current) {
214
+ return activeLockWorkspaceIdRef.current === workspaceKey; // Already holding the lock for this workspace
215
+ }
216
+ const lockName = `taskforce.sync.pullLock.${workspaceKey}`;
217
+ const controller = new AbortController();
218
+ try {
219
+ const acquired = await new Promise((resolve) => {
220
+ navigator.locks.request(lockName, { mode: 'exclusive', ifAvailable: true, signal: controller.signal }, async (lock) => {
221
+ if (!lock) {
222
+ resolve(false);
223
+ return;
224
+ }
225
+ activeLockAbortControllerRef.current = controller;
226
+ activeLockWorkspaceIdRef.current = workspaceKey;
227
+ resolve(true);
228
+ // Hold the lock indefinitely until aborted
229
+ return new Promise(() => { });
230
+ }).catch(() => {
231
+ resolve(false);
232
+ });
233
+ });
234
+ return acquired;
235
+ }
236
+ catch {
237
+ return fallbackAcquire();
238
+ }
239
+ }, []);
240
+ const releaseWorkspacePullLease = useCallback((workspaceId) => {
241
+ const workspaceKey = String(workspaceId || '').trim();
242
+ if (workspaceKey
243
+ && activeLockWorkspaceIdRef.current
244
+ && activeLockWorkspaceIdRef.current !== workspaceKey) {
245
+ return;
246
+ }
247
+ if (activeLockAbortControllerRef.current) {
248
+ activeLockAbortControllerRef.current.abort();
249
+ activeLockAbortControllerRef.current = null;
250
+ }
251
+ activeLockWorkspaceIdRef.current = null;
252
+ if (!workspaceKey || typeof window === 'undefined')
253
+ return;
254
+ try {
255
+ const leaseKey = getWorkspacePullLeaseStorageKey(workspaceKey);
256
+ const raw = window.localStorage.getItem(leaseKey);
257
+ const parsed = raw ? JSON.parse(raw) : null;
258
+ if (parsed?.owner === workspacePullLeaseOwnerRef.current) {
259
+ window.localStorage.removeItem(leaseKey);
260
+ }
261
+ }
262
+ catch {
263
+ // best-effort only
264
+ }
265
+ }, []);
266
+ const recordWorkspaceSyncFailure = useCallback((source, statusCode) => {
267
+ const normalizedStatusCode = typeof statusCode === 'number' && Number.isFinite(statusCode) ? statusCode : null;
268
+ setWorkspaceSyncFailureCount((prev) => prev + 1);
269
+ setWorkspaceSyncLastFailureSource(source);
270
+ setWorkspaceSyncLastFailureStatusCode(normalizedStatusCode);
271
+ const onFailureRecorded = onFailureRecordedRef.current;
272
+ if (onFailureRecorded)
273
+ onFailureRecorded(source, normalizedStatusCode);
274
+ }, []);
275
+ const incrementWorkspaceSyncAuthFailure = useCallback(() => {
276
+ setWorkspaceSyncAuthFailureCount((prev) => prev + 1);
277
+ }, []);
278
+ const persistWorkspaceSyncPatch = useCallback(async (patch) => {
279
+ if (!isValidWorkspaceKey(currentWorkspaceId))
280
+ return;
281
+ try {
282
+ await fetch('/api/taskforce/ui-state', {
283
+ method: 'POST',
284
+ headers: { 'Content-Type': 'application/json' },
285
+ credentials: 'include',
286
+ body: JSON.stringify({
287
+ workspaceId: currentWorkspaceId,
288
+ stateKey: 'workspace-sync',
289
+ patch: {
290
+ ...patch,
291
+ updatedAt: new Date().toISOString()
292
+ }
293
+ })
294
+ });
295
+ }
296
+ catch {
297
+ // ignore persistence errors
298
+ }
299
+ }, [currentWorkspaceId]);
300
+ const loadWorkspaceSyncState = useCallback(async () => {
301
+ const workspaceId = String(currentWorkspaceId || '').trim();
302
+ if (!isValidWorkspaceKey(workspaceId)) {
303
+ setWorkspaceCloudSyncEnabled(false);
304
+ setWorkspaceSyncSourceOfTruth(null);
305
+ setWorkspaceSyncOnboardingCompleted(false);
306
+ workspacePullCursorRef.current = null;
307
+ workspacePullBootstrapDoneRef.current = false;
308
+ logSyncDebug('workspace_sync_state_read_skipped_invalid_workspace', { workspaceId });
309
+ return;
310
+ }
311
+ workspaceSyncStateReadCountRef.current += 1;
312
+ const readCount = workspaceSyncStateReadCountRef.current;
313
+ try {
314
+ const res = await fetch(`/api/taskforce/ui-state?key=workspace-sync&workspaceId=${encodeURIComponent(workspaceId)}`, {
315
+ method: 'GET',
316
+ credentials: 'include'
317
+ });
318
+ if (!res.ok) {
319
+ logSyncDebug('workspace_sync_state_read_failed', {
320
+ workspaceId,
321
+ readCount,
322
+ status: res.status
323
+ });
324
+ if (res.status === 429) {
325
+ logSyncDebug('workspace_sync_state_rate_limited', {
326
+ workspaceId,
327
+ readCount
328
+ });
329
+ }
330
+ return;
331
+ }
332
+ const data = await res.json().catch(() => ({}));
333
+ const state = (data?.state && typeof data.state === 'object')
334
+ ? data.state
335
+ : null;
336
+ if (!state) {
337
+ logSyncDebug('workspace_sync_state_read_empty', {
338
+ workspaceId,
339
+ readCount
340
+ });
341
+ return;
342
+ }
343
+ setWorkspaceCloudSyncEnabled(Boolean(state.cloudSyncEnabled));
344
+ const source = String(state.sourceOfTruth || '').trim().toLowerCase();
345
+ if (source === 'cloud' || source === 'local') {
346
+ setWorkspaceSyncSourceOfTruth(source);
347
+ }
348
+ else {
349
+ setWorkspaceSyncSourceOfTruth(null);
350
+ }
351
+ setWorkspaceSyncOnboardingCompleted(Boolean(state.onboardingCompleted));
352
+ const lastSyncedAt = typeof state.lastSyncedAt === 'string' ? state.lastSyncedAt : '';
353
+ if (lastSyncedAt.trim().length > 0) {
354
+ setUserGlobalLastSyncedAt(lastSyncedAt.trim());
355
+ }
356
+ const lastPullAt = typeof state.lastPullAt === 'string' ? state.lastPullAt : '';
357
+ if (lastPullAt.trim().length > 0) {
358
+ setWorkspaceLastPullAt(lastPullAt.trim());
359
+ }
360
+ const lastPushAt = typeof state.lastPushAt === 'string' ? state.lastPushAt : '';
361
+ if (lastPushAt.trim().length > 0) {
362
+ setWorkspaceLastPushAt(lastPushAt.trim());
363
+ }
364
+ const pullCursor = typeof state.pullCursor === 'string' ? state.pullCursor : '';
365
+ const trimmedCursor = pullCursor.trim();
366
+ workspacePullCursorRef.current = trimmedCursor.length > 0 ? trimmedCursor : null;
367
+ // Only mark bootstrap done if we actually have a valid persisted cursor.
368
+ // An empty cursor means the previous bootstrap was incomplete; let it re-run.
369
+ workspacePullBootstrapDoneRef.current = trimmedCursor.length > 0;
370
+ logSyncDebug('workspace_sync_state_read_ok', {
371
+ workspaceId,
372
+ readCount,
373
+ sourceOfTruth: source === 'cloud' || source === 'local' ? source : null,
374
+ cloudSyncEnabled: Boolean(state.cloudSyncEnabled)
375
+ });
376
+ }
377
+ catch {
378
+ logSyncDebug('workspace_sync_state_read_exception', {
379
+ workspaceId,
380
+ readCount
381
+ });
382
+ }
383
+ }, [
384
+ currentWorkspaceId,
385
+ setWorkspaceCloudSyncEnabled,
386
+ setWorkspaceSyncSourceOfTruth,
387
+ setWorkspaceSyncOnboardingCompleted,
388
+ setUserGlobalLastSyncedAt,
389
+ setWorkspaceLastPullAt,
390
+ setWorkspaceLastPushAt
391
+ ]);
392
+ const applyWorkspaceSyncStateSnapshot = useCallback((nextState) => {
393
+ const enabled = Boolean(nextState.cloudSyncEnabled);
394
+ setWorkspaceCloudSyncEnabled(enabled);
395
+ const source = nextState.sourceOfTruth === 'cloud' || nextState.sourceOfTruth === 'local'
396
+ ? nextState.sourceOfTruth
397
+ : null;
398
+ setWorkspaceSyncSourceOfTruth(source);
399
+ setWorkspaceSyncOnboardingCompleted(Boolean(nextState.onboardingCompleted));
400
+ if (typeof nextState.pullCursor === 'string') {
401
+ const trimmed = nextState.pullCursor.trim();
402
+ workspacePullCursorRef.current = trimmed.length > 0 ? trimmed : null;
403
+ }
404
+ else if (nextState.pullCursor === null) {
405
+ workspacePullCursorRef.current = null;
406
+ }
407
+ }, [
408
+ setWorkspaceCloudSyncEnabled,
409
+ setWorkspaceSyncSourceOfTruth,
410
+ setWorkspaceSyncOnboardingCompleted
411
+ ]);
412
+ const saveWorkspaceCloudSyncSettings = useCallback(async (nextState, deps) => {
413
+ if (!isValidWorkspaceKey(currentWorkspaceId)) {
414
+ return { success: false, error: 'Workspace setup is required before enabling sync.' };
415
+ }
416
+ const nextEnabled = Boolean(nextState.enabled);
417
+ const nextSource = nextState.sourceOfTruth === 'cloud' || nextState.sourceOfTruth === 'local'
418
+ ? nextState.sourceOfTruth
419
+ : null;
420
+ let resolvedSource = nextSource;
421
+ // I6: when enabling sync, always start with onboardingCompleted=false so the
422
+ // orchestrator can verify the first pull succeeds before marking it complete.
423
+ // When disabling, preserve whatever the caller passes (usually the current value).
424
+ const nextOnboardingCompleted = nextEnabled ? false : Boolean(nextState.onboardingCompleted);
425
+ try {
426
+ if (nextEnabled) {
427
+ if (!deps.isAuthenticated) {
428
+ return { success: false, error: 'Sign in is required before enabling workspace sync.' };
429
+ }
430
+ if (!deps.cloudAuthConfigured) {
431
+ return { success: false, error: 'Cloud authentication endpoint is not configured.' };
432
+ }
433
+ const ensured = await deps.ensureCloudWorkspaceReadyForSync();
434
+ if (!ensured.success) {
435
+ return { success: false, error: ensured.error || 'Workspace sync handshake failed.' };
436
+ }
437
+ const shouldBootstrapLocal = nextSource === 'local'
438
+ || (nextSource === null && ensured.provisioned === true);
439
+ if (resolvedSource === null) {
440
+ resolvedSource = shouldBootstrapLocal ? 'local' : 'cloud';
441
+ }
442
+ if (shouldBootstrapLocal) {
443
+ const snapshot = await deps.fetchLocalWorkspaceSnapshot();
444
+ if (snapshot.error) {
445
+ return { success: false, error: snapshot.error };
446
+ }
447
+ const [taxonomiesRes, documentsRes] = await Promise.allSettled([
448
+ fetch('/api/taskforce/taxonomies', { method: 'GET', credentials: 'include' })
449
+ .then((r) => r.ok
450
+ ? r.json().catch(() => ({}))
451
+ : Promise.resolve({}))
452
+ .catch(() => ({})),
453
+ fetch('/api/taskforce/sync/workspace/documents', { method: 'GET', credentials: 'include' })
454
+ .then((r) => r.ok
455
+ ? r.json().catch(() => ({}))
456
+ : Promise.resolve({}))
457
+ .catch(() => ({}))
458
+ ]);
459
+ const taxonomies = taxonomiesRes.status === 'fulfilled' && Array.isArray(taxonomiesRes.value?.taxonomies)
460
+ ? taxonomiesRes.value.taxonomies
461
+ : undefined;
462
+ const documents = documentsRes.status === 'fulfilled' && Array.isArray(documentsRes.value?.documents)
463
+ ? documentsRes.value.documents
464
+ .filter((doc) => {
465
+ if (!doc || typeof doc !== 'object')
466
+ return false;
467
+ const candidate = doc;
468
+ return typeof candidate.path === 'string'
469
+ && typeof candidate.updatedAt === 'string'
470
+ && typeof candidate.content === 'string';
471
+ })
472
+ : undefined;
473
+ const bootstrapRes = await bootstrapPushWorkspaceSnapshot(deps.resolveCloudAuthUrl, {
474
+ workspaceId: currentWorkspaceId,
475
+ tasks: snapshot.tasks,
476
+ archived: snapshot.archived,
477
+ taxonomies,
478
+ documents
479
+ });
480
+ if (!bootstrapRes.ok) {
481
+ const data = (bootstrapRes.data && typeof bootstrapRes.data === 'object')
482
+ ? bootstrapRes.data
483
+ : {};
484
+ return {
485
+ success: false,
486
+ error: String(data.error || `Workspace bootstrap push failed (${bootstrapRes.status})`)
487
+ };
488
+ }
489
+ }
490
+ }
491
+ await persistWorkspaceSyncPatch({
492
+ cloudSyncEnabled: nextEnabled,
493
+ sourceOfTruth: resolvedSource,
494
+ onboardingCompleted: nextOnboardingCompleted
495
+ });
496
+ setWorkspaceCloudSyncEnabled(nextEnabled);
497
+ setWorkspaceSyncSourceOfTruth(resolvedSource);
498
+ setWorkspaceSyncOnboardingCompleted(nextOnboardingCompleted);
499
+ return { success: true };
500
+ }
501
+ catch {
502
+ return { success: false, error: 'Failed to save workspace sync settings.' };
503
+ }
504
+ }, [
505
+ setWorkspaceCloudSyncEnabled,
506
+ setWorkspaceSyncSourceOfTruth,
507
+ setWorkspaceSyncOnboardingCompleted,
508
+ persistWorkspaceSyncPatch
509
+ ]);
510
+ useEffect(() => {
511
+ workspaceLastPushedSignatureRef.current = '';
512
+ workspacePendingSignatureRef.current = '';
513
+ workspaceLastPushedTaskIdsRef.current = new Set();
514
+ workspaceDeletedTaskIdsRef.current = new Set();
515
+ workspacePullCursorRef.current = null;
516
+ workspacePullBootstrapDoneRef.current = false;
517
+ workspaceSyncStateReadCountRef.current = 0;
518
+ workspaceBackfillForcedRef.current.delete(currentWorkspaceId);
519
+ workspaceSyncReadyRef.current.delete(currentWorkspaceId);
520
+ workspaceCloudHydratedRef.current.delete(currentWorkspaceId);
521
+ workspaceRetryAttemptRef.current = 0;
522
+ workspaceRetryAtMsRef.current = null;
523
+ workspacePushInFlightRef.current = false;
524
+ workspacePullInFlightRef.current = false;
525
+ setWorkspaceSyncReady(false);
526
+ setWorkspaceCloudHydrated(false);
527
+ setWorkspaceSyncFailureCount(0);
528
+ setWorkspaceSyncRetryScheduledCount(0);
529
+ setWorkspaceSyncAuthFailureCount(0);
530
+ setWorkspaceSyncLastFailureSource(null);
531
+ setWorkspaceSyncLastFailureStatusCode(null);
532
+ setWorkspaceBootstrapSyncInProgress(false);
533
+ setWorkspaceBootstrapSyncPages(0);
534
+ setWorkspaceBootstrapSyncAppliedChanges(0);
535
+ if (workspaceRetryTimerRef.current !== null && typeof window !== 'undefined') {
536
+ window.clearTimeout(workspaceRetryTimerRef.current);
537
+ workspaceRetryTimerRef.current = null;
538
+ }
539
+ if (activeLockAbortControllerRef.current) {
540
+ activeLockAbortControllerRef.current.abort();
541
+ activeLockAbortControllerRef.current = null;
542
+ }
543
+ activeLockWorkspaceIdRef.current = null;
544
+ setWorkspaceRetryAt(null);
545
+ }, [currentWorkspaceId, setWorkspaceSyncReady, setWorkspaceCloudHydrated]);
546
+ useEffect(() => {
547
+ return () => {
548
+ if (workspaceRetryTimerRef.current !== null && typeof window !== 'undefined') {
549
+ window.clearTimeout(workspaceRetryTimerRef.current);
550
+ workspaceRetryTimerRef.current = null;
551
+ }
552
+ if (activeLockAbortControllerRef.current) {
553
+ activeLockAbortControllerRef.current.abort();
554
+ activeLockAbortControllerRef.current = null;
555
+ }
556
+ activeLockWorkspaceIdRef.current = null;
557
+ };
558
+ }, []);
559
+ return {
560
+ workspaceRetryAt,
561
+ setWorkspaceRetryAt,
562
+ isWorkspacePullRetryPending,
563
+ workspaceBootstrapSyncInProgress,
564
+ setWorkspaceBootstrapSyncInProgress,
565
+ workspaceBootstrapSyncPages,
566
+ setWorkspaceBootstrapSyncPages,
567
+ workspaceBootstrapSyncAppliedChanges,
568
+ setWorkspaceBootstrapSyncAppliedChanges,
569
+ workspaceSyncFailureCount,
570
+ workspaceSyncRetryScheduledCount,
571
+ workspaceSyncAuthFailureCount,
572
+ workspaceSyncLastFailureSource,
573
+ workspaceSyncLastFailureStatusCode,
574
+ workspacePushInFlightRef,
575
+ workspacePullInFlightRef,
576
+ workspaceLastPushedSignatureRef,
577
+ workspacePendingSignatureRef,
578
+ workspaceLastPushedTaskIdsRef,
579
+ workspaceDeletedTaskIdsRef,
580
+ workspacePullCursorRef,
581
+ workspacePullBootstrapDoneRef,
582
+ workspaceBackfillForcedRef,
583
+ workspaceSyncReadyRef,
584
+ workspaceCloudHydratedRef,
585
+ clearWorkspaceRetry,
586
+ scheduleWorkspacePullRetry,
587
+ isWorkspacePullCooldownActive,
588
+ markWorkspacePullCooldown,
589
+ tryAcquireWorkspacePullLease,
590
+ releaseWorkspacePullLease,
591
+ recordWorkspaceSyncFailure,
592
+ incrementWorkspaceSyncAuthFailure,
593
+ persistWorkspaceSyncPatch,
594
+ loadWorkspaceSyncState,
595
+ applyWorkspaceSyncStateSnapshot,
596
+ saveWorkspaceCloudSyncSettings
597
+ };
598
+ }
@@ -0,0 +1 @@
1
+ export {};