@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
@@ -348,6 +348,59 @@ describe('TaskforceCore taxonomy validation', () => {
348
348
  core.close();
349
349
  fs.rmSync(projectRoot, { recursive: true, force: true });
350
350
  });
351
+ it('migrates legacy config-backed taxonomy data once and no longer depends on config for runtime state', () => {
352
+ const projectRoot = makeTempProjectRoot();
353
+ fs.mkdirSync(path.join(projectRoot, '.taskforce'), { recursive: true });
354
+ const configPath = path.join(projectRoot, '.taskforce', 'config.json');
355
+ fs.writeFileSync(configPath, JSON.stringify({
356
+ categories: [{ value: 'legacy', label: 'Legacy' }],
357
+ types: [{ value: 'legacy-type', label: 'Legacy Type' }],
358
+ priorities: [{ value: 9, label: 'Legacy Priority', color: 'red-500', icon: 'AlertTriangle' }],
359
+ taxonomies: [{ id: 'legacy-taxonomy', label: 'Legacy Taxonomy', widgetType: 'select', isSystem: false, multiSelect: false, options: [] }]
360
+ }, null, 2));
361
+ const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
362
+ const migratedState = core.getTaxonomyState('default');
363
+ expect((migratedState.categories || []).map((category) => (typeof category === 'string' ? category : category.value))).toEqual(['default', 'legacy']);
364
+ expect((migratedState.types || []).map((type) => type.value)).toEqual(['legacy-type']);
365
+ expect((migratedState.priorities || []).map((priority) => priority.value)).toEqual([9]);
366
+ expect((migratedState.taxonomies || []).map((taxonomy) => taxonomy.id)).toEqual(['legacy-taxonomy']);
367
+ core.close();
368
+ fs.writeFileSync(configPath, JSON.stringify({ checklistDropdownEnabled: true }, null, 2));
369
+ const reopened = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
370
+ const persistedState = reopened.getTaxonomyState('default');
371
+ expect((persistedState.categories || []).map((category) => (typeof category === 'string' ? category : category.value))).toEqual(['default', 'legacy']);
372
+ expect((persistedState.types || []).map((type) => type.value)).toEqual(['legacy-type']);
373
+ expect((persistedState.priorities || []).map((priority) => priority.value)).toEqual([9]);
374
+ expect((persistedState.taxonomies || []).map((taxonomy) => taxonomy.id)).toEqual(['legacy-taxonomy']);
375
+ reopened.close();
376
+ fs.rmSync(projectRoot, { recursive: true, force: true });
377
+ });
378
+ it('does not seed new workspaces from legacy config-backed taxonomy definitions', () => {
379
+ const projectRoot = makeTempProjectRoot();
380
+ fs.mkdirSync(path.join(projectRoot, '.taskforce'), { recursive: true });
381
+ const configPath = path.join(projectRoot, '.taskforce', 'config.json');
382
+ fs.writeFileSync(configPath, JSON.stringify({
383
+ categories: [{ value: 'legacy', label: 'Legacy' }],
384
+ types: [{ value: 'legacy-type', label: 'Legacy Type' }],
385
+ priorities: [{ value: 9, label: 'Legacy Priority' }],
386
+ taxonomies: [{ id: 'legacy-taxonomy', label: 'Legacy Taxonomy' }]
387
+ }, null, 2));
388
+ const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
389
+ core.createWorkspace({ workspaceId: 'workspace-new', name: 'New Workspace' });
390
+ const state = core.getTaxonomyState('workspace-new');
391
+ expect((state.categories || []).map((category) => (typeof category === 'string' ? category : category.value))).toEqual(['default']);
392
+ expect((state.types || []).map((type) => type.value)).toEqual([
393
+ 'task',
394
+ 'deliverable',
395
+ 'issue',
396
+ 'idea',
397
+ 'review'
398
+ ]);
399
+ expect((state.priorities || []).map((priority) => priority.value)).toEqual([1, 2, 3]);
400
+ expect(state.taxonomies || []).toEqual([]);
401
+ core.close();
402
+ fs.rmSync(projectRoot, { recursive: true, force: true });
403
+ });
351
404
  it('preserves existing first-class taxonomy sections when taxonomy state updates are partial', () => {
352
405
  const projectRoot = makeTempProjectRoot();
353
406
  const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
@@ -3,6 +3,8 @@ import { WorkspaceAssetStore } from '../storage/workspaceAssetStore.js';
3
3
  import { type SyncEventInput } from './SyncReconciliationService.js';
4
4
  import { type FirstClassTaxonomyDisplayLabels, type WorkspaceTaxonomyState } from '../shared/taxonomyState.js';
5
5
  import { type AiProfileSurfaceType } from '../shared/aiProfileSurfaceType.js';
6
+ import type { TaskActor } from '../shared/taskActor.js';
7
+ export type { TaskActor } from '../shared/taskActor.js';
6
8
  export type { WorkspaceDeletedAssetRecord, WorkspaceDeleteSummary } from './types.js';
7
9
  import type { GlobalTaskforceSettings, UserGlobalSyncSettings, WorkspaceDeleteSummary } from './types.js';
8
10
  import type { AnnotatedAttachmentAnnotation, AnnotatedAttachmentPayload, AnnotatedAttachmentSession, AnnotatedAttachmentSessionRecord } from '../annotatedAttachments/types.js';
@@ -73,16 +75,6 @@ export interface DeletedTaskRecord {
73
75
  }
74
76
  export type TaskStatus = 'task' | 'on-hold' | 'in-progress' | 'review' | 'done' | 'cancelled';
75
77
  export type TaskAssignee = string;
76
- export interface TaskActor {
77
- id: string;
78
- kind: 'human' | 'ai';
79
- label: string;
80
- username: string;
81
- icon: string;
82
- color: string;
83
- isNamed: boolean;
84
- meta?: Record<string, unknown>;
85
- }
86
78
  export interface TaskItem {
87
79
  id: string;
88
80
  referenceNumber?: number | null;
@@ -755,6 +747,9 @@ export interface TaskforceSyncCapable {
755
747
  workspaceId?: string | null;
756
748
  includeDeleted?: boolean;
757
749
  }): AnnotatedAttachmentSessionRecord[];
750
+ listTaskEventsForSync(input: {
751
+ workspaceId?: string | null;
752
+ }): TaskEvent[];
758
753
  upsertAiProfileForSync(profile: AiProfileRecord): AiProfileRecord;
759
754
  replaceWorkspaceUsersForSync(input: {
760
755
  workspaceId: string;
@@ -765,6 +760,7 @@ export interface TaskforceSyncCapable {
765
760
  upsertDocumentReviewSessionForSync(session: DocumentReviewSessionRecord): DocumentReviewSessionRecord;
766
761
  upsertDocumentReviewCommentForSync(comment: DocumentReviewCommentSyncRecord): DocumentReviewCommentSyncRecord;
767
762
  upsertAnnotatedAttachmentSessionForSync(session: AnnotatedAttachmentSessionRecord): AnnotatedAttachmentSessionRecord;
763
+ upsertTaskEventForSync(event: TaskEvent): TaskEvent;
768
764
  deleteDocumentReviewSessionForSync(input: {
769
765
  workspaceId?: string | null;
770
766
  sessionId: string;
@@ -847,6 +843,7 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
847
843
  private areTaskEventValuesEqual;
848
844
  private normalizeTaskEventValue;
849
845
  private buildTaskUpdateChanges;
846
+ private filterAutosaveTaskEventChanges;
850
847
  private normalizeTaxonomyToken;
851
848
  private normalizeStatusValue;
852
849
  private sanitizeAssignee;
@@ -893,16 +890,18 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
893
890
  effectiveAt?: string | null;
894
891
  effectiveUntil?: string | null;
895
892
  billingMirrorPlanId?: string | null;
896
- source: 'registration' | 'checkout-start' | 'checkout-confirm' | 'stripe-webhook' | 'backfill' | 'legacy-plan-mode' | 'admin' | 'system';
893
+ source: 'registration' | 'invite-accept' | 'checkout-start' | 'checkout-confirm' | 'stripe-webhook' | 'backfill' | 'legacy-plan-mode' | 'admin' | 'system';
897
894
  updatedBy: string;
898
895
  createdAt?: string | null;
899
896
  updatedAt?: string | null;
900
897
  }): import("./types.js").AccountEntitlementSnapshot;
901
898
  private resolveWorkspaceLimitForUser;
902
899
  private resolveRegistrationOnboardingOutcome;
900
+ private ensureDefaultOnboardingEntitlement;
903
901
  createUserBillingRecord(userId: string, options?: {
904
902
  entitlementState?: string;
905
903
  planId?: string | null;
904
+ effectiveUntil?: string | null;
906
905
  }): void;
907
906
  getUserEntitlement(userId: string): {
908
907
  entitlementState: string;
@@ -912,6 +911,7 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
912
911
  getAccountAccessStatus(accountId: string): import("./types.js").AccountAccessStatus;
913
912
  getEffectivePlanVersion(accountId: string): import("./types.js").PlanVersionSnapshot;
914
913
  getAccountEntitlement(accountId: string): import("./types.js").AccountEntitlementSnapshot;
914
+ countAiProfileUsage(workspaceId: string): import("./types.js").SeatUsageSnapshot;
915
915
  countWorkspaceSeatUsage(workspaceId: string, options?: {
916
916
  includeInvited?: boolean;
917
917
  }): import("./types.js").SeatUsageSnapshot;
@@ -969,6 +969,8 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
969
969
  private resolveDefaultTypeValue;
970
970
  private getLegacyTaxonomiesConfig;
971
971
  private getLegacyPrioritiesConfig;
972
+ private readLegacyWorkspaceTaxonomyStateFromConfig;
973
+ private migrateLegacyWorkspaceTaxonomyStateIfNeeded;
972
974
  private buildEmergencyWorkspaceTaxonomyState;
973
975
  private getConfiguredDefaultStarterTaxonomyPackId;
974
976
  private getResolvedStarterWorkspaceTaxonomyState;
@@ -1268,6 +1270,7 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1268
1270
  upsertDocumentReviewSessionForSync(session: DocumentReviewSessionRecord): DocumentReviewSessionRecord;
1269
1271
  upsertDocumentReviewCommentForSync(comment: DocumentReviewCommentSyncRecord): DocumentReviewCommentSyncRecord;
1270
1272
  upsertAnnotatedAttachmentSessionForSync(session: AnnotatedAttachmentSessionRecord): AnnotatedAttachmentSessionRecord;
1273
+ upsertTaskEventForSync(event: TaskEvent): TaskEvent;
1271
1274
  deleteDocumentReviewSessionForSync(input: {
1272
1275
  workspaceId?: string | null;
1273
1276
  sessionId: string;
@@ -1668,6 +1671,9 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1668
1671
  updateWorkspaceUserRole(userId: string, workspaceId: string, role: AccessRole): boolean;
1669
1672
  getTask(id: string, workspaceId?: string): TaskItem | null;
1670
1673
  listTaskEvents(taskId: string, workspaceId?: string): TaskEvent[];
1674
+ listTaskEventsForSync(input: {
1675
+ workspaceId?: string | null;
1676
+ }): TaskEvent[];
1671
1677
  getScheduleBoard(weekKey: string, showWeekends?: boolean, workspaceId?: string): ScheduleBoardResult;
1672
1678
  updateTaskSchedule(id: string, input: {
1673
1679
  scheduledDate: string | null;
@@ -1699,6 +1705,7 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1699
1705
  syncUpdatedAt?: string;
1700
1706
  actorRef?: string;
1701
1707
  skipTaskEvents?: boolean;
1708
+ saveSource?: 'manual' | 'autosave';
1702
1709
  }): TaskItem | null;
1703
1710
  addComment(id: string, text: string, author: string, workspaceId?: string, options?: {
1704
1711
  persistBackup?: boolean;
@@ -9,7 +9,7 @@ import { WorkspaceAssetStore } from '../storage/workspaceAssetStore.js';
9
9
  import { AuthTokenService } from './AuthTokenService.js';
10
10
  import { getAiProfileByTokenService, listAiProfilesService, mergeAiProfilesService, resolveAiProfileService, upsertAiProfileForSyncService, } from './AiProfileService.js';
11
11
  import { syncCanonicalTaskAttachmentLinksService, } from './AttachmentLinkService.js';
12
- import { addTaskEventService, buildTaskActivityService, listTaskEventsInternalService, replaceTaskCommentsService, replaceTaskEventsService, } from './TaskActivityService.js';
12
+ import { addTaskEventService, buildTaskActivityService, listTaskEventsInternalService, listWorkspaceTaskEventsService, replaceTaskCommentsService, replaceTaskEventsService, upsertTaskEventForSyncService, } from './TaskActivityService.js';
13
13
  import { addDeletedTaskRecordService, getDeletedTaskService, listDeletedTasksService, mapDeletedTaskRowService, normalizeTaskSnapshotForRestoreService, pruneDeletedTasksService, purgeExpiredDeletedTasksService, } from './DeletedTaskLifecycleService.js';
14
14
  import { applyWorkspaceSyncSnapshotService, deleteDocumentWatermarkService, getDocumentWatermarkService, listTasksForSyncService, listWorkspaceSyncDeletesSinceService, readPushIdempotencyService, recordSyncEventService, upsertDocumentWatermarkService, writePushIdempotencyService, } from './SyncReconciliationService.js';
15
15
  import { getGlobalSettingsService, hasPersistedGlobalSettingsService, readGlobalConfigService, updateGlobalSettingsService, writeGlobalConfigService, } from './GlobalSettingsService.js';
@@ -79,7 +79,7 @@ const DEFAULT_PLAN_FEATURE_CATALOG = [
79
79
  featureKey: DOCUMENT_CLOUD_STORAGE_FEATURE_KEY,
80
80
  label: 'Document Cloud Storage',
81
81
  description: 'Allow cloud document storage with an optional storage allowance in MB.',
82
- allowedAccessModes: ['enabled', 'disabled'],
82
+ allowedAccessModes: ['enabled', 'limited', 'disabled'],
83
83
  configTemplate: {
84
84
  [DOCUMENT_CLOUD_STORAGE_LIMIT_CONFIG_KEY]: 1024
85
85
  }
@@ -666,6 +666,9 @@ export class TaskforceCore {
666
666
  }
667
667
  return changes;
668
668
  }
669
+ filterAutosaveTaskEventChanges(changes) {
670
+ return Object.fromEntries(Object.entries(changes).filter(([field]) => field !== 'title' && field !== 'description'));
671
+ }
669
672
  normalizeTaxonomyToken(value) {
670
673
  return String(value || '')
671
674
  .trim()
@@ -1217,6 +1220,29 @@ export class TaskforceCore {
1217
1220
  resolveRegistrationOnboardingOutcome(selectedPlanVersionIdRaw) {
1218
1221
  return this.planEntitlementService.resolveRegistrationOnboardingOutcome(selectedPlanVersionIdRaw);
1219
1222
  }
1223
+ ensureDefaultOnboardingEntitlement(accountIdRaw, options) {
1224
+ const accountId = String(accountIdRaw || '').trim();
1225
+ if (!accountId) {
1226
+ throw new TaskforceRuleError('accountId is required', 400, 'ACCOUNT_ID_REQUIRED');
1227
+ }
1228
+ if (this.hasAccountEntitlement(accountId)) {
1229
+ return this.getAccountEntitlement(accountId);
1230
+ }
1231
+ const now = options?.now || new Date().toISOString();
1232
+ const onboardingOutcome = this.resolveRegistrationOnboardingOutcome();
1233
+ this.transitionCanonicalCommercialState({
1234
+ accountId,
1235
+ planVersionId: onboardingOutcome.planVersionId,
1236
+ state: onboardingOutcome.commercialState,
1237
+ effectiveAt: now,
1238
+ billingMirrorPlanId: onboardingOutcome.planId,
1239
+ source: options?.source || 'registration',
1240
+ updatedBy: options?.updatedBy || 'registration',
1241
+ createdAt: now,
1242
+ updatedAt: now
1243
+ });
1244
+ return this.getAccountEntitlement(accountId);
1245
+ }
1220
1246
  createUserBillingRecord(userId, options) {
1221
1247
  return this.planEntitlementService.createUserBillingRecord(userId, options);
1222
1248
  }
@@ -1235,6 +1261,9 @@ export class TaskforceCore {
1235
1261
  getAccountEntitlement(accountId) {
1236
1262
  return this.planEntitlementService.getAccountEntitlement(accountId);
1237
1263
  }
1264
+ countAiProfileUsage(workspaceId) {
1265
+ return this.planEntitlementService.countAiProfileUsage(workspaceId);
1266
+ }
1238
1267
  countWorkspaceSeatUsage(workspaceId, options = {}) {
1239
1268
  return this.planEntitlementService.countWorkspaceSeatUsage(workspaceId, options);
1240
1269
  }
@@ -2388,6 +2417,42 @@ export class TaskforceCore {
2388
2417
  }
2389
2418
  return [];
2390
2419
  }
2420
+ readLegacyWorkspaceTaxonomyStateFromConfig() {
2421
+ const legacyCategories = this.getLegacyCategoriesConfig();
2422
+ const legacyTypes = this.getLegacyTypesConfig();
2423
+ const legacyPriorities = this.getLegacyPrioritiesConfig();
2424
+ const legacyTaxonomies = this.getLegacyTaxonomiesConfig();
2425
+ if (legacyCategories.length === 0
2426
+ && legacyTypes.length === 0
2427
+ && legacyPriorities.length === 0
2428
+ && legacyTaxonomies.length === 0) {
2429
+ return null;
2430
+ }
2431
+ return normalizeWorkspaceTaxonomyStatePayload({
2432
+ categories: legacyCategories,
2433
+ types: legacyTypes,
2434
+ priorities: legacyPriorities,
2435
+ taxonomies: legacyTaxonomies
2436
+ });
2437
+ }
2438
+ migrateLegacyWorkspaceTaxonomyStateIfNeeded(workspaceId) {
2439
+ const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
2440
+ if (normalizedWorkspaceId !== 'default') {
2441
+ return null;
2442
+ }
2443
+ const stored = this.readStoredWorkspaceTaxonomyState(normalizedWorkspaceId);
2444
+ if (Object.keys(stored.state).length > 0) {
2445
+ return stored;
2446
+ }
2447
+ const legacyState = this.readLegacyWorkspaceTaxonomyStateFromConfig();
2448
+ if (!legacyState)
2449
+ return null;
2450
+ const persisted = this.writeWorkspaceTaxonomyState(legacyState, normalizedWorkspaceId);
2451
+ return {
2452
+ state: persisted.state,
2453
+ updatedAt: persisted.updatedAt
2454
+ };
2455
+ }
2391
2456
  buildEmergencyWorkspaceTaxonomyState() {
2392
2457
  const emergencyPack = getEmergencyStarterTaxonomyLibraryPack();
2393
2458
  return {
@@ -2428,17 +2493,13 @@ export class TaskforceCore {
2428
2493
  }
2429
2494
  getEffectiveWorkspaceTaxonomyState(workspaceId) {
2430
2495
  const { state } = this.readStoredWorkspaceTaxonomyState(workspaceId);
2431
- const legacyCategories = this.getLegacyCategoriesConfig();
2432
- const legacyTypes = this.getLegacyTypesConfig();
2433
- const legacyPriorities = this.getLegacyPrioritiesConfig();
2434
- const legacyTaxonomies = this.getLegacyTaxonomiesConfig();
2435
2496
  const starterState = this.getResolvedStarterWorkspaceTaxonomyState();
2436
2497
  const emergencyState = this.buildEmergencyWorkspaceTaxonomyState();
2437
2498
  return normalizeWorkspaceTaxonomyStatePayload({
2438
- categories: state.categories || (legacyCategories.length > 0 ? legacyCategories : starterState.categories) || emergencyState.categories,
2439
- types: state.types || (legacyTypes.length > 0 ? legacyTypes : starterState.types) || emergencyState.types,
2440
- priorities: state.priorities || (legacyPriorities.length > 0 ? legacyPriorities : starterState.priorities) || emergencyState.priorities,
2441
- taxonomies: state.taxonomies || (legacyTaxonomies.length > 0 ? legacyTaxonomies : starterState.taxonomies) || emergencyState.taxonomies
2499
+ categories: state.categories || starterState.categories || emergencyState.categories,
2500
+ types: state.types || starterState.types || emergencyState.types,
2501
+ priorities: state.priorities || starterState.priorities || emergencyState.priorities,
2502
+ taxonomies: state.taxonomies || starterState.taxonomies || emergencyState.taxonomies
2442
2503
  });
2443
2504
  }
2444
2505
  ensureWorkspaceTaxonomyStateSeeded(workspaceId) {
@@ -2447,6 +2508,10 @@ export class TaskforceCore {
2447
2508
  if (Object.keys(stored.state).length > 0) {
2448
2509
  return stored;
2449
2510
  }
2511
+ const migratedLegacy = this.migrateLegacyWorkspaceTaxonomyStateIfNeeded(normalizedWorkspaceId);
2512
+ if (migratedLegacy) {
2513
+ return migratedLegacy;
2514
+ }
2450
2515
  const seededState = this.getEffectiveWorkspaceTaxonomyState(normalizedWorkspaceId);
2451
2516
  const persisted = this.writeWorkspaceTaxonomyState(seededState, normalizedWorkspaceId);
2452
2517
  return {
@@ -4841,6 +4906,9 @@ export class TaskforceCore {
4841
4906
  deletedAt: session.deletedAt,
4842
4907
  });
4843
4908
  }
4909
+ upsertTaskEventForSync(event) {
4910
+ return upsertTaskEventForSyncService(this.getTaskActivityServiceDeps(), event);
4911
+ }
4844
4912
  deleteDocumentReviewSessionForSync(input) {
4845
4913
  const deps = this.getDocumentReviewServiceDeps();
4846
4914
  return deps.sessionStore.softDelete(input.sessionId, deps.normalizeWorkspaceId(input.workspaceId), input.deletedAt || undefined);
@@ -4883,7 +4951,8 @@ export class TaskforceCore {
4883
4951
  aiProfileRowToRecord: (row) => this.aiProfileRowToRecord(row),
4884
4952
  generateEntityId: (prefix) => this.generateEntityId(prefix),
4885
4953
  issueProfileToken: () => `tfp_${randomBytes(24).toString('hex')}`,
4886
- createRuleError: (message, statusCode = 400, code = 'TASK_RULE_VIOLATION') => new TaskforceRuleError(message, statusCode, code),
4954
+ countAiProfileUsage: (workspaceId) => this.countAiProfileUsage(workspaceId),
4955
+ createRuleError: (message, statusCode = 400, code = 'TASK_RULE_VIOLATION', details) => new TaskforceRuleError(message, statusCode, code, details),
4887
4956
  };
4888
4957
  }
4889
4958
  resolveWorkspaceActor(workspaceId, actorRef) {
@@ -5795,6 +5864,11 @@ export class TaskforceCore {
5795
5864
  updated_at = ?
5796
5865
  WHERE tenant_id = ? AND id = ?
5797
5866
  `).run(hashPassword(password), 'password', user.email, now, now, this.tenantId, consumed.userId);
5867
+ this.ensureDefaultOnboardingEntitlement(consumed.userId, {
5868
+ source: 'invite-accept',
5869
+ updatedBy: 'invite-accept',
5870
+ now
5871
+ });
5798
5872
  return this.completeInviteWorkspaceActivation({
5799
5873
  userId: consumed.userId,
5800
5874
  workspaceId: inviteWorkspaceId
@@ -5953,6 +6027,10 @@ export class TaskforceCore {
5953
6027
  const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
5954
6028
  return this.listTaskEventsInternal(taskId, normalizedWorkspaceId);
5955
6029
  }
6030
+ listTaskEventsForSync(input) {
6031
+ const normalizedWorkspaceId = this.normalizeWorkspaceId(input.workspaceId || undefined);
6032
+ return listWorkspaceTaskEventsService(this.getTaskActivityServiceDeps(), normalizedWorkspaceId);
6033
+ }
5956
6034
  getScheduleBoard(weekKey, showWeekends = false, workspaceId) {
5957
6035
  const parsedWeek = this.parseIsoWeekKeyToDates(weekKey);
5958
6036
  if (!parsedWeek) {
@@ -6510,7 +6588,10 @@ export class TaskforceCore {
6510
6588
  if (!nextTask)
6511
6589
  return null;
6512
6590
  if (options?.skipTaskEvents !== true) {
6513
- const changes = this.buildTaskUpdateChanges(existing, nextTask, patch);
6591
+ const rawChanges = this.buildTaskUpdateChanges(existing, nextTask, patch);
6592
+ const changes = options?.saveSource === 'autosave'
6593
+ ? this.filterAutosaveTaskEventChanges(rawChanges)
6594
+ : rawChanges;
6514
6595
  if (Object.keys(changes).length > 0) {
6515
6596
  let action = 'task-updated';
6516
6597
  if (changes.status) {
@@ -20,19 +20,30 @@ function openCore(projectRoot) {
20
20
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
21
21
  `).run('default', planVersionId, 'month', `free:${planVersionId}:month`, 'free', 1, 0, null, now, now);
22
22
  };
23
- core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter' });
24
- addFreeMapping('starter-v1');
25
- core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter', status: 'active' });
26
- core.upsertPlanCatalog({ planId: 'collab', displayName: 'Collab' });
27
- addFreeMapping('collab-v1');
28
- core.upsertPlanCatalog({ planId: 'collab', displayName: 'Collab', status: 'active' });
23
+ core.upsertPlanCatalog({ planId: 'free', displayName: 'Free' });
24
+ addFreeMapping('free-v1');
25
+ core.upsertPlanCatalog({ planId: 'free', displayName: 'Free', status: 'active' });
29
26
  core.updatePlanVersion({
30
- planId: 'collab',
27
+ planId: 'free',
28
+ versionNumber: 1,
29
+ seatLimit: 1
30
+ });
31
+ core.updatePlanVersionFeatures({
32
+ planVersionId: 'free-v1',
33
+ features: [
34
+ { featureKey: 'workspace.switching', access: 'enabled' }
35
+ ]
36
+ });
37
+ core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5' });
38
+ addFreeMapping('team-5-v1');
39
+ core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5', status: 'active' });
40
+ core.updatePlanVersion({
41
+ planId: 'team-5',
31
42
  versionNumber: 1,
32
43
  seatLimit: 5
33
44
  });
34
45
  core.updatePlanVersionFeatures({
35
- planVersionId: 'collab-v1',
46
+ planVersionId: 'team-5-v1',
36
47
  features: [
37
48
  { featureKey: 'collaboration.invites', access: 'enabled' },
38
49
  { featureKey: 'collaboration.team_management', access: 'enabled' }
@@ -214,4 +225,58 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
214
225
  fs.rmSync(projectRoot, { recursive: true, force: true });
215
226
  }
216
227
  });
228
+ it('grants the default onboarding entitlement to newly accepted invited accounts', () => {
229
+ const projectRoot = makeProjectRoot();
230
+ const core = openCore(projectRoot);
231
+ try {
232
+ core.updateGlobalSettings({
233
+ billing: {
234
+ onboardingPolicy: {
235
+ mode: 'auto_grant_free',
236
+ planVersionId: 'free-v1'
237
+ }
238
+ }
239
+ });
240
+ core.createWorkspace({ workspaceId: 'workspace-invite', name: 'Invite Workspace' });
241
+ core.ensureBootstrapUserAccess({
242
+ userId: 'owner-user',
243
+ workspaceId: 'workspace-invite',
244
+ role: 'owner',
245
+ email: 'owner@example.com'
246
+ });
247
+ core.setUserAccountPlanMode('owner-user', 'team');
248
+ core.ensureBootstrapUserAccess({
249
+ userId: 'invited-user',
250
+ workspaceId: 'workspace-invite',
251
+ role: 'member',
252
+ email: 'invited@example.com'
253
+ });
254
+ core.setWorkspaceUserDisabled('invited-user', 'workspace-invite', true);
255
+ const db = core.db;
256
+ db.prepare(`
257
+ UPDATE workspace_memberships
258
+ SET status = 'invited'
259
+ WHERE tenant_id = ? AND user_id = ? AND workspace_id = ?
260
+ `).run('default', 'invited-user', 'workspace-invite');
261
+ const invite = core.requestInviteAcceptance({ userId: 'invited-user', workspaceId: 'workspace-invite' });
262
+ const accepted = core.acceptInviteWithToken({
263
+ token: String(invite.token),
264
+ password: 'Password123!'
265
+ });
266
+ expect(accepted.workspaceId).toBe('workspace-invite');
267
+ expect(core.hasAccountEntitlement('invited-user')).toBe(true);
268
+ expect(core.getAccountEntitlement('invited-user')).toEqual(expect.objectContaining({
269
+ planVersionId: 'free-v1',
270
+ state: 'active'
271
+ }));
272
+ expect(core.getAccountAccessStatus('invited-user')).toEqual(expect.objectContaining({
273
+ gate: 'ok',
274
+ canAccessApp: true
275
+ }));
276
+ }
277
+ finally {
278
+ core.close();
279
+ fs.rmSync(projectRoot, { recursive: true, force: true });
280
+ }
281
+ });
217
282
  });
@@ -17,8 +17,10 @@
17
17
  import type { McpAccessTokenScope, PlanFeatureCatalogItem } from './types.js';
18
18
  export declare const DOCUMENT_CLOUD_STORAGE_FEATURE_KEY = "context.uploads";
19
19
  export declare const WORKSPACES_FEATURE_KEY = "workspace.switching";
20
+ export declare const AI_PROFILES_FEATURE_KEY = "collaboration.ai_profiles";
20
21
  export declare const DOCUMENT_CLOUD_STORAGE_LIMIT_CONFIG_KEY = "storageLimitMb";
21
22
  export declare const WORKSPACES_LIMIT_CONFIG_KEY = "maxWorkspaces";
23
+ export declare const AI_PROFILES_LIMIT_CONFIG_KEY = "maxAiProfiles";
22
24
  export declare const MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE = 10;
23
25
  export declare const MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE = 20;
24
26
  export declare const MCP_ACCESS_TOKEN_MAX_PAGE_SIZE = 100;
@@ -18,8 +18,10 @@ import { createHash, pbkdf2Sync, randomBytes, timingSafeEqual } from 'crypto';
18
18
  // === SECTION: Constants ===
19
19
  export const DOCUMENT_CLOUD_STORAGE_FEATURE_KEY = 'context.uploads';
20
20
  export const WORKSPACES_FEATURE_KEY = 'workspace.switching';
21
+ export const AI_PROFILES_FEATURE_KEY = 'collaboration.ai_profiles';
21
22
  export const DOCUMENT_CLOUD_STORAGE_LIMIT_CONFIG_KEY = 'storageLimitMb';
22
23
  export const WORKSPACES_LIMIT_CONFIG_KEY = 'maxWorkspaces';
24
+ export const AI_PROFILES_LIMIT_CONFIG_KEY = 'maxAiProfiles';
23
25
  export const MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE = 10;
24
26
  export const MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE = 20;
25
27
  export const MCP_ACCESS_TOKEN_MAX_PAGE_SIZE = 100;
@@ -50,6 +52,15 @@ export const DEFAULT_PLAN_FEATURE_CATALOG = [
50
52
  configTemplate: {
51
53
  [WORKSPACES_LIMIT_CONFIG_KEY]: 1
52
54
  }
55
+ },
56
+ {
57
+ featureKey: AI_PROFILES_FEATURE_KEY,
58
+ label: 'AI Profiles',
59
+ description: 'Allow registered AI collaborator profiles with an optional per-workspace limit.',
60
+ allowedAccessModes: ['enabled', 'limited', 'disabled'],
61
+ configTemplate: {
62
+ [AI_PROFILES_LIMIT_CONFIG_KEY]: 1
63
+ }
53
64
  }
54
65
  ];
55
66
  export const PLAN_FEATURE_DEPENDENCIES = Object.freeze(DEFAULT_PLAN_FEATURE_CATALOG.reduce((acc, feature) => {
@@ -1,4 +1,4 @@
1
- import type { TaskActor } from '../core/Taskforce.js';
1
+ import type { TaskActor } from '../shared/taskActor.js';
2
2
  import type { WorkspaceAssetStore } from '../storage/workspaceAssetStore.js';
3
3
  import { DocumentReviewCommentStore } from './DocumentReviewCommentStore.js';
4
4
  import { DocumentReviewSessionStore } from './DocumentReviewSessionStore.js';
@@ -1,4 +1,4 @@
1
- import type { TaskActor } from '../core/Taskforce.js';
1
+ import type { TaskActor } from '../shared/taskActor.js';
2
2
  export type DocumentReviewSessionStatus = 'open' | 'resolved';
3
3
  export type DocumentReviewAnchorKind = 'document' | 'quote' | 'line-range';
4
4
  export interface DocumentReviewDocumentAnchor {
@@ -0,0 +1,48 @@
1
+ import { type MutableRefObject } from 'react';
2
+ import { createDefaultAssigneeOptions } from '../../utils/assignees';
3
+ export type BootstrapPhase = 'idle' | 'auth' | 'workspace' | 'config' | 'ready' | 'stalled';
4
+ interface UseTaskforceAuthBootstrapArgs {
5
+ runtimeConfigReady: boolean;
6
+ shouldProbeCloudAuth: boolean;
7
+ authSessionResolved: boolean;
8
+ resolveCloudAuthUrl: (path: string) => string;
9
+ authOnlyCloudMode: boolean;
10
+ authRequiredError: string;
11
+ runtimeMode: 'local' | 'cloud';
12
+ workspaceSelectionScope: string;
13
+ markBootstrapPhase: (phase: Exclude<BootstrapPhase, 'stalled'>) => void;
14
+ markBootstrapStalled: (message: string) => void;
15
+ setRuntimeMode: (mode: 'local' | 'cloud') => void;
16
+ setAuthRequiredForApi: (value: boolean) => void;
17
+ setIsAuthenticated: (value: boolean) => void;
18
+ setAuthUserId: (value: string) => void;
19
+ setAuthWorkspaceId: (value: string) => void;
20
+ setAuthUserEmail: (value: string) => void;
21
+ setAuthUserDisplayName: (value: string) => void;
22
+ setAuthUserAvatarUrl: (value: string) => void;
23
+ setUserGlobalSyncStatus: (value: 'idle' | 'disconnected') => void;
24
+ setUserGlobalSyncError: (value: string | null) => void;
25
+ setHasBetaAccess: (value: boolean) => void;
26
+ setAvailableWorkspaces: (value: any[]) => void;
27
+ setHydratedWorkspaceRole: (value: any) => void;
28
+ setAssigneeOptions: (value: ReturnType<typeof createDefaultAssigneeOptions>) => void;
29
+ setAuthBlocked: (value: boolean) => void;
30
+ setAuthSessionResolved: (value: boolean) => void;
31
+ setError: (value: string | ((prev: string) => string)) => void;
32
+ applyResolvedWorkspaceId: (workspaceId: string, options?: {
33
+ preserveNonDefaultDefault?: boolean;
34
+ }) => void;
35
+ readPersistedWorkspaceId: (scope: string) => string;
36
+ authSessionRequestRef: MutableRefObject<Promise<boolean> | null>;
37
+ authSessionEpochRef: MutableRefObject<number>;
38
+ authSessionLastCheckedAtRef: MutableRefObject<number>;
39
+ authSessionLastResultRef: MutableRefObject<boolean>;
40
+ isLoopbackHost: boolean;
41
+ setAuthStateReadyRefs: (authenticated: boolean, authSessionResolved: boolean, authRequiredForApi: boolean) => void;
42
+ }
43
+ export declare function useTaskforceAuthBootstrap({ runtimeConfigReady, shouldProbeCloudAuth, authSessionResolved, resolveCloudAuthUrl, authOnlyCloudMode, authRequiredError, runtimeMode, workspaceSelectionScope, markBootstrapPhase, markBootstrapStalled, setRuntimeMode, setAuthRequiredForApi, setIsAuthenticated, setAuthUserId, setAuthWorkspaceId, setAuthUserEmail, setAuthUserDisplayName, setAuthUserAvatarUrl, setUserGlobalSyncStatus, setUserGlobalSyncError, setHasBetaAccess, setAvailableWorkspaces, setHydratedWorkspaceRole, setAssigneeOptions, setAuthBlocked, setAuthSessionResolved, setError, applyResolvedWorkspaceId, readPersistedWorkspaceId, authSessionRequestRef, authSessionEpochRef, authSessionLastCheckedAtRef, authSessionLastResultRef, isLoopbackHost, setAuthStateReadyRefs }: UseTaskforceAuthBootstrapArgs): {
44
+ checkAuthSession: (options?: {
45
+ force?: boolean;
46
+ }) => Promise<boolean>;
47
+ };
48
+ export {};