@taskforcehq/taskforce 0.3.301 → 0.3.302

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 (147) hide show
  1. package/dist/Taskforce.module.css +26 -2
  2. package/dist/TaskforceCore.filter-persistence.test.js +1 -1
  3. package/dist/TaskforceCore.js +13 -24
  4. package/dist/TaskforceCore.test.js +167 -152
  5. package/dist/components/features/TaskSettings.js +14 -82
  6. package/dist/components/features/TaskSettings.test.js +33 -70
  7. package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
  8. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
  9. package/dist/components/task/TaskActionHeader.js +6 -3
  10. package/dist/components/task/TaskCard.js +6 -3
  11. package/dist/components/task/TaskForm.js +2 -2
  12. package/dist/components/task/TaskForm.test.js +41 -2
  13. package/dist/components/ui/ReferenceBadgeButton.js +1 -1
  14. package/dist/components/views/PlansPage.js +9 -8
  15. package/dist/components/views/StandaloneLayout.js +466 -204
  16. package/dist/components/views/panels/PlanningDrawer.js +18 -1
  17. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +43 -0
  18. package/dist/components/views/standalone/modals/AccountHubModal.js +22 -0
  19. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
  20. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
  21. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
  22. package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
  23. package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
  24. package/dist/components/views/standalone/modals/HelpModal.js +8 -0
  25. package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
  26. package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
  27. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
  28. package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
  29. package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
  30. package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
  31. package/dist/components/views/standalone/modals/types.d.ts +2 -0
  32. package/dist/components/views/standalone/modals/types.js +1 -0
  33. package/dist/components/views/teamManagementAccess.d.ts +1 -1
  34. package/dist/components/views/teamManagementAccess.js +2 -2
  35. package/dist/components/views/teamManagementAccess.test.js +3 -3
  36. package/dist/config/deployment.js +2 -2
  37. package/dist/config/deployment.test.js +5 -3
  38. package/dist/core/AiProfileService.js +7 -2
  39. package/dist/core/AiProfiles.test.js +19 -0
  40. package/dist/core/SyncReconciliationService.d.ts +2 -0
  41. package/dist/core/SyncReconciliationService.js +3 -0
  42. package/dist/core/TaskAuditTimeline.test.js +40 -0
  43. package/dist/core/Taskforce.d.ts +49 -0
  44. package/dist/core/Taskforce.ids.test.js +133 -0
  45. package/dist/core/Taskforce.js +241 -78
  46. package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
  47. package/dist/core/UserProfileAvatarDrafts.test.js +102 -0
  48. package/dist/core/taskReferencePolicy.d.ts +28 -0
  49. package/dist/core/taskReferencePolicy.js +189 -0
  50. package/dist/core/types.d.ts +2 -0
  51. package/dist/hooks/tasks/useTaskFormActions.js +3 -0
  52. package/dist/hooks/useTaskData.js +3 -2
  53. package/dist/hooks/useTaskData.test.js +66 -0
  54. package/dist/hooks/useTaskforce.d.ts +5 -0
  55. package/dist/hooks/useTaskforce.js +63 -15
  56. package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
  57. package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
  58. package/dist/mcp/aiProfileBootstrap.js +80 -0
  59. package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
  60. package/dist/mcp/aiProfileBootstrap.test.js +68 -0
  61. package/dist/mcp/clientIntegrations.d.ts +2 -8
  62. package/dist/mcp/clientIntegrations.js +21 -40
  63. package/dist/mcp/clientIntegrations.test.js +28 -2
  64. package/dist/mcp/clientProfileDefaults.d.ts +12 -0
  65. package/dist/mcp/clientProfileDefaults.js +37 -0
  66. package/dist/mcp/runtime.d.ts +10 -7
  67. package/dist/mcp/runtime.js +41 -19
  68. package/dist/mcp/runtime.test.js +49 -2
  69. package/dist/migrations/taskSchemaMigrations.js +53 -1
  70. package/dist/qaOpen.test.d.ts +1 -0
  71. package/dist/qaOpen.test.js +56 -0
  72. package/dist/server/index.cookieProxy.test.js +1 -0
  73. package/dist/server/index.d.ts +1 -0
  74. package/dist/server/index.js +15 -1
  75. package/dist/server/index.test.js +14 -1
  76. package/dist/server/routes/admin.js +25 -22
  77. package/dist/server/routes/auth.d.ts +5 -0
  78. package/dist/server/routes/auth.js +350 -16
  79. package/dist/server/routes/billing.js +11 -1
  80. package/dist/server/routes/billing.test.js +32 -2
  81. package/dist/server/routes/documents.js +6 -1
  82. package/dist/server/routes/shared.d.ts +1 -1
  83. package/dist/server/routes/shared.js +11 -3
  84. package/dist/server/routes/sync.integration.test.js +130 -0
  85. package/dist/server/routes/sync.js +226 -1924
  86. package/dist/server/routes/tasks.js +18 -4
  87. package/dist/server/routes.js +26 -19
  88. package/dist/server/routes.test.js +352 -6
  89. package/dist/shared/runtimeContract.d.ts +1 -1
  90. package/dist/sync/collaborationSyncPayload.d.ts +17 -0
  91. package/dist/sync/collaborationSyncPayload.js +131 -0
  92. package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
  93. package/dist/sync/collaborationSyncPayload.test.js +104 -0
  94. package/dist/sync/collaborationSyncState.d.ts +15 -0
  95. package/dist/sync/collaborationSyncState.js +60 -0
  96. package/dist/sync/contentSyncPayload.d.ts +45 -0
  97. package/dist/sync/contentSyncPayload.js +104 -0
  98. package/dist/sync/contentSyncPayload.test.d.ts +1 -0
  99. package/dist/sync/contentSyncPayload.test.js +104 -0
  100. package/dist/sync/contentSyncState.d.ts +88 -0
  101. package/dist/sync/contentSyncState.js +743 -0
  102. package/dist/sync/planningSyncPayload.d.ts +9 -0
  103. package/dist/sync/planningSyncPayload.js +82 -0
  104. package/dist/sync/planningSyncPayload.test.d.ts +1 -0
  105. package/dist/sync/planningSyncPayload.test.js +68 -0
  106. package/dist/sync/syncApplyHandlers.d.ts +83 -0
  107. package/dist/sync/syncApplyHandlers.js +445 -0
  108. package/dist/sync/taskSyncPayload.d.ts +33 -0
  109. package/dist/sync/taskSyncPayload.js +126 -0
  110. package/dist/sync/taskSyncPayload.test.d.ts +1 -0
  111. package/dist/sync/taskSyncPayload.test.js +54 -0
  112. package/dist/sync/workspacePullFeed.d.ts +28 -0
  113. package/dist/sync/workspacePullFeed.js +301 -0
  114. package/dist/sync/workspacePullFeed.test.d.ts +1 -0
  115. package/dist/sync/workspacePullFeed.test.js +166 -0
  116. package/dist/sync/workspaceSyncModel.d.ts +4 -1
  117. package/dist/sync/workspaceSyncModel.js +9 -2
  118. package/dist/sync/workspaceSyncModel.test.js +29 -0
  119. package/dist/test/setup.js +1 -1
  120. package/dist/types.d.ts +1 -0
  121. package/dist/ui/assets/{AgentsModule-Bdq3T1Ts.js → AgentsModule-JG5jc3he.js} +1 -1
  122. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-jKUVo-PJ.js → AnnotatedAttachmentWorkspace-Dvi02Y7q.js} +1 -1
  123. package/dist/ui/assets/{DocumentWorkspace-DuQa-uIC.js → DocumentWorkspace-iys3zfca.js} +2 -2
  124. package/dist/ui/assets/{InitiativesModule-C8Pi2VTk.js → InitiativesModule-DJvLJI3A.js} +1 -1
  125. package/dist/ui/assets/PlansPage-BMPlpFpw.js +1 -0
  126. package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +8 -0
  127. package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
  128. package/dist/ui/assets/{WorkflowsModule-b6TLuHwJ.js → WorkflowsModule-BguHeQaV.js} +1 -1
  129. package/dist/ui/assets/index-MXeWoebC.css +1 -0
  130. package/dist/ui/assets/index-YJ4k5QNa.js +6 -0
  131. package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-C8EED3Oh.js} +1 -1
  132. package/dist/ui/index.html +3 -3
  133. package/dist/utils/billingStatusCache.d.ts +21 -0
  134. package/dist/utils/billingStatusCache.js +70 -0
  135. package/dist/utils/taskActivity.js +17 -4
  136. package/dist/utils/taskActivity.test.js +27 -0
  137. package/dist/utils/taskNormalization.js +10 -2
  138. package/dist/utils/taskReferences.d.ts +11 -1
  139. package/dist/utils/taskReferences.js +33 -1
  140. package/package.json +2 -1
  141. package/scripts/qa-open-lib.mjs +175 -0
  142. package/scripts/qa-open-lib.mjs.d.ts +49 -0
  143. package/scripts/qa-open.mjs +352 -0
  144. package/dist/ui/assets/PlansPage-B6BDECSi.js +0 -1
  145. package/dist/ui/assets/TaskSettings-DUJgRTPU.js +0 -8
  146. package/dist/ui/assets/index-BiiF1cLQ.css +0 -1
  147. package/dist/ui/assets/index-xBWLPOEb.js +0 -6
