@taskforcehq/taskforce 0.3.303 → 0.3.305

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 (237) hide show
  1. package/README.md +140 -247
  2. package/dist/Taskforce.module.css +102 -5
  3. package/dist/TaskforceCore.js +635 -238
  4. package/dist/TaskforceCore.test.js +2243 -495
  5. package/dist/annotatedAttachments/service.d.ts +1 -1
  6. package/dist/annotatedAttachments/types.d.ts +1 -1
  7. package/dist/compat/workspaceSyncCompat.js +6 -0
  8. package/dist/components/features/AgentsModule.js +1 -1
  9. package/dist/components/features/AgentsModule.test.js +1 -1
  10. package/dist/components/features/DocumentIndex.d.ts +1 -1
  11. package/dist/components/features/DocumentIndex.js +1 -1
  12. package/dist/components/features/DocumentViewer.d.ts +1 -1
  13. package/dist/components/features/DocumentViewer.js +32 -9
  14. package/dist/components/features/DocumentWorkspace.d.ts +5 -22
  15. package/dist/components/features/DocumentWorkspace.js +12 -155
  16. package/dist/components/features/DocumentWorkspace.test.js +193 -6
  17. package/dist/components/features/TaskSettings.js +87 -41
  18. package/dist/components/features/TaskSettings.test.js +180 -40
  19. package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
  20. package/dist/components/features/documentWorkspaceModel.js +57 -0
  21. package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
  22. package/dist/components/features/useDocumentWorkspaceController.js +113 -0
  23. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  24. package/dist/components/task/TaskActionHeader.js +3 -3
  25. package/dist/components/task/TaskActionHeader.test.js +8 -0
  26. package/dist/components/task/TaskCard.js +16 -1
  27. package/dist/components/task/TaskCard.test.js +31 -0
  28. package/dist/components/task/TaskForm.d.ts +9 -8
  29. package/dist/components/task/TaskForm.js +374 -219
  30. package/dist/components/task/TaskForm.test.js +482 -361
  31. package/dist/components/task/TaskKanban.d.ts +1 -1
  32. package/dist/components/task/TaskKanban.js +33 -161
  33. package/dist/components/task/TaskStatusActions.d.ts +6 -1
  34. package/dist/components/task/TaskStatusActions.js +8 -3
  35. package/dist/components/task/TaskStatusActions.test.js +9 -0
  36. package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
  37. package/dist/components/task/taskKanbanDropRules.js +187 -0
  38. package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
  39. package/dist/components/task/taskKanbanDropRules.test.js +114 -0
  40. package/dist/components/ui/Modal.d.ts +1 -1
  41. package/dist/components/ui/Modal.js +1 -0
  42. package/dist/components/views/AppShellHeader.js +5 -1
  43. package/dist/components/views/AppShellHeader.test.js +4 -0
  44. package/dist/components/views/PlanComparisonPage.d.ts +3 -2
  45. package/dist/components/views/PlanComparisonPage.js +9 -10
  46. package/dist/components/views/PlanComparisonPage.test.js +55 -41
  47. package/dist/components/views/PlansPage.d.ts +5 -1
  48. package/dist/components/views/PlansPage.js +150 -81
  49. package/dist/components/views/PlansPage.test.js +367 -41
  50. package/dist/components/views/StandaloneLayout.js +135 -54
  51. package/dist/components/views/WidgetView.js +11 -2
  52. package/dist/components/views/panels/FilterToolbar.test.js +6 -4
  53. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
  54. package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
  55. package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
  56. package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
  57. package/dist/config/envSchema.js +1 -1
  58. package/dist/core/AiProfileService.d.ts +6 -1
  59. package/dist/core/AiProfileService.js +161 -16
  60. package/dist/core/AiProfileService.test.js +3 -1
  61. package/dist/core/AiProfiles.test.js +200 -0
  62. package/dist/core/AuthTokenService.test.d.ts +1 -0
  63. package/dist/core/AuthTokenService.test.js +78 -0
  64. package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
  65. package/dist/core/EntitlementsPolicy.test.js +75 -13
  66. package/dist/core/PlanEntitlementService.d.ts +9 -1
  67. package/dist/core/PlanEntitlementService.js +144 -19
  68. package/dist/core/PlanVersionPolicy.test.js +19 -26
  69. package/dist/core/SignupMonetizationSettings.test.js +75 -15
  70. package/dist/core/SystemAdmin.test.js +212 -56
  71. package/dist/core/TaskActivityService.d.ts +4 -1
  72. package/dist/core/TaskActivityService.js +45 -0
  73. package/dist/core/TaskAuditTimeline.test.js +52 -1
  74. package/dist/core/TaskTaxonomyValidation.test.js +53 -0
  75. package/dist/core/Taskforce.d.ts +18 -11
  76. package/dist/core/Taskforce.js +93 -12
  77. package/dist/core/WorkspacePlanMode.test.js +73 -8
  78. package/dist/core/shared.d.ts +2 -0
  79. package/dist/core/shared.js +11 -0
  80. package/dist/documentReviews/service.d.ts +1 -1
  81. package/dist/documentReviews/types.d.ts +1 -1
  82. package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
  83. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
  84. package/dist/hooks/sync/auth.d.ts +37 -0
  85. package/dist/hooks/sync/auth.js +63 -0
  86. package/dist/hooks/sync/bootstrap.d.ts +34 -0
  87. package/dist/hooks/sync/bootstrap.js +52 -0
  88. package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
  89. package/dist/hooks/sync/orchestratorShared.js +209 -0
  90. package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
  91. package/dist/hooks/sync/orchestratorShared.test.js +49 -0
  92. package/dist/hooks/sync/realtime.d.ts +25 -0
  93. package/dist/hooks/sync/realtime.js +60 -0
  94. package/dist/hooks/sync/recovery.d.ts +69 -0
  95. package/dist/hooks/sync/recovery.js +118 -0
  96. package/dist/hooks/sync/transfers.d.ts +123 -0
  97. package/dist/hooks/sync/transfers.js +435 -0
  98. package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
  99. package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
  100. package/dist/hooks/ui/useResourceExport.d.ts +3 -1
  101. package/dist/hooks/ui/useResourceExport.js +5 -2
  102. package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
  103. package/dist/hooks/useRealtimeSync.js +2 -1
  104. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
  105. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
  106. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
  107. package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
  108. package/dist/hooks/useSyncOrchestrator.js +388 -879
  109. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
  110. package/dist/hooks/useTaskMutations.d.ts +2 -1
  111. package/dist/hooks/useTaskMutations.js +57 -7
  112. package/dist/hooks/useTaskMutations.test.js +176 -0
  113. package/dist/hooks/useTaskforce.d.ts +13 -4
  114. package/dist/hooks/useTaskforce.js +302 -817
  115. package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
  116. package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
  117. package/dist/hooks/useWorkspaceSyncController.js +33 -11
  118. package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
  119. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
  120. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
  121. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
  122. package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
  123. package/dist/hooks/workspace/workspaceLocalState.js +100 -0
  124. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
  125. package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
  126. package/dist/localization/locales/en-US.js +2 -2
  127. package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
  128. package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
  129. package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
  130. package/dist/mcp/canonicalAssetHelpers.js +259 -0
  131. package/dist/mcp/clientIntegrations.d.ts +12 -1
  132. package/dist/mcp/clientIntegrations.js +161 -6
  133. package/dist/mcp/clientIntegrations.test.js +76 -4
  134. package/dist/mcp/collaborationRegistrar.d.ts +2 -0
  135. package/dist/mcp/collaborationRegistrar.js +71 -0
  136. package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
  137. package/dist/mcp/documentAssetRegistrar.js +346 -0
  138. package/dist/mcp/documentHelpers.d.ts +100 -0
  139. package/dist/mcp/documentHelpers.js +161 -0
  140. package/dist/mcp/runtime.js +2455 -3925
  141. package/dist/mcp/runtime.test.js +240 -8
  142. package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
  143. package/dist/mcp/taskAttachmentHelpers.js +60 -0
  144. package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
  145. package/dist/mcp/taskAttachmentTypes.js +1 -0
  146. package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
  147. package/dist/mcp/taskPlanningRegistrar.js +418 -0
  148. package/dist/mcp/toolCatalog.d.ts +35 -0
  149. package/dist/mcp/toolCatalog.js +3 -0
  150. package/dist/plugins/vite.js +1 -1
  151. package/dist/runtime/appGateDefinitions.d.ts +1 -1
  152. package/dist/runtime/appGateDefinitions.js +1 -1
  153. package/dist/server/index.d.ts +1 -0
  154. package/dist/server/index.js +46 -3
  155. package/dist/server/index.test.js +12 -1
  156. package/dist/server/routes/admin.d.ts +1 -5
  157. package/dist/server/routes/admin.js +151 -80
  158. package/dist/server/routes/annotatedAttachments.d.ts +1 -1
  159. package/dist/server/routes/annotatedAttachments.js +1 -1
  160. package/dist/server/routes/auth.d.ts +1 -4
  161. package/dist/server/routes/auth.js +59 -64
  162. package/dist/server/routes/authSupport.d.ts +30 -0
  163. package/dist/server/routes/authSupport.js +81 -0
  164. package/dist/server/routes/billing.d.ts +1 -1
  165. package/dist/server/routes/billing.js +122 -255
  166. package/dist/server/routes/billing.test.js +257 -86
  167. package/dist/server/routes/documentReviews.d.ts +1 -1
  168. package/dist/server/routes/documentReviews.js +1 -1
  169. package/dist/server/routes/documents.d.ts +4 -2
  170. package/dist/server/routes/documents.js +73 -7
  171. package/dist/server/routes/primitives.d.ts +11 -0
  172. package/dist/server/routes/primitives.js +73 -0
  173. package/dist/server/routes/resources.d.ts +1 -1
  174. package/dist/server/routes/resources.js +1 -1
  175. package/dist/server/routes/shared.d.ts +31 -1
  176. package/dist/server/routes/shared.js +26 -0
  177. package/dist/server/routes/sync.d.ts +1 -1
  178. package/dist/server/routes/sync.integration.test.js +20 -0
  179. package/dist/server/routes/sync.js +111 -238
  180. package/dist/server/routes/tasks.d.ts +1 -1
  181. package/dist/server/routes/tasks.js +50 -8
  182. package/dist/server/routes/workspaces.d.ts +1 -1
  183. package/dist/server/routes/workspaces.js +1 -1
  184. package/dist/server/routes.d.ts +2 -9
  185. package/dist/server/routes.js +84 -181
  186. package/dist/server/routes.test.js +912 -32
  187. package/dist/shared/taskActor.d.ts +10 -0
  188. package/dist/shared/taskActor.js +1 -0
  189. package/dist/sync/collaborationSyncPayload.js +4 -0
  190. package/dist/sync/collaborationSyncPayload.test.js +8 -0
  191. package/dist/sync/collaborationSyncState.d.ts +3 -1
  192. package/dist/sync/collaborationSyncState.js +19 -0
  193. package/dist/sync/contentSyncState.js +28 -17
  194. package/dist/sync/syncApplyHandlers.d.ts +87 -1
  195. package/dist/sync/syncApplyHandlers.js +370 -8
  196. package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
  197. package/dist/sync/syncApplyHandlers.test.js +379 -0
  198. package/dist/sync/syncService.d.ts +9 -1
  199. package/dist/sync/syncService.js +44 -2
  200. package/dist/sync/syncService.test.js +62 -0
  201. package/dist/sync/workspacePullFeed.d.ts +1 -0
  202. package/dist/sync/workspacePullFeed.js +66 -59
  203. package/dist/sync/workspacePullFeed.test.js +72 -4
  204. package/dist/sync/workspaceSyncModel.d.ts +25 -1
  205. package/dist/sync/workspaceSyncModel.js +77 -3
  206. package/dist/sync/workspaceSyncModel.test.js +153 -0
  207. package/dist/sync/workspaceSyncState.d.ts +2 -0
  208. package/dist/sync/workspaceSyncState.js +1 -0
  209. package/dist/types.d.ts +2 -10
  210. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
  211. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
  212. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
  213. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
  214. package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
  215. package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
  216. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
  217. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
  218. package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
  219. package/dist/ui/assets/index-C2-OXZV7.css +1 -0
  220. package/dist/ui/assets/index-DcSI5F86.js +6 -0
  221. package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
  222. package/dist/ui/index.html +4 -4
  223. package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
  224. package/dist/utils/commercialLifecyclePresentation.js +196 -0
  225. package/dist/utils/taskActivity.js +4 -0
  226. package/dist/utils/taskActivity.test.js +25 -1
  227. package/package.json +10 -1
  228. package/scripts/check-cloud-mcp.mjs +83 -0
  229. package/scripts/playwright-auth.sh +9 -1
  230. package/scripts/run-billing-performance-smoke.sh +24 -0
  231. package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
  232. package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
  233. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
  234. package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
  235. package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
  236. package/dist/ui/assets/index-CLIMgR6g.js +0 -6
  237. package/dist/ui/assets/index-DneETxcu.css +0 -1
