@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { applyTaskCreateForDurableSync, assertTaskCreateIdentityAvailable, validateDurableTaskCreateInput, } from '../../core/TaskDurableCreateMutationService.js';
|
|
2
2
|
import { serializeTaskForSync } from '../taskSyncPayload.js';
|
|
3
3
|
import { buildTaskCreateActivityEventId } from './taskCreateActivityIdentity.js';
|
|
4
|
-
import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
|
|
4
|
+
import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
|
|
5
5
|
import { asWorkspaceSyncActivityRevision, buildEntityActivityJournalEntry, buildTaskActivityJournalEntry, serializeTaskActivityForSync, } from './taskActivityJournal.js';
|
|
6
6
|
import { fingerprintWorkspaceSyncPayload, WORKSPACE_SYNC_CONTRACT_VERSION, } from './syncDurabilityStore.js';
|
|
7
7
|
function requireZeroBaseRevision(value, label) {
|
|
@@ -151,13 +151,15 @@ export function runDurableTaskCreate(input) {
|
|
|
151
151
|
domain: 'task-attachment-links', entityId: taskId, mutationKind: 'replace', revisionKind: 'metadata',
|
|
152
152
|
baseEntityRevision: 0, baseLifecycleRevision: 0,
|
|
153
153
|
include: () => attachmentLinks.length > 0,
|
|
154
|
-
buildCanonicalFingerprint: (task) => fingerprintWorkspaceSyncPayload(
|
|
154
|
+
buildCanonicalFingerprint: (task) => fingerprintWorkspaceSyncPayload(serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
|
|
155
155
|
taskId: task.id,
|
|
156
156
|
links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
|
|
157
|
+
cardCoverAssetId: task.cardCoverAssetId ?? null,
|
|
157
158
|
})),
|
|
158
|
-
buildAuthoritativePayload: (task) =>
|
|
159
|
+
buildAuthoritativePayload: (task) => serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
|
|
159
160
|
taskId: task.id,
|
|
160
161
|
links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
|
|
162
|
+
cardCoverAssetId: task.cardCoverAssetId ?? null,
|
|
161
163
|
}),
|
|
162
164
|
}, buildTaskActivityJournalEntry({ eventId: activityEventId, readEvent: readActivity }), {
|
|
163
165
|
...buildEntityActivityJournalEntry({
|
|
@@ -257,9 +259,10 @@ export function runDurableTaskCreate(input) {
|
|
|
257
259
|
sequence: attachmentRevision.sequence,
|
|
258
260
|
entityRevision: attachmentRevision.entityRevision,
|
|
259
261
|
lifecycleRevision: attachmentRevision.lifecycleRevision,
|
|
260
|
-
payload:
|
|
262
|
+
payload: serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
|
|
261
263
|
taskId: task.id,
|
|
262
264
|
links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
|
|
265
|
+
cardCoverAssetId: task.cardCoverAssetId ?? null,
|
|
263
266
|
}),
|
|
264
267
|
} } : {}),
|
|
265
268
|
activity: serializeTaskActivityForSync(activity),
|
|
@@ -45,6 +45,7 @@ export interface DurableTaskMetadataServiceCore extends DurableTaskMetadataMutat
|
|
|
45
45
|
workspaceId: string;
|
|
46
46
|
eventId: string;
|
|
47
47
|
}): EntityEvent | null;
|
|
48
|
+
assertTaskDetailRevisionForUpdate?(id: string, expectedDetailRevision: string, workspaceId?: string): void;
|
|
48
49
|
}
|
|
49
50
|
export declare function runDurableTaskMetadataUpdate(input: {
|
|
50
51
|
store: WorkspaceSyncDurabilityStore;
|
|
@@ -67,5 +68,7 @@ export declare function runDurableTaskMetadataUpdate(input: {
|
|
|
67
68
|
originActorRef?: string;
|
|
68
69
|
saveSource?: 'manual' | 'autosave';
|
|
69
70
|
activityAction?: 'task-updated' | 'task-schedule-changed';
|
|
71
|
+
expectedDetailRevision?: string;
|
|
72
|
+
assertPrecondition?: () => void;
|
|
70
73
|
onPostCommitError?: (error: unknown) => void;
|
|
71
74
|
}): DurableTaskMetadataMutationResult;
|
|
@@ -35,6 +35,9 @@ export function runDurableTaskMetadataUpdate(input) {
|
|
|
35
35
|
...(input.activityAction && input.activityAction !== 'task-updated'
|
|
36
36
|
? { activityAction: input.activityAction }
|
|
37
37
|
: {}),
|
|
38
|
+
...(input.expectedDetailRevision
|
|
39
|
+
? { expectedDetailRevision: input.expectedDetailRevision }
|
|
40
|
+
: {}),
|
|
38
41
|
},
|
|
39
42
|
});
|
|
40
43
|
const activityEventId = buildTaskMetadataActivityEventId({
|
|
@@ -112,6 +115,7 @@ export function runDurableTaskMetadataUpdate(input) {
|
|
|
112
115
|
})),
|
|
113
116
|
}],
|
|
114
117
|
applyMutation: () => {
|
|
118
|
+
input.assertPrecondition?.();
|
|
115
119
|
const actorRef = resolveDeferredMutationActorRef(input);
|
|
116
120
|
if (!carriers)
|
|
117
121
|
return applyTaskMetadataUpdateForDurableSync({
|
|
@@ -2,7 +2,7 @@ import type { TaskEvent } from '../../core/Taskforce.js';
|
|
|
2
2
|
import type { TaskRestoreDestination } from '../../shared/taskRestoreDestination.js';
|
|
3
3
|
import { type DurableTaskRestoreCore } from '../../core/TaskDurableRestoreMutationService.js';
|
|
4
4
|
import { serializeTaskForSync } from '../taskSyncPayload.js';
|
|
5
|
-
import {
|
|
5
|
+
import { serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, type WorkspaceSyncV3TaskAttachmentLink } from './workspaceSyncV3NestedTaskCodecs.js';
|
|
6
6
|
import { type WorkspaceSyncActivityRevision } from './taskActivityJournal.js';
|
|
7
7
|
import { WorkspaceSyncDurabilityStore } from './syncDurabilityStore.js';
|
|
8
8
|
export interface DurableTaskRestoreServiceCore extends DurableTaskRestoreCore {
|
|
@@ -57,7 +57,7 @@ export type DurableTaskRestoreResponse = {
|
|
|
57
57
|
sequence: string;
|
|
58
58
|
entityRevision: string;
|
|
59
59
|
lifecycleRevision: string;
|
|
60
|
-
payload: ReturnType<typeof
|
|
60
|
+
payload: ReturnType<typeof serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload>;
|
|
61
61
|
};
|
|
62
62
|
activity: TaskEvent;
|
|
63
63
|
activityRevision: WorkspaceSyncActivityRevision;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { applyTaskRestoreForDurableSync, } from '../../core/TaskDurableRestoreMutationService.js';
|
|
2
2
|
import { serializeTaskForSync } from '../taskSyncPayload.js';
|
|
3
3
|
import { buildTaskLifecycleActivityEventId } from './taskLifecycleActivityIdentity.js';
|
|
4
|
-
import {
|
|
4
|
+
import { serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
|
|
5
5
|
import { asWorkspaceSyncActivityRevision, buildTaskActivityJournalEntry, } from './taskActivityJournal.js';
|
|
6
6
|
import { fingerprintWorkspaceSyncPayload, WORKSPACE_SYNC_CONTRACT_VERSION, } from './syncDurabilityStore.js';
|
|
7
7
|
export function runDurableTaskRestore(input) {
|
|
@@ -90,13 +90,15 @@ export function runDurableTaskRestore(input) {
|
|
|
90
90
|
domain: 'task-attachment-links', entityId: input.taskId, mutationKind: 'replace', revisionKind: 'metadata',
|
|
91
91
|
baseEntityRevision: 0, baseLifecycleRevision: 0,
|
|
92
92
|
baseRevisionPolicy: 'current',
|
|
93
|
-
buildCanonicalFingerprint: (task) => fingerprintWorkspaceSyncPayload(
|
|
93
|
+
buildCanonicalFingerprint: (task) => fingerprintWorkspaceSyncPayload(serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
|
|
94
94
|
taskId: task.id,
|
|
95
95
|
links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
|
|
96
|
+
cardCoverAssetId: task.cardCoverAssetId ?? null,
|
|
96
97
|
})),
|
|
97
|
-
buildAuthoritativePayload: (task) =>
|
|
98
|
+
buildAuthoritativePayload: (task) => serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
|
|
98
99
|
taskId: task.id,
|
|
99
100
|
links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
|
|
101
|
+
cardCoverAssetId: task.cardCoverAssetId ?? null,
|
|
100
102
|
}),
|
|
101
103
|
}, buildTaskActivityJournalEntry({ eventId: activityEventId, readEvent: readActivity })],
|
|
102
104
|
applyMutation: () => applyTaskRestoreForDurableSync({
|
|
@@ -151,9 +153,10 @@ export function runDurableTaskRestore(input) {
|
|
|
151
153
|
domain: 'task-attachment-links', entityId: task.id, mutationKind: 'replace',
|
|
152
154
|
sequence: attachmentRevision.sequence, entityRevision: attachmentRevision.entityRevision,
|
|
153
155
|
lifecycleRevision: attachmentRevision.lifecycleRevision,
|
|
154
|
-
payload:
|
|
156
|
+
payload: serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
|
|
155
157
|
taskId: task.id,
|
|
156
158
|
links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
|
|
159
|
+
cardCoverAssetId: task.cardCoverAssetId ?? null,
|
|
157
160
|
}),
|
|
158
161
|
},
|
|
159
162
|
activity: readActivity(),
|
|
@@ -64,6 +64,8 @@ export declare function runDurableTaskStatusMutation(input: {
|
|
|
64
64
|
resolveActorRef?: () => string;
|
|
65
65
|
originActorRef?: string;
|
|
66
66
|
includeWorkflowRuntimeJournal?: boolean;
|
|
67
|
+
expectedDetailRevision?: string;
|
|
68
|
+
assertPrecondition?: () => void;
|
|
67
69
|
onPostCommitError?: (error: unknown) => void;
|
|
68
70
|
}): {
|
|
69
71
|
state: 'in_progress';
|
|
@@ -34,6 +34,9 @@ export function runDurableTaskStatusMutation(input) {
|
|
|
34
34
|
baseLifecycleRevision: String(input.baseLifecycleRevision),
|
|
35
35
|
payload: operationPayload,
|
|
36
36
|
...(input.originActorRef ? { originActorRef: input.originActorRef } : {}),
|
|
37
|
+
...(input.expectedDetailRevision
|
|
38
|
+
? { expectedDetailRevision: input.expectedDetailRevision }
|
|
39
|
+
: {}),
|
|
37
40
|
},
|
|
38
41
|
});
|
|
39
42
|
const activityEventId = buildTaskLifecycleActivityEventId({
|
|
@@ -184,6 +187,7 @@ export function runDurableTaskStatusMutation(input) {
|
|
|
184
187
|
})),
|
|
185
188
|
}, ...workflowEntries],
|
|
186
189
|
applyMutation: () => {
|
|
190
|
+
input.assertPrecondition?.();
|
|
187
191
|
const currentTask = input.core.getTask(input.taskId, input.workspaceId);
|
|
188
192
|
if (operationPayload.workflowAssignment) {
|
|
189
193
|
if (payload.status !== 'in-progress'
|
|
@@ -2,6 +2,7 @@ import type { WorkflowAssignmentSyncRecord, WorkflowExecutionEventRecord, Workfl
|
|
|
2
2
|
import { type DurableTaskStatusMutationPayload } from '../../core/TaskDurableStatusMutationService.js';
|
|
3
3
|
export type TaskStatusOperationWithWorkflowAssignment = DurableTaskStatusMutationPayload & {
|
|
4
4
|
workflowAssignment?: WorkflowAssignmentSyncRecord;
|
|
5
|
+
expectedDetailRevision?: string;
|
|
5
6
|
};
|
|
6
7
|
export declare class WorkflowAssignmentSyncError extends Error {
|
|
7
8
|
readonly code = "WORKFLOW_ASSIGNMENT_SYNC_INVALID";
|
|
@@ -209,5 +209,8 @@ export function normalizeTaskStatusOperationWithWorkflowAssignment(raw) {
|
|
|
209
209
|
? {}
|
|
210
210
|
: { workflowAssignment: normalizeWorkflowAssignmentSyncRecord(source.workflowAssignment) }),
|
|
211
211
|
...(originActor ? { originActor } : {}),
|
|
212
|
+
...(typeof source.expectedDetailRevision === 'string' && source.expectedDetailRevision.trim()
|
|
213
|
+
? { expectedDetailRevision: source.expectedDetailRevision.trim() }
|
|
214
|
+
: {}),
|
|
212
215
|
};
|
|
213
216
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WorkspaceSyncIncomingTaskApplyModel } from '../syncApplyHandlers.js';
|
|
2
2
|
import type { WorkspaceSyncChange } from '../workspaceSyncModel.js';
|
|
3
|
-
export declare const WORKSPACE_SYNC_V2_EXCLUDABLE_PROJECTIONS: readonly ["task/root-v1", "task-relationship/full-v1", "taxonomy/full-v1", "initiative/full-v1", "workstream/full-v1", "task-comments/full-v1", "task-checklist/full-v1", "task-attachment-links/full-v1", "entity-event/full-v1", "workstream-task-order/full-v1"];
|
|
3
|
+
export declare const WORKSPACE_SYNC_V2_EXCLUDABLE_PROJECTIONS: readonly ["task/root-v1", "task-relationship/full-v1", "taxonomy/full-v1", "initiative/full-v1", "workstream/full-v1", "task-comments/full-v1", "task-checklist/full-v1", "task-attachment-links/full-v1", "task-attachment-links/full-v2", "entity-event/full-v1", "workstream-task-order/full-v1"];
|
|
4
4
|
export type WorkspaceSyncV2ProjectionExclusion = typeof WORKSPACE_SYNC_V2_EXCLUDABLE_PROJECTIONS[number];
|
|
5
5
|
export declare const WORKSPACE_SYNC_V3_COMBINED_V2_EXCLUSIONS: readonly WorkspaceSyncV2ProjectionExclusion[];
|
|
6
6
|
export declare function normalizeWorkspaceSyncV2ProjectionExclusions(values: Iterable<unknown> | null | undefined): WorkspaceSyncV2ProjectionExclusion[];
|
|
@@ -8,6 +8,7 @@ export const WORKSPACE_SYNC_V2_EXCLUDABLE_PROJECTIONS = Object.freeze([
|
|
|
8
8
|
'task-comments/full-v1',
|
|
9
9
|
'task-checklist/full-v1',
|
|
10
10
|
'task-attachment-links/full-v1',
|
|
11
|
+
'task-attachment-links/full-v2',
|
|
11
12
|
'entity-event/full-v1',
|
|
12
13
|
'workstream-task-order/full-v1',
|
|
13
14
|
]);
|
|
@@ -49,7 +50,9 @@ export function prepareWorkspaceSyncV2TaskCarriers(taskMutations, excludedProjec
|
|
|
49
50
|
return taskMutations;
|
|
50
51
|
const commentsOwned = excludedProjections.has('task-comments/full-v1');
|
|
51
52
|
const checklistOwned = excludedProjections.has('task-checklist/full-v1');
|
|
52
|
-
const
|
|
53
|
+
const cardCoverOwned = excludedProjections.has('task-attachment-links/full-v2');
|
|
54
|
+
const attachmentsOwned = excludedProjections.has('task-attachment-links/full-v1')
|
|
55
|
+
|| cardCoverOwned;
|
|
53
56
|
return {
|
|
54
57
|
upserts: taskMutations.upserts.map(({ archived, task }) => {
|
|
55
58
|
const existing = readExistingTask?.(String(task.id || '').trim());
|
|
@@ -63,6 +66,7 @@ export function prepareWorkspaceSyncV2TaskCarriers(taskMutations, excludedProjec
|
|
|
63
66
|
...(commentsOwned ? { comments: existing.comments || [] } : {}),
|
|
64
67
|
...(checklistOwned ? { checklistItems: existing.checklistItems || [] } : {}),
|
|
65
68
|
...(attachmentsOwned ? { attachments: existing.attachments || [] } : {}),
|
|
69
|
+
...(cardCoverOwned ? { cardCoverAssetId: existing.cardCoverAssetId ?? null } : {}),
|
|
66
70
|
},
|
|
67
71
|
};
|
|
68
72
|
}),
|
|
@@ -100,7 +104,10 @@ export function partitionWorkspaceSyncV2LegacyChanges(changes, excludedProjectio
|
|
|
100
104
|
if (change.op === 'upsert') {
|
|
101
105
|
const commentsOwned = excludedProjections.has('task-comments/full-v1');
|
|
102
106
|
const checklistOwned = excludedProjections.has('task-checklist/full-v1');
|
|
103
|
-
const
|
|
107
|
+
const attachmentProjection = excludedProjections.has('task-attachment-links/full-v2')
|
|
108
|
+
? 'task-attachment-links/full-v2'
|
|
109
|
+
: 'task-attachment-links/full-v1';
|
|
110
|
+
const attachmentsOwned = excludedProjections.has(attachmentProjection);
|
|
104
111
|
if (excludedProjections.has('task/root-v1'))
|
|
105
112
|
suppress('task/root-v1');
|
|
106
113
|
if (commentsOwned || checklistOwned || attachmentsOwned) {
|
|
@@ -115,7 +122,10 @@ export function partitionWorkspaceSyncV2LegacyChanges(changes, excludedProjectio
|
|
|
115
122
|
}
|
|
116
123
|
if (attachmentsOwned) {
|
|
117
124
|
delete task.attachments;
|
|
118
|
-
|
|
125
|
+
if (attachmentProjection === 'task-attachment-links/full-v2') {
|
|
126
|
+
delete task.cardCoverAssetId;
|
|
127
|
+
}
|
|
128
|
+
suppress(attachmentProjection);
|
|
119
129
|
}
|
|
120
130
|
accepted.push({ ...change, task });
|
|
121
131
|
continue;
|
|
@@ -15,10 +15,13 @@ export interface WorkspaceSyncV3AttachmentApplyObligation {
|
|
|
15
15
|
createdAt: string;
|
|
16
16
|
updatedAt: string;
|
|
17
17
|
nextAttemptAt: string;
|
|
18
|
+
terminalAt: string | null;
|
|
19
|
+
terminalReason: string | null;
|
|
18
20
|
resolvedAt: string | null;
|
|
19
21
|
}
|
|
20
22
|
export interface WorkspaceSyncV3AttachmentApplyObligationSummary {
|
|
21
23
|
open: number;
|
|
24
|
+
terminal: number;
|
|
22
25
|
due: number;
|
|
23
26
|
oldestOpenCreatedAt: string | null;
|
|
24
27
|
nextAttemptAt: string | null;
|
|
@@ -70,6 +73,14 @@ export declare class WorkspaceSyncV3AttachmentApplyObligationStore {
|
|
|
70
73
|
taskId: string;
|
|
71
74
|
now: string;
|
|
72
75
|
}): void;
|
|
76
|
+
markTerminalUnavailable(input: {
|
|
77
|
+
tenantId: string;
|
|
78
|
+
workspaceId: string;
|
|
79
|
+
taskId: string;
|
|
80
|
+
canonicalFingerprint: string;
|
|
81
|
+
reason: string;
|
|
82
|
+
now: string;
|
|
83
|
+
}): void;
|
|
73
84
|
recordAttemptFailure(input: {
|
|
74
85
|
tenantId: string;
|
|
75
86
|
workspaceId: string;
|
|
@@ -28,6 +28,8 @@ function mapRow(row) {
|
|
|
28
28
|
createdAt: row.created_at,
|
|
29
29
|
updatedAt: row.updated_at,
|
|
30
30
|
nextAttemptAt: row.next_attempt_at,
|
|
31
|
+
terminalAt: row.terminal_at,
|
|
32
|
+
terminalReason: row.terminal_reason,
|
|
31
33
|
resolvedAt: row.resolved_at,
|
|
32
34
|
};
|
|
33
35
|
}
|
|
@@ -52,8 +54,8 @@ export class WorkspaceSyncV3AttachmentApplyObligationStore {
|
|
|
52
54
|
tenant_id, workspace_id, task_id, asset_ids_json, payload_json,
|
|
53
55
|
canonical_fingerprint, source_sequence, attempts, last_error,
|
|
54
56
|
entity_revision, lifecycle_revision, revision_fingerprint,
|
|
55
|
-
created_at, updated_at, next_attempt_at, resolved_at
|
|
56
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, NULL)
|
|
57
|
+
created_at, updated_at, next_attempt_at, terminal_at, terminal_reason, resolved_at
|
|
58
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL)
|
|
57
59
|
ON CONFLICT (tenant_id, workspace_id, task_id) DO UPDATE SET
|
|
58
60
|
asset_ids_json = excluded.asset_ids_json,
|
|
59
61
|
payload_json = excluded.payload_json,
|
|
@@ -66,6 +68,8 @@ export class WorkspaceSyncV3AttachmentApplyObligationStore {
|
|
|
66
68
|
last_error = excluded.last_error,
|
|
67
69
|
updated_at = excluded.updated_at,
|
|
68
70
|
next_attempt_at = excluded.next_attempt_at,
|
|
71
|
+
terminal_at = NULL,
|
|
72
|
+
terminal_reason = NULL,
|
|
69
73
|
resolved_at = NULL
|
|
70
74
|
WHERE workspace_sync_v3_attachment_apply_obligations.resolved_at IS NOT NULL
|
|
71
75
|
OR CAST(excluded.source_sequence AS ${this.db.provider === 'postgres' ? 'BIGINT' : 'INTEGER'})
|
|
@@ -77,7 +81,8 @@ export class WorkspaceSyncV3AttachmentApplyObligationStore {
|
|
|
77
81
|
const rows = this.db.prepare(`
|
|
78
82
|
SELECT *
|
|
79
83
|
FROM workspace_sync_v3_attachment_apply_obligations
|
|
80
|
-
WHERE tenant_id = ? AND workspace_id = ?
|
|
84
|
+
WHERE tenant_id = ? AND workspace_id = ?
|
|
85
|
+
AND resolved_at IS NULL AND terminal_at IS NULL AND next_attempt_at <= ?
|
|
81
86
|
ORDER BY next_attempt_at ASC, task_id ASC
|
|
82
87
|
LIMIT ?
|
|
83
88
|
`).all(input.tenantId, input.workspaceId, input.now, limit);
|
|
@@ -87,30 +92,34 @@ export class WorkspaceSyncV3AttachmentApplyObligationStore {
|
|
|
87
92
|
const row = this.db.prepare(`
|
|
88
93
|
SELECT COUNT(*) AS count
|
|
89
94
|
FROM workspace_sync_v3_attachment_apply_obligations
|
|
90
|
-
WHERE tenant_id = ? AND workspace_id = ?
|
|
95
|
+
WHERE tenant_id = ? AND workspace_id = ?
|
|
96
|
+
AND resolved_at IS NULL AND terminal_at IS NULL
|
|
91
97
|
`).get(input.tenantId, input.workspaceId);
|
|
92
98
|
return Number(row?.count || 0);
|
|
93
99
|
}
|
|
94
100
|
readSummary(input) {
|
|
95
101
|
const aggregate = this.db.prepare(`
|
|
96
|
-
SELECT
|
|
97
|
-
SUM(CASE WHEN
|
|
98
|
-
|
|
99
|
-
MIN(
|
|
100
|
-
|
|
102
|
+
SELECT SUM(CASE WHEN terminal_at IS NULL THEN 1 ELSE 0 END) AS open,
|
|
103
|
+
SUM(CASE WHEN terminal_at IS NOT NULL THEN 1 ELSE 0 END) AS terminal,
|
|
104
|
+
SUM(CASE WHEN terminal_at IS NULL AND next_attempt_at <= ? THEN 1 ELSE 0 END) AS due,
|
|
105
|
+
MIN(CASE WHEN terminal_at IS NULL THEN created_at END) AS oldest_open_created_at,
|
|
106
|
+
MIN(CASE WHEN terminal_at IS NULL THEN next_attempt_at END) AS next_attempt_at,
|
|
107
|
+
SUM(CASE WHEN terminal_at IS NULL THEN attempts ELSE 0 END) AS total_attempts
|
|
101
108
|
FROM workspace_sync_v3_attachment_apply_obligations
|
|
102
109
|
WHERE tenant_id = ? AND workspace_id = ? AND resolved_at IS NULL
|
|
103
110
|
`).get(input.now, input.tenantId, input.workspaceId);
|
|
104
111
|
const latest = this.db.prepare(`
|
|
105
112
|
SELECT last_error
|
|
106
113
|
FROM workspace_sync_v3_attachment_apply_obligations
|
|
107
|
-
WHERE tenant_id = ? AND workspace_id = ?
|
|
114
|
+
WHERE tenant_id = ? AND workspace_id = ?
|
|
115
|
+
AND resolved_at IS NULL AND terminal_at IS NULL
|
|
108
116
|
AND last_error IS NOT NULL
|
|
109
117
|
ORDER BY updated_at DESC, task_id ASC
|
|
110
118
|
LIMIT 1
|
|
111
119
|
`).get(input.tenantId, input.workspaceId);
|
|
112
120
|
return {
|
|
113
121
|
open: Number(aggregate?.open || 0),
|
|
122
|
+
terminal: Number(aggregate?.terminal || 0),
|
|
114
123
|
due: Number(aggregate?.due || 0),
|
|
115
124
|
oldestOpenCreatedAt: aggregate?.oldest_open_created_at || null,
|
|
116
125
|
nextAttemptAt: aggregate?.next_attempt_at || null,
|
|
@@ -142,6 +151,14 @@ export class WorkspaceSyncV3AttachmentApplyObligationStore {
|
|
|
142
151
|
WHERE tenant_id = ? AND workspace_id = ? AND task_id = ? AND resolved_at IS NULL
|
|
143
152
|
`).run(input.now, input.now, input.tenantId, input.workspaceId, input.taskId);
|
|
144
153
|
}
|
|
154
|
+
markTerminalUnavailable(input) {
|
|
155
|
+
this.db.prepare(`
|
|
156
|
+
UPDATE workspace_sync_v3_attachment_apply_obligations
|
|
157
|
+
SET terminal_at = ?, terminal_reason = ?, last_error = ?, updated_at = ?
|
|
158
|
+
WHERE tenant_id = ? AND workspace_id = ? AND task_id = ?
|
|
159
|
+
AND canonical_fingerprint = ? AND resolved_at IS NULL AND terminal_at IS NULL
|
|
160
|
+
`).run(input.now, String(input.reason || '').slice(0, 500), String(input.reason || '').slice(0, 500), input.now, input.tenantId, input.workspaceId, input.taskId, input.canonicalFingerprint);
|
|
161
|
+
}
|
|
145
162
|
recordAttemptFailure(input) {
|
|
146
163
|
this.db.prepare(`
|
|
147
164
|
UPDATE workspace_sync_v3_attachment_apply_obligations
|
|
@@ -7,10 +7,11 @@ export type WorkspaceSyncV3AttachmentAssetHydrationResult = {
|
|
|
7
7
|
} | {
|
|
8
8
|
success: false;
|
|
9
9
|
error: string;
|
|
10
|
+
disposition?: 'retryable' | 'terminal-unavailable';
|
|
10
11
|
};
|
|
11
12
|
export declare function hydrateWorkspaceSyncV3AttachmentAsset(input: {
|
|
12
13
|
workspaceId: string;
|
|
13
14
|
assetId: string;
|
|
14
|
-
remoteServices: Pick<WorkspaceSyncEngineRemoteServices, 'pullWorkspaceContent' | 'abandonTransferReceipts' | '
|
|
15
|
+
remoteServices: Pick<WorkspaceSyncEngineRemoteServices, 'pullWorkspaceContent' | 'abandonTransferReceipts' | 'buildIndependentContentApplyCompletionGroup'>;
|
|
15
16
|
localServices: Pick<WorkspaceSyncEngineLocalServices, 'applyV2Batch'>;
|
|
16
17
|
}): Promise<WorkspaceSyncV3AttachmentAssetHydrationResult>;
|
|
@@ -3,20 +3,48 @@ import { fingerprintWorkspaceContentBody } from '../workspaceContentManifest.js'
|
|
|
3
3
|
export async function hydrateWorkspaceSyncV3AttachmentAsset(input) {
|
|
4
4
|
const remoteServices = input.remoteServices;
|
|
5
5
|
const applyV2Batch = input.localServices.applyV2Batch;
|
|
6
|
+
const completionIdentity = `attachment-asset:${input.assetId}`;
|
|
6
7
|
const response = await remoteServices.pullWorkspaceContent({
|
|
7
8
|
workspaceId: input.workspaceId,
|
|
8
9
|
requests: [{ kind: 'asset', assetId: input.assetId }],
|
|
9
10
|
previousCursor: null,
|
|
11
|
+
independentCompletionIdentity: completionIdentity,
|
|
10
12
|
});
|
|
11
13
|
const receipt = response.transferReceipt;
|
|
12
14
|
let transferCommitted = false;
|
|
15
|
+
let localApplyStarted = false;
|
|
13
16
|
try {
|
|
14
17
|
if (!response.ok) {
|
|
15
18
|
return { success: false, error: `Workspace attachment asset hydration failed (${response.status || 0}).` };
|
|
16
19
|
}
|
|
17
20
|
const data = response.data || {};
|
|
18
|
-
if ((data.
|
|
19
|
-
return {
|
|
21
|
+
if ((data.remaining?.length || 0) > 0) {
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
error: `Workspace attachment asset hydration is incomplete: ${input.assetId}`,
|
|
25
|
+
disposition: 'retryable',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const exactMissing = Array.isArray(data.missing) && data.missing.find((entry) => (entry?.kind === 'asset'
|
|
29
|
+
&& 'assetId' in entry
|
|
30
|
+
&& String(entry.assetId || '').trim() === input.assetId));
|
|
31
|
+
if (exactMissing) {
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
error: exactMissing.disposition === 'terminal-unavailable'
|
|
35
|
+
? `Workspace attachment asset is authoritatively unavailable in cloud metadata: ${input.assetId}`
|
|
36
|
+
: `Workspace attachment asset body is temporarily unavailable in cloud content: ${input.assetId}`,
|
|
37
|
+
disposition: exactMissing.disposition === 'terminal-unavailable'
|
|
38
|
+
? 'terminal-unavailable'
|
|
39
|
+
: 'retryable',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if ((data.missing?.length || 0) > 0) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: `Workspace attachment asset hydration returned an unexpected missing result: ${input.assetId}`,
|
|
46
|
+
disposition: 'retryable',
|
|
47
|
+
};
|
|
20
48
|
}
|
|
21
49
|
const rawChanges = Array.isArray(data.changes) ? data.changes : [];
|
|
22
50
|
const changes = rawChanges.filter((change) => ((change.op === 'document-upsert' || change.op === 'asset-upsert')
|
|
@@ -50,10 +78,9 @@ export async function hydrateWorkspaceSyncV3AttachmentAsset(input) {
|
|
|
50
78
|
return { success: false, error: `Workspace attachment asset hydration verification failed: ${input.assetId}` };
|
|
51
79
|
}
|
|
52
80
|
const completionGroup = receipt
|
|
53
|
-
? remoteServices.
|
|
81
|
+
? remoteServices.buildIndependentContentApplyCompletionGroup({
|
|
54
82
|
workspaceId: input.workspaceId,
|
|
55
|
-
|
|
56
|
-
nextCursor: null,
|
|
83
|
+
identity: completionIdentity,
|
|
57
84
|
receipts: [receipt],
|
|
58
85
|
})
|
|
59
86
|
: undefined;
|
|
@@ -63,6 +90,7 @@ export async function hydrateWorkspaceSyncV3AttachmentAsset(input) {
|
|
|
63
90
|
error: 'Workspace attachment asset hydration could not bind its transfer to local apply.',
|
|
64
91
|
};
|
|
65
92
|
}
|
|
93
|
+
localApplyStarted = true;
|
|
66
94
|
const applyResult = completionGroup
|
|
67
95
|
? await applyV2Batch(input.workspaceId, changes, { completionGroup })
|
|
68
96
|
: await applyV2Batch(input.workspaceId, changes);
|
|
@@ -80,11 +108,25 @@ export async function hydrateWorkspaceSyncV3AttachmentAsset(input) {
|
|
|
80
108
|
}
|
|
81
109
|
finally {
|
|
82
110
|
if (receipt && !transferCommitted) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
111
|
+
if (!localApplyStarted) {
|
|
112
|
+
const completionGroup = remoteServices.buildIndependentContentApplyCompletionGroup({
|
|
113
|
+
workspaceId: input.workspaceId,
|
|
114
|
+
identity: completionIdentity,
|
|
115
|
+
receipts: [receipt],
|
|
116
|
+
});
|
|
117
|
+
if (completionGroup) {
|
|
118
|
+
localApplyStarted = true;
|
|
119
|
+
const settlement = await applyV2Batch(input.workspaceId, [], { completionGroup });
|
|
120
|
+
transferCommitted = settlement.ok;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!transferCommitted) {
|
|
124
|
+
await remoteServices.abandonTransferReceipts({
|
|
125
|
+
workspaceId: input.workspaceId,
|
|
126
|
+
receipts: [receipt],
|
|
127
|
+
errorCode: 'WORKSPACE_SYNC_V3_ATTACHMENT_HYDRATION_NOT_COMMITTED',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
88
130
|
}
|
|
89
131
|
}
|
|
90
132
|
}
|
|
@@ -71,6 +71,10 @@ export function createWorkspaceSyncV3CloudOperationHandlers() {
|
|
|
71
71
|
...withDeferredOriginActor(context, operation.payload.originActor),
|
|
72
72
|
taskId: operation.entityId, patch: operation.payload.patch,
|
|
73
73
|
saveSource: operation.payload.saveSource,
|
|
74
|
+
expectedDetailRevision: operation.payload.expectedDetailRevision,
|
|
75
|
+
assertPrecondition: operation.payload.expectedDetailRevision && context.core.assertTaskDetailRevisionForUpdate
|
|
76
|
+
? () => context.core.assertTaskDetailRevisionForUpdate(operation.entityId, operation.payload.expectedDetailRevision, context.workspaceId)
|
|
77
|
+
: undefined,
|
|
74
78
|
baseEntityRevision: operation.baseEntityRevision, baseLifecycleRevision: operation.baseLifecycleRevision,
|
|
75
79
|
});
|
|
76
80
|
},
|
|
@@ -187,11 +191,15 @@ export function createWorkspaceSyncV3CloudOperationHandlers() {
|
|
|
187
191
|
supports: (envelope) => envelope.operation.domain === 'task' && envelope.operation.mutationKind === 'set-status',
|
|
188
192
|
apply: (envelope, context) => {
|
|
189
193
|
const operation = envelope.operation;
|
|
190
|
-
const { originActor, ...payload } = operation.payload;
|
|
194
|
+
const { originActor, expectedDetailRevision, ...payload } = operation.payload;
|
|
191
195
|
return runDurableTaskStatusMutation({
|
|
192
196
|
...withDeferredOriginActor(context, originActor),
|
|
193
197
|
taskId: operation.entityId,
|
|
194
198
|
payload,
|
|
199
|
+
expectedDetailRevision,
|
|
200
|
+
assertPrecondition: expectedDetailRevision && context.core.assertTaskDetailRevisionForUpdate
|
|
201
|
+
? () => context.core.assertTaskDetailRevisionForUpdate(operation.entityId, expectedDetailRevision, context.workspaceId)
|
|
202
|
+
: undefined,
|
|
195
203
|
baseEntityRevision: operation.baseEntityRevision,
|
|
196
204
|
baseLifecycleRevision: operation.baseLifecycleRevision,
|
|
197
205
|
});
|
|
@@ -325,10 +325,14 @@ async function runWorkspaceSyncV3CombinedProjectionCycleInternal(input, staleRep
|
|
|
325
325
|
let projection = initial;
|
|
326
326
|
let recoveryRepairApplied = false;
|
|
327
327
|
let restartStaleRepair = false;
|
|
328
|
+
let restartCoverageRepair = false;
|
|
328
329
|
let restartedStaleRepair = staleRepairAlreadyRestarted;
|
|
329
330
|
const persistedCoverage = projectionCoverage(projection);
|
|
330
331
|
if (!persistedCoverage)
|
|
331
332
|
return localFailure(projection, 'Persisted combined workspace sync coverage is unsupported.');
|
|
333
|
+
restartCoverageRepair = projection.owner === 'v3'
|
|
334
|
+
&& projection.repair !== null
|
|
335
|
+
&& !exactCoverage(persistedCoverage, coverage);
|
|
332
336
|
if (projection.coverageRepairRequired
|
|
333
337
|
|| (projection.owner === 'v3' && !projection.repair && !exactCoverage(persistedCoverage, coverage))) {
|
|
334
338
|
const feed = await dependencies.pullFeed(input.resolveCloudAuthUrl, {
|
|
@@ -413,12 +417,12 @@ async function runWorkspaceSyncV3CombinedProjectionCycleInternal(input, staleRep
|
|
|
413
417
|
}
|
|
414
418
|
let restartExpiredRepair = false;
|
|
415
419
|
let restartedExpiredRepair = false;
|
|
416
|
-
while (projection.owner === 'v2' || projection.repair || restartStaleRepair) {
|
|
417
|
-
const previousCursor = restartExpiredRepair || restartStaleRepair
|
|
420
|
+
while (projection.owner === 'v2' || projection.repair || restartStaleRepair || restartCoverageRepair) {
|
|
421
|
+
const previousCursor = restartExpiredRepair || restartStaleRepair || restartCoverageRepair
|
|
418
422
|
? null : projection.repair?.cursor || null;
|
|
419
423
|
const feed = await dependencies.pullFeed(input.resolveCloudAuthUrl, {
|
|
420
424
|
workspaceId: input.workspaceId,
|
|
421
|
-
...(restartExpiredRepair || restartStaleRepair
|
|
425
|
+
...(restartExpiredRepair || restartStaleRepair || restartCoverageRepair
|
|
422
426
|
? { forceRepair: true }
|
|
423
427
|
: { repairCursor: previousCursor }),
|
|
424
428
|
limit: 100,
|
|
@@ -449,6 +453,7 @@ async function runWorkspaceSyncV3CombinedProjectionCycleInternal(input, staleRep
|
|
|
449
453
|
projection = updated;
|
|
450
454
|
restartExpiredRepair = false;
|
|
451
455
|
restartStaleRepair = false;
|
|
456
|
+
restartCoverageRepair = false;
|
|
452
457
|
if (repairSnapshotWasInvalidated(applied)) {
|
|
453
458
|
if (restartedStaleRepair) {
|
|
454
459
|
return localFailure(projection, 'A refreshed combined repair snapshot still predates authoritative local state.');
|