@taskforcehq/taskforce 0.3.306 → 0.3.307

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +317 -27
  3. package/dist/TaskforceCore.js +1 -0
  4. package/dist/TaskforceCore.test.js +178 -0
  5. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  6. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  7. package/dist/components/context/ContextAttachmentManager.js +15 -3
  8. package/dist/components/features/AgentsModule.d.ts +5 -1
  9. package/dist/components/features/AgentsModule.js +142 -39
  10. package/dist/components/features/AgentsModule.test.js +260 -11
  11. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  12. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  13. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  14. package/dist/components/features/DocumentIndex.d.ts +2 -2
  15. package/dist/components/features/DocumentIndex.js +3 -4
  16. package/dist/components/features/DocumentViewer.d.ts +3 -1
  17. package/dist/components/features/DocumentViewer.js +6 -5
  18. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  19. package/dist/components/features/DocumentWorkspace.js +15 -4
  20. package/dist/components/features/DocumentWorkspace.test.js +20 -3
  21. package/dist/components/features/TaskSettings.d.ts +2 -0
  22. package/dist/components/features/TaskSettings.js +83 -78
  23. package/dist/components/features/TaskSettings.test.js +171 -82
  24. package/dist/components/task/TaskActionHeader.js +11 -5
  25. package/dist/components/task/TaskActionHeader.test.js +10 -0
  26. package/dist/components/task/TaskCard.js +6 -2
  27. package/dist/components/task/TaskContextUpload.test.js +3 -3
  28. package/dist/components/task/TaskForm.js +14 -64
  29. package/dist/components/task/TaskKanban.js +0 -8
  30. package/dist/components/task/TaskList.js +5 -1
  31. package/dist/components/task/TaskList.test.js +4 -0
  32. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  33. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  34. package/dist/components/views/AppShellHeader.d.ts +3 -1
  35. package/dist/components/views/AppShellHeader.js +27 -11
  36. package/dist/components/views/AppShellHeader.test.js +18 -1
  37. package/dist/components/views/PlanComparisonPage.js +9 -1
  38. package/dist/components/views/PlanComparisonPage.test.js +19 -0
  39. package/dist/components/views/PlansPage.js +1 -1
  40. package/dist/components/views/StandaloneLayout.js +256 -69
  41. package/dist/components/views/WidgetView.js +10 -8
  42. package/dist/components/views/panels/PlanningDrawer.d.ts +19 -6
  43. package/dist/components/views/panels/PlanningDrawer.js +259 -51
  44. package/dist/components/views/panels/PlanningDrawer.test.js +392 -4
  45. package/dist/config/envSchema.js +1 -0
  46. package/dist/core/AiProfileService.d.ts +7 -0
  47. package/dist/core/AiProfileService.js +460 -35
  48. package/dist/core/AiProfiles.test.js +648 -2
  49. package/dist/core/AttachmentLinkService.d.ts +3 -0
  50. package/dist/core/AttachmentLinkService.js +58 -13
  51. package/dist/core/GlobalSettingsService.js +12 -0
  52. package/dist/core/McpTokenService.d.ts +12 -0
  53. package/dist/core/McpTokenService.js +44 -1
  54. package/dist/core/PlanningEntities.test.js +366 -4
  55. package/dist/core/TaskActivityService.d.ts +28 -1
  56. package/dist/core/TaskActivityService.js +227 -46
  57. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  58. package/dist/core/TaskActivitySync.test.js +90 -0
  59. package/dist/core/TaskAuditTimeline.test.js +96 -0
  60. package/dist/core/Taskforce.d.ts +190 -26
  61. package/dist/core/Taskforce.ids.test.js +58 -0
  62. package/dist/core/Taskforce.js +775 -71
  63. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  64. package/dist/core/types.d.ts +7 -0
  65. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +2 -1
  66. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  67. package/dist/hooks/bootstrapTimeouts.js +8 -0
  68. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  69. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  70. package/dist/hooks/sync/orchestratorShared.d.ts +3 -3
  71. package/dist/hooks/sync/orchestratorShared.js +61 -1
  72. package/dist/hooks/sync/recovery.js +8 -0
  73. package/dist/hooks/sync/recovery.test.js +5 -5
  74. package/dist/hooks/sync/transfers.d.ts +2 -1
  75. package/dist/hooks/sync/transfers.js +36 -9
  76. package/dist/hooks/sync/transfers.test.js +534 -2
  77. package/dist/hooks/useFilterSort.js +13 -13
  78. package/dist/hooks/useFilterSort.test.js +87 -1
  79. package/dist/hooks/useSyncOrchestrator.js +50 -5
  80. package/dist/hooks/useTaskforce.d.ts +85 -50
  81. package/dist/hooks/useTaskforce.js +44 -14
  82. package/dist/hooks/useTaskforce.runtime-routing.test.js +94 -0
  83. package/dist/hooks/useTaskforce.sync-behavior.test.js +99 -0
  84. package/dist/hooks/useWorkspaceSyncController.js +7 -1
  85. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  86. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +14 -7
  87. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  88. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  89. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  90. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  91. package/dist/mcp/clientIntegrations.d.ts +1 -0
  92. package/dist/mcp/clientIntegrations.js +25 -0
  93. package/dist/mcp/clientIntegrations.test.js +14 -3
  94. package/dist/mcp/clientProfileDefaults.js +3 -3
  95. package/dist/mcp/collaborationRegistrar.js +7 -7
  96. package/dist/mcp/documentAssetRegistrar.js +113 -7
  97. package/dist/mcp/runtime.d.ts +1 -0
  98. package/dist/mcp/runtime.js +969 -122
  99. package/dist/mcp/runtime.test.js +1083 -11
  100. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  101. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  102. package/dist/mcp/toolRegistry.d.ts +56 -0
  103. package/dist/mcp/toolRegistry.js +8 -0
  104. package/dist/mcp/toolRegistry.test.js +4 -0
  105. package/dist/migrations/taskSchemaMigrations.js +21 -0
  106. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  107. package/dist/server/aiProfileAvatarAssets.js +319 -0
  108. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  109. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  110. package/dist/server/annotatedAttachmentsRoutes.test.js +66 -7
  111. package/dist/server/index.js +4 -0
  112. package/dist/server/index.test.js +6 -1
  113. package/dist/server/routes/admin.js +187 -9
  114. package/dist/server/routes/annotatedAttachments.js +51 -0
  115. package/dist/server/routes/auth.d.ts +11 -0
  116. package/dist/server/routes/auth.js +189 -27
  117. package/dist/server/routes/authSupport.d.ts +4 -0
  118. package/dist/server/routes/authSupport.js +12 -1
  119. package/dist/server/routes/billing.js +11 -0
  120. package/dist/server/routes/billing.test.js +3 -0
  121. package/dist/server/routes/documents.js +10 -0
  122. package/dist/server/routes/shared.js +0 -10
  123. package/dist/server/routes/sync.integration.test.js +221 -0
  124. package/dist/server/routes/sync.js +13 -3
  125. package/dist/server/routes/syncSnapshotRoutes.js +2 -0
  126. package/dist/server/routes/tasks.js +12 -9
  127. package/dist/server/routes.js +87 -8
  128. package/dist/server/routes.test.js +890 -22
  129. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  130. package/dist/shared/aiProfileSurfaceType.js +10 -8
  131. package/dist/storage/objectStorage.d.ts +1 -0
  132. package/dist/storage/objectStorage.js +2 -1
  133. package/dist/storage/objectStorageClient.d.ts +1 -0
  134. package/dist/storage/objectStorageClient.js +41 -8
  135. package/dist/storage/objectStorageClient.test.js +26 -1
  136. package/dist/storage/objectStorageConfig.js +3 -1
  137. package/dist/storage/objectStorageConfig.test.js +13 -1
  138. package/dist/sync/collaborationSyncPayload.js +2 -0
  139. package/dist/sync/collaborationSyncPayload.test.js +1 -0
  140. package/dist/sync/collaborationSyncState.d.ts +3 -3
  141. package/dist/sync/collaborationSyncState.js +19 -12
  142. package/dist/sync/contentSyncPayload.d.ts +5 -1
  143. package/dist/sync/contentSyncPayload.js +9 -0
  144. package/dist/sync/contentSyncPayload.test.js +39 -0
  145. package/dist/sync/contentSyncState.d.ts +5 -1
  146. package/dist/sync/contentSyncState.js +231 -37
  147. package/dist/sync/planningSyncPayload.js +5 -0
  148. package/dist/sync/planningSyncPayload.test.js +24 -0
  149. package/dist/sync/syncApplyHandlers.d.ts +4 -4
  150. package/dist/sync/syncApplyHandlers.js +89 -31
  151. package/dist/sync/syncApplyHandlers.test.js +184 -4
  152. package/dist/sync/syncResourceAdapters.d.ts +2 -0
  153. package/dist/sync/syncResourceAdapters.js +2 -0
  154. package/dist/sync/syncService.d.ts +1 -0
  155. package/dist/sync/syncService.js +13 -1
  156. package/dist/sync/syncService.test.js +39 -0
  157. package/dist/sync/workspacePullFeed.d.ts +4 -0
  158. package/dist/sync/workspacePullFeed.js +129 -70
  159. package/dist/sync/workspacePullFeed.test.js +49 -0
  160. package/dist/sync/workspaceSyncModel.d.ts +28 -6
  161. package/dist/sync/workspaceSyncModel.js +159 -45
  162. package/dist/sync/workspaceSyncModel.test.js +269 -3
  163. package/dist/types.d.ts +33 -2
  164. package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
  165. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
  166. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  167. package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
  168. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  169. package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
  170. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  171. package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
  172. package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-D4XseTwW.js} +1 -1
  173. package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
  174. package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
  175. package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
  176. package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
  177. package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-oOA9bcdK.js} +4 -4
  178. package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
  179. package/dist/ui/assets/index-607WPo_X.js +4 -0
  180. package/dist/ui/assets/index-CYyodXsg.css +1 -0
  181. package/dist/ui/assets/{vendor-icons-DuEd_65c.js → vendor-icons-B32hGuF2.js} +1 -1
  182. package/dist/ui/index.html +3 -3
  183. package/dist/ui/product/agent manager.png +0 -0
  184. package/dist/ui/product/agent_manager_02.png +0 -0
  185. package/dist/utils/aiProfileEvents.d.ts +7 -0
  186. package/dist/utils/aiProfileEvents.js +6 -0
  187. package/dist/utils/assignees.d.ts +6 -0
  188. package/dist/utils/assignees.js +8 -1
  189. package/dist/utils/assignees.test.d.ts +1 -0
  190. package/dist/utils/assignees.test.js +25 -0
  191. package/dist/utils/avatarDisplay.d.ts +1 -0
  192. package/dist/utils/avatarDisplay.js +15 -0
  193. package/dist/utils/contextFiles.d.ts +3 -1
  194. package/dist/utils/contextFiles.js +2 -1
  195. package/dist/utils/taskActivity.d.ts +7 -1
  196. package/dist/utils/taskActivity.js +63 -1
  197. package/dist/utils/taskActivity.test.js +23 -1
  198. package/dist/utils/taskSearch.d.ts +22 -0
  199. package/dist/utils/taskSearch.js +87 -0
  200. package/dist/utils/taskSearch.test.d.ts +1 -0
  201. package/dist/utils/taskSearch.test.js +69 -0
  202. package/dist/utils/taskUtils.js +10 -3
  203. package/dist/utils/taskUtils.test.d.ts +1 -0
  204. package/dist/utils/taskUtils.test.js +19 -0
  205. package/package.json +1 -1
  206. package/dist/ui/assets/AgentsModule-DrLawwNl.js +0 -1
  207. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  208. package/dist/ui/assets/AnnotatedAttachmentWorkspace-Z9_whf7F.js +0 -3
  209. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +0 -252
  210. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +0 -1
  211. package/dist/ui/assets/PlansPage-CvfnMiWq.css +0 -1
  212. package/dist/ui/assets/PlansPage-DRXscYxH.js +0 -1
  213. package/dist/ui/assets/TaskSettings-DZX4jk7e.js +0 -9
  214. package/dist/ui/assets/index-9eT8e0Lu.css +0 -1
  215. package/dist/ui/assets/index-C6k75jr8.js +0 -6