@@ -140,6 +140,49 @@ describe('Server Routes Workspace Sync Integration', () => {
140
140
  expect(task?.status).toBe('in-progress');
141
141
  expect(defaultWorkspaceTasks.find((item) => item.id === 'task-cloud-1')).toBeUndefined();
142
142
  });
143
+ it('lets the cloud task keep a colliding reference number during apply-local and records a diagnostic', async () => {
144
+ const projectRoot = createIsolatedProjectRoot('taskforce-sync-task-ref-collision-');
145
+ const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
146
+ core.createWorkspace({ workspaceId, name: 'Local Active Workspace' });
147
+ const localTask = core.createTask({ title: 'Local incumbent' }, { workspaceId });
148
+ expect(localTask.referenceLabel).toBe('T-1');
149
+ const routes = createRoutes(core, { authConfig: { runtimeMode: 'local', enabled: false } });
150
+ const match = matchRoute('POST', '/api/taskforce/sync/workspace/apply-local', routes);
151
+ expect(match).toBeTruthy();
152
+ const req = createMockReq('POST', '/api/taskforce/sync/workspace/apply-local', {
153
+ changes: [
154
+ {
155
+ op: 'upsert',
156
+ archived: false,
157
+ task: {
158
+ id: 'task-cloud-ref-1',
159
+ title: 'Cloud winner',
160
+ status: 'task',
161
+ referenceNumber: 1,
162
+ updatedAt: '2026-04-10T16:00:00.000Z'
163
+ }
164
+ }
165
+ ]
166
+ }, { 'x-taskforce-workspace-id': workspaceId });
167
+ const res = createMockRes();
168
+ await match?.handler(req, res, {});
169
+ expect(res.writeHead).toHaveBeenCalledWith(200, expect.any(Object));
170
+ const reloadedCore = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
171
+ expect(reloadedCore.getTask('task-cloud-ref-1', workspaceId)?.referenceLabel).toBe('T-1');
172
+ expect(reloadedCore.getTask(localTask.id, workspaceId)?.referenceLabel).toBe('T-2');
173
+ expect(reloadedCore.listSyncEvents(workspaceId, 10)).toEqual(expect.arrayContaining([
174
+ expect.objectContaining({
175
+ eventType: 'apply',
176
+ status: 'error',
177
+ errorMessage: 'Detected task reference number mismatch during normal sync.',
178
+ details: expect.objectContaining({
179
+ referenceLabel: 'T-1',
180
+ existingTaskId: localTask.id,
181
+ taskId: 'task-cloud-ref-1'
182
+ })
183
+ })
184
+ ]));
185
+ });
143
186
  it('persists synced comments via apply-local route', async () => {
144
187
  const projectRoot = createIsolatedProjectRoot('taskforce-sync-apply-comments-');
145
188
  const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
@@ -1487,6 +1530,93 @@ describe('Server Routes Workspace Sync Integration', () => {
1487
1530
  })
1488
1531
  ]);