@@ -7,14 +7,14 @@
7
7
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" />
8
8
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" />
9
9
  <link rel="apple-touch-icon" href="/taskforce/favicon/apple-touch-icon.png" />
10
- <title>Taskforce Dashboard</title>
11
- <script type="module" crossorigin src="/taskforce/assets/index-CLIMgR6g.js"></script>
10
+ <title>Taskforce</title>
11
+ <script type="module" crossorigin src="/taskforce/assets/index-DcSI5F86.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="/taskforce/assets/vendor-react-CKJs5o3c.js">
13
- <link rel="modulepreload" crossorigin href="/taskforce/assets/vendor-icons-pWocEipT.js">
13
+ <link rel="modulepreload" crossorigin href="/taskforce/assets/vendor-icons-BSUaRwF8.js">
14
14
  <link rel="modulepreload" crossorigin href="/taskforce/assets/vendor-markdown-BUxTU7dS.js">
15
15
  <link rel="modulepreload" crossorigin href="/taskforce/assets/vendor-dnd-DRzYolkg.js">
16
16
  <link rel="modulepreload" crossorigin href="/taskforce/assets/vendor-router-BbWMxlnO.js">
17
- <link rel="stylesheet" crossorigin href="/taskforce/assets/index-DneETxcu.css">
17
+ <link rel="stylesheet" crossorigin href="/taskforce/assets/index-C2-OXZV7.css">
18
18
  </head>