@@ -50,6 +50,8 @@ describe('workspaceSyncModel', () => {
50
50
  taskEvents: [
51
51
  {
52
52
  id: 'task-event-1',
53
+ entityType: 'task',
54
+ entityId: 'task-1',
53
55
  taskId: 'task-1',
54
56
  workspaceId: 'workspace-a',
55
57
  action: 'task-status-changed',
@@ -91,6 +93,8 @@ describe('workspaceSyncModel', () => {
91
93
  taskEvents: [
92
94
  {
93
95
  id: 'task-event-keep',
96
+ entityType: 'task',
97
+ entityId: 'task-1',
94
98
  taskId: 'task-1',
95
99
  workspaceId: 'workspace-a',
96
100
  action: 'task-status-changed',
@@ -100,6 +104,8 @@ describe('workspaceSyncModel', () => {
100
104
  },
101
105
  {
102
106
  id: 'task-event-new',
107
+ entityType: 'task',
108
+ entityId: 'task-2',
103
109
  taskId: 'task-2',
104
110
  workspaceId: 'workspace-a',
105
111
  action: 'task-archived',
@@ -147,6 +153,41 @@ describe('workspaceSyncModel', () => {
147
153
  ['docs/new.md', '2026-02-03T00:00:00.000Z']
148
154
  ]);
149
155
  });
156
+ it('emits a document upsert when a synced document receives a newer task link', () => {
157
+ const payload = buildWorkspaceSyncPayloadModel({
158
+ tasks: [],
159
+ archivedTasks: [],
160
+ lastPushedTaskIds: new Set(),
161
+ pendingDeletedTaskIds: new Set(),
162
+ documents: [
163
+ {
164
+ path: 'workspaces/default/assets/documents/asset-doc-1-notes.md',
165
+ updatedAt: '2026-03-18T14:45:00.000Z',
166
+ content: '# Notes',
167
+ assetId: 'asset-doc-1',
168
+ documentId: 'doc-1',
169
+ referenceNumber: 8,
170
+ version: 1,
171
+ taskId: 'task-attach-1',
172
+ originalFilename: 'notes.md',
173
+ linkRole: 'attachment'
174
+ }
175
+ ],
176
+ lastPushedDocumentPaths: new Set(['workspaces/default/assets/documents/asset-doc-1-notes.md']),
177
+ lastPushedDocumentWatermarks: new Map([
178
+ ['workspaces/default/assets/documents/asset-doc-1-notes.md', '2026-03-18T13:30:00.000Z']
179
+ ])
180
+ });
181
+ expect(payload.changes).toEqual(expect.arrayContaining([
182
+ expect.objectContaining({
183
+ op: 'document-upsert',
184
+ path: 'workspaces/default/assets/documents/asset-doc-1-notes.md',
185
+ updatedAt: '2026-03-18T14:45:00.000Z',
186
+ taskId: 'task-attach-1',
187
+ linkRole: 'attachment'
188
+ })
189
+ ]));
190
+ });
150
191
  it('reports send-all diagnostics when a domain has no baseline', () => {
151
192
  const payload = buildWorkspaceSyncPayloadModel({
152
193
  tasks: [],
@@ -385,6 +426,8 @@ describe('workspaceSyncModel', () => {
385
426
  provider: 'OpenAI',
386
427
  model: 'gpt-5.4',
387
428
  surfaceType: 'coding_tool',
429
+ avatarRevision: 0,
430
+ avatarUpdatedAt: null,
388
431
  rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z'
389
432
  })
390
433
  })
@@ -443,6 +486,8 @@ describe('workspaceSyncModel', () => {
443
486
  color: '#0ea5e9',
444
487
  avatarUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Favatar.webp',
445
488
  avatarSourceUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Fsource.webp',
489
+ avatarRevision: 1,
490
+ avatarUpdatedAt: '2026-03-22T10:04:00.000Z',
446
491
  description: 'Terminal-first coding agent',
447
492
  role: 'Implementer',
448
493
  provider: 'OpenAI',
@@ -463,7 +508,9 @@ describe('workspaceSyncModel', () => {
463
508
  profile: expect.objectContaining({
464
509
  id: 'ai-profile-1',
465
510
  avatarUrl: expect.stringContaining('/api/taskforce/context/'),
466
- avatarSourceUrl: expect.stringContaining('/api/taskforce/context/')
511
+ avatarSourceUrl: expect.stringContaining('/api/taskforce/context/'),
512
+ avatarRevision: 1,
513
+ avatarUpdatedAt: '2026-03-22T10:04:00.000Z'
467
514
  })
468
515
  })
469
516
  ]));
@@ -478,6 +525,8 @@ describe('workspaceSyncModel', () => {
478
525
  color: '#0ea5e9',
479
526
  avatarUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Favatar.webp',
480
527
  avatarSourceUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Fsource.webp',
528
+ avatarRevision: 1,
529
+ avatarUpdatedAt: '2026-03-22T10:04:00.000Z',
481
530
  description: 'Terminal-first coding agent',
482
531
  role: 'Implementer',
483
532
  provider: 'OpenAI',
@@ -550,6 +599,12 @@ describe('workspaceSyncModel', () => {
550
599
  createdAt: '2026-03-29T10:00:00.000Z',
551
600
  updatedAt: '2026-03-29T11:00:00.000Z',
552
601
  order: 1,
602
+ attachments: [{
603
+ path: 'workspaces/ws-1/assets/documents/asset-in.md',
604
+ assetId: 'asset-in',
605
+ referenceLabel: 'D-31',
606
+ caption: 'Revenue context'
607
+ }],
553
608
  isArchived: false
554
609
  }],
555
610
  workstreams: [{
@@ -563,6 +618,12 @@ describe('workspaceSyncModel', () => {
563
618
  createdAt: '2026-03-29T10:30:00.000Z',
564
619
  updatedAt: '2026-03-29T11:30:00.000Z',
565
620
  order: 2,
621
+ attachments: [{
622
+ path: 'workspaces/ws-1/assets/documents/asset-ws.md',
623
+ assetId: 'asset-ws',
624
+ referenceLabel: 'D-32',
625
+ caption: 'Checkout context'
626
+ }],
566
627
  isArchived: false
567
628
  }]
568
629
  });
@@ -571,7 +632,13 @@ describe('workspaceSyncModel', () => {
571
632
  op: 'initiative-upsert',
572
633
  initiative: expect.objectContaining({
573
634
  id: 'initiative-1',
574
- title: 'Revenue Expansion'
635
+ title: 'Revenue Expansion',
636
+ attachments: expect.arrayContaining([
637
+ expect.objectContaining({
638
+ path: 'workspaces/ws-1/assets/documents/asset-in.md',
639
+ referenceLabel: 'D-31'
640
+ })
641
+ ])
575
642
  })
576
643
  }),
577
644
  expect.objectContaining({
@@ -579,7 +646,13 @@ describe('workspaceSyncModel', () => {
579
646
  workstream: expect.objectContaining({
580
647
  id: 'workstream-1',
581
648
  initiativeId: 'initiative-1',
582
- title: 'Checkout UX'
649
+ title: 'Checkout UX',
650
+ attachments: expect.arrayContaining([
651
+ expect.objectContaining({
652
+ path: 'workspaces/ws-1/assets/documents/asset-ws.md',
653
+ referenceLabel: 'D-32'
654
+ })
655
+ ])
583
656
  })
584
657
  })
585
658
  ]));
@@ -764,6 +837,138 @@ describe('workspaceSyncModel', () => {
764
837
  })
765
838
  ]));
766
839
  });
840
+ it('does not resend already-acknowledged deleted collaboration records', () => {
841
+ const payload = buildWorkspaceSyncPayloadModel({
842
+ tasks: [],
843
+ archivedTasks: [],
844
+ lastPushedTaskIds: new Set(),
845
+ pendingDeletedTaskIds: new Set(),
846
+ documentReviewSessions: [
847
+ {
848
+ id: 'review-deleted',
849
+ assetId: 'asset-doc-1',
850
+ documentId: 'doc-1',
851
+ documentVersion: 2,
852
+ title: 'Review',
853
+ status: 'open',
854
+ comments: [],
855
+ createdByActorId: null,
856
+ updatedByActorId: null,
857
+ createdAt: '2026-03-24T10:00:00.000Z',
858
+ updatedAt: '2026-03-24T10:05:00.000Z',
859
+ deletedAt: '2026-03-24T10:06:00.000Z'
860
+ }
861
+ ],
862
+ documentReviewComments: [
863
+ {
864
+ id: 'comment-deleted',
865
+ sessionId: 'review-deleted',
866
+ body: 'Delete me.',
867
+ anchor: { kind: 'document' },
868
+ order: 0,
869
+ authorActorId: null,
870
+ createdAt: '2026-03-24T10:00:00.000Z',
871
+ updatedAt: '2026-03-24T10:05:00.000Z',
872
+ deletedAt: '2026-03-24T10:06:30.000Z'
873
+ }
874
+ ],
875
+ annotatedAttachmentSessions: [
876
+ {
877
+ id: 'annotation-deleted',
878
+ workspaceId: 'ws-1',
879
+ taskId: 'task-1',
880
+ baseImageAssetId: 'asset-image-1',
881
+ title: 'Annotations',
882
+ globalInstruction: null,
883
+ annotations: [],
884
+ createdByActorId: null,
885
+ updatedByActorId: null,
886
+ createdAt: '2026-03-24T10:00:00.000Z',
887
+ updatedAt: '2026-03-24T10:05:00.000Z',
888
+ deletedAt: '2026-03-24T10:07:00.000Z'
889
+ }
890
+ ],
891
+ lastPushedDocumentReviewSessionIds: new Set(),
892
+ lastPushedDocumentReviewSessionWatermarks: new Map([
893
+ ['review-deleted', '2026-03-24T10:06:00.000Z']
894
+ ]),
895
+ lastPushedDocumentReviewCommentIds: new Set(),
896
+ lastPushedDocumentReviewCommentWatermarks: new Map([
897
+ ['comment-deleted', '2026-03-24T10:06:30.000Z']
898
+ ]),
899
+ lastPushedAnnotatedAttachmentSessionIds: new Set(),
900
+ lastPushedAnnotatedAttachmentSessionWatermarks: new Map([
901
+ ['annotation-deleted', '2026-03-24T10:07:00.000Z']
902
+ ])
903
+ });
904
+ expect(payload.currentDocumentReviewSessionWatermarks.get('review-deleted')).toBe('2026-03-24T10:06:00.000Z');
905
+ expect(payload.currentDocumentReviewCommentWatermarks.get('comment-deleted')).toBe('2026-03-24T10:06:30.000Z');
906
+ expect(payload.currentAnnotatedAttachmentSessionWatermarks.get('annotation-deleted')).toBe('2026-03-24T10:07:00.000Z');
907
+ expect(payload.changes).not.toEqual(expect.arrayContaining([
908
+ expect.objectContaining({ op: 'document-review-session-delete', sessionId: 'review-deleted' }),
909
+ expect.objectContaining({ op: 'document-review-comment-delete', commentId: 'comment-deleted' }),
910
+ expect.objectContaining({ op: 'annotated-attachment-session-delete', sessionId: 'annotation-deleted' })
911
+ ]));
912
+ expect(payload.deltaDiagnostics?.domains.documentReviewSessions.changeCount).toBe(0);
913
+ expect(payload.deltaDiagnostics?.domains.documentReviewComments.changeCount).toBe(0);
914
+ expect(payload.deltaDiagnostics?.domains.annotatedAttachmentSessions.changeCount).toBe(0);
915
+ });
916
+ it('emits a single delete op when a previously pushed collaboration record is now soft-deleted', () => {
917
+ const payload = buildWorkspaceSyncPayloadModel({
918
+ tasks: [],
919
+ archivedTasks: [],
920
+ lastPushedTaskIds: new Set(),
921
+ pendingDeletedTaskIds: new Set(),
922
+ documentReviewSessions: [{
923
+ id: 'review-deleted',
924
+ assetId: 'asset-doc-1',
925
+ documentId: 'doc-1',
926
+ documentVersion: 2,
927
+ title: 'Review',
928
+ status: 'open',
929
+ comments: [],
930
+ createdByActorId: null,
931
+ updatedByActorId: null,
932
+ createdAt: '2026-03-24T10:00:00.000Z',
933
+ updatedAt: '2026-03-24T10:05:00.000Z',
934
+ deletedAt: '2026-03-24T10:06:00.000Z'
935
+ }],
936
+ documentReviewComments: [{
937
+ id: 'comment-deleted',
938
+ sessionId: 'review-deleted',
939
+ body: 'Delete me.',
940
+ anchor: { kind: 'document' },
941
+ order: 0,
942
+ authorActorId: null,
943
+ createdAt: '2026-03-24T10:00:00.000Z',
944
+ updatedAt: '2026-03-24T10:05:00.000Z',
945
+ deletedAt: '2026-03-24T10:06:30.000Z'
946
+ }],
947
+ annotatedAttachmentSessions: [{
948
+ id: 'annotation-deleted',
949
+ workspaceId: 'ws-1',
950
+ taskId: 'task-1',
951
+ baseImageAssetId: 'asset-image-1',
952
+ title: 'Annotations',
953
+ globalInstruction: null,
954
+ annotations: [],
955
+ createdByActorId: null,
956
+ updatedByActorId: null,
957
+ createdAt: '2026-03-24T10:00:00.000Z',
958
+ updatedAt: '2026-03-24T10:05:00.000Z',
959
+ deletedAt: '2026-03-24T10:07:00.000Z'
960
+ }],
961
+ lastPushedDocumentReviewSessionIds: new Set(['review-deleted']),
962
+ lastPushedDocumentReviewSessionWatermarks: new Map(),
963
+ lastPushedDocumentReviewCommentIds: new Set(['comment-deleted']),
964
+ lastPushedDocumentReviewCommentWatermarks: new Map(),
965
+ lastPushedAnnotatedAttachmentSessionIds: new Set(['annotation-deleted']),
966
+ lastPushedAnnotatedAttachmentSessionWatermarks: new Map()
967
+ });
968
+ expect(payload.changes.filter((change) => change.op === 'document-review-session-delete')).toHaveLength(1);
969
+ expect(payload.changes.filter((change) => change.op === 'document-review-comment-delete')).toHaveLength(1);
970
+ expect(payload.changes.filter((change) => change.op === 'annotated-attachment-session-delete')).toHaveLength(1);
971
+ });
767
972
  });
768
973
  describe('buildWorkspaceSyncSignatureModel', () => {
769
974
  it('includes pending delete operations in the signature hash', () => {
@@ -921,6 +1126,63 @@ describe('workspaceSyncModel', () => {
921
1126
  });
922
1127
  expect(changedSignature).not.toEqual(baseSignature);
923
1128
  });
1129
+ it('changes when initiative or workstream attachments change', () => {
1130
+ const baseSignature = buildWorkspaceSyncSignatureModel({
1131
+ workspaceId: 'ws-1',
1132
+ tasks: [],
1133
+ archivedTasks: [],
1134
+ initiatives: [{
1135
+ id: 'initiative-1',
1136
+ title: 'Revenue Expansion',
1137
+ description: null,
1138
+ createdAt: '2026-03-29T10:00:00.000Z',
1139
+ updatedAt: '2026-03-29T11:00:00.000Z',
1140
+ attachments: []
1141
+ }],
1142
+ workstreams: [{
1143
+ id: 'workstream-1',
1144
+ initiativeId: 'initiative-1',
1145
+ title: 'Checkout UX',
1146
+ description: null,
1147
+ createdAt: '2026-03-29T10:30:00.000Z',
1148
+ updatedAt: '2026-03-29T11:30:00.000Z',
1149
+ attachments: []
1150
+ }]
1151
+ });
1152
+ const changedSignature = buildWorkspaceSyncSignatureModel({
1153
+ workspaceId: 'ws-1',
1154
+ tasks: [],
1155
+ archivedTasks: [],
1156
+ initiatives: [{
1157
+ id: 'initiative-1',
1158
+ title: 'Revenue Expansion',
1159
+ description: null,
1160
+ createdAt: '2026-03-29T10:00:00.000Z',
1161
+ updatedAt: '2026-03-29T11:00:00.000Z',
1162
+ attachments: [{
1163
+ path: 'workspaces/ws-1/assets/documents/asset-in.md',
1164
+ assetId: 'asset-in',
1165
+ referenceLabel: 'D-31',
1166
+ caption: 'Revenue context'
1167
+ }]
1168
+ }],
1169
+ workstreams: [{
1170
+ id: 'workstream-1',
1171
+ initiativeId: 'initiative-1',
1172
+ title: 'Checkout UX',
1173
+ description: null,
1174
+ createdAt: '2026-03-29T10:30:00.000Z',
1175
+ updatedAt: '2026-03-29T11:30:00.000Z',
1176
+ attachments: [{
1177
+ path: 'workspaces/ws-1/assets/documents/asset-ws.md',
1178
+ assetId: 'asset-ws',
1179
+ referenceLabel: 'D-32',
1180
+ caption: 'Checkout context'
1181
+ }]
1182
+ }]
1183
+ });
1184
+ expect(changedSignature).not.toEqual(baseSignature);
1185
+ });
924
1186
  it('changes when document review sessions change', () => {
925
1187
  const baseSignature = buildWorkspaceSyncSignatureModel({
926
1188
  workspaceId: 'ws-1',
@@ -1013,6 +1275,8 @@ describe('workspaceSyncModel', () => {
1013
1275
  archivedTasks: [],
1014
1276
  taskEvents: [{
1015
1277
  id: 'task-event-1',
1278
+ entityType: 'task',
1279
+ entityId: 'task-1',
1016
1280
  taskId: 'task-1',
1017
1281
  workspaceId: 'ws-1',
1018
1282
  action: 'task-status-changed',
@@ -1027,6 +1291,8 @@ describe('workspaceSyncModel', () => {
1027
1291
  archivedTasks: [],
1028
1292
  taskEvents: [{
1029
1293
  id: 'task-event-1',
1294
+ entityType: 'task',
1295
+ entityId: 'task-1',
1030
1296
  taskId: 'task-1',
1031
1297
  workspaceId: 'ws-1',
1032
1298
  action: 'task-status-changed',
package/dist/types.d.ts CHANGED
@@ -29,10 +29,25 @@ export interface TaskEventDetails {
29
29
  changes?: Record<string, TaskEventChange>;
30
30
  [key: string]: unknown;
31
31
  }
32
+ export type ActivityEntityType = 'task' | 'initiative' | 'workstream';
33
+ interface BaseEntityEvent {
34
+ id: string;
35
+ workspaceId: string;
36
+ entityType: ActivityEntityType;
37
+ entityId: string;
38
+ action: string;
39
+ actor: string;
40
+ actorType: 'human' | 'ai' | 'system';
41
+ createdAt: string;
42
+ details?: TaskEventDetails;
43
+ actorProfile?: TaskActor | null;
44
+ }
32
45
  export interface TaskEvent {
33
46
  id: string;
34
47
  taskId: string;
35
48
  workspaceId: string;
49
+ entityType?: 'task';
50
+ entityId?: string;
36
51
  action: string;
37
52
  actor: string;
38
53
  actorType: 'human' | 'ai' | 'system';
@@ -40,6 +55,11 @@ export interface TaskEvent {
40
55
  details?: TaskEventDetails;
41
56
  actorProfile?: TaskActor | null;
42
57
  }
58
+ export interface PlanningEntityEvent extends BaseEntityEvent {
59
+ entityType: 'initiative' | 'workstream';
60
+ entityId: string;
61
+ }
62
+ export type EntityEvent = TaskEvent | PlanningEntityEvent;
43
63
  export interface CommentActivityItem {
44
64
  id: string;
45
65
  type: 'comment';
@@ -53,6 +73,13 @@ export interface AuditActivityItem {
53
73
  event: TaskEvent;
54
74
  }
55
75
  export type TaskActivityItem = CommentActivityItem | AuditActivityItem;
76
+ export interface EntityAuditActivityItem {
77
+ id: string;
78
+ type: 'event';
79
+ timestamp: string;
80
+ event: EntityEvent;
81
+ }
82
+ export type EntityActivityItem = CommentActivityItem | EntityAuditActivityItem;
56
83
  export type DeletedTaskSource = 'ui' | 'mcp' | 'sync' | 'workspace-delete' | 'system';
57
84
  export interface DeletedTaskDetails {
58
85
  reason?: string | null;
@@ -76,9 +103,9 @@ export interface AttachmentItem {
76
103
  caption?: string;
77
104
  displayName?: string;
78
105
  originalFilename?: string;
79
- assetId?: string;
106
+ assetId?: string | null;
80
107
  referenceNumber?: number | null;
81
- referenceLabel?: string;
108
+ referenceLabel?: string | null;
82
109
  taskId?: string | null;
83
110
  timestamp?: string;
84
111
  }
@@ -122,6 +149,8 @@ export interface InitiativeItem {
122
149
  description: string | null;
123
150
  ownerId?: string | null;
124
151
  comments?: Comment[];
152
+ activity?: EntityActivityItem[];
153
+ entityEvents?: PlanningEntityEvent[];
125
154
  attachments?: Array<string | AttachmentItem>;
126
155
  createdAt: string;
127
156
  updatedAt?: string;
@@ -137,6 +166,8 @@ export interface WorkstreamItem {
137
166
  description: string | null;
138
167
  ownerId?: string | null;
139
168
  comments?: Comment[];
169
+ activity?: EntityActivityItem[];
170
+ entityEvents?: PlanningEntityEvent[];
140
171
  attachments?: Array<string | AttachmentItem>;
141
172
  createdAt: string;
142
173
  updatedAt?: string;
@@ -0,0 +1 @@
1
+ import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-607WPo_X.js";import{Y as Re,w as $e,h as Ee,B as ne,m as C,b as Le,W as Be,a2 as Ge}from"./vendor-icons-B32hGuF2.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};