@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,138 @@
1
+ function toStatus(error) {
2
+ const status = Number(error?.status);
3
+ if (Number.isFinite(status) && status > 0)
4
+ return Math.floor(status);
5
+ return 0;
6
+ }
7
+ async function requestJson(request) {
8
+ try {
9
+ const res = await request;
10
+ const rawText = await res.text().catch(() => '');
11
+ let data = {};
12
+ if (rawText) {
13
+ try {
14
+ data = JSON.parse(rawText);
15
+ }
16
+ catch {
17
+ const fallbackError = (!res.ok && res.statusText)
18
+ ? `${res.status} ${res.statusText}`
19
+ : rawText.slice(0, 400);
20
+ data = { error: fallbackError };
21
+ }
22
+ }
23
+ else if (!res.ok && res.statusText) {
24
+ data = { error: `${res.status} ${res.statusText}` };
25
+ }
26
+ const retryAfterRaw = res.headers.get('retry-after');
27
+ let retryAfterMs;
28
+ if (retryAfterRaw) {
29
+ const seconds = Number.parseInt(retryAfterRaw, 10);
30
+ if (Number.isFinite(seconds) && seconds > 0) {
31
+ retryAfterMs = seconds * 1000;
32
+ }
33
+ else {
34
+ const retryAt = Date.parse(retryAfterRaw);
35
+ if (Number.isFinite(retryAt)) {
36
+ const delta = retryAt - Date.now();
37
+ if (delta > 0)
38
+ retryAfterMs = delta;
39
+ }
40
+ }
41
+ }
42
+ return { ok: res.ok, status: res.status, data, retryAfterMs };
43
+ }
44
+ catch (error) {
45
+ return { ok: false, status: toStatus(error), data: {} };
46
+ }
47
+ }
48
+ export async function pullUserSettings(resolveCloudAuthUrl) {
49
+ return requestJson(fetch(resolveCloudAuthUrl('/api/taskforce/sync/user-settings'), {
50
+ method: 'GET',
51
+ credentials: 'include'
52
+ }));
53
+ }
54
+ export async function pushUserSettings(resolveCloudAuthUrl, payload) {
55
+ return requestJson(fetch(resolveCloudAuthUrl('/api/taskforce/sync/user-settings'), {
56
+ method: 'POST',
57
+ headers: { 'Content-Type': 'application/json' },
58
+ credentials: 'include',
59
+ body: JSON.stringify(payload)
60
+ }));
61
+ }
62
+ export async function handshakeWorkspace(resolveCloudAuthUrl, workspaceId) {
63
+ return requestJson(fetch(resolveCloudAuthUrl('/api/taskforce/sync/workspace/handshake'), {
64
+ method: 'POST',
65
+ headers: { 'Content-Type': 'application/json' },
66
+ credentials: 'include',
67
+ body: JSON.stringify({ workspaceId })
68
+ }));
69
+ }
70
+ export async function provisionWorkspace(resolveCloudAuthUrl, input) {
71
+ return requestJson(fetch(resolveCloudAuthUrl('/api/taskforce/sync/workspace/provision'), {
72
+ method: 'POST',
73
+ headers: { 'Content-Type': 'application/json' },
74
+ credentials: 'include',
75
+ body: JSON.stringify(input)
76
+ }));
77
+ }
78
+ export async function pullWorkspaceChanges(resolveCloudAuthUrl, input) {
79
+ const params = new URLSearchParams({ limit: String(input.limit), workspaceId: input.workspaceId });
80
+ if (input.cursor)
81
+ params.set('cursor', input.cursor);
82
+ const baseUrl = resolveCloudAuthUrl('/api/taskforce/sync/workspace/pull');
83
+ const separator = baseUrl.includes('?') ? '&' : '?';
84
+ return requestJson(fetch(`${baseUrl}${separator}${params.toString()}`, {
85
+ method: 'GET',
86
+ credentials: 'include'
87
+ }));
88
+ }
89
+ export async function pushWorkspaceChanges(resolveCloudAuthUrl, input) {
90
+ return requestJson(fetch(resolveCloudAuthUrl('/api/taskforce/sync/workspace/push'), {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ credentials: 'include',
94
+ body: JSON.stringify(input)
95
+ }));
96
+ }
97
+ export async function bootstrapPushWorkspaceSnapshot(resolveCloudAuthUrl, input) {
98
+ return requestJson(fetch(resolveCloudAuthUrl('/api/taskforce/sync/workspace/bootstrap-push'), {
99
+ method: 'POST',
100
+ headers: { 'Content-Type': 'application/json' },
101
+ credentials: 'include',
102
+ body: JSON.stringify(input)
103
+ }));
104
+ }
105
+ export async function applyLocalWorkspaceChanges(workspaceId, changes) {
106
+ const applyPayload = JSON.stringify({ workspaceId, changes });
107
+ const resolveApplyLocalEndpoint = () => {
108
+ if (typeof window === 'undefined')
109
+ return '/api/taskforce/sync/workspace/apply-local';
110
+ const pathname = String(window.location?.pathname || '/');
111
+ if (pathname === '/taskforce' || pathname.startsWith('/taskforce/')) {
112
+ return '/taskforce/api/taskforce/sync/workspace/apply-local';
113
+ }
114
+ return '/api/taskforce/sync/workspace/apply-local';
115
+ };
116
+ const applyEndpoint = resolveApplyLocalEndpoint();
117
+ const failures = [];
118
+ try {
119
+ const response = await fetch(applyEndpoint, {
120
+ method: 'POST',
121
+ headers: {
122
+ 'Content-Type': 'application/json',
123
+ 'x-taskforce-workspace-id': workspaceId
124
+ },
125
+ credentials: 'include',
126
+ body: applyPayload
127
+ });
128
+ if (response.ok) {
129
+ const data = await response.json().catch(() => ({}));
130
+ return { ok: true, data, failures };
131
+ }
132
+ failures.push(`${applyEndpoint}:${response.status}`);
133
+ }
134
+ catch {
135
+ failures.push(`${applyEndpoint}:network`);
136
+ }
137
+ return { ok: false, data: {}, failures };
138
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { applyLocalWorkspaceChanges } from './cloudSyncApi';
3
+ describe('cloudSyncApi.applyLocalWorkspaceChanges', () => {
4
+ afterEach(() => {
5
+ vi.unstubAllGlobals();
6
+ vi.restoreAllMocks();
7
+ window.history.replaceState({}, '', '/');
8
+ });
9
+ it('uses canonical local apply endpoint by default', async () => {
10
+ const fetchMock = vi.fn().mockResolvedValue({
11
+ ok: true,
12
+ status: 200,
13
+ json: () => Promise.resolve({ success: true })
14
+ });
15
+ vi.stubGlobal('fetch', fetchMock);
16
+ const result = await applyLocalWorkspaceChanges('ws-1', [{ op: 'upsert', archived: false, task: { id: 't-1' } }]);
17
+ expect(result.ok).toBe(true);
18
+ expect(fetchMock).toHaveBeenCalledTimes(1);
19
+ expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/sync/workspace/apply-local', expect.any(Object));
20
+ });
21
+ it('uses canonical /taskforce apply endpoint when running under /taskforce path', async () => {
22
+ window.history.replaceState({}, '', '/taskforce');
23
+ const fetchMock = vi.fn().mockResolvedValue({
24
+ ok: true,
25
+ status: 200,
26
+ json: () => Promise.resolve({ success: true })
27
+ });
28
+ vi.stubGlobal('fetch', fetchMock);
29
+ const result = await applyLocalWorkspaceChanges('ws-2', [{ op: 'delete', taskId: 't-2' }]);
30
+ expect(result.ok).toBe(true);
31
+ expect(fetchMock).toHaveBeenCalledTimes(1);
32
+ expect(fetchMock).toHaveBeenCalledWith('/taskforce/api/taskforce/sync/workspace/apply-local', expect.any(Object));
33
+ });
34
+ it('returns single-endpoint status diagnostics on non-ok response', async () => {
35
+ const fetchMock = vi.fn().mockResolvedValue({
36
+ ok: false,
37
+ status: 503,
38
+ json: () => Promise.resolve({ error: 'unavailable' })
39
+ });
40
+ vi.stubGlobal('fetch', fetchMock);
41
+ const result = await applyLocalWorkspaceChanges('ws-3', [{ op: 'upsert', archived: false, task: { id: 't-3' } }]);
42
+ expect(result.ok).toBe(false);
43
+ expect(result.failures).toEqual(['/api/taskforce/sync/workspace/apply-local:503']);
44
+ expect(fetchMock).toHaveBeenCalledTimes(1);
45
+ });
46
+ it('returns single-endpoint network diagnostics on fetch error', async () => {
47
+ const fetchMock = vi.fn().mockRejectedValue(new Error('network down'));
48
+ vi.stubGlobal('fetch', fetchMock);
49
+ const result = await applyLocalWorkspaceChanges('ws-4', [{ op: 'upsert', archived: false, task: { id: 't-4' } }]);
50
+ expect(result.ok).toBe(false);
51
+ expect(result.failures).toEqual(['/api/taskforce/sync/workspace/apply-local:network']);
52
+ expect(fetchMock).toHaveBeenCalledTimes(1);
53
+ });
54
+ });
@@ -0,0 +1,11 @@
1
+ export declare const ENCRYPTION_PREFIX = "V1:AESGCM:";
2
+ /**
3
+ * Encrypts a plaintext string into a base64 encoded payload with the V1 format.
4
+ * Format: `V1:AESGCM:<base64(iv)>:<base64(ciphertext)>`
5
+ */
6
+ export declare function encryptDocument(plaintext: string, base64Key: string): Promise<string>;
7
+ /**
8
+ * Decrypts a V1 format payload back into plaintext.
9
+ * Throws an error if the payload cannot be decrypted or format is invalid.
10
+ */
11
+ export declare function decryptDocument(payload: string, base64Key: string): Promise<string>;
@@ -0,0 +1,61 @@
1
+ export const ENCRYPTION_PREFIX = 'V1:AESGCM:';
2
+ const ALGORITHM = 'AES-GCM';
3
+ const IV_LENGTH_BYTES = 12;
4
+ /**
5
+ * Derives a CryptoKey from a given 256-bit base64 string.
6
+ */
7
+ async function importKey(base64Key) {
8
+ const keyBytes = Buffer.from(base64Key, 'base64');
9
+ if (keyBytes.length !== 32) {
10
+ throw new Error('Encryption key must be exactly 32 bytes (256-bit).');
11
+ }
12
+ return crypto.subtle.importKey('raw', keyBytes, { name: ALGORITHM }, false, ['encrypt', 'decrypt']);
13
+ }
14
+ /**
15
+ * Encrypts a plaintext string into a base64 encoded payload with the V1 format.
16
+ * Format: `V1:AESGCM:<base64(iv)>:<base64(ciphertext)>`
17
+ */
18
+ export async function encryptDocument(plaintext, base64Key) {
19
+ if (!plaintext)
20
+ return plaintext;
21
+ // Generate an initialization vector
22
+ const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH_BYTES));
23
+ const key = await importKey(base64Key);
24
+ const encoder = new TextEncoder();
25
+ const encodedPlaintext = encoder.encode(plaintext);
26
+ const ciphertextBuffer = await crypto.subtle.encrypt({
27
+ name: ALGORITHM,
28
+ iv
29
+ }, key, encodedPlaintext);
30
+ const ciphertextBase64 = Buffer.from(ciphertextBuffer).toString('base64');
31
+ const ivBase64 = Buffer.from(iv).toString('base64');
32
+ return `${ENCRYPTION_PREFIX}${ivBase64}:${ciphertextBase64}`;
33
+ }
34
+ /**
35
+ * Decrypts a V1 format payload back into plaintext.
36
+ * Throws an error if the payload cannot be decrypted or format is invalid.
37
+ */
38
+ export async function decryptDocument(payload, base64Key) {
39
+ if (!payload || !payload.startsWith(ENCRYPTION_PREFIX)) {
40
+ return payload; // Return as-is if not encrypted
41
+ }
42
+ const unmappedPayload = payload.slice(ENCRYPTION_PREFIX.length);
43
+ const [ivBase64, ciphertextBase64] = unmappedPayload.split(':');
44
+ if (!ivBase64 || !ciphertextBase64) {
45
+ throw new Error('Malformed encrypted document payload.');
46
+ }
47
+ const iv = Buffer.from(ivBase64, 'base64');
48
+ const ciphertext = Buffer.from(ciphertextBase64, 'base64');
49
+ const key = await importKey(base64Key);
50
+ try {
51
+ const decryptedBuffer = await crypto.subtle.decrypt({
52
+ name: ALGORITHM,
53
+ iv
54
+ }, key, ciphertext);
55
+ const decoder = new TextDecoder('utf-8');
56
+ return decoder.decode(decryptedBuffer);
57
+ }
58
+ catch (e) {
59
+ throw new Error(`Failed to decrypt document: ${e.message}`);
60
+ }
61
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { encryptDocument, decryptDocument, ENCRYPTION_PREFIX } from './encryptionService';
3
+ import crypto from 'crypto';
4
+ // Polyfill node crypto to webcrypto for testing
5
+ if (!globalThis.crypto) {
6
+ globalThis.crypto = crypto.webcrypto;
7
+ }
8
+ describe('AES-256-GCM Document Encryption', () => {
9
+ // 32-byte key in Base64
10
+ const TEST_SYNC_KEY = 'NmhazvMJ8D0kG5btEJtW0uh9rM4l+xOCray0cWFZyBE=';
11
+ // A distinct 32-byte key
12
+ const DIFFERENT_TEST_KEY = 'h/ONWavchmshsOGoNWOePgNdQAtbCMmVGqCLL5dzej4=';
13
+ it('should correctly encrypt and decrypt a markdown document', async () => {
14
+ const plaintext = '# Hello World\n\nThis is a secret task document.';
15
+ const ciphertext = await encryptDocument(plaintext, TEST_SYNC_KEY);
16
+ expect(ciphertext).toBeDefined();
17
+ expect(ciphertext.startsWith(ENCRYPTION_PREFIX)).toBe(true);
18
+ expect(ciphertext).not.toContain('Hello World');
19
+ const decrypted = await decryptDocument(ciphertext, TEST_SYNC_KEY);
20
+ expect(decrypted).toBe(plaintext);
21
+ });
22
+ it('should generate different ciphertexts for the exact same plaintext (random IV)', async () => {
23
+ const plaintext = 'Repeat after me: randomness is secure.';
24
+ const cipher1 = await encryptDocument(plaintext, TEST_SYNC_KEY);
25
+ const cipher2 = await encryptDocument(plaintext, TEST_SYNC_KEY);
26
+ expect(cipher1).not.toBe(cipher2);
27
+ expect(cipher1.startsWith(ENCRYPTION_PREFIX)).toBe(true);
28
+ expect(cipher2.startsWith(ENCRYPTION_PREFIX)).toBe(true);
29
+ });
30
+ it('should fail to decrypt with the wrong key (Auth Tag mismatch / Bad Mac)', async () => {
31
+ const plaintext = 'Secret Document';
32
+ const ciphertext = await encryptDocument(plaintext, TEST_SYNC_KEY);
33
+ await expect(decryptDocument(ciphertext, DIFFERENT_TEST_KEY)).rejects.toThrow('Failed to decrypt document');
34
+ });
35
+ it('should ignore and pass through unencrypted plaintexts during decryption', async () => {
36
+ const plaintext = 'I am just a normal document.';
37
+ const result = await decryptDocument(plaintext, TEST_SYNC_KEY);
38
+ expect(result).toBe(plaintext);
39
+ });
40
+ it('should return empty string if asked to encrypt empty input', async () => {
41
+ const ciphertext = await encryptDocument('', TEST_SYNC_KEY);
42
+ expect(ciphertext).toBe('');
43
+ });
44
+ it('should gracefully handle empty or invalid decryption payloads', async () => {
45
+ await expect(decryptDocument(`${ENCRYPTION_PREFIX}invalid`, TEST_SYNC_KEY)).rejects.toThrow('Malformed encrypted document payload.');
46
+ await expect(decryptDocument(`${ENCRYPTION_PREFIX}validBase64=`, TEST_SYNC_KEY)).rejects.toThrow('Malformed encrypted document payload.');
47
+ });
48
+ it('should throw immediately if the provided encryption key is not 256-bit (32 bytes)', async () => {
49
+ const tooShortKey = Buffer.from('too short key').toString('base64');
50
+ await expect(encryptDocument('test', tooShortKey)).rejects.toThrow('Encryption key must be exactly 32 bytes (256-bit).');
51
+ });
52
+ });
@@ -0,0 +1,6 @@
1
+ export type RealtimeSyncFlagSource = 'env' | 'global' | 'default';
2
+ export interface RealtimeSyncFlagResolution {
3
+ enabled: boolean;
4
+ source: RealtimeSyncFlagSource;
5
+ }
6
+ export declare function resolveRealtimeSyncFlag(env: NodeJS.ProcessEnv, globalSetting?: boolean): RealtimeSyncFlagResolution;
@@ -0,0 +1,20 @@
1
+ function parseBoolean(value) {
2
+ const normalized = String(value || '').trim().toLowerCase();
3
+ if (!normalized)
4
+ return null;
5
+ if (['1', 'true', 'yes', 'on'].includes(normalized))
6
+ return true;
7
+ if (['0', 'false', 'no', 'off'].includes(normalized))
8
+ return false;
9
+ return null;
10
+ }
11
+ export function resolveRealtimeSyncFlag(env, globalSetting) {
12
+ const envValue = parseBoolean(env.TASKFORCE_REALTIME_SYNC);
13
+ if (envValue !== null) {
14
+ return { enabled: envValue, source: 'env' };
15
+ }
16
+ if (typeof globalSetting === 'boolean') {
17
+ return { enabled: globalSetting, source: 'global' };
18
+ }
19
+ return { enabled: false, source: 'default' };
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolveRealtimeSyncFlag } from './realtimeFeature';
3
+ describe('sync/realtimeFeature', () => {
4
+ it('defaults to disabled when neither env nor global setting is provided', () => {
5
+ expect(resolveRealtimeSyncFlag({})).toEqual({
6
+ enabled: false,
7
+ source: 'default'
8
+ });
9
+ });
10
+ it('uses global setting when env is unset', () => {
11
+ expect(resolveRealtimeSyncFlag({}, true)).toEqual({
12
+ enabled: true,
13
+ source: 'global'
14
+ });
15
+ expect(resolveRealtimeSyncFlag({}, false)).toEqual({
16
+ enabled: false,
17
+ source: 'global'
18
+ });
19
+ });
20
+ it('env override takes precedence over global setting', () => {
21
+ expect(resolveRealtimeSyncFlag({ TASKFORCE_REALTIME_SYNC: 'true' }, false)).toEqual({
22
+ enabled: true,
23
+ source: 'env'
24
+ });
25
+ expect(resolveRealtimeSyncFlag({ TASKFORCE_REALTIME_SYNC: '0' }, true)).toEqual({
26
+ enabled: false,
27
+ source: 'env'
28
+ });
29
+ });
30
+ });
@@ -0,0 +1,30 @@
1
+ export type RealtimeDiscardReason = 'duplicate' | 'out-of-order' | 'invalid';
2
+ export interface RealtimeReconcileInput {
3
+ eventId?: unknown;
4
+ serverEpoch?: unknown;
5
+ seq?: unknown;
6
+ }
7
+ export interface RealtimeReconcileResult {
8
+ accepted: boolean;
9
+ reason?: RealtimeDiscardReason;
10
+ shouldRecover: boolean;
11
+ }
12
+ export interface RealtimeReconcileTelemetry {
13
+ accepted: number;
14
+ duplicateDiscarded: number;
15
+ outOfOrderDiscarded: number;
16
+ invalidDiscarded: number;
17
+ recoveryTriggered: number;
18
+ }
19
+ export declare class RealtimeReconcileTracker {
20
+ private readonly maxSeenEventIds;
21
+ private seenEventIds;
22
+ private seenEventIdQueue;
23
+ private activeEpoch;
24
+ private lastSeq;
25
+ private telemetry;
26
+ constructor(maxSeenEventIds?: number);
27
+ process(input: RealtimeReconcileInput): RealtimeReconcileResult;
28
+ getTelemetry(): RealtimeReconcileTelemetry;
29
+ private rememberEventId;
30
+ }
@@ -0,0 +1,90 @@
1
+ export class RealtimeReconcileTracker {
2
+ maxSeenEventIds;
3
+ seenEventIds = new Set();
4
+ seenEventIdQueue = [];
5
+ activeEpoch = '';
6
+ lastSeq = null;
7
+ telemetry = {
8
+ accepted: 0,
9
+ duplicateDiscarded: 0,
10
+ outOfOrderDiscarded: 0,
11
+ invalidDiscarded: 0,
12
+ recoveryTriggered: 0
13
+ };
14
+ constructor(maxSeenEventIds = 2048) {
15
+ this.maxSeenEventIds = maxSeenEventIds;
16
+ }
17
+ process(input) {
18
+ const eventId = typeof input.eventId === 'string' ? input.eventId.trim() : '';
19
+ if (eventId && this.seenEventIds.has(eventId)) {
20
+ this.telemetry.duplicateDiscarded += 1;
21
+ return { accepted: false, reason: 'duplicate', shouldRecover: false };
22
+ }
23
+ const serverEpoch = typeof input.serverEpoch === 'string' ? input.serverEpoch.trim() : '';
24
+ const seq = Number(input.seq);
25
+ const hasOrderedCursor = serverEpoch.length > 0 && Number.isFinite(seq) && seq >= 0;
26
+ if (!hasOrderedCursor) {
27
+ if (eventId) {
28
+ this.rememberEventId(eventId);
29
+ this.telemetry.accepted += 1;
30
+ return { accepted: true, shouldRecover: false };
31
+ }
32
+ this.telemetry.invalidDiscarded += 1;
33
+ return { accepted: false, reason: 'invalid', shouldRecover: false };
34
+ }
35
+ const normalizedSeq = Math.floor(seq);
36
+ if (this.activeEpoch && this.activeEpoch !== serverEpoch) {
37
+ this.activeEpoch = serverEpoch;
38
+ this.lastSeq = normalizedSeq;
39
+ if (eventId)
40
+ this.rememberEventId(eventId);
41
+ this.telemetry.accepted += 1;
42
+ return { accepted: true, shouldRecover: false };
43
+ }
44
+ if (!this.activeEpoch) {
45
+ this.activeEpoch = serverEpoch;
46
+ this.lastSeq = normalizedSeq;
47
+ if (eventId)
48
+ this.rememberEventId(eventId);
49
+ this.telemetry.accepted += 1;
50
+ return { accepted: true, shouldRecover: false };
51
+ }
52
+ const previous = this.lastSeq;
53
+ if (typeof previous === 'number') {
54
+ if (normalizedSeq <= previous) {
55
+ this.telemetry.outOfOrderDiscarded += (normalizedSeq === previous ? 0 : 1);
56
+ this.telemetry.duplicateDiscarded += (normalizedSeq === previous ? 1 : 0);
57
+ const shouldRecover = normalizedSeq < previous;
58
+ if (shouldRecover)
59
+ this.telemetry.recoveryTriggered += 1;
60
+ return {
61
+ accepted: false,
62
+ reason: normalizedSeq === previous ? 'duplicate' : 'out-of-order',
63
+ shouldRecover
64
+ };
65
+ }
66
+ if (normalizedSeq > previous + 1) {
67
+ this.telemetry.outOfOrderDiscarded += 1;
68
+ this.telemetry.recoveryTriggered += 1;
69
+ return { accepted: false, reason: 'out-of-order', shouldRecover: true };
70
+ }
71
+ }
72
+ this.lastSeq = normalizedSeq;
73
+ if (eventId)
74
+ this.rememberEventId(eventId);
75
+ this.telemetry.accepted += 1;
76
+ return { accepted: true, shouldRecover: false };
77
+ }
78
+ getTelemetry() {
79
+ return { ...this.telemetry };
80
+ }
81
+ rememberEventId(eventId) {
82
+ this.seenEventIds.add(eventId);
83
+ this.seenEventIdQueue.push(eventId);
84
+ while (this.seenEventIdQueue.length > this.maxSeenEventIds) {
85
+ const drop = this.seenEventIdQueue.shift();
86
+ if (drop)
87
+ this.seenEventIds.delete(drop);
88
+ }
89
+ }
90
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { RealtimeReconcileTracker } from './realtimeReconcile';
3
+ describe('RealtimeReconcileTracker', () => {
4
+ it('accepts ordered events and discards duplicate eventId safely', () => {
5
+ const tracker = new RealtimeReconcileTracker();
6
+ const first = tracker.process({ eventId: 'epoch-1:1', serverEpoch: 'epoch-1', seq: 1 });
7
+ const duplicate = tracker.process({ eventId: 'epoch-1:1', serverEpoch: 'epoch-1', seq: 1 });
8
+ expect(first.accepted).toBe(true);
9
+ expect(duplicate.accepted).toBe(false);
10
+ expect(duplicate.reason).toBe('duplicate');
11
+ expect(duplicate.shouldRecover).toBe(false);
12
+ const telemetry = tracker.getTelemetry();
13
+ expect(telemetry.accepted).toBe(1);
14
+ expect(telemetry.duplicateDiscarded).toBe(1);
15
+ });
16
+ it('flags out-of-order gaps for recovery', () => {
17
+ const tracker = new RealtimeReconcileTracker();
18
+ tracker.process({ eventId: 'epoch-1:1', serverEpoch: 'epoch-1', seq: 1 });
19
+ const gap = tracker.process({ eventId: 'epoch-1:3', serverEpoch: 'epoch-1', seq: 3 });
20
+ expect(gap.accepted).toBe(false);
21
+ expect(gap.reason).toBe('out-of-order');
22
+ expect(gap.shouldRecover).toBe(true);
23
+ const telemetry = tracker.getTelemetry();
24
+ expect(telemetry.outOfOrderDiscarded).toBe(1);
25
+ expect(telemetry.recoveryTriggered).toBe(1);
26
+ });
27
+ it('accepts new epoch as ordered reset baseline', () => {
28
+ const tracker = new RealtimeReconcileTracker();
29
+ tracker.process({ eventId: 'epoch-1:5', serverEpoch: 'epoch-1', seq: 5 });
30
+ const nextEpoch = tracker.process({ eventId: 'epoch-2:1', serverEpoch: 'epoch-2', seq: 1 });
31
+ expect(nextEpoch.accepted).toBe(true);
32
+ expect(nextEpoch.shouldRecover).toBe(false);
33
+ });
34
+ it('treats same epoch/seq with different eventId as duplicate no-op', () => {
35
+ const tracker = new RealtimeReconcileTracker();
36
+ const first = tracker.process({ eventId: 'epoch-1:7', serverEpoch: 'epoch-1', seq: 7 });
37
+ const duplicateSeq = tracker.process({ eventId: 'epoch-1:7b', serverEpoch: 'epoch-1', seq: 7 });
38
+ expect(first.accepted).toBe(true);
39
+ expect(duplicateSeq.accepted).toBe(false);
40
+ expect(duplicateSeq.reason).toBe('duplicate');
41
+ expect(duplicateSeq.shouldRecover).toBe(false);
42
+ });
43
+ });
@@ -0,0 +1,96 @@
1
+ import type { WorkspaceSyncPayload } from './workspaceSyncModel';
2
+ export interface EnsureWorkspaceReadyInput {
3
+ cloudAuthConfigured: boolean;
4
+ runtimeMode: 'local' | 'cloud';
5
+ isAuthenticated: boolean;
6
+ resolveCloudAuthUrl: (path: string) => string;
7
+ workspaceId: string;
8
+ workspaceName: string;
9
+ }
10
+ export interface EnsureWorkspaceReadyResult {
11
+ success: boolean;
12
+ provisioned?: boolean;
13
+ error?: string;
14
+ /** HTTP status code from the handshake/provision step, if it failed. */
15
+ statusCode?: number;
16
+ /** Milliseconds to wait before retrying, forwarded from a 429 Retry-After header. */
17
+ retryAfterMs?: number;
18
+ }
19
+ export declare function ensureCloudWorkspaceReadyForSyncService(input: EnsureWorkspaceReadyInput): Promise<EnsureWorkspaceReadyResult>;
20
+ export interface UserSettingsSyncInput {
21
+ resolveCloudAuthUrl: (path: string) => string;
22
+ userId: string;
23
+ preferCloudOnFirstSync: boolean;
24
+ getLocalUpdatedAt: (userId: string) => string | null;
25
+ buildLocalSettings: () => Record<string, unknown>;
26
+ applyRemoteSettings: (settings: Record<string, unknown>) => Promise<void>;
27
+ }
28
+ export interface UserSettingsSyncResult {
29
+ success: boolean;
30
+ mode?: 'pulled' | 'pushed';
31
+ updatedAt?: string;
32
+ error?: string;
33
+ statusCode?: number;
34
+ retryAfterMs?: number;
35
+ transient?: boolean;
36
+ }
37
+ export interface WorkspacePushSyncInput {
38
+ resolveCloudAuthUrl: (path: string) => string;
39
+ workspaceId: string;
40
+ payload: WorkspaceSyncPayload;
41
+ ensureCloudWorkspaceReadyForSync: () => Promise<EnsureWorkspaceReadyResult>;
42
+ }
43
+ export interface WorkspacePushSyncResult {
44
+ success: boolean;
45
+ status?: number;
46
+ error?: string;
47
+ code?: string;
48
+ retryAfterMs?: number;
49
+ syncedAt?: string;
50
+ requestMs: number;
51
+ changeCount: number;
52
+ deleteCount: number;
53
+ currentTaskIds: Set<string>;
54
+ deleteTaskIds: Set<string>;
55
+ currentDocumentPaths: Set<string>;
56
+ deleteDocumentPaths: Set<string>;
57
+ currentDocumentWatermarks: Map<string, string>;
58
+ /** Watermarks for every task upsert that was successfully pushed. */
59
+ pushedWatermarks: Map<string, string>;
60
+ /** Server-emitted realtime mutation event ids for this push apply. */
61
+ emittedEventIds: string[];
62
+ }
63
+ export declare function runWorkspacePushSyncService(input: WorkspacePushSyncInput): Promise<WorkspacePushSyncResult>;
64
+ export type WorkspacePullFailureKind = 'pull_http' | 'apply_auth' | 'apply_all_candidates' | 'apply_payload' | 'exception';
65
+ export interface WorkspacePullSyncInput {
66
+ resolveCloudAuthUrl: (path: string) => string;
67
+ workspaceId: string;
68
+ cursor: string | null;
69
+ bootstrap: boolean;
70
+ ensureCloudWorkspaceReadyForSync?: () => Promise<EnsureWorkspaceReadyResult>;
71
+ maxPages?: number;
72
+ onPagePulled?: () => void;
73
+ onChangesApplied?: (count: number) => void;
74
+ }
75
+ export interface WorkspacePullSyncResult {
76
+ success: boolean;
77
+ kind?: WorkspacePullFailureKind;
78
+ status?: number;
79
+ retryAfterMs?: number;
80
+ failures?: string[];
81
+ hasTransientApplyFailure?: boolean;
82
+ error?: string;
83
+ syncedAt?: string;
84
+ cursor: string | null;
85
+ pages: number;
86
+ appliedChanges: number;
87
+ pullRequestMs: number;
88
+ applyMs: number;
89
+ pulledDeleteTaskIds: Set<string>;
90
+ pulledActiveUpserts: boolean;
91
+ pulledArchivedUpserts: boolean;
92
+ documentDecryptFailures?: string[];
93
+ }
94
+ export declare function runWorkspacePullSyncService(input: WorkspacePullSyncInput): Promise<WorkspacePullSyncResult>;
95
+ export declare function syncUserGlobalSettingsService(input: UserSettingsSyncInput): Promise<UserSettingsSyncResult>;
96
+ export declare function calculateWorkspaceRetryDelayMs(attempt: number): number;