1489
1532
  });
1533
+ it('omits local provisional reference fields from authenticated pull payloads', async () => {
1534
+ const projectRoot = createIsolatedProjectRoot('taskforce-sync-pull-task-ref-payload-');
1535
+ const seedCore = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
1536
+ seedAuthenticatedWorkspace(seedCore);
1537
+ const created = seedCore.createTask({
1538
+ title: 'Offline draft',
1539
+ status: 'task'
1540
+ }, {
1541
+ workspaceId,
1542
+ provisionalLocalReference: true
1543
+ });
1544
+ expect(created.localReferenceNumber).toBe(1);
1545
+ expect(created.referenceLabel).toBe('LT-1');
1546
+ const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
1547
+ const routes = createRoutes(core, { authConfig });
1548
+ const pullMatch = matchRoute('GET', '/api/taskforce/sync/workspace/pull', routes);
1549
+ expect(pullMatch).toBeTruthy();
1550
+ const cookie = createAuthCookie(workspaceId);
1551
+ const pullReq = createMockReq('GET', '/api/taskforce/sync/workspace/pull?workspaceId=workspace-local-active&limit=200', undefined, { cookie });
1552
+ const pullRes = createMockRes();
1553
+ await pullMatch?.handler(pullReq, pullRes, {});
1554
+ expect(pullRes.writeHead).toHaveBeenCalledWith(200, expect.any(Object));
1555
+ const pullBody = readJsonBody(pullRes);
1556
+ const upsert = (Array.isArray(pullBody?.changes) ? pullBody.changes : [])
1557
+ .find((change) => change?.op === 'upsert' && change?.task?.id === created.id);
1558
+ expect(upsert?.task).toEqual(expect.objectContaining({
1559
+ id: created.id,
1560
+ title: 'Offline draft',
1561
+ referenceNumber: null
1562
+ }));
1563
+ expect(upsert?.task).not.toHaveProperty('localReferenceNumber');
1564
+ expect(upsert?.task).not.toHaveProperty('referenceLabel');
1565
+ });
1566
+ it('returns a pushed provisional task on pull even when the caller cursor is newer than the stale local watermark', async () => {
1567
+ const projectRoot = createIsolatedProjectRoot('taskforce-sync-push-pull-provisional-task-');
1568
+ const seedCore = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
1569
+ seedAuthenticatedWorkspace(seedCore);
1570
+ const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
1571
+ const routes = createRoutes(core, { authConfig });
1572
+ const pushMatch = matchRoute('POST', '/api/taskforce/sync/workspace/push', routes);
1573
+ const pullMatch = matchRoute('GET', '/api/taskforce/sync/workspace/pull', routes);
1574
+ expect(pushMatch).toBeTruthy();
1575
+ expect(pullMatch).toBeTruthy();
1576
+ const cookie = createAuthCookie(workspaceId);
1577
+ const taskId = 'task-provisional-roundtrip-1';
1578
+ const staleUpdatedAt = '2026-04-11T21:29:04.121Z';
1579
+ const pushReq = createMockReq('POST', '/api/taskforce/sync/workspace/push', {
1580
+ workspaceId,
1581
+ idempotencyKey: 'task-provisional-roundtrip-1',
1582
+ changes: [
1583
+ {
1584
+ op: 'upsert',
1585
+ archived: false,
1586
+ task: {
1587
+ id: taskId,
1588
+ title: 'Offline provisional task',
1589
+ status: 'task',
1590
+ createdAt: '2026-04-11T21:28:52.410Z',
1591
+ updatedAt: staleUpdatedAt,
1592
+ referenceNumber: null
1593
+ }
1594
+ }
1595
+ ]
1596
+ }, { cookie });
1597
+ const pushRes = createMockRes();
1598
+ await pushMatch?.handler(pushReq, pushRes, {});
1599
+ expect(pushRes.writeHead).toHaveBeenCalledWith(200, expect.any(Object));
1600
+ const persisted = core.getTask(taskId, workspaceId);
1601
+ expect(persisted?.referenceNumber).toBe(1);
1602
+ expect(Date.parse(String(persisted?.updatedAt || ''))).toBeGreaterThan(Date.parse(staleUpdatedAt));
1603
+ const cursor = Buffer.from(JSON.stringify({
1604
+ u: '2026-04-11T21:29:05.000Z',
1605
+ id: 'seed'
1606
+ })).toString('base64url');
1607
+ const pullReq = createMockReq('GET', `/api/taskforce/sync/workspace/pull?workspaceId=${workspaceId}&limit=200&cursor=${cursor}`, undefined, { cookie });
1608
+ const pullRes = createMockRes();
1609
+ await pullMatch?.handler(pullReq, pullRes, {});
1610
+ expect(pullRes.writeHead).toHaveBeenCalledWith(200, expect.any(Object));
1611
+ const pullBody = readJsonBody(pullRes);
1612
+ const upsert = (Array.isArray(pullBody?.changes) ? pullBody.changes : [])
1613
+ .find((change) => change?.op === 'upsert' && change?.task?.id === taskId);
1614
+ expect(upsert?.task).toEqual(expect.objectContaining({
1615
+ id: taskId,
1616
+ title: 'Offline provisional task',
1617
+ referenceNumber: 1
1618
+ }));
1619
+ });
1490
1620
  it('keeps document deleted when push batch includes upsert and delete for same path', async () => {
1491
1621
  const projectRoot = createIsolatedProjectRoot('taskforce-sync-push-doc-delete-wins-');
1492
1622
  const seedCore = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });