@taskforcehq/taskforce 0.3.301 → 0.3.303

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 (171) hide show
  1. package/dist/Taskforce.module.css +37 -2
  2. package/dist/TaskforceCore.filter-persistence.test.js +1 -1
  3. package/dist/TaskforceCore.js +72 -33
  4. package/dist/TaskforceCore.test.js +848 -130
  5. package/dist/components/features/TaskSettings.js +14 -82
  6. package/dist/components/features/TaskSettings.test.js +33 -70
  7. package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
  8. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
  9. package/dist/components/task/TaskActionHeader.js +6 -3
  10. package/dist/components/task/TaskCard.js +6 -3
  11. package/dist/components/task/TaskForm.js +2 -2
  12. package/dist/components/task/TaskForm.test.js +41 -2
  13. package/dist/components/task/TaskKanban.test.js +17 -0
  14. package/dist/components/ui/ReferenceBadgeButton.js +1 -1
  15. package/dist/components/views/PlansPage.d.ts +3 -1
  16. package/dist/components/views/PlansPage.js +109 -49
  17. package/dist/components/views/PlansPage.test.d.ts +1 -0
  18. package/dist/components/views/PlansPage.test.js +100 -0
  19. package/dist/components/views/StandaloneLayout.d.ts +1 -0
  20. package/dist/components/views/StandaloneLayout.js +473 -256
  21. package/dist/components/views/panels/PlanningDrawer.js +18 -1
  22. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +48 -0
  23. package/dist/components/views/standalone/modals/AccountHubModal.js +36 -0
  24. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
  25. package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
  26. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
  27. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
  28. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
  29. package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
  30. package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
  31. package/dist/components/views/standalone/modals/HelpModal.js +8 -0
  32. package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
  33. package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
  34. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
  35. package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
  36. package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
  37. package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
  38. package/dist/components/views/standalone/modals/types.d.ts +2 -0
  39. package/dist/components/views/standalone/modals/types.js +1 -0
  40. package/dist/components/views/teamManagementAccess.d.ts +1 -1
  41. package/dist/components/views/teamManagementAccess.js +2 -2
  42. package/dist/components/views/teamManagementAccess.test.js +3 -3
  43. package/dist/config/deployment.js +2 -2
  44. package/dist/config/deployment.test.js +5 -3
  45. package/dist/core/AiProfileService.js +7 -2
  46. package/dist/core/AiProfiles.test.js +19 -0
  47. package/dist/core/EntitlementsPolicy.test.js +2 -2
  48. package/dist/core/GlobalSettingsService.js +78 -18
  49. package/dist/core/PlanEntitlementService.d.ts +29 -5
  50. package/dist/core/PlanEntitlementService.js +297 -219
  51. package/dist/core/SignupMonetizationSettings.test.js +173 -49
  52. package/dist/core/SyncReconciliationService.d.ts +2 -0
  53. package/dist/core/SyncReconciliationService.js +3 -0
  54. package/dist/core/SystemAdmin.test.js +186 -96
  55. package/dist/core/TaskAuditTimeline.test.js +40 -0
  56. package/dist/core/Taskforce.d.ts +100 -10
  57. package/dist/core/Taskforce.ids.test.js +133 -0
  58. package/dist/core/Taskforce.js +459 -127
  59. package/dist/core/Taskforce.mcpAuth.test.js +0 -2
  60. package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
  61. package/dist/core/UserProfileAvatarDrafts.test.js +111 -0
  62. package/dist/core/taskReferencePolicy.d.ts +28 -0
  63. package/dist/core/taskReferencePolicy.js +189 -0
  64. package/dist/core/types.d.ts +13 -12
  65. package/dist/hooks/tasks/useTaskFormActions.js +3 -0
  66. package/dist/hooks/useSyncOrchestrator.js +17 -3
  67. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +97 -5
  68. package/dist/hooks/useTaskData.js +3 -2
  69. package/dist/hooks/useTaskData.test.js +66 -0
  70. package/dist/hooks/useTaskforce.d.ts +8 -0
  71. package/dist/hooks/useTaskforce.js +72 -19
  72. package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
  73. package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
  74. package/dist/mcp/aiProfileBootstrap.js +80 -0
  75. package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
  76. package/dist/mcp/aiProfileBootstrap.test.js +68 -0
  77. package/dist/mcp/clientIntegrations.d.ts +2 -8
  78. package/dist/mcp/clientIntegrations.js +21 -40
  79. package/dist/mcp/clientIntegrations.test.js +28 -2
  80. package/dist/mcp/clientProfileDefaults.d.ts +12 -0
  81. package/dist/mcp/clientProfileDefaults.js +37 -0
  82. package/dist/mcp/runtime.d.ts +10 -7
  83. package/dist/mcp/runtime.js +131 -48
  84. package/dist/mcp/runtime.test.js +321 -83
  85. package/dist/migrations/billingSchemaParity.test.js +3 -0
  86. package/dist/migrations/taskSchemaMigrations.js +68 -1
  87. package/dist/qaOpen.test.d.ts +1 -0
  88. package/dist/qaOpen.test.js +56 -0
  89. package/dist/server/index.cookieProxy.test.js +2 -0
  90. package/dist/server/index.d.ts +1 -0
  91. package/dist/server/index.js +17 -1
  92. package/dist/server/index.test.js +15 -1
  93. package/dist/server/mockStripe.d.ts +1 -0
  94. package/dist/server/mockStripe.js +2 -0
  95. package/dist/server/routes/admin.js +105 -45
  96. package/dist/server/routes/auth.d.ts +6 -0
  97. package/dist/server/routes/auth.js +505 -30
  98. package/dist/server/routes/billing.js +517 -24
  99. package/dist/server/routes/billing.test.js +967 -45
  100. package/dist/server/routes/documents.js +6 -1
  101. package/dist/server/routes/shared.d.ts +1 -1
  102. package/dist/server/routes/shared.js +16 -6
  103. package/dist/server/routes/sync.integration.test.js +290 -0
  104. package/dist/server/routes/sync.js +234 -1922
  105. package/dist/server/routes/tasks.js +18 -4
  106. package/dist/server/routes.js +29 -22
  107. package/dist/server/routes.test.js +639 -82
  108. package/dist/shared/runtimeContract.d.ts +1 -1
  109. package/dist/sync/cloudSyncApi.d.ts +1 -0
  110. package/dist/sync/collaborationSyncPayload.d.ts +17 -0
  111. package/dist/sync/collaborationSyncPayload.js +131 -0
  112. package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
  113. package/dist/sync/collaborationSyncPayload.test.js +104 -0
  114. package/dist/sync/collaborationSyncState.d.ts +15 -0
  115. package/dist/sync/collaborationSyncState.js +60 -0
  116. package/dist/sync/contentSyncPayload.d.ts +45 -0
  117. package/dist/sync/contentSyncPayload.js +104 -0
  118. package/dist/sync/contentSyncPayload.test.d.ts +1 -0
  119. package/dist/sync/contentSyncPayload.test.js +104 -0
  120. package/dist/sync/contentSyncState.d.ts +98 -0
  121. package/dist/sync/contentSyncState.js +1004 -0
  122. package/dist/sync/planningSyncPayload.d.ts +9 -0
  123. package/dist/sync/planningSyncPayload.js +82 -0
  124. package/dist/sync/planningSyncPayload.test.d.ts +1 -0
  125. package/dist/sync/planningSyncPayload.test.js +68 -0
  126. package/dist/sync/syncApplyHandlers.d.ts +83 -0
  127. package/dist/sync/syncApplyHandlers.js +445 -0
  128. package/dist/sync/syncService.d.ts +1 -0
  129. package/dist/sync/syncService.js +21 -8
  130. package/dist/sync/taskSyncPayload.d.ts +33 -0
  131. package/dist/sync/taskSyncPayload.js +126 -0
  132. package/dist/sync/taskSyncPayload.test.d.ts +1 -0
  133. package/dist/sync/taskSyncPayload.test.js +54 -0
  134. package/dist/sync/workspacePullFeed.d.ts +38 -0
  135. package/dist/sync/workspacePullFeed.js +349 -0
  136. package/dist/sync/workspacePullFeed.test.d.ts +1 -0
  137. package/dist/sync/workspacePullFeed.test.js +278 -0
  138. package/dist/sync/workspaceSyncModel.d.ts +4 -1
  139. package/dist/sync/workspaceSyncModel.js +9 -2
  140. package/dist/sync/workspaceSyncModel.test.js +29 -0
  141. package/dist/test/setup.js +1 -1
  142. package/dist/types.d.ts +1 -0
  143. package/dist/ui/assets/{AgentsModule-Bdq3T1Ts.js → AgentsModule-CpsTmrIW.js} +1 -1
  144. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-jKUVo-PJ.js → AnnotatedAttachmentWorkspace-C_TmXZwA.js} +1 -1
  145. package/dist/ui/assets/{DocumentWorkspace-DuQa-uIC.js → DocumentWorkspace-C3GyzrWm.js} +2 -2
  146. package/dist/ui/assets/{InitiativesModule-C8Pi2VTk.js → InitiativesModule-4-Rh2K2n.js} +1 -1
  147. package/dist/ui/assets/PlansPage-BP7AYOqr.js +1 -0
  148. package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +8 -0
  149. package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
  150. package/dist/ui/assets/{WorkflowsModule-b6TLuHwJ.js → WorkflowsModule-CGk9s3NJ.js} +1 -1
  151. package/dist/ui/assets/index-CLIMgR6g.js +6 -0
  152. package/dist/ui/assets/index-DneETxcu.css +1 -0
  153. package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-pWocEipT.js} +1 -1
  154. package/dist/ui/index.html +3 -3
  155. package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
  156. package/dist/utils/accountProfileSummaryCache.js +97 -0
  157. package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
  158. package/dist/utils/accountProfileSummaryCache.test.js +63 -0
  159. package/dist/utils/taskActivity.js +17 -4
  160. package/dist/utils/taskActivity.test.js +27 -0
  161. package/dist/utils/taskNormalization.js +10 -2
  162. package/dist/utils/taskReferences.d.ts +11 -1
  163. package/dist/utils/taskReferences.js +33 -1
  164. package/package.json +4 -3
  165. package/scripts/qa-open-lib.mjs +175 -0
  166. package/scripts/qa-open-lib.mjs.d.ts +49 -0
  167. package/scripts/qa-open.mjs +352 -0
  168. package/dist/ui/assets/PlansPage-B6BDECSi.js +0 -1
  169. package/dist/ui/assets/TaskSettings-DUJgRTPU.js +0 -8
  170. package/dist/ui/assets/index-BiiF1cLQ.css +0 -1
  171. package/dist/ui/assets/index-xBWLPOEb.js +0 -6
