@taskforcehq/taskforce 0.3.328 → 0.3.329
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Taskforce.module.css +233 -34
- package/dist/TaskforceCore.js +237 -230
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
- package/dist/cli.js +34 -0
- package/dist/cliCloudTargetGuard.d.ts +5 -0
- package/dist/cliCloudTargetGuard.js +11 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
- package/dist/components/features/TaskSettings.d.ts +12 -1
- package/dist/components/features/TaskSettings.js +147 -40
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
- package/dist/components/features/planning/PlanningModule.js +2 -5
- package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
- package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
- package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
- package/dist/components/features/planning/planningTaskArrangement.js +5 -5
- package/dist/components/features/taskforceAgentEditorModel.js +3 -1
- package/dist/components/task/TaskCard.d.ts +5 -0
- package/dist/components/task/TaskCard.js +65 -10
- package/dist/components/task/TaskImageReviews.js +14 -5
- package/dist/components/task/TaskKanban.d.ts +6 -1
- package/dist/components/task/TaskKanban.js +152 -6
- package/dist/components/task/TaskList.d.ts +5 -0
- package/dist/components/task/TaskList.js +3 -3
- package/dist/components/ui/PasswordInput.d.ts +6 -0
- package/dist/components/ui/PasswordInput.js +11 -0
- package/dist/components/ui/TopNoticeLayer.js +1 -1
- package/dist/components/views/AppShellHeader.js +32 -11
- package/dist/components/views/PlanComparisonPage.js +12 -0
- package/dist/components/views/StandaloneLayout.js +7 -31
- package/dist/components/views/WidgetView.js +3 -3
- package/dist/components/views/panels/PlanningDrawer.js +5 -12
- package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
- package/dist/config/envSchema.js +12 -0
- package/dist/config/localServiceRuntimeContract.d.ts +13 -0
- package/dist/config/localServiceRuntimeContract.js +135 -0
- package/dist/core/AiProfileService.js +46 -8
- package/dist/core/DeletedTaskLifecycleService.d.ts +1 -0
- package/dist/core/DeletedTaskLifecycleService.js +37 -6
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +3 -2
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +33 -9
- package/dist/core/Taskforce.d.ts +62 -2
- package/dist/core/Taskforce.js +635 -32
- package/dist/core/types.d.ts +6 -0
- package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
- package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
- package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
- package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
- package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
- package/dist/hooks/tasks/taskHttpMutation.js +1 -0
- package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
- package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
- package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
- package/dist/hooks/tasks/useTaskFormActions.d.ts +6 -0
- package/dist/hooks/tasks/useTaskFormActions.js +65 -12
- package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
- package/dist/hooks/ui/useSettingsModel.js +13 -2
- package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
- package/dist/hooks/ui/useSettingsPersistence.js +74 -47
- package/dist/hooks/useFilterSort.d.ts +7 -3
- package/dist/hooks/useFilterSort.js +15 -5
- package/dist/hooks/useTaskData.d.ts +10 -1
- package/dist/hooks/useTaskData.js +216 -7
- package/dist/hooks/useTaskMutations.d.ts +10 -2
- package/dist/hooks/useTaskMutations.js +223 -43
- package/dist/hooks/useTaskforce.d.ts +37 -9
- package/dist/hooks/useTaskforce.js +349 -39
- package/dist/hooks/useUiNotice.d.ts +4 -0
- package/dist/hooks/useUiNotice.js +4 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
- package/dist/mcp/documentAssetRegistrar.js +44 -35
- package/dist/mcp/documentHelpers.d.ts +1 -0
- package/dist/mcp/documentHelpers.js +7 -3
- package/dist/mcp/runtime.js +172 -42
- package/dist/mcp/taskPlanningRegistrar.js +41 -38
- package/dist/mcp/toolCallCompatibility.js +38 -1
- package/dist/mcp/toolCatalog.d.ts +1 -0
- package/dist/migrations/taskSchemaMigrations.js +12 -0
- package/dist/runtime/appGateDefinitions.d.ts +30 -6
- package/dist/runtime/appGateDefinitions.js +6 -6
- package/dist/runtime/appGates.d.ts +12 -13
- package/dist/runtime/appGates.js +29 -13
- package/dist/runtime/nodeAppGates.d.ts +8 -0
- package/dist/runtime/nodeAppGates.js +27 -0
- package/dist/server/cloudCollectionRead.d.ts +1 -1
- package/dist/server/cloudCollectionRead.js +2 -2
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +15 -3
- package/dist/server/localServiceLease.d.ts +3 -0
- package/dist/server/localServiceLease.js +9 -1
- package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
- package/dist/server/rateLimit.d.ts +1 -0
- package/dist/server/rateLimit.js +4 -0
- package/dist/server/realtimeSyncWs.js +36 -12
- package/dist/server/routes/admin.js +1 -0
- package/dist/server/routes/agentRoles.js +3 -2
- package/dist/server/routes/agentSkills.js +3 -2
- package/dist/server/routes/agents.js +118 -16
- package/dist/server/routes/annotatedAttachments.js +3 -2
- package/dist/server/routes/auth.js +3 -1
- package/dist/server/routes/durableTaskHttpRouters.d.ts +9 -2
- package/dist/server/routes/localService.js +2 -0
- package/dist/server/routes/primitives.d.ts +2 -0
- package/dist/server/routes/syncAuxRoutes.js +5 -2
- package/dist/server/routes/syncPullApplyRoutes.js +23 -4
- package/dist/server/routes/syncV3FeedRoutes.js +18 -16
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +28 -2
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
- package/dist/server/routes/syncV3OperationRoutes.js +10 -0
- package/dist/server/routes/tasks.js +103 -11
- package/dist/server/routes/workspaces.js +43 -1
- package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
- package/dist/server/workspaceDeletionAdmission.js +50 -0
- package/dist/service/localServiceCli.js +16 -0
- package/dist/service/localServiceClientLifecycle.js +36 -10
- package/dist/services/codexAppServerModelGateway.d.ts +112 -0
- package/dist/services/codexAppServerModelGateway.js +438 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
- package/dist/services/taskforceAgentMcpBridge.js +23 -3
- package/dist/shared/taskforceAgentDefinition.js +7 -1
- package/dist/shared/taskforceAgentModels.d.ts +1 -0
- package/dist/shared/taskforceAgentModels.js +30 -1
- package/dist/storage/postgresAdapter.d.ts +20 -3
- package/dist/storage/postgresAdapter.js +106 -14
- package/dist/storage/postgresWorker.js +13 -4
- package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
- package/dist/storage/postgresWorkerProtocol.js +30 -0
- package/dist/sync/cloudSyncApi.d.ts +4 -1
- package/dist/sync/cloudSyncApi.js +7 -2
- package/dist/sync/contentSyncState.d.ts +1 -1
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +1 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +7 -1
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
- package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
- package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
- package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +31 -4
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +25 -8
- package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
- package/dist/sync/syncService.d.ts +2 -0
- package/dist/sync/syncService.js +4 -1
- package/dist/sync/v3/combinedFeedCapability.js +4 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +2 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +11 -1
- package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
- package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +30 -8
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +47 -7
- package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
- package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
- package/dist/sync/v3/localTaskMetadataClient.js +1 -0
- package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
- package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
- package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
- package/dist/sync/v3/localTaskStatusClient.js +6 -1
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
- package/dist/sync/v3/syncDurabilityStore.d.ts +7 -0
- package/dist/sync/v3/syncDurabilityStore.js +31 -11
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +6 -4
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +19 -10
- package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
- package/dist/sync/v3/taskChecklistMutationService.js +4 -0
- package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
- package/dist/sync/v3/taskCreateMutationService.js +7 -4
- package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
- package/dist/sync/v3/taskMetadataMutationService.js +4 -0
- package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
- package/dist/sync/v3/taskRestoreMutationService.js +7 -4
- package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
- package/dist/sync/v3/taskStatusMutationService.js +4 -0
- package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
- package/dist/sync/v3/workflowAssignmentSync.js +3 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +13 -3
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -1
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +8 -3
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +51 -10
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +30 -18
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -4
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +98 -47
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +58 -21
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
- package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
- package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
- package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
- package/dist/sync/workspacePullFeed.js +2 -1
- package/dist/sync/workspaceRepair.d.ts +3 -0
- package/dist/sync/workspaceRepair.js +67 -6
- package/dist/types.d.ts +47 -1
- package/dist/types.js +6 -0
- package/dist/ui/assets/{AiIdentityRosterCard-MZs7lVED.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
- package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
- package/dist/ui/assets/{AssetTraySortControl-Kf1pelSy.js → AssetTraySortControl-C8Rztyal.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-B8boOs6Q.js → ContextAttachmentManager-Cj3c7X9y.js} +2 -2
- package/dist/ui/assets/{DocumentWorkspace-DXH4XUsB.js → DocumentWorkspace-B5uvzems.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-BtSTq0ON.js → EntityActivityTimeline-BVFvCB5R.js} +1 -1
- package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
- package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
- package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
- package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
- package/dist/ui/assets/{TaskforceAgentsModule-BazdUwxZ.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
- package/dist/ui/assets/{WorkflowManagerModule-DWFspC-o.js → WorkflowManagerModule-DEr5di_r.js} +1 -1
- package/dist/ui/assets/index-I5zhdtgt.js +7 -0
- package/dist/ui/assets/index-VRFsx3wz.css +1 -0
- package/dist/ui/assets/{vendor-icons-BkFLXavV.js → vendor-icons-D9Lpw-j4.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/taskActivity.d.ts +3 -2
- package/dist/utils/taskActivity.js +19 -2
- package/dist/utils/taskEditorReconciliation.d.ts +40 -0
- package/dist/utils/taskEditorReconciliation.js +217 -0
- package/dist/utils/taskNormalization.d.ts +1 -0
- package/dist/utils/taskNormalization.js +39 -0
- package/dist/utils/taskSearch.js +2 -2
- package/dist/utils/uiNotice.d.ts +4 -0
- package/package.json +16 -4
- package/dist/ui/assets/AiProfilesModule-C3i0LOxe.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-DVZk5Tln.js +0 -3
- package/dist/ui/assets/PlanningModule-BPVyEa-f.js +0 -1
- package/dist/ui/assets/PlansPage-ChqgxALc.js +0 -1
- package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +0 -1
- package/dist/ui/assets/TaskSettings-g4ECKNOz.js +0 -12
- package/dist/ui/assets/index-BUplSsv_.js +0 -7
- package/dist/ui/assets/index-gz2EXuoK.css +0 -1
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
* - getTaskRevisionKey(task) — stable revision string for change detection
|
|
24
24
|
*/
|
|
25
25
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
26
|
-
import { getTaskContentRevision, mergeNormalizedTaskIntoCollections, normalizeTaskFromApi, reconcileTaskCollection } from '../utils/taskNormalization.js';
|
|
26
|
+
import { getTaskContentRevision, mergeNormalizedTaskIntoCollections, normalizeTaskFromApi, reconcileTaskCollection, toTaskCollectionSummary } from '../utils/taskNormalization.js';
|
|
27
27
|
import { resolveTaskReferenceId } from '../utils/taskReferences.js';
|
|
28
|
-
export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks, setLoadingTasks, getCurrentWorkspaceId, workspaceResetKey, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls, handleUnauthorized, authRequiredForApi }) {
|
|
28
|
+
export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks, setLoadingTasks, getCurrentWorkspaceId, workspaceResetKey, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls, handleUnauthorized, authRequiredForApi, setError, setLoading }) {
|
|
29
29
|
// === SECTION: Revision Tracking ===
|
|
30
30
|
const taskRevisionMapRef = useRef(new Map());
|
|
31
31
|
const hasFetchedTasksRef = useRef(false);
|
|
@@ -34,6 +34,21 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
34
34
|
const archivedTasksRef = useRef(archivedTasks);
|
|
35
35
|
const tasksAbortRef = useRef(null);
|
|
36
36
|
const archiveAbortRef = useRef(null);
|
|
37
|
+
const taskDetailAbortRef = useRef(null);
|
|
38
|
+
const taskDetailCacheRef = useRef(new Map());
|
|
39
|
+
const [taskDetails, setTaskDetails] = useState([]);
|
|
40
|
+
const [staleTaskDetailRevisions, setStaleTaskDetailRevisions] = useState({});
|
|
41
|
+
const getTaskSummaryRevision = useCallback((task) => (String(task.detailRevision || JSON.stringify([
|
|
42
|
+
task.updatedAt || task.createdAt || '',
|
|
43
|
+
task.commentCount || 0,
|
|
44
|
+
task.checklistCount || 0,
|
|
45
|
+
task.completedChecklistCount || 0,
|
|
46
|
+
task.attachmentCount || 0,
|
|
47
|
+
task.relationshipCount || 0,
|
|
48
|
+
]))), []);
|
|
49
|
+
const publishTaskDetailCache = useCallback(() => {
|
|
50
|
+
setTaskDetails(Array.from(taskDetailCacheRef.current.values(), (entry) => entry.detail));
|
|
51
|
+
}, []);
|
|
37
52
|
useEffect(() => {
|
|
38
53
|
tasksRef.current = tasks;
|
|
39
54
|
}, [tasks]);
|
|
@@ -87,12 +102,68 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
87
102
|
tasksAbortRef.current = null;
|
|
88
103
|
archiveAbortRef.current?.abort('workspace-reset');
|
|
89
104
|
archiveAbortRef.current = null;
|
|
105
|
+
const hadPendingTaskDetail = taskDetailAbortRef.current !== null;
|
|
106
|
+
taskDetailAbortRef.current?.abort('workspace-reset');
|
|
107
|
+
taskDetailAbortRef.current = null;
|
|
108
|
+
if (hadPendingTaskDetail)
|
|
109
|
+
setLoading?.(false);
|
|
110
|
+
taskDetailCacheRef.current = new Map();
|
|
111
|
+
setTaskDetails([]);
|
|
112
|
+
setStaleTaskDetailRevisions({});
|
|
90
113
|
if (typeof window !== 'undefined') {
|
|
91
114
|
highlightTimersRef.current.forEach((timerId) => window.clearTimeout(timerId));
|
|
92
115
|
highlightTimersRef.current.clear();
|
|
93
116
|
}
|
|
94
117
|
setRecentlyChangedTaskIds([]);
|
|
95
|
-
}, [workspaceResetKey]);
|
|
118
|
+
}, [setLoading, workspaceResetKey]);
|
|
119
|
+
useEffect(() => () => {
|
|
120
|
+
taskDetailAbortRef.current?.abort('unmount');
|
|
121
|
+
}, []);
|
|
122
|
+
const reconcileTaskDetailCache = useCallback((summaries, archived) => {
|
|
123
|
+
const summaryById = new Map(summaries.map((task) => [task.id, task]));
|
|
124
|
+
let changed = false;
|
|
125
|
+
const next = new Map(taskDetailCacheRef.current);
|
|
126
|
+
const nextStaleRevisions = {};
|
|
127
|
+
const reconciledTaskIds = new Set();
|
|
128
|
+
for (const [taskId, cached] of next) {
|
|
129
|
+
const summary = summaryById.get(taskId);
|
|
130
|
+
const belongsToCollection = Boolean(cached.detail.isArchived) === archived;
|
|
131
|
+
if (belongsToCollection)
|
|
132
|
+
reconciledTaskIds.add(taskId);
|
|
133
|
+
if (!summary && belongsToCollection) {
|
|
134
|
+
next.delete(taskId);
|
|
135
|
+
changed = true;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (summary) {
|
|
139
|
+
const summaryRevision = getTaskSummaryRevision(summary);
|
|
140
|
+
if (summaryRevision !== cached.summaryRevision) {
|
|
141
|
+
nextStaleRevisions[taskId] = summaryRevision;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
setStaleTaskDetailRevisions((current) => {
|
|
146
|
+
const retained = Object.fromEntries(Object.entries(current).filter(([taskId]) => (!reconciledTaskIds.has(taskId))));
|
|
147
|
+
const combined = { ...retained, ...nextStaleRevisions };
|
|
148
|
+
const currentEntries = Object.entries(current);
|
|
149
|
+
const combinedEntries = Object.entries(combined);
|
|
150
|
+
return currentEntries.length === combinedEntries.length
|
|
151
|
+
&& currentEntries.every(([taskId, revision]) => combined[taskId] === revision)
|
|
152
|
+
? current
|
|
153
|
+
: combined;
|
|
154
|
+
});
|
|
155
|
+
if (changed) {
|
|
156
|
+
taskDetailCacheRef.current = next;
|
|
157
|
+
publishTaskDetailCache();
|
|
158
|
+
}
|
|
159
|
+
}, [getTaskSummaryRevision, publishTaskDetailCache]);
|
|
160
|
+
const cancelTaskDetailLoad = useCallback(() => {
|
|
161
|
+
const hadPendingTaskDetail = taskDetailAbortRef.current !== null;
|
|
162
|
+
taskDetailAbortRef.current?.abort('navigation');
|
|
163
|
+
taskDetailAbortRef.current = null;
|
|
164
|
+
if (hadPendingTaskDetail)
|
|
165
|
+
setLoading?.(false);
|
|
166
|
+
}, [setLoading]);
|
|
96
167
|
// === SECTION: Fetch Functions ===
|
|
97
168
|
const fetchTasks = useCallback(async (isSilent = false, options) => {
|
|
98
169
|
const ignoreAuthGuard = options?.ignoreAuthGuard === true;
|
|
@@ -126,6 +197,7 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
126
197
|
if (liveWorkspaceId !== requestWorkspaceId) {
|
|
127
198
|
return;
|
|
128
199
|
}
|
|
200
|
+
reconcileTaskDetailCache(sanitized, false);
|
|
129
201
|
const nextRevisionMap = new Map();
|
|
130
202
|
const changedTaskIds = [];
|
|
131
203
|
const highlightChangedTaskIds = options?.highlightChangedTaskIds === undefined
|
|
@@ -178,7 +250,8 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
178
250
|
setLoadingTasks,
|
|
179
251
|
authRequiredForApi,
|
|
180
252
|
shouldDeferProtectedApiCalls,
|
|
181
|
-
shouldBlockProtectedApiCalls
|
|
253
|
+
shouldBlockProtectedApiCalls,
|
|
254
|
+
reconcileTaskDetailCache
|
|
182
255
|
]);
|
|
183
256
|
const fetchArchive = useCallback(async (isSilent = false, options) => {
|
|
184
257
|
const ignoreAuthGuard = options?.ignoreAuthGuard === true;
|
|
@@ -202,6 +275,7 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
202
275
|
...normalizeTaskFromApi(t),
|
|
203
276
|
isArchived: true
|
|
204
277
|
}));
|
|
278
|
+
reconcileTaskDetailCache(sanitized, true);
|
|
205
279
|
const previousArchivedTasks = archivedTasksRef.current;
|
|
206
280
|
const reconciledArchivedTasks = reconcileTaskCollection(previousArchivedTasks, sanitized);
|
|
207
281
|
archivedTasksRef.current = reconciledArchivedTasks;
|
|
@@ -225,7 +299,109 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
225
299
|
setLoadingTasks(false);
|
|
226
300
|
}
|
|
227
301
|
}
|
|
228
|
-
}, [getCurrentWorkspaceId, isExpectedAbortFailure, setArchivedTasks, setLoadingTasks, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls]);
|
|
302
|
+
}, [getCurrentWorkspaceId, isExpectedAbortFailure, reconcileTaskDetailCache, setArchivedTasks, setLoadingTasks, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls]);
|
|
303
|
+
const loadTaskDetail = useCallback(async (task, options) => {
|
|
304
|
+
const force = options?.force === true;
|
|
305
|
+
if (!force && !task.isSummary) {
|
|
306
|
+
const summary = toTaskCollectionSummary(task);
|
|
307
|
+
taskDetailCacheRef.current.set(task.id, { detail: task, summaryRevision: getTaskSummaryRevision(summary) });
|
|
308
|
+
setStaleTaskDetailRevisions((current) => {
|
|
309
|
+
if (!Object.prototype.hasOwnProperty.call(current, task.id))
|
|
310
|
+
return current;
|
|
311
|
+
const next = { ...current };
|
|
312
|
+
delete next[task.id];
|
|
313
|
+
return next;
|
|
314
|
+
});
|
|
315
|
+
publishTaskDetailCache();
|
|
316
|
+
return task;
|
|
317
|
+
}
|
|
318
|
+
const cached = taskDetailCacheRef.current.get(task.id);
|
|
319
|
+
let requestedSummary = task;
|
|
320
|
+
let summaryRevision = getTaskSummaryRevision(requestedSummary);
|
|
321
|
+
if (!force && cached && cached.summaryRevision === summaryRevision)
|
|
322
|
+
return cached.detail;
|
|
323
|
+
const requestWorkspaceId = String(getCurrentWorkspaceId() || '').trim();
|
|
324
|
+
taskDetailAbortRef.current?.abort('superseded');
|
|
325
|
+
const abortController = new AbortController();
|
|
326
|
+
taskDetailAbortRef.current = abortController;
|
|
327
|
+
setLoading?.(true);
|
|
328
|
+
setError?.('');
|
|
329
|
+
try {
|
|
330
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
331
|
+
const response = await fetch(`/api/taskforce/task/${encodeURIComponent(requestedSummary.id)}`, {
|
|
332
|
+
signal: abortController.signal,
|
|
333
|
+
});
|
|
334
|
+
if (response.status === 401) {
|
|
335
|
+
handleUnauthorized();
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
if (response.status === 404) {
|
|
339
|
+
setError?.('Task not found. It may have moved or been deleted.');
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
if (!response.ok) {
|
|
343
|
+
setError?.('Unable to load task details.');
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const normalizedDetail = normalizeTaskFromApi(await response.json());
|
|
347
|
+
const liveWorkspaceId = String(getCurrentWorkspaceId() || '').trim();
|
|
348
|
+
if (abortController.signal.aborted || liveWorkspaceId !== requestWorkspaceId)
|
|
349
|
+
return null;
|
|
350
|
+
const currentSummary = [...tasksRef.current, ...archivedTasksRef.current]
|
|
351
|
+
.find((candidate) => candidate.id === requestedSummary.id);
|
|
352
|
+
if (!currentSummary) {
|
|
353
|
+
setError?.('Task not found. It may have moved or been deleted.');
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const currentRevision = getTaskSummaryRevision(currentSummary);
|
|
357
|
+
if (currentRevision !== summaryRevision) {
|
|
358
|
+
if (attempt === 0 && currentSummary) {
|
|
359
|
+
requestedSummary = currentSummary;
|
|
360
|
+
summaryRevision = currentRevision;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
setError?.('Task details changed while loading. Please open the task again.');
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
if (requestedSummary.updatedAt
|
|
367
|
+
&& normalizedDetail.updatedAt
|
|
368
|
+
&& Date.parse(normalizedDetail.updatedAt) < Date.parse(requestedSummary.updatedAt)) {
|
|
369
|
+
setError?.('Task details changed while loading. Please open the task again.');
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
const detail = {
|
|
373
|
+
...normalizedDetail,
|
|
374
|
+
detailRevision: normalizedDetail.detailRevision || summaryRevision,
|
|
375
|
+
};
|
|
376
|
+
if (options?.shouldCommit && !options.shouldCommit())
|
|
377
|
+
return null;
|
|
378
|
+
taskDetailCacheRef.current.set(detail.id, { detail, summaryRevision });
|
|
379
|
+
setStaleTaskDetailRevisions((current) => {
|
|
380
|
+
if (!Object.prototype.hasOwnProperty.call(current, detail.id))
|
|
381
|
+
return current;
|
|
382
|
+
const next = { ...current };
|
|
383
|
+
delete next[detail.id];
|
|
384
|
+
return next;
|
|
385
|
+
});
|
|
386
|
+
publishTaskDetailCache();
|
|
387
|
+
return detail;
|
|
388
|
+
}
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
if (isExpectedAbortFailure(error, abortController.signal))
|
|
393
|
+
return null;
|
|
394
|
+
console.error('[Taskforce] Failed to fetch task detail:', error);
|
|
395
|
+
setError?.('Unable to load task details.');
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
if (taskDetailAbortRef.current === abortController) {
|
|
400
|
+
taskDetailAbortRef.current = null;
|
|
401
|
+
setLoading?.(false);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}, [getCurrentWorkspaceId, getTaskSummaryRevision, handleUnauthorized, isExpectedAbortFailure, publishTaskDetailCache, setError, setLoading]);
|
|
229
405
|
const refreshTaskCollections = useCallback(async (options) => {
|
|
230
406
|
const isSilent = options?.isSilent !== false;
|
|
231
407
|
const ignoreAuthGuard = options?.ignoreAuthGuard === true;
|
|
@@ -253,7 +429,36 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
253
429
|
}, [refreshTaskCollections]);
|
|
254
430
|
// === SECTION: Merge Helper ===
|
|
255
431
|
const mergeTaskFromServer = useCallback((task) => {
|
|
256
|
-
const
|
|
432
|
+
const normalized = normalizeTaskFromApi(task);
|
|
433
|
+
const existingSummary = [...tasksRef.current, ...archivedTasksRef.current]
|
|
434
|
+
.find((candidate) => candidate.id === normalized.id);
|
|
435
|
+
const collectionSummary = normalized.isSummary
|
|
436
|
+
? normalized
|
|
437
|
+
: toTaskCollectionSummary({
|
|
438
|
+
...normalized,
|
|
439
|
+
detailRevision: undefined,
|
|
440
|
+
commentCount: normalized.commentCount ?? (normalized.comments ? undefined : existingSummary?.commentCount),
|
|
441
|
+
checklistCount: normalized.checklistCount ?? (normalized.checklistItems ? undefined : existingSummary?.checklistCount),
|
|
442
|
+
completedChecklistCount: normalized.completedChecklistCount
|
|
443
|
+
?? (normalized.checklistItems ? undefined : existingSummary?.completedChecklistCount),
|
|
444
|
+
attachmentCount: normalized.attachmentCount ?? (normalized.attachments ? undefined : existingSummary?.attachmentCount),
|
|
445
|
+
relationshipCount: normalized.relationshipCount ?? existingSummary?.relationshipCount ?? 0,
|
|
446
|
+
});
|
|
447
|
+
if (!normalized.isSummary) {
|
|
448
|
+
taskDetailCacheRef.current.set(normalized.id, {
|
|
449
|
+
detail: normalized,
|
|
450
|
+
summaryRevision: getTaskSummaryRevision(collectionSummary),
|
|
451
|
+
});
|
|
452
|
+
publishTaskDetailCache();
|
|
453
|
+
setStaleTaskDetailRevisions((current) => {
|
|
454
|
+
if (!Object.prototype.hasOwnProperty.call(current, normalized.id))
|
|
455
|
+
return current;
|
|
456
|
+
const next = { ...current };
|
|
457
|
+
delete next[normalized.id];
|
|
458
|
+
return next;
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
const merged = mergeNormalizedTaskIntoCollections(collectionSummary, tasksRef.current, archivedTasksRef.current);
|
|
257
462
|
const tasksChanged = merged.tasks !== tasksRef.current;
|
|
258
463
|
const archivedTasksChanged = merged.archivedTasks !== archivedTasksRef.current;
|
|
259
464
|
tasksRef.current = merged.tasks;
|
|
@@ -262,17 +467,21 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
262
467
|
setTasks(merged.tasks);
|
|
263
468
|
if (archivedTasksChanged)
|
|
264
469
|
setArchivedTasks(merged.archivedTasks);
|
|
265
|
-
}, [setTasks, setArchivedTasks]);
|
|
470
|
+
}, [getTaskSummaryRevision, publishTaskDetailCache, setTasks, setArchivedTasks]);
|
|
266
471
|
return {
|
|
267
472
|
// Stable refs (for use inside closures)
|
|
268
473
|
tasksRef,
|
|
269
474
|
archivedTasksRef,
|
|
475
|
+
taskDetails,
|
|
476
|
+
staleTaskDetailRevisions,
|
|
270
477
|
// Recently changed
|
|
271
478
|
recentlyChangedTaskIds,
|
|
272
479
|
markRecentlyChangedTasks,
|
|
273
480
|
// Fetch functions
|
|
274
481
|
fetchTasks,
|
|
275
482
|
fetchArchive,
|
|
483
|
+
loadTaskDetail,
|
|
484
|
+
cancelTaskDetailLoad,
|
|
276
485
|
refreshTaskCollections,
|
|
277
486
|
refreshTaskCollectionsFromInvalidation,
|
|
278
487
|
// Helpers
|
|
@@ -13,6 +13,7 @@ export interface UseTaskMutationsProps {
|
|
|
13
13
|
tasks: TaskItem[];
|
|
14
14
|
archivedTasks: TaskItem[];
|
|
15
15
|
editingTaskId: string | null;
|
|
16
|
+
editingTaskDetailRevision?: string | null;
|
|
16
17
|
setTasks: React.Dispatch<React.SetStateAction<TaskItem[]>>;
|
|
17
18
|
setArchivedTasks: React.Dispatch<React.SetStateAction<TaskItem[]>>;
|
|
18
19
|
setDeletedTasks: React.Dispatch<React.SetStateAction<DeletedTaskRecord[]>>;
|
|
@@ -44,6 +45,13 @@ export interface UseTaskMutationsProps {
|
|
|
44
45
|
durableTaskHttpRootActivationEnabled?: boolean;
|
|
45
46
|
workspaceId?: string;
|
|
46
47
|
taskHttpMutationClient?: ReturnType<typeof createCloudTaskHttpMutationClient>;
|
|
48
|
+
onTaskRevisionConflict?: (taskId: string, currentDetailRevision: string | null) => TaskRevisionConflictResolution | void | Promise<TaskRevisionConflictResolution | void>;
|
|
49
|
+
onTaskMutationAccepted?: (task: TaskItem) => void;
|
|
50
|
+
}
|
|
51
|
+
export interface TaskRevisionConflictResolution {
|
|
52
|
+
handled: boolean;
|
|
53
|
+
retryDetailRevision?: string;
|
|
54
|
+
retryPatch?: Partial<TaskItem>;
|
|
47
55
|
}
|
|
48
56
|
export interface DeletedTaskRestoreFailure {
|
|
49
57
|
deletedRecordId: string;
|
|
@@ -58,13 +66,13 @@ export interface DeletedTaskRestoreBatchResult {
|
|
|
58
66
|
succeeded: DeletedTaskRecord[];
|
|
59
67
|
failed: DeletedTaskRestoreFailure[];
|
|
60
68
|
}
|
|
61
|
-
export declare function useTaskMutations({ tasks, archivedTasks, editingTaskId, setTasks, setArchivedTasks, setDeletedTasks, setError, resetForm, setActiveTab, fetchTasks, fetchArchive, fetchDeletedTasks, mergeTaskFromServer, pushNotice, cloudAuthConfigured, runtimeMode, isAuthenticated, workspaceCloudSyncEnabled, buildWorkspaceSyncSignature, pushWorkspaceChangesToCloud, workspacePendingSignatureRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, mutateTaskMetadata, mutateTaskStatus, mutateTaskLifecycle, mutateTaskDelete, mutateTaskRestore, durableTaskMetadataActivationEnabled, durableTaskStatusActivationEnabled, durableTaskDeleteActivationEnabled, durableTaskHttpRootActivationEnabled, workspaceId, taskHttpMutationClient, }: UseTaskMutationsProps): {
|
|
69
|
+
export declare function useTaskMutations({ tasks, archivedTasks, editingTaskId, editingTaskDetailRevision, setTasks, setArchivedTasks, setDeletedTasks, setError, resetForm, setActiveTab, fetchTasks, fetchArchive, fetchDeletedTasks, mergeTaskFromServer, pushNotice, cloudAuthConfigured, runtimeMode, isAuthenticated, workspaceCloudSyncEnabled, buildWorkspaceSyncSignature, pushWorkspaceChangesToCloud, workspacePendingSignatureRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, mutateTaskMetadata, mutateTaskStatus, mutateTaskLifecycle, mutateTaskDelete, mutateTaskRestore, durableTaskMetadataActivationEnabled, durableTaskStatusActivationEnabled, durableTaskDeleteActivationEnabled, durableTaskHttpRootActivationEnabled, workspaceId, taskHttpMutationClient, onTaskRevisionConflict, onTaskMutationAccepted, }: UseTaskMutationsProps): {
|
|
62
70
|
copiedId: string | null;
|
|
63
71
|
handleDelete: (id: string, archive?: boolean, options?: {
|
|
64
72
|
skipConfirm?: boolean;
|
|
65
73
|
}) => Promise<boolean>;
|
|
66
74
|
handleUpdateTask: (id: string, updates: Partial<TaskItem>) => Promise<boolean | undefined>;
|
|
67
|
-
handleSetStatus: (task: TaskItem, status: DurableTaskTargetStatus, updates?: Partial<TaskItem
|
|
75
|
+
handleSetStatus: (task: TaskItem, status: DurableTaskTargetStatus, updates?: Partial<TaskItem>, expectedDetailRevisionOverride?: string | null) => Promise<void>;
|
|
68
76
|
handleToggleComplete: (task: TaskItem) => Promise<void>;
|
|
69
77
|
handleToggleCancel: (task: TaskItem) => Promise<void>;
|
|
70
78
|
handleToggleInProgress: (task: TaskItem) => Promise<void>;
|