19
19
  <body>
20
20
  <div id="root"></div>
@@ -0,0 +1,19 @@
1
+ import type { AccountProfileSummary } from './accountProfileSummaryCache';
2
+ export type CommercialLifecyclePrimaryAction = 'choose_plan' | 'open_plans' | 'manage_billing' | 'none';
3
+ export type CommercialLifecycleTone = 'success' | 'info' | 'error';
4
+ export interface CommercialLifecyclePresentation {
5
+ entitlementState: string | null;
6
+ gate: string | null;
7
+ isActive: boolean;
8
+ allowReturnToApp: boolean;
9
+ statusTone: CommercialLifecycleTone;
10
+ message: string | null;
11
+ primaryAction: CommercialLifecyclePrimaryAction;
12
+ primaryLabel: string | null;
13
+ showManageBilling: boolean;
14
+ markCurrentPlan: boolean;
15
+ }
16
+ type AccountProfileSummaryLike = Pick<AccountProfileSummary, 'billingConflictCode' | 'effectiveUntil' | 'entitlementState' | 'gate' | 'message' | 'planSelectionRequired' | 'stripeCustomerId' | 'stripeSubscriptionId' | 'trialEnd'> | null | undefined;
17
+ export declare function isCommercialEntitlementActive(raw: string | null | undefined): boolean;
18
+ export declare function resolveCommercialLifecyclePresentation(summary: AccountProfileSummaryLike, fallbackGate?: string | null): CommercialLifecyclePresentation;
19
+ export {};
@@ -0,0 +1,196 @@
1
+ const ACTIVE_ENTITLEMENT_STATES = new Set(['active', 'trialing', 'grace']);
2
+ const BILLING_CONFLICT_MESSAGES = {
3
+ CHECKOUT_SESSION_EXPIRED: 'Your previous checkout expired. Choose a plan to continue.',
4
+ CHECKOUT_SESSION_SUPERSEDED: 'A newer checkout replaced your previous checkout. Choose a plan to continue.'
5
+ };
6
+ const GATE_MESSAGES = {
7
+ planSelectionRequired: 'Choose a plan to continue.',
8
+ checkoutPending: 'Complete checkout to continue.',
9
+ misconfigured: 'Onboarding policy is misconfigured. Contact support or a system administrator.',
10
+ missingEntitlement: 'No entitlement is linked to this account.'
11
+ };
12
+ function normalizeString(value) {
13
+ return String(value || '').trim();
14
+ }
15
+ function normalizeEntitlementState(value) {
16
+ return normalizeString(value).toLowerCase();
17
+ }
18
+ function normalizeGate(value) {
19
+ return normalizeString(value).toLowerCase();
20
+ }
21
+ function hasBillingContext(summary) {
22
+ return Boolean(normalizeString(summary?.stripeCustomerId) || normalizeString(summary?.stripeSubscriptionId));
23
+ }
24
+ function resolveTrialMessage(summary) {
25
+ const trialEnd = normalizeString(summary?.trialEnd || summary?.effectiveUntil);
26
+ if (trialEnd) {
27
+ const formatted = new Date(trialEnd);
28
+ if (!Number.isNaN(formatted.getTime())) {
29
+ return `Your trial is active through ${formatted.toLocaleString()}.`;
30
+ }
31
+ }
32
+ return 'Your trial is active. Continue to Taskforce or manage billing anytime.';
33
+ }
34
+ export function isCommercialEntitlementActive(raw) {
35
+ return ACTIVE_ENTITLEMENT_STATES.has(normalizeEntitlementState(raw));
36
+ }
37
+ export function resolveCommercialLifecyclePresentation(summary, fallbackGate) {
38
+ const entitlementState = normalizeEntitlementState(summary?.entitlementState);
39
+ const gate = normalizeGate(summary?.gate || fallbackGate);
40
+ const billingConflictCode = normalizeString(summary?.billingConflictCode).toUpperCase();
41
+ const summaryMessage = normalizeString(summary?.message);
42
+ const isActive = isCommercialEntitlementActive(entitlementState);
43
+ const billingAvailable = hasBillingContext(summary);
44
+ if (billingConflictCode === 'CHECKOUT_SESSION_EXPIRED') {
45
+ return {
46
+ entitlementState: entitlementState || null,
47
+ gate: gate || null,
48
+ isActive: false,
49
+ allowReturnToApp: false,
50
+ statusTone: 'info',
51
+ message: BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_EXPIRED,
52
+ primaryAction: 'choose_plan',
53
+ primaryLabel: 'Choose Plan',
54
+ showManageBilling: billingAvailable,
55
+ markCurrentPlan: false,
56
+ };
57
+ }
58
+ if (billingConflictCode === 'CHECKOUT_SESSION_SUPERSEDED') {
59
+ return {
60
+ entitlementState: entitlementState || null,
61
+ gate: gate || null,
62
+ isActive: false,
63
+ allowReturnToApp: false,
64
+ statusTone: 'info',
65
+ message: BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_SUPERSEDED,
66
+ primaryAction: 'choose_plan',
67
+ primaryLabel: 'Choose Plan',
68
+ showManageBilling: billingAvailable,
69
+ markCurrentPlan: false,
70
+ };
71
+ }
72
+ if (entitlementState === 'trialing') {
73
+ return {
74
+ entitlementState,
75
+ gate: gate || null,
76
+ isActive: true,
77
+ allowReturnToApp: true,
78
+ statusTone: 'success',
79
+ message: resolveTrialMessage(summary),
80
+ primaryAction: billingAvailable ? 'manage_billing' : 'none',
81
+ primaryLabel: billingAvailable ? 'Manage Billing' : null,
82
+ showManageBilling: billingAvailable,
83
+ markCurrentPlan: true,
84
+ };
85
+ }
86
+ if (isActive) {
87
+ return {
88
+ entitlementState: entitlementState || null,
89
+ gate: gate || null,
90
+ isActive: true,
91
+ allowReturnToApp: true,
92
+ statusTone: 'success',
93
+ message: summaryMessage || null,
94
+ primaryAction: 'none',
95
+ primaryLabel: null,
96
+ showManageBilling: billingAvailable,
97
+ markCurrentPlan: true,
98
+ };
99
+ }
100
+ if (entitlementState === 'suspended') {
101
+ return {
102
+ entitlementState,
103
+ gate: gate || null,
104
+ isActive: false,
105
+ allowReturnToApp: false,
106
+ statusTone: 'error',
107
+ message: 'Your access is suspended because billing needs attention. Update billing to restore access.',
108
+ primaryAction: billingAvailable ? 'manage_billing' : 'open_plans',
109
+ primaryLabel: billingAvailable ? 'Manage Billing' : 'Open Plans',
110
+ showManageBilling: billingAvailable,
111
+ markCurrentPlan: false,
112
+ };
113
+ }
114
+ if (entitlementState === 'canceled') {
115
+ return {
116
+ entitlementState,
117
+ gate: gate || null,
118
+ isActive: false,
119
+ allowReturnToApp: false,
120
+ statusTone: 'error',
121
+ message: 'Your access has ended. Choose a plan to reactivate your workspace.',
122
+ primaryAction: 'choose_plan',
123
+ primaryLabel: 'Choose Plan',
124
+ showManageBilling: billingAvailable,
125
+ markCurrentPlan: false,
126
+ };
127
+ }
128
+ if (entitlementState === 'pending_plan_selection' || summary?.planSelectionRequired === true || gate === 'plan_selection_required') {
129
+ return {
130
+ entitlementState: entitlementState || 'pending_plan_selection',
131
+ gate: gate || 'plan_selection_required',
132
+ isActive: false,
133
+ allowReturnToApp: false,
134
+ statusTone: 'info',
135
+ message: summaryMessage || GATE_MESSAGES.planSelectionRequired,
136
+ primaryAction: 'choose_plan',
137
+ primaryLabel: 'Choose Plan',
138
+ showManageBilling: false,
139
+ markCurrentPlan: false,
140
+ };
141
+ }
142
+ if (entitlementState === 'checkout_pending' || gate === 'checkout_pending') {
143
+ return {
144
+ entitlementState: entitlementState || 'checkout_pending',
145
+ gate: gate || 'checkout_pending',
146
+ isActive: false,
147
+ allowReturnToApp: false,
148
+ statusTone: 'info',
149
+ message: summaryMessage || GATE_MESSAGES.checkoutPending,
150
+ primaryAction: 'open_plans',
151
+ primaryLabel: 'Open Plans',
152
+ showManageBilling: billingAvailable,
153
+ markCurrentPlan: false,
154
+ };
155
+ }
156
+ if (gate === 'misconfigured') {
157
+ return {
158
+ entitlementState: entitlementState || null,
159
+ gate,
160
+ isActive: false,
161
+ allowReturnToApp: false,
162
+ statusTone: 'error',
163
+ message: GATE_MESSAGES.misconfigured,
164
+ primaryAction: 'none',
165
+ primaryLabel: null,
166
+ showManageBilling: false,
167
+ markCurrentPlan: false,
168
+ };
169
+ }
170
+ if (gate === 'missing_entitlement') {
171
+ return {
172
+ entitlementState: entitlementState || null,
173
+ gate,
174
+ isActive: false,
175
+ allowReturnToApp: false,
176
+ statusTone: 'error',
177
+ message: GATE_MESSAGES.missingEntitlement,
178
+ primaryAction: 'choose_plan',
179
+ primaryLabel: 'Choose Plan',
180
+ showManageBilling: false,
181
+ markCurrentPlan: false,
182
+ };
183
+ }
184
+ return {
185
+ entitlementState: entitlementState || null,
186
+ gate: gate || null,
187
+ isActive: false,
188
+ allowReturnToApp: false,
189
+ statusTone: summaryMessage ? 'error' : 'info',
190
+ message: summaryMessage || null,
191
+ primaryAction: 'none',
192
+ primaryLabel: null,
193
+ showManageBilling: billingAvailable,
194
+ markCurrentPlan: false,
195
+ };
196
+ }
@@ -5,6 +5,10 @@ function getActivityFieldLabel(field) {
5
5
  return 'DESCRIPTION';
6
6
  if (field === 'assignee')
7
7
  return 'ASSIGNED TO';
8
+ if (field === 'workstreamId')
9
+ return 'WORKSTREAM';
10
+ if (field === 'initiativeId')
11
+ return 'INITIATIVE';
8
12
  return field;
9
13
  }
10
14
  function formatValue(value, field, formatter) {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { summarizeTaskChange } from './taskActivity';
2
+ import { summarizeTaskChange, summarizeTaskEvent } from './taskActivity';
3
3
  describe('summarizeTaskChange', () => {
4
4
  it('summarizes title edits without dumping before and after text', () => {
5
5
  expect(summarizeTaskChange('title', {
@@ -58,4 +58,28 @@ describe('summarizeTaskChange', () => {
58
58
  ],
59
59
  })).toBe('Checklist reordered');
60
60
  });
61
+ it('uses readable labels for workstream relationship changes', () => {
62
+ expect(summarizeTaskChange('workstreamId', { from: null, to: 'workstream-1' }, (_field, value) => value === 'workstream-1' ? 'WS-12' : undefined)).toBe('WORKSTREAM: none -> WS-12');
63
+ });
64
+ });
65
+ describe('summarizeTaskEvent', () => {
66
+ it('uses readable labels for task relationship events', () => {
67
+ expect(summarizeTaskEvent({
68
+ id: 'event-1',
69
+ taskId: 'task-1',
70
+ workspaceId: 'default',
71
+ action: 'task-relationship-changed',
72
+ actor: 'user',
73
+ actorType: 'human',
74
+ createdAt: '2026-04-12T21:42:00.000Z',
75
+ details: {
76
+ changes: {
77
+ workstreamId: {
78
+ from: null,
79
+ to: 'workstream-1',
80
+ },
81
+ },
82
+ },
83
+ }, (_field, value) => value === 'workstream-1' ? 'WS-12' : undefined)).toBe('Workstream: none -> WS-12');
84
+ });
61
85
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskforcehq/taskforce",
3
- "version": "0.3.303",
3
+ "version": "0.3.305",
4
4
  "description": "Mission Command center for AI-powered task management (beta - under active development)",
5
5
  "author": "Taskforce HQ <hello@taskforcehq.ai> (https://taskforcehq.ai)",
6
6
  "license": "MIT",
@@ -76,11 +76,20 @@
76
76
  "test:e2e:realtime:attachments": "npm run test:e2e:realtime -- tests/e2e/attachment-sync.spec.ts",
77
77
  "test:e2e:realtime:multiclient": "npm run test:e2e:realtime -- tests/e2e/realtime-multiclient.spec.ts",
78
78
  "test:e2e:realtime:reconnect": "npm run test:e2e:realtime -- tests/e2e/realtime-reconnect.spec.ts",
79
+ "test:e2e:onboarding": "npx playwright test -c playwright.e2e.config.ts tests/e2e/auth-runtime.spec.ts tests/e2e/billing-runtime.spec.ts",
80
+ "test:billing:deterministic": "npx vitest run src/server/routes/billing.test.ts src/components/views/PlansPage.test.tsx src/components/views/PlanComparisonPage.test.tsx src/migrations/billingSchemaParity.test.ts",
81
+ "test:billing:runtime:mock": "npx playwright test -c playwright.e2e.config.ts tests/e2e/billing-runtime.spec.ts",
82
+ "test:billing:local": "npm run test:billing:deterministic && npm run test:billing:runtime:mock",
83
+ "test:billing:performance:smoke": "sh scripts/run-billing-performance-smoke.sh",
84
+ "test:billing:performance:smoke:live": "TASKFORCE_BILLING_ENABLE_LIVE_PURCHASE=1 sh scripts/run-billing-performance-smoke.sh",
79
85
  "playwright:auth:bootstrap": "node scripts/bootstrap-playwright-auth.mjs",
80
86
  "playwright:auth": "sh scripts/playwright-auth.sh",
87
+ "playwright:interactive": "playwright test -c playwright.interactive.config.ts tests/e2e/interactive.smoke.spec.ts",
88
+ "playwright:interactive:headed": "playwright test --headed -c playwright.interactive.config.ts tests/e2e/interactive.smoke.spec.ts",
81
89
  "qa:open": "node scripts/qa-open.mjs",
82
90
  "check:env": "node --import tsx scripts/check-env.ts --target=server --strict=true",
83
91
  "check:env:soak": "node --import tsx scripts/check-env.ts --target=soak --strict=true",
92
+ "check:mcp-smoke": "node scripts/check-cloud-mcp.mjs",
84
93
  "check:compat": "node scripts/check-compat-boundaries.mjs",
85
94
  "gen:env-example": "node --import tsx scripts/generate-env-example.ts",
86
95
  "check:sync-smoke-preflight": "node --import tsx src/soak/preflight.cli.ts",
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ const DEFAULT_ENDPOINTS = {
4
+ production: 'https://mcp.taskforcehq.ai/mcp',
5
+ staging: 'https://staging-mcp.taskforcehq.ai/mcp',
6
+ performance: 'https://performance-mcp.taskforcehq.ai/mcp'
7
+ };
8
+
9
+ const REQUEST_TIMEOUT_MS = 10000;
10
+
11
+ function normalizeTargets(argv) {
12
+ const values = argv.length > 0 ? argv : Object.keys(DEFAULT_ENDPOINTS);
13
+ return values.map((value) => {
14
+ const normalized = String(value || '').trim().toLowerCase();
15
+ if (DEFAULT_ENDPOINTS[normalized]) {
16
+ return {
17
+ label: normalized,
18
+ url: DEFAULT_ENDPOINTS[normalized]
19
+ };
20
+ }
21
+ return {
22
+ label: normalized || 'custom',
23
+ url: String(value || '').trim()
24
+ };
25
+ });
26
+ }
27
+
28
+ async function checkEndpoint(target) {
29
+ const controller = new AbortController();
30
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
31
+ try {
32
+ const response = await fetch(target.url, {
33
+ method: 'GET',
34
+ headers: {
35
+ Accept: 'application/json'
36
+ },
37
+ signal: controller.signal
38
+ });
39
+ const contentType = String(response.headers.get('content-type') || '');
40
+ const responseText = await response.text();
41
+ let payload = null;
42
+ try {
43
+ payload = responseText ? JSON.parse(responseText) : null;
44
+ } catch {
45
+ payload = null;
46
+ }
47
+ const message = typeof payload?.error?.message === 'string'
48
+ ? payload.error.message.trim()
49
+ : '';
50
+
51
+ if (response.status !== 401) {
52
+ throw new Error(`expected HTTP 401 but received ${response.status}`);
53
+ }
54
+ if (!contentType.includes('application/json')) {
55
+ throw new Error(`expected application/json response but received ${contentType || 'unknown content-type'}`);
56
+ }
57
+ if (message !== 'Unauthorized.') {
58
+ throw new Error(`expected MCP Unauthorized. payload but received message=${JSON.stringify(message || responseText.slice(0, 200))}`);
59
+ }
60
+
61
+ console.log(`[mcp-smoke] ${target.label} ok ${target.url} -> 401 Unauthorized.`);
62
+ } catch (error) {
63
+ const message = error instanceof Error ? error.message : String(error);
64
+ throw new Error(`[mcp-smoke] ${target.label} failed ${target.url}: ${message}`);
65
+ } finally {
66
+ clearTimeout(timeout);
67
+ }
68
+ }
69
+
70
+ async function main() {
71
+ const targets = normalizeTargets(process.argv.slice(2));
72
+ for (const target of targets) {
73
+ if (!target.url) {
74
+ throw new Error(`[mcp-smoke] ${target.label} has no URL to check.`);
75
+ }
76
+ await checkEndpoint(target);
77
+ }
78
+ }
79
+
80
+ main().catch((error) => {
81
+ console.error(error instanceof Error ? error.message : String(error));
82
+ process.exitCode = 1;
83
+ });
@@ -1,11 +1,19 @@
1
1
  #!/usr/bin/env sh
2
2
  set -eu
3
3
 
4
- LOCAL_ENV_FILE="${TASKFORCE_PLAYWRIGHT_ENV_FILE:-/Users/rossburke/.codex/taskforce-playwright.env}"
4
+ LOCAL_ENV_FILE="${TASKFORCE_PLAYWRIGHT_ENV_FILE:-$HOME/.codex/taskforce-playwright.env}"
5
+ REPO_E2E_ENV_FILE="${TASKFORCE_PLAYWRIGHT_REPO_E2E_ENV_FILE:-.env.e2e}"
5
6
 
6
7
  if [ -f "$LOCAL_ENV_FILE" ]; then
8
+ set -a
7
9
  # shellcheck disable=SC1090
8
10
  . "$LOCAL_ENV_FILE"
11
+ set +a
12
+ elif [ -f "$REPO_E2E_ENV_FILE" ]; then
13
+ set -a
14
+ # shellcheck disable=SC1090
15
+ . "$REPO_E2E_ENV_FILE"
16
+ set +a
9
17
  fi
10
18
 
11
19
  exec node scripts/bootstrap-playwright-auth.mjs "$@"
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env sh
2
+ set -eu
3
+
4
+ LOCAL_ENV_FILE="${TASKFORCE_PLAYWRIGHT_ENV_FILE:-$HOME/.codex/taskforce-playwright.env}"
5
+ REPO_E2E_ENV_FILE="${TASKFORCE_PLAYWRIGHT_REPO_E2E_ENV_FILE:-.env.e2e}"
6
+
7
+ if [ -f "$LOCAL_ENV_FILE" ]; then
8
+ set -a
9
+ # shellcheck disable=SC1090
10
+ . "$LOCAL_ENV_FILE"
11
+ set +a
12
+ elif [ -f "$REPO_E2E_ENV_FILE" ]; then
13
+ set -a
14
+ # shellcheck disable=SC1090
15
+ . "$REPO_E2E_ENV_FILE"
16
+ set +a
17
+ fi
18
+
19
+ TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL="${TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL:-https://performance-app.taskforcehq.ai}"
20
+ export TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL
21
+ export TASKFORCE_PLAYWRIGHT_BASE_URL="${TASKFORCE_PLAYWRIGHT_BASE_URL:-$TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL}"
22
+
23
+ node scripts/bootstrap-playwright-auth.mjs
24
+ exec npx playwright test -c playwright.interactive.config.ts tests/e2e/billing-performance.smoke.spec.ts "$@"
@@ -1 +0,0 @@
1
- import{r as n,j as e}from"./vendor-react-CKJs5o3c.js";import{p as T,u as C,v as w,w as S,t as s,x as M}from"./index-CLIMgR6g.js";import{h as L,B as A,k as I,a9 as H,aH as B,aI as D,b as R}from"./vendor-icons-pWocEipT.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function F({workspaceId:h}){const[x,k]=n.useState([]),[j,y]=n.useState(!1),[c,P]=n.useState(null),[p,m]=n.useState(null),[d,g]=n.useState(null),N=n.useCallback(async()=>{if(h){y(!0);try{const a=await(await fetch(`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(h)}`,{credentials:"include"})).json().catch(()=>({})),t=Array.isArray(a?.assignees)?a.assignees.filter(i=>i.kind==="agent").map(i=>({id:String(i.value||""),name:String(i.label||i.value||"Unknown Agent"),username:String(i.username||i.value||""),icon:String(i.icon||"Bot"),color:String(i.color||"#6B7280"),kind:String(i.kind||"agent"),description:typeof i.description=="string"?i.description:null,role:typeof i.role=="string"?i.role:null,provider:typeof i.provider=="string"?i.provider:null,model:typeof i.model=="string"?i.model:null,surfaceType:T(i.surfaceType),createdAt:String(i.createdAt||""),updatedAt:String(i.updatedAt||i.createdAt||""),lastActiveAt:typeof i.lastActiveAt=="string"?i.lastActiveAt:null})):[];k(t)}finally{y(!1)}}},[h]);n.useEffect(()=>{N()},[N]);const f=n.useMemo(()=>{const r=new Map;for(const a of x){const t=C(a.surfaceType);r.has(t)||r.set(t,new Map);const i=r.get(t),v=a.name.toLowerCase();i.has(v)||i.set(v,[]),i.get(v).push(a)}return w.map(a=>({section:a,label:S(a),groups:Array.from(r.get(a)?.entries()||[]).map(([t,i])=>({groupId:`${a}:${t}`,section:a,sectionLabel:S(a),profiles:i,primaryProfile:i[0]}))})).filter(a=>a.groups.length>0)},[x]),o=n.useMemo(()=>f.flatMap(r=>r.groups),[f]),l=n.useMemo(()=>o.find(r=>r.groupId===d)||o[0]||null,[o,d]);n.useEffect(()=>{if(!o.length){d!==null&&g(null);return}(!d||!o.some(r=>r.groupId===d))&&g(o[0].groupId)},[o,d]);const b=async(r,a)=>{m(null);try{const t=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:r,mergeId:a})}),i=await t.json().catch(()=>({}));if(!t.ok){m({type:"error",message:String(i?.error||"Failed to merge AI profiles.")});return}P(null),m({type:"success",message:"AI profiles merged."}),await N()}catch{m({type:"error",message:"Failed to merge AI profiles."})}},u=r=>{const a=String(r||"").trim();if(!a)return"Unknown";const t=Date.parse(a);return Number.isFinite(t)?new Date(t).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):a};return e.jsx("div",{className:`${s.standalonePage} ${s.standaloneContent}`,style:{overflow:"auto"},children:e.jsx("div",{className:s.settingsContent,style:{maxWidth:960,margin:"0 auto",width:"100%"},children:e.jsx("div",{className:s.settingGroup,children:e.jsxs("div",{children:[e.jsx("h4",{className:s.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${s.settingsHint} ${s.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost."}),j&&e.jsxs("div",{className:s.settingsHint,children:[e.jsx(L,{size:13,className:s.spinner})," Loading profiles…"]}),!j&&x.length===0&&e.jsx("div",{className:s.settingsHint,children:"No AI profiles registered yet."}),!j&&f.length>0&&e.jsxs("div",{className:s.aiProfilesExplorer,children:[e.jsx("div",{className:s.aiProfilesListPane,children:e.jsx("div",{className:s.aiProfilesList,children:f.map(r=>e.jsxs("div",{className:s.aiProfilesSection,children:[e.jsx("div",{className:s.aiProfilesSectionHeader,children:r.label}),r.groups.map(a=>{const t=l?.groupId===a.groupId,i=a.primaryProfile;return e.jsxs("button",{type:"button",className:`${s.aiProfileGroup} ${a.profiles.length>1?s.aiProfileGroupDuplicate:""} ${t?s.aiProfileGroupSelected:""}`,onClick:()=>g(a.groupId),"aria-pressed":t,children:[e.jsxs("div",{className:s.aiProfileGroupHeader,children:[e.jsx(A,{size:13,style:{color:i.color}}),e.jsx("span",{className:s.aiProfileName,children:i.name}),a.profiles.length>1&&e.jsxs("span",{className:s.aiProfileDuplicateBadge,children:[e.jsx(I,{size:11})," ",a.profiles.length," duplicates"]})]}),e.jsxs("div",{className:s.settingsHint,style:{marginTop:6},children:["@",i.username]}),(i.surfaceType||i.role||i.provider||i.model)&&e.jsxs("div",{className:s.aiProfileMetaRow,children:[i.surfaceType&&e.jsx("span",{className:s.aiProfileSurfaceBadge,children:M(i.surfaceType)}),i.role&&e.jsx("span",{children:i.role}),i.provider&&e.jsx("span",{children:i.provider}),i.model&&e.jsx("span",{children:i.model})]}),i.description&&e.jsx("div",{className:s.settingsHint,style:{marginTop:4},children:i.description})]},a.groupId)})]},r.section))})}),l&&e.jsx("div",{className:s.aiProfileDetailPane,children:e.jsxs("div",{className:s.aiProfileDetailCard,children:[e.jsxs("div",{className:s.aiProfileDetailHero,children:[e.jsx("div",{className:s.aiProfileDetailIcon,style:{color:l.primaryProfile.color},children:e.jsx(A,{size:18})}),e.jsxs("div",{className:s.aiProfileDetailHeading,children:[e.jsx("div",{className:s.aiProfileDetailTitleRow,children:e.jsx("h5",{className:s.aiProfileDetailTitle,children:l.primaryProfile.name})}),e.jsxs("div",{className:s.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:s.aiProfileDetailHandle,children:["@",l.primaryProfile.username]}),e.jsx("span",{className:s.aiProfileDetailSectionBadge,children:l.sectionLabel}),l.profiles.length>1&&e.jsxs("span",{className:s.aiProfileDetailLinkedCount,children:[l.profiles.length," linked"]})]})]})]}),l.primaryProfile.description&&e.jsx("div",{className:s.aiProfileDetailDescription,children:l.primaryProfile.description}),e.jsxs("div",{className:s.aiProfileDetailGrid,children:[e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(H,{size:12})," Role"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:l.primaryProfile.role||"Not set"})]}),e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(B,{size:12})," Provider"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:[l.primaryProfile.provider,l.primaryProfile.model].filter(Boolean).join(" · ")||"Not set"})]}),e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(D,{size:12})," Last active"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:u(l.primaryProfile.lastActiveAt)})]}),e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(D,{size:12})," Updated"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:u(l.primaryProfile.updatedAt)})]})]}),e.jsxs("div",{className:s.aiProfileInstanceSection,children:[e.jsxs("div",{className:s.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:s.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:s.aiProfileInstanceList,children:l.profiles.map(r=>e.jsxs("div",{className:s.aiProfileInstanceCard,children:[e.jsxs("div",{className:s.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:s.aiProfileInstanceName,children:["@",r.username]}),e.jsx("div",{className:s.aiProfileIdChip,children:r.id})]}),l.profiles.length>1&&c?.keepId===r.id&&e.jsx("span",{className:s.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:s.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",u(r.createdAt)]}),e.jsxs("span",{children:["Updated ",u(r.updatedAt)]})]}),l.profiles.length>1&&c?.keepId!==r.id&&e.jsx("button",{className:s.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const a=l.profiles.find(t=>t.id!==r.id)?.id;a&&P({keepId:r.id,mergeId:a})},children:"Keep this"})]},r.id))}),c&&l.profiles.some(r=>r.id===c.keepId)&&e.jsxs("div",{className:s.aiProfileMergeActions,children:[e.jsx("button",{className:s.dangerBtn,onClick:()=>{b(c.keepId,c.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:s.secondaryHeaderBtn,onClick:()=>P(null),children:"Cancel"})]})]})]})})]}),p&&e.jsxs("div",{className:`${p.type==="success"?s.successMessage:s.errorMessage} ${s.marginTop12}`,children:[p.type==="success"?e.jsx(R,{size:14}):e.jsx(I,{size:14}),p.message]})]})})})})}export{F as AgentsModule};