@@ -86,6 +86,7 @@ export interface TaskActor {
86
86
  export interface TaskItem {
87
87
  id: string;
88
88
  referenceNumber?: number | null;
89
+ localReferenceNumber?: number | null;
89
90
  referenceLabel?: string;
90
91
  title: string;
91
92
  description: string | null;
@@ -342,14 +343,10 @@ export interface InitiativeTemplate {
342
343
  milestones: InitiativeTemplateMilestone[];
343
344
  }
344
345
  type WorkspaceSyncSettingsPayload = Record<string, Record<string, unknown>>;
345
- type SignupMonetizationStrategy = 'auto_assign_default' | 'plan_selection_required';
346
- interface SignupMonetizationSettings {
347
- strategy: SignupMonetizationStrategy;
348
- defaultSignupPlanVersionId: string | null;
349
- }
350
346
  type AccessRole = 'owner' | 'admin' | 'member' | 'read-only';
351
347
  export type SystemRole = 'system_admin' | 'user';
352
348
  type PermissionMode = 'read-only' | 'read-write';
349
+ type AuthTokenPurpose = 'email_verification' | 'password_reset' | 'invite_accept';
353
350
  export type McpAccessTokenScope = 'tasks:read' | 'tasks:write' | 'workspace:read';
354
351
  type McpAccessTokenStatus = 'active' | 'revoked' | 'expired';
355
352
  export type McpOAuthProvider = 'chatgpt' | 'claude';
@@ -367,9 +364,24 @@ interface UserIdentityRecord {
367
364
  userId: string;
368
365
  email: string;
369
366
  displayName?: string | null;
367
+ avatarUrl?: string | null;
370
368
  systemRole?: SystemRole;
371
369
  betaAccess?: boolean;
372
370
  }
371
+ interface UserAvatarUploadDraftRecord {
372
+ draftId: string;
373
+ userId: string;
374
+ storageKey: string;
375
+ mimeType: string;
376
+ originalFilename: string;
377
+ sizeBytes: number;
378
+ storageProvider: 'local' | 'r2' | 'external';
379
+ finalizedAt?: string | null;
380
+ consumedAt?: string | null;
381
+ discardedAt?: string | null;
382
+ createdAt: string;
383
+ updatedAt: string;
384
+ }
373
385
  interface WorkspaceUserRecord {
374
386
  userId: string;
375
387
  email: string;
@@ -437,6 +449,24 @@ interface SystemUserRecord {
437
449
  updatedAt?: string | null;
438
450
  emailVerifiedAt?: string | null;
439
451
  }
452
+ export interface PurgedSystemUserSummary {
453
+ targetUserId: string;
454
+ email: string;
455
+ unassignedTaskCount: number;
456
+ clearedInitiativeOwnerCount: number;
457
+ clearedWorkstreamOwnerCount: number;
458
+ deletedWorkspaceMembershipCount: number;
459
+ deletedAuthTokenCount: number;
460
+ deletedAvatarDraftCount: number;
461
+ deletedMcpAccessTokenCount: number;
462
+ deletedMcpOauthAuthCodeCount: number;
463
+ deletedMcpOauthConnectorGrantCount: number;
464
+ deletedUiStateCount: number;
465
+ deletedUserPreferenceCount: number;
466
+ deletedEntitlementCount: number;
467
+ deletedUserBillingCount: number;
468
+ deletedUserCount: number;
469
+ }
440
470
  export type SetupState = 'present' | 'missing' | 'unreadable';
441
471
  export type RuntimeMode = 'local' | 'cloud';
442
472
  export type WorkspaceMode = 'core' | 'operations';
@@ -476,6 +506,8 @@ interface CreateTaskOptions {
476
506
  persistBackup?: boolean;
477
507
  workspaceId?: string;
478
508
  allowClientProvidedId?: boolean;
509
+ provisionalLocalReference?: boolean;
510
+ preferIncomingReferenceNumberOnConflict?: boolean;
479
511
  skipRecompute?: boolean;
480
512
  syncUpdatedAt?: string;
481
513
  actorRef?: string;
@@ -511,7 +543,7 @@ interface McpAuditLogRecord {
511
543
  reasonCode: string | null;
512
544
  details: Record<string, unknown>;
513
545
  }
514
- export type EntitlementState = 'active' | 'trialing' | 'grace' | 'suspended' | 'canceled';
546
+ export type EntitlementState = 'pending_plan_selection' | 'checkout_pending' | 'active' | 'trialing' | 'grace' | 'suspended' | 'canceled';
515
547
  export type PlanFeatureAccess = 'enabled' | 'disabled' | 'limited';
516
548
  export interface PlanFeatureSnapshot {
517
549
  featureKey: string;
@@ -573,6 +605,11 @@ interface PasswordAuthResult {
573
605
  };
574
606
  code?: 'INVALID_CREDENTIALS' | 'EMAIL_NOT_VERIFIED';
575
607
  }
608
+ interface AuthTokenInspection {
609
+ state: 'valid' | 'expired' | 'used' | 'missing' | 'revoked';
610
+ userId?: string;
611
+ workspaceId?: string;
612
+ }
576
613
  export interface McpAccessTokenRecord {
577
614
  id: string;
578
615
  workspaceId: string;
@@ -849,8 +886,20 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
849
886
  getWorkspaceAssetStore(): WorkspaceAssetStore;
850
887
  private normalizeEntitlementState;
851
888
  private upsertCanonicalEntitlementRecord;
889
+ transitionCanonicalCommercialState(input: {
890
+ accountId: string;
891
+ planVersionId?: string | null;
892
+ state: EntitlementState;
893
+ effectiveAt?: string | null;
894
+ effectiveUntil?: string | null;
895
+ billingMirrorPlanId?: string | null;
896
+ source: 'registration' | 'checkout-start' | 'checkout-confirm' | 'stripe-webhook' | 'backfill' | 'legacy-plan-mode' | 'admin' | 'system';
897
+ updatedBy: string;
898
+ createdAt?: string | null;
899
+ updatedAt?: string | null;
900
+ }): import("./types.js").AccountEntitlementSnapshot;
852
901
  private resolveWorkspaceLimitForUser;
853
- private resolveRegistrationPlanAssignment;
902
+ private resolveRegistrationOnboardingOutcome;
854
903
  createUserBillingRecord(userId: string, options?: {
855
904
  entitlementState?: string;
856
905
  planId?: string | null;
@@ -940,8 +989,11 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
940
989
  hasProjectThemeOverride(workspaceId?: string, userId?: string): boolean;
941
990
  getEffectiveTheme(workspaceId?: string, userId?: string): TaskforceTheme;
942
991
  hasProjectJsonBackupOverride(workspaceId?: string, userId?: string): boolean;
943
- getSignupMonetizationSettings(): import("./types.js").SignupMonetizationSettings;
944
- validateSignupMonetizationSettings(input?: Partial<SignupMonetizationSettings>): import("./types.js").SignupMonetizationValidationResult;
992
+ getOnboardingPolicy(): import("./types.js").OnboardingPolicy;
993
+ validateOnboardingPolicy(input?: {
994
+ mode?: 'require_plan_selection' | 'auto_grant_free' | 'auto_start_trial';
995
+ planVersionId?: string | null;
996
+ }): import("./types.js").OnboardingPolicyValidationResult;
945
997
  updateGlobalSettings(settings: GlobalTaskforceSettings): void;
946
998
  private sanitizeUserGlobalSyncSettings;
947
999
  getUserGlobalSyncSettings(userId: string): {
@@ -971,7 +1023,9 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
971
1023
  private rowToWorkstream;
972
1024
  private rowToChecklistItem;
973
1025
  private sanitizeTaskReferenceNumber;
1026
+ private getTaskReferencePolicyDeps;
974
1027
  private getTaskByReferenceNumber;
1028
+ private getTaskByLocalReferenceNumber;
975
1029
  resolveTaskIdentifier(value: string, workspaceId?: string): TaskItem | null;
976
1030
  getInitiative(id: string, workspaceId?: string): InitiativeItem | null;
977
1031
  listInitiatives(workspaceId?: string): InitiativeItem[];
@@ -989,6 +1043,15 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
989
1043
  upsertWorkstreamForSync(workstream: WorkstreamItem, workspaceId?: string | null): WorkstreamItem;
990
1044
  listChecklistItems(taskId: string, workspaceId?: string): ChecklistItem[];
991
1045
  replaceChecklistItems(taskId: string, items: Array<Partial<ChecklistItem>>, workspaceId?: string): ChecklistItem[];
1046
+ private getTaskReferenceSequenceValue;
1047
+ private getLocalTaskReferenceSequenceValue;
1048
+ private getMaxTaskReferenceNumber;
1049
+ private getMaxLocalTaskReferenceNumber;
1050
+ private persistTaskReferenceSequence;
1051
+ private persistLocalTaskReferenceSequence;
1052
+ private allocateNextTaskReferenceNumber;
1053
+ private allocateNextLocalTaskReferenceNumber;
1054
+ private relocateTaskReferenceConflict;
992
1055
  private reserveTaskReferenceNumber;
993
1056
  private reserveGenericReferenceNumber;
994
1057
  private reserveInitiativeReferenceNumber;
@@ -1081,7 +1144,25 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1081
1144
  }): UserAccessRecord;
1082
1145
  resolveUserAccess(userId: string, workspaceId: string, fallbackRole?: AccessRole): UserAccessRecord | null;
1083
1146
  resolveUserIdentity(userId: string): UserIdentityRecord | null;
1147
+ updateUserProfile(userId: string, input: {
1148
+ displayName: string;
1149
+ avatarUrl?: string | null;
1150
+ }): UserIdentityRecord | null;
1084
1151
  updateUserDisplayName(userId: string, displayName: string): UserIdentityRecord | null;
1152
+ createUserAvatarUploadDraft(input: {
1153
+ userId: string;
1154
+ draftId: string;
1155
+ storageKey: string;
1156
+ mimeType: string;
1157
+ originalFilename: string;
1158
+ sizeBytes: number;
1159
+ storageProvider?: 'local' | 'r2' | 'external';
1160
+ }): UserAvatarUploadDraftRecord;
1161
+ getUserAvatarUploadDraft(userId: string, draftId: string): UserAvatarUploadDraftRecord | null;
1162
+ markUserAvatarUploadDraftFinalized(userId: string, draftId: string): UserAvatarUploadDraftRecord | null;
1163
+ markUserAvatarUploadDraftConsumed(userId: string, draftId: string): UserAvatarUploadDraftRecord | null;
1164
+ discardUserAvatarUploadDraft(userId: string, draftId: string): UserAvatarUploadDraftRecord | null;
1165
+ cleanupStaleUserAvatarUploadDrafts(userId: string, olderThanIso: string): UserAvatarUploadDraftRecord[];
1085
1166
  getSystemRole(userId: string): SystemRole;
1086
1167
  getUserBetaAccess(userId: string): boolean;
1087
1168
  countSystemAdmins(): number;
@@ -1090,6 +1171,11 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1090
1171
  setSystemRole(userId: string, role: SystemRole): boolean;
1091
1172
  setUserDisabledGlobal(userId: string, disabled: boolean): boolean;
1092
1173
  setUserBetaAccessGlobal(userId: string, enabled: boolean): boolean;
1174
+ purgeSystemUser(input: {
1175
+ targetUserId: string;
1176
+ actorUserId: string;
1177
+ emailConfirmation: string;
1178
+ }): PurgedSystemUserSummary;
1093
1179
  private clearActiveAssignmentsForUnavailableActor;
1094
1180
  listWorkspaceUsers(workspaceId: string): WorkspaceUserRecord[];
1095
1181
  replaceWorkspaceUsersForSync(input: {
@@ -1424,10 +1510,12 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1424
1510
  workspaceId: string | null;
1425
1511
  role: AccessRole;
1426
1512
  planSelectionRequired: boolean;
1513
+ checkoutPending: boolean;
1514
+ commercialState: EntitlementState;
1427
1515
  };
1428
1516
  private createOneTimeAuthToken;
1429
1517
  private consumeOneTimeAuthToken;
1430
- private inspectOneTimeAuthToken;
1518
+ inspectOneTimeAuthToken(token: string, purpose: AuthTokenPurpose): AuthTokenInspection;
1431
1519
  revokeMcpCredentialsForUserWorkspace(userId: string, workspaceId: string, revokedByUserId?: string | null): void;
1432
1520
  private revokeMcpCredentialsForWorkspace;
1433
1521
  listMcpAccessTokens(input: {
@@ -1602,9 +1690,11 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
1602
1690
  validateOnly?: boolean;
1603
1691
  workspaceId?: string;
1604
1692
  allowClientProvidedId?: boolean;
1693
+ provisionalLocalReference?: boolean;
1605
1694
  }): BulkCreateTaskResult;
1606
1695
  updateTask(id: string, updates: Partial<TaskItem>, workspaceId?: string, options?: {
1607
1696
  persistBackup?: boolean;
1697
+ preferIncomingReferenceNumberOnConflict?: boolean;
1608
1698
  skipRecompute?: boolean;
1609
1699
  syncUpdatedAt?: string;
1610
1700
  actorRef?: string;
@@ -80,6 +80,139 @@ describe('TaskforceCore id policy', () => {
80
80
  cleanup();
81
81
  }
82
82
  });
83
+ it('assigns provisional local task references without consuming canonical task references', () => {
84
+ const { core, cleanup } = makeCore();
85
+ try {
86
+ const provisionalFirst = core.createTask({ title: 'Offline first' }, {
87
+ workspaceId: 'workspace-a',
88
+ provisionalLocalReference: true
89
+ });
90
+ const provisionalSecond = core.createTask({ title: 'Offline second' }, {
91
+ workspaceId: 'workspace-a',
92
+ provisionalLocalReference: true
93
+ });
94
+ const canonical = core.createTask({ title: 'Cloud-backed task' }, { workspaceId: 'workspace-a' });
95
+ expect(provisionalFirst.referenceNumber).toBeNull();
96
+ expect(provisionalFirst.localReferenceNumber).toBe(1);
97
+ expect(provisionalFirst.referenceLabel).toBe('LT-1');
98
+ expect(provisionalSecond.referenceNumber).toBeNull();
99
+ expect(provisionalSecond.localReferenceNumber).toBe(2);
100
+ expect(provisionalSecond.referenceLabel).toBe('LT-2');
101
+ expect(canonical.referenceNumber).toBe(1);
102
+ expect(canonical.localReferenceNumber).toBeNull();
103
+ expect(canonical.referenceLabel).toBe('T-1');
104
+ }
105
+ finally {
106
+ cleanup();
107
+ }
108
+ });
109
+ it('replaces a provisional local reference with a canonical cloud reference during sync apply', () => {
110
+ const { core, cleanup } = makeCore();
111
+ try {
112
+ const provisional = core.createTask({ title: 'Offline draft' }, {
113
+ workspaceId: 'workspace-a',
114
+ provisionalLocalReference: true
115
+ });
116
+ expect(provisional.referenceLabel).toBe('LT-1');
117
+ const syncResult = core.applyWorkspaceSyncSnapshot({
118
+ tasks: [
119
+ {
120
+ id: provisional.id,
121
+ title: 'Offline draft',
122
+ status: 'task',
123
+ referenceNumber: 7,
124
+ updatedAt: '2099-04-10T17:00:00.000Z'
125
+ }
126
+ ]
127
+ }, 'workspace-a');
128
+ expect(syncResult.updated).toBe(1);
129
+ expect(core.getTask(provisional.id, 'workspace-a')).toEqual(expect.objectContaining({
130
+ referenceNumber: 7,
131
+ localReferenceNumber: null,
132
+ referenceLabel: 'T-7'
133
+ }));
134
+ }
135
+ finally {
136
+ cleanup();
137
+ }
138
+ });
139
+ it('preserves provisional local references across core reloads', () => {
140
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'taskforce-id-reload-test-'));
141
+ const storagePath = path.join(projectRoot, '.taskforce');
142
+ const firstCore = new TaskforceCore({ projectRoot, storagePath });
143
+ try {
144
+ const provisional = firstCore.createTask({ title: 'Offline draft' }, {
145
+ workspaceId: 'workspace-a',
146
+ provisionalLocalReference: true
147
+ });
148
+ expect(provisional.referenceNumber).toBeNull();
149
+ expect(provisional.localReferenceNumber).toBe(1);
150
+ firstCore.close();
151
+ const reloadedCore = new TaskforceCore({ projectRoot, storagePath });
152
+ try {
153
+ const reloadedTask = reloadedCore.getTask(provisional.id, 'workspace-a');
154
+ expect(reloadedTask).toEqual(expect.objectContaining({
155
+ referenceNumber: null,
156
+ localReferenceNumber: 1,
157
+ referenceLabel: 'LT-1'
158
+ }));
159
+ }
160
+ finally {
161
+ reloadedCore.close();
162
+ }
163
+ }
164
+ finally {
165
+ fs.rmSync(projectRoot, { recursive: true, force: true });
166
+ }
167
+ });
168
+ it('lets the incoming synced task keep its preferred reference and renumbers the local incumbent', () => {
169
+ const { core, cleanup } = makeCore();
170
+ try {
171
+ const localTask = core.createTask({ title: 'Local task' }, { workspaceId: 'workspace-a' });
172
+ const syncResult = core.applyWorkspaceSyncSnapshot({
173
+ tasks: [
174
+ {
175
+ id: 'task-sync-imported-collision',
176
+ title: 'Remote task',
177
+ status: 'task',
178
+ referenceNumber: localTask.referenceNumber,
179
+ createdAt: '2026-04-10T15:00:00.000Z',
180
+ updatedAt: '2026-04-10T15:00:00.000Z',
181
+ }
182
+ ]
183
+ }, 'workspace-a');
184
+ expect(syncResult.created).toBe(1);
185
+ expect(core.getTask(localTask.id, 'workspace-a')?.referenceLabel).toBe('T-2');
186
+ expect(core.getTask('task-sync-imported-collision', 'workspace-a')?.referenceLabel).toBe('T-1');
187
+ }
188
+ finally {
189
+ cleanup();
190
+ }
191
+ });
192
+ it('lets incoming synced updates claim the preferred reference and renumbers the displaced task', () => {
193
+ const { core, cleanup } = makeCore();
194
+ try {
195
+ const first = core.createTask({ title: 'First' }, { workspaceId: 'workspace-a' });
196
+ const second = core.createTask({ title: 'Second' }, { workspaceId: 'workspace-a' });
197
+ const syncResult = core.applyWorkspaceSyncSnapshot({
198
+ tasks: [
199
+ {
200
+ id: second.id,
201
+ title: 'Second',
202
+ status: 'task',
203
+ referenceNumber: first.referenceNumber,
204
+ updatedAt: '2099-04-11T16:00:00.000Z',
205
+ }
206
+ ]
207
+ }, 'workspace-a');
208
+ expect(syncResult.updated).toBe(1);
209
+ expect(core.getTask(first.id, 'workspace-a')?.referenceLabel).toBe('T-3');
210
+ expect(core.getTask(second.id, 'workspace-a')?.referenceLabel).toBe('T-1');
211
+ }
212
+ finally {
213
+ cleanup();
214
+ }
215
+ });
83
216
  it('resolves task identifiers by task reference or internal id', () => {
84
217
  const { core, cleanup } = makeCore();
85
218
  try {