@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.
- package/README.md +0 -2
- package/dist/Taskforce.module.css +317 -27
- package/dist/TaskforceCore.js +1 -0
- package/dist/TaskforceCore.test.js +178 -0
- package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
- package/dist/components/activity/EntityActivityTimeline.js +81 -0
- package/dist/components/context/ContextAttachmentManager.js +15 -3
- package/dist/components/features/AgentsModule.d.ts +5 -1
- package/dist/components/features/AgentsModule.js +142 -39
- package/dist/components/features/AgentsModule.test.js +260 -11
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
- package/dist/components/features/DocumentIndex.d.ts +2 -2
- package/dist/components/features/DocumentIndex.js +3 -4
- package/dist/components/features/DocumentViewer.d.ts +3 -1
- package/dist/components/features/DocumentViewer.js +6 -5
- package/dist/components/features/DocumentWorkspace.d.ts +10 -2
- package/dist/components/features/DocumentWorkspace.js +15 -4
- package/dist/components/features/DocumentWorkspace.test.js +20 -3
- package/dist/components/features/TaskSettings.d.ts +2 -0
- package/dist/components/features/TaskSettings.js +83 -78
- package/dist/components/features/TaskSettings.test.js +171 -82
- package/dist/components/task/TaskActionHeader.js +11 -5
- package/dist/components/task/TaskActionHeader.test.js +10 -0
- package/dist/components/task/TaskCard.js +6 -2
- package/dist/components/task/TaskContextUpload.test.js +3 -3
- package/dist/components/task/TaskForm.js +14 -64
- package/dist/components/task/TaskKanban.js +0 -8
- package/dist/components/task/TaskList.js +5 -1
- package/dist/components/task/TaskList.test.js +4 -0
- package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
- package/dist/components/ui/HierarchyActionBadge.js +5 -0
- package/dist/components/views/AppShellHeader.d.ts +3 -1
- package/dist/components/views/AppShellHeader.js +27 -11
- package/dist/components/views/AppShellHeader.test.js +18 -1
- package/dist/components/views/PlanComparisonPage.js +9 -1
- package/dist/components/views/PlanComparisonPage.test.js +19 -0
- package/dist/components/views/PlansPage.js +1 -1
- package/dist/components/views/StandaloneLayout.js +256 -69
- package/dist/components/views/WidgetView.js +10 -8
- package/dist/components/views/panels/PlanningDrawer.d.ts +19 -6
- package/dist/components/views/panels/PlanningDrawer.js +259 -51
- package/dist/components/views/panels/PlanningDrawer.test.js +392 -4
- package/dist/config/envSchema.js +1 -0
- package/dist/core/AiProfileService.d.ts +7 -0
- package/dist/core/AiProfileService.js +460 -35
- package/dist/core/AiProfiles.test.js +648 -2
- package/dist/core/AttachmentLinkService.d.ts +3 -0
- package/dist/core/AttachmentLinkService.js +58 -13
- package/dist/core/GlobalSettingsService.js +12 -0
- package/dist/core/McpTokenService.d.ts +12 -0
- package/dist/core/McpTokenService.js +44 -1
- package/dist/core/PlanningEntities.test.js +366 -4
- package/dist/core/TaskActivityService.d.ts +28 -1
- package/dist/core/TaskActivityService.js +227 -46
- package/dist/core/TaskActivitySync.test.d.ts +1 -0
- package/dist/core/TaskActivitySync.test.js +90 -0
- package/dist/core/TaskAuditTimeline.test.js +96 -0
- package/dist/core/Taskforce.d.ts +190 -26
- package/dist/core/Taskforce.ids.test.js +58 -0
- package/dist/core/Taskforce.js +775 -71
- package/dist/core/Taskforce.mcpAuth.test.js +109 -0
- package/dist/core/types.d.ts +7 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +2 -1
- package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
- package/dist/hooks/bootstrapTimeouts.js +8 -0
- package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
- package/dist/hooks/bootstrapTimeouts.test.js +12 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +3 -3
- package/dist/hooks/sync/orchestratorShared.js +61 -1
- package/dist/hooks/sync/recovery.js +8 -0
- package/dist/hooks/sync/recovery.test.js +5 -5
- package/dist/hooks/sync/transfers.d.ts +2 -1
- package/dist/hooks/sync/transfers.js +36 -9
- package/dist/hooks/sync/transfers.test.js +534 -2
- package/dist/hooks/useFilterSort.js +13 -13
- package/dist/hooks/useFilterSort.test.js +87 -1
- package/dist/hooks/useSyncOrchestrator.js +50 -5
- package/dist/hooks/useTaskforce.d.ts +85 -50
- package/dist/hooks/useTaskforce.js +44 -14
- package/dist/hooks/useTaskforce.runtime-routing.test.js +94 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +99 -0
- package/dist/hooks/useWorkspaceSyncController.js +7 -1
- package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +14 -7
- package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
- package/dist/mcp/canonicalAssetHelpers.js +18 -10
- package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
- package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
- package/dist/mcp/clientIntegrations.d.ts +1 -0
- package/dist/mcp/clientIntegrations.js +25 -0
- package/dist/mcp/clientIntegrations.test.js +14 -3
- package/dist/mcp/clientProfileDefaults.js +3 -3
- package/dist/mcp/collaborationRegistrar.js +7 -7
- package/dist/mcp/documentAssetRegistrar.js +113 -7
- package/dist/mcp/runtime.d.ts +1 -0
- package/dist/mcp/runtime.js +969 -122
- package/dist/mcp/runtime.test.js +1083 -11
- package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
- package/dist/mcp/taskPlanningRegistrar.js +48 -0
- package/dist/mcp/toolRegistry.d.ts +56 -0
- package/dist/mcp/toolRegistry.js +8 -0
- package/dist/mcp/toolRegistry.test.js +4 -0
- package/dist/migrations/taskSchemaMigrations.js +21 -0
- package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
- package/dist/server/aiProfileAvatarAssets.js +319 -0
- package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
- package/dist/server/aiProfileAvatarAssets.test.js +213 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +66 -7
- package/dist/server/index.js +4 -0
- package/dist/server/index.test.js +6 -1
- package/dist/server/routes/admin.js +187 -9
- package/dist/server/routes/annotatedAttachments.js +51 -0
- package/dist/server/routes/auth.d.ts +11 -0
- package/dist/server/routes/auth.js +189 -27
- package/dist/server/routes/authSupport.d.ts +4 -0
- package/dist/server/routes/authSupport.js +12 -1
- package/dist/server/routes/billing.js +11 -0
- package/dist/server/routes/billing.test.js +3 -0
- package/dist/server/routes/documents.js +10 -0
- package/dist/server/routes/shared.js +0 -10
- package/dist/server/routes/sync.integration.test.js +221 -0
- package/dist/server/routes/sync.js +13 -3
- package/dist/server/routes/syncSnapshotRoutes.js +2 -0
- package/dist/server/routes/tasks.js +12 -9
- package/dist/server/routes.js +87 -8
- package/dist/server/routes.test.js +890 -22
- package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
- package/dist/shared/aiProfileSurfaceType.js +10 -8
- package/dist/storage/objectStorage.d.ts +1 -0
- package/dist/storage/objectStorage.js +2 -1
- package/dist/storage/objectStorageClient.d.ts +1 -0
- package/dist/storage/objectStorageClient.js +41 -8
- package/dist/storage/objectStorageClient.test.js +26 -1
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.js +13 -1
- package/dist/sync/collaborationSyncPayload.js +2 -0
- package/dist/sync/collaborationSyncPayload.test.js +1 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -3
- package/dist/sync/collaborationSyncState.js +19 -12
- package/dist/sync/contentSyncPayload.d.ts +5 -1
- package/dist/sync/contentSyncPayload.js +9 -0
- package/dist/sync/contentSyncPayload.test.js +39 -0
- package/dist/sync/contentSyncState.d.ts +5 -1
- package/dist/sync/contentSyncState.js +231 -37
- package/dist/sync/planningSyncPayload.js +5 -0
- package/dist/sync/planningSyncPayload.test.js +24 -0
- package/dist/sync/syncApplyHandlers.d.ts +4 -4
- package/dist/sync/syncApplyHandlers.js +89 -31
- package/dist/sync/syncApplyHandlers.test.js +184 -4
- package/dist/sync/syncResourceAdapters.d.ts +2 -0
- package/dist/sync/syncResourceAdapters.js +2 -0
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +13 -1
- package/dist/sync/syncService.test.js +39 -0
- package/dist/sync/workspacePullFeed.d.ts +4 -0
- package/dist/sync/workspacePullFeed.js +129 -70
- package/dist/sync/workspacePullFeed.test.js +49 -0
- package/dist/sync/workspaceSyncModel.d.ts +28 -6
- package/dist/sync/workspaceSyncModel.js +159 -45
- package/dist/sync/workspaceSyncModel.test.js +269 -3
- package/dist/types.d.ts +33 -2
- package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
- package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
- package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
- package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
- package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-D4XseTwW.js} +1 -1
- package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
- package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
- package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
- package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-oOA9bcdK.js} +4 -4
- package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
- package/dist/ui/assets/index-607WPo_X.js +4 -0
- package/dist/ui/assets/index-CYyodXsg.css +1 -0
- package/dist/ui/assets/{vendor-icons-DuEd_65c.js → vendor-icons-B32hGuF2.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/product/agent manager.png +0 -0
- package/dist/ui/product/agent_manager_02.png +0 -0
- package/dist/utils/aiProfileEvents.d.ts +7 -0
- package/dist/utils/aiProfileEvents.js +6 -0
- package/dist/utils/assignees.d.ts +6 -0
- package/dist/utils/assignees.js +8 -1
- package/dist/utils/assignees.test.d.ts +1 -0
- package/dist/utils/assignees.test.js +25 -0
- package/dist/utils/avatarDisplay.d.ts +1 -0
- package/dist/utils/avatarDisplay.js +15 -0
- package/dist/utils/contextFiles.d.ts +3 -1
- package/dist/utils/contextFiles.js +2 -1
- package/dist/utils/taskActivity.d.ts +7 -1
- package/dist/utils/taskActivity.js +63 -1
- package/dist/utils/taskActivity.test.js +23 -1
- package/dist/utils/taskSearch.d.ts +22 -0
- package/dist/utils/taskSearch.js +87 -0
- package/dist/utils/taskSearch.test.d.ts +1 -0
- package/dist/utils/taskSearch.test.js +69 -0
- package/dist/utils/taskUtils.js +10 -3
- package/dist/utils/taskUtils.test.d.ts +1 -0
- package/dist/utils/taskUtils.test.js +19 -0
- package/package.json +1 -1
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-Z9_whf7F.js +0 -3
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +0 -252
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +0 -1
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +0 -1
- package/dist/ui/assets/PlansPage-DRXscYxH.js +0 -1
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +0 -9
- package/dist/ui/assets/index-9eT8e0Lu.css +0 -1
- 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};
|