@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
|
@@ -3,13 +3,30 @@ import { Worker } from 'worker_threads';
|
|
|
3
3
|
import { performance } from 'node:perf_hooks';
|
|
4
4
|
import { assertSynchronousTransactionResult, } from './databaseAdapter.js';
|
|
5
5
|
import { recordPostgresWorkerWait } from './postgresRequestDiagnostics.js';
|
|
6
|
+
import { resolvePostgresWorkerDeadlines, } from './postgresWorkerProtocol.js';
|
|
6
7
|
const RESPONSE_STATUS_INDEX = 0;
|
|
7
8
|
const RESPONSE_LENGTH_INDEX = 1;
|
|
8
9
|
const RESPONSE_READY = 1;
|
|
9
10
|
const RESPONSE_INITIAL = 0;
|
|
10
|
-
const RESPONSE_TIMEOUT_MS = 20_000;
|
|
11
11
|
const RESPONSE_BUFFER_BYTES = 2 * 1024 * 1024;
|
|
12
12
|
const MAX_RESPONSE_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
13
|
+
export class PostgresWorkerOutcomeUnknownError extends Error {
|
|
14
|
+
code = 'POSTGRES_WORKER_OUTCOME_UNKNOWN';
|
|
15
|
+
operation;
|
|
16
|
+
constructor(operation, detail = 'response deadline exceeded') {
|
|
17
|
+
super(`[Taskforce] Postgres worker ${operation} outcome is unknown (${detail}). `
|
|
18
|
+
+ 'Do not retry a write without the same idempotency identity.');
|
|
19
|
+
this.name = 'PostgresWorkerOutcomeUnknownError';
|
|
20
|
+
this.operation = operation;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class PostgresWorkerUnavailableError extends Error {
|
|
24
|
+
code = 'POSTGRES_WORKER_UNAVAILABLE';
|
|
25
|
+
constructor() {
|
|
26
|
+
super('[Taskforce] Postgres worker is recovering after a failed operation.');
|
|
27
|
+
this.name = 'PostgresWorkerUnavailableError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
13
30
|
function isPlainObject(value) {
|
|
14
31
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
15
32
|
}
|
|
@@ -62,39 +79,102 @@ function toRowCount(result) {
|
|
|
62
79
|
}
|
|
63
80
|
export class PostgresAdapter {
|
|
64
81
|
provider = 'postgres';
|
|
65
|
-
|
|
66
|
-
|
|
82
|
+
worker = null;
|
|
83
|
+
connectionString;
|
|
84
|
+
deadlines;
|
|
85
|
+
retiringWorkers = new WeakSet();
|
|
86
|
+
consecutiveUnexpectedExits = 0;
|
|
87
|
+
closed = false;
|
|
67
88
|
encoder = new TextEncoder();
|
|
68
89
|
decoder = new TextDecoder();
|
|
69
90
|
txId = null;
|
|
70
91
|
savepointCounter = 0;
|
|
71
|
-
constructor(connectionString) {
|
|
92
|
+
constructor(connectionString, deadlineOptions = {}) {
|
|
72
93
|
if (!connectionString || !connectionString.trim()) {
|
|
73
94
|
throw new Error('[Taskforce] Postgres adapter requires a non-empty connection string.');
|
|
74
95
|
}
|
|
75
|
-
this.
|
|
96
|
+
this.connectionString = connectionString;
|
|
97
|
+
this.deadlines = resolvePostgresWorkerDeadlines(deadlineOptions);
|
|
98
|
+
this.worker = this.createWorker();
|
|
99
|
+
}
|
|
100
|
+
get open() {
|
|
101
|
+
return !this.closed;
|
|
102
|
+
}
|
|
103
|
+
createWorker() {
|
|
104
|
+
const worker = new Worker(new URL('./postgresWorker.js', import.meta.url), {
|
|
76
105
|
workerData: {
|
|
77
|
-
connectionString
|
|
106
|
+
connectionString: this.connectionString,
|
|
107
|
+
deadlines: this.deadlines,
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
worker.on('error', () => {
|
|
111
|
+
// The exit handler replaces an unexpectedly failed worker. Keeping an error
|
|
112
|
+
// listener here also prevents Worker from escalating it as an uncaught error.
|
|
113
|
+
});
|
|
114
|
+
worker.on('exit', () => {
|
|
115
|
+
if (this.closed || this.retiringWorkers.has(worker) || this.worker !== worker)
|
|
116
|
+
return;
|
|
117
|
+
this.worker = null;
|
|
118
|
+
if (this.consecutiveUnexpectedExits >= 1)
|
|
119
|
+
return;
|
|
120
|
+
this.consecutiveUnexpectedExits += 1;
|
|
121
|
+
try {
|
|
122
|
+
this.worker = this.createWorker();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Leave the adapter unavailable instead of creating an unbounded restart loop.
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return worker;
|
|
129
|
+
}
|
|
130
|
+
quarantineWorker(worker) {
|
|
131
|
+
if (this.worker === worker)
|
|
132
|
+
this.worker = null;
|
|
133
|
+
this.txId = null;
|
|
134
|
+
this.retiringWorkers.add(worker);
|
|
135
|
+
void worker.terminate()
|
|
136
|
+
.catch(() => undefined)
|
|
137
|
+
.finally(() => {
|
|
138
|
+
if (this.closed || this.worker)
|
|
139
|
+
return;
|
|
140
|
+
try {
|
|
141
|
+
this.worker = this.createWorker();
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// A later process-level health check can restart a persistently broken runtime.
|
|
78
145
|
}
|
|
79
146
|
});
|
|
80
147
|
}
|
|
81
148
|
callWorker(payload, responseBufferBytes = RESPONSE_BUFFER_BYTES) {
|
|
149
|
+
if (this.closed)
|
|
150
|
+
throw new Error('[Taskforce] Postgres adapter is closed.');
|
|
151
|
+
const worker = this.worker;
|
|
152
|
+
if (!worker)
|
|
153
|
+
throw new PostgresWorkerUnavailableError();
|
|
82
154
|
const statusBuffer = new SharedArrayBuffer(8);
|
|
83
155
|
const status = new Int32Array(statusBuffer);
|
|
84
156
|
status[RESPONSE_STATUS_INDEX] = RESPONSE_INITIAL;
|
|
85
157
|
status[RESPONSE_LENGTH_INDEX] = 0;
|
|
86
158
|
const responseBuffer = new SharedArrayBuffer(responseBufferBytes);
|
|
87
159
|
const responseBytes = new Uint8Array(responseBuffer);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
160
|
+
try {
|
|
161
|
+
worker.postMessage({
|
|
162
|
+
payload,
|
|
163
|
+
statusBuffer,
|
|
164
|
+
responseBuffer
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
this.quarantineWorker(worker);
|
|
169
|
+
const detail = error instanceof Error ? error.message : 'worker transport failed';
|
|
170
|
+
throw new PostgresWorkerOutcomeUnknownError(payload.op, detail);
|
|
171
|
+
}
|
|
93
172
|
const waitStartedAt = performance.now();
|
|
94
|
-
const waitResult = Atomics.wait(status, RESPONSE_STATUS_INDEX, RESPONSE_INITIAL,
|
|
173
|
+
const waitResult = Atomics.wait(status, RESPONSE_STATUS_INDEX, RESPONSE_INITIAL, this.deadlines.responseTimeoutMs);
|
|
95
174
|
recordPostgresWorkerWait(payload.op, performance.now() - waitStartedAt);
|
|
96
175
|
if (waitResult === 'timed-out') {
|
|
97
|
-
|
|
176
|
+
this.quarantineWorker(worker);
|
|
177
|
+
throw new PostgresWorkerOutcomeUnknownError(payload.op);
|
|
98
178
|
}
|
|
99
179
|
const responseLength = status[RESPONSE_LENGTH_INDEX];
|
|
100
180
|
if (!Number.isFinite(responseLength) || responseLength <= 0 || responseLength > responseBufferBytes) {
|
|
@@ -102,6 +182,7 @@ export class PostgresAdapter {
|
|
|
102
182
|
}
|
|
103
183
|
const body = this.decoder.decode(responseBytes.subarray(0, responseLength));
|
|
104
184
|
const parsed = JSON.parse(body);
|
|
185
|
+
this.consecutiveUnexpectedExits = 0;
|
|
105
186
|
if (!parsed.ok && parsed.errorCode === 'RESPONSE_BUFFER_TOO_SMALL') {
|
|
106
187
|
const requiredBytes = Number(parsed.requiredBytes);
|
|
107
188
|
const responseToken = String(parsed.responseToken || '').trim();
|
|
@@ -230,11 +311,22 @@ export class PostgresAdapter {
|
|
|
230
311
|
return options?.simple ? null : [];
|
|
231
312
|
}
|
|
232
313
|
close() {
|
|
314
|
+
if (this.closed)
|
|
315
|
+
return;
|
|
316
|
+
const worker = this.worker;
|
|
317
|
+
if (!worker) {
|
|
318
|
+
this.closed = true;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
233
321
|
try {
|
|
234
322
|
this.callWorker({ op: 'close' });
|
|
235
323
|
}
|
|
236
324
|
finally {
|
|
237
|
-
this.
|
|
325
|
+
this.closed = true;
|
|
326
|
+
if (this.worker === worker)
|
|
327
|
+
this.worker = null;
|
|
328
|
+
this.retiringWorkers.add(worker);
|
|
329
|
+
void worker.terminate();
|
|
238
330
|
}
|
|
239
331
|
}
|
|
240
332
|
}
|
|
@@ -14,11 +14,16 @@ function pruneRetainedResponses(now = Date.now()) {
|
|
|
14
14
|
retainedResponses.delete(token);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
function createPool(connectionString) {
|
|
17
|
+
function createPool(connectionString, deadlines) {
|
|
18
18
|
const require = createRequire(import.meta.url);
|
|
19
19
|
try {
|
|
20
20
|
const pg = require('pg');
|
|
21
|
-
return new pg.Pool({
|
|
21
|
+
return new pg.Pool({
|
|
22
|
+
connectionString,
|
|
23
|
+
connectionTimeoutMillis: deadlines.connectionTimeoutMs,
|
|
24
|
+
statement_timeout: deadlines.statementTimeoutMs,
|
|
25
|
+
idle_in_transaction_session_timeout: deadlines.idleTransactionTimeoutMs,
|
|
26
|
+
});
|
|
22
27
|
}
|
|
23
28
|
catch (error) {
|
|
24
29
|
const detail = error instanceof Error ? ` (${error.message})` : '';
|
|
@@ -96,11 +101,15 @@ function writeRetainedResponse(statusBuffer, responseBuffer, responseToken) {
|
|
|
96
101
|
Atomics.store(status, RESPONSE_STATUS_INDEX, RESPONSE_READY);
|
|
97
102
|
Atomics.notify(status, RESPONSE_STATUS_INDEX, 1);
|
|
98
103
|
}
|
|
99
|
-
const
|
|
104
|
+
const runtimeOptions = workerData;
|
|
105
|
+
const connectionString = String(runtimeOptions?.connectionString || '').trim();
|
|
100
106
|
if (!connectionString) {
|
|
101
107
|
throw new Error('[Taskforce] Postgres worker requires connection string.');
|
|
102
108
|
}
|
|
103
|
-
|
|
109
|
+
if (!runtimeOptions.deadlines) {
|
|
110
|
+
throw new Error('[Taskforce] Postgres worker requires deadline configuration.');
|
|
111
|
+
}
|
|
112
|
+
const pool = createPool(connectionString, runtimeOptions.deadlines);
|
|
104
113
|
const txClients = new Map();
|
|
105
114
|
async function handleMessage(message) {
|
|
106
115
|
const { payload } = message;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type PostgresWorkerOperation = 'query' | 'begin' | 'commit' | 'rollback' | 'close' | 'read-response';
|
|
2
|
+
export type PostgresWorkerRequestPayload = {
|
|
3
|
+
op: PostgresWorkerOperation;
|
|
4
|
+
sql?: string;
|
|
5
|
+
values?: unknown[];
|
|
6
|
+
txId?: string;
|
|
7
|
+
responseToken?: string;
|
|
8
|
+
};
|
|
9
|
+
export type PostgresWorkerQueryResult = {
|
|
10
|
+
rows: Record<string, unknown>[];
|
|
11
|
+
rowCount?: number;
|
|
12
|
+
};
|
|
13
|
+
export type PostgresWorkerResponsePayload = {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
result?: PostgresWorkerQueryResult;
|
|
16
|
+
error?: string;
|
|
17
|
+
errorCode?: 'RESPONSE_BUFFER_TOO_SMALL';
|
|
18
|
+
requiredBytes?: number;
|
|
19
|
+
responseToken?: string;
|
|
20
|
+
};
|
|
21
|
+
export type PostgresWorkerDeadlines = {
|
|
22
|
+
responseTimeoutMs: number;
|
|
23
|
+
statementTimeoutMs: number;
|
|
24
|
+
connectionTimeoutMs: number;
|
|
25
|
+
idleTransactionTimeoutMs: number;
|
|
26
|
+
};
|
|
27
|
+
export type PostgresAdapterDeadlineOptions = Partial<PostgresWorkerDeadlines>;
|
|
28
|
+
export declare const DEFAULT_POSTGRES_WORKER_DEADLINES: PostgresWorkerDeadlines;
|
|
29
|
+
export declare function resolvePostgresWorkerDeadlines(options?: PostgresAdapterDeadlineOptions): PostgresWorkerDeadlines;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const DEFAULT_POSTGRES_WORKER_DEADLINES = {
|
|
2
|
+
responseTimeoutMs: 20_000,
|
|
3
|
+
statementTimeoutMs: 15_000,
|
|
4
|
+
connectionTimeoutMs: 3_000,
|
|
5
|
+
idleTransactionTimeoutMs: 60_000,
|
|
6
|
+
};
|
|
7
|
+
const MIN_RESPONSE_MARGIN_MS = 100;
|
|
8
|
+
function positiveInteger(value, name) {
|
|
9
|
+
const parsed = Number(value);
|
|
10
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
11
|
+
throw new Error(`[Taskforce] ${name} must be a positive integer.`);
|
|
12
|
+
}
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
export function resolvePostgresWorkerDeadlines(options = {}) {
|
|
16
|
+
const deadlines = {
|
|
17
|
+
responseTimeoutMs: positiveInteger(options.responseTimeoutMs ?? DEFAULT_POSTGRES_WORKER_DEADLINES.responseTimeoutMs, 'Postgres worker response timeout'),
|
|
18
|
+
statementTimeoutMs: positiveInteger(options.statementTimeoutMs ?? DEFAULT_POSTGRES_WORKER_DEADLINES.statementTimeoutMs, 'Postgres statement timeout'),
|
|
19
|
+
connectionTimeoutMs: positiveInteger(options.connectionTimeoutMs ?? DEFAULT_POSTGRES_WORKER_DEADLINES.connectionTimeoutMs, 'Postgres connection timeout'),
|
|
20
|
+
idleTransactionTimeoutMs: positiveInteger(options.idleTransactionTimeoutMs ?? DEFAULT_POSTGRES_WORKER_DEADLINES.idleTransactionTimeoutMs, 'Postgres idle transaction timeout'),
|
|
21
|
+
};
|
|
22
|
+
if (deadlines.connectionTimeoutMs
|
|
23
|
+
+ deadlines.statementTimeoutMs
|
|
24
|
+
+ MIN_RESPONSE_MARGIN_MS
|
|
25
|
+
> deadlines.responseTimeoutMs) {
|
|
26
|
+
throw new Error('[Taskforce] Postgres connection and statement timeouts must together leave at least '
|
|
27
|
+
+ `${MIN_RESPONSE_MARGIN_MS}ms before the worker response timeout.`);
|
|
28
|
+
}
|
|
29
|
+
return deadlines;
|
|
30
|
+
}
|
|
@@ -23,10 +23,13 @@ export type WorkspaceContentRequest = Pick<WorkspaceContentManifestEntry, 'kind'
|
|
|
23
23
|
kind: 'asset';
|
|
24
24
|
assetId: string;
|
|
25
25
|
};
|
|
26
|
+
export type WorkspaceContentMissingRequest = WorkspaceContentRequest & {
|
|
27
|
+
disposition?: 'retryable' | 'terminal-unavailable';
|
|
28
|
+
};
|
|
26
29
|
export interface WorkspaceContentPullResponse {
|
|
27
30
|
success?: boolean;
|
|
28
31
|
changes?: WorkspaceSyncChange[];
|
|
29
|
-
missing?:
|
|
32
|
+
missing?: WorkspaceContentMissingRequest[];
|
|
30
33
|
remaining?: WorkspaceContentRequest[];
|
|
31
34
|
contentMetadata?: Array<{
|
|
32
35
|
kind: 'document' | 'asset';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { partitionWorkspaceSyncV2LegacyChanges } from './v3/workspaceSyncV2ProjectionExclusions.js';
|
|
1
|
+
import { normalizeWorkspaceSyncV2ProjectionExclusions, partitionWorkspaceSyncV2LegacyChanges, } from './v3/workspaceSyncV2ProjectionExclusions.js';
|
|
2
2
|
import { validateWorkspaceSyncEngineTransferCompletionGroup, } from './engine/workspaceSyncEngineTransferCompletion.js';
|
|
3
3
|
import { WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR, resolveWorkspaceSyncV3CombinedCoverageForExclusions, resolveWorkspaceSyncV3CombinedWireContract } from './v3/workspaceSyncV3CombinedWireContract.js';
|
|
4
4
|
const WORKSPACE_PULL_TIMEOUT_MS = 45_000;
|
|
@@ -696,12 +696,17 @@ export async function bootstrapPushWorkspaceSnapshot(resolveCloudAuthUrl, input)
|
|
|
696
696
|
}));
|
|
697
697
|
}
|
|
698
698
|
export async function applyLocalWorkspaceChanges(workspaceId, changes, options) {
|
|
699
|
+
const excludedProjections = normalizeWorkspaceSyncV2ProjectionExclusions(options?.excludedProjections || []);
|
|
700
|
+
const combinedCoverage = excludedProjections.length
|
|
701
|
+
? resolveWorkspaceSyncV3CombinedCoverageForExclusions(excludedProjections)
|
|
702
|
+
: null;
|
|
699
703
|
const applyPayload = JSON.stringify({
|
|
700
704
|
workspaceId,
|
|
701
705
|
changes,
|
|
702
706
|
workspaceMembers: Array.isArray(options?.workspaceMembers) ? options.workspaceMembers : undefined,
|
|
703
707
|
...(options?.repairMode === true ? { repair: true } : {}),
|
|
704
|
-
excludedProjections
|
|
708
|
+
excludedProjections,
|
|
709
|
+
...(combinedCoverage ? { combinedCoverage } : {}),
|
|
705
710
|
contentPullObligations: options?.contentPullObligations,
|
|
706
711
|
contentPullResolutionClaims: options?.contentPullResolutionClaims,
|
|
707
712
|
...localBrowserApplyPermitContext(workspaceId, options),
|
|
@@ -130,7 +130,7 @@ export declare function createContentSyncState(deps: ContentSyncStateDeps): {
|
|
|
130
130
|
activeLinks: WorkspaceAssetLinkEntry[];
|
|
131
131
|
effectiveUpdatedAt: string;
|
|
132
132
|
};
|
|
133
|
-
getCanonicalSyncDocumentMetadata: (relativePath: string, workspaceId: string) => Omit<DocumentSyncPayload, "
|
|
133
|
+
getCanonicalSyncDocumentMetadata: (relativePath: string, workspaceId: string) => Omit<DocumentSyncPayload, "updatedAt" | "path" | "content">;
|
|
134
134
|
hasDocumentSyncMetadataDrift: (workspaceId: string, doc: DocumentSyncPayload, _options?: {
|
|
135
135
|
allowReferenceNumberReassignment?: boolean;
|
|
136
136
|
}) => boolean;
|
|
@@ -152,6 +152,7 @@ export declare class LocalSyncExecutionPermitStore {
|
|
|
152
152
|
coordinatorGeneration: number;
|
|
153
153
|
serverInstanceId: string;
|
|
154
154
|
errorCode?: string;
|
|
155
|
+
readResponseUnavailableBeforeApply?: boolean;
|
|
155
156
|
}): LocalSyncExecutionPermit;
|
|
156
157
|
recoverExpiredPermits(input: {
|
|
157
158
|
currentServerInstanceId: string;
|
|
@@ -619,9 +619,15 @@ export class LocalSyncExecutionPermitStore {
|
|
|
619
619
|
if (current.state === 'local-commit-entered') {
|
|
620
620
|
throw new LocalSyncExecutionPermitError(`Execution permit ${input.permitId} is owned by the server local commit.`, 'SYNC_COORDINATOR_PERMIT_STATE_CONFLICT');
|
|
621
621
|
}
|
|
622
|
+
const cleanReadFailure = input.readResponseUnavailableBeforeApply === true
|
|
623
|
+
&& input.errorCode === 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED'
|
|
624
|
+
&& current.state === 'gateway-complete-awaiting-apply'
|
|
625
|
+
&& current.upstreamDisposition === 'unknown'
|
|
626
|
+
&& (current.operationClass === 'pull' || current.operationClass === 'repair');
|
|
622
627
|
const clean = current.state === 'issued-idle'
|
|
623
628
|
|| (current.state === 'gateway-complete-awaiting-apply'
|
|
624
|
-
&& current.upstreamDisposition === 'rejected')
|
|
629
|
+
&& current.upstreamDisposition === 'rejected')
|
|
630
|
+
|| cleanReadFailure;
|
|
625
631
|
const disposition = current.upstreamDisposition === 'accepted'
|
|
626
632
|
? 'accepted'
|
|
627
633
|
: current.upstreamDisposition === 'rejected'
|
|
@@ -17,6 +17,10 @@ const RUNNER_COMMANDS = new Set([
|
|
|
17
17
|
]);
|
|
18
18
|
const SCHEDULED_RETRY_BASE_DELAY_MS = 1_000;
|
|
19
19
|
const SCHEDULED_RETRY_MAX_DELAY_MS = 5 * 60_000;
|
|
20
|
+
const CONTENT_PREREQUISITE_BYPASSED_RETRY_KINDS = new Set([
|
|
21
|
+
'pull',
|
|
22
|
+
'repair',
|
|
23
|
+
]);
|
|
20
24
|
function cloneCheckpoint(checkpoint) {
|
|
21
25
|
return JSON.parse(canonicalWorkspaceSyncJson(checkpoint));
|
|
22
26
|
}
|
|
@@ -159,7 +163,7 @@ export class LocalWorkspaceSyncRunner {
|
|
|
159
163
|
if (!this.context || this.terminated || !this.acceptingWork) {
|
|
160
164
|
return Promise.resolve();
|
|
161
165
|
}
|
|
162
|
-
return this.enqueue(async () => this.runInspection(this.options.operations.pushContentPrerequisite, {
|
|
166
|
+
return this.enqueue(async () => this.runInspection(this.options.operations.pushContentPrerequisite, { bypassRetryKinds: CONTENT_PREREQUISITE_BYPASSED_RETRY_KINDS }));
|
|
163
167
|
}
|
|
164
168
|
observeRemoteHint(observedAt = this.now()) {
|
|
165
169
|
if (!this.context || this.terminated || !this.acceptingWork) {
|
|
@@ -476,7 +480,7 @@ export class LocalWorkspaceSyncRunner {
|
|
|
476
480
|
if (!state.checkpoint.syncEnabled || !this.hasExactCapability(context))
|
|
477
481
|
return;
|
|
478
482
|
if (state.checkpoint.retry
|
|
479
|
-
&& state.checkpoint.retry.kind
|
|
483
|
+
&& !options.bypassRetryKinds?.has(state.checkpoint.retry.kind)
|
|
480
484
|
&& Date.parse(state.checkpoint.retry.retryAt) > Date.parse(this.now())) {
|
|
481
485
|
this.schedulePersistedRetry(state, this.lifecycleToken);
|
|
482
486
|
return;
|
|
@@ -40,6 +40,18 @@ function v3Boundary(input) {
|
|
|
40
40
|
...(input.coverage ? { coverage: { ...input.coverage } } : {}),
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
function independentContentBoundary(identity) {
|
|
44
|
+
const normalizedIdentity = String(identity || '').trim();
|
|
45
|
+
if (!normalizedIdentity) {
|
|
46
|
+
throw new TypeError('Independent content transfer identity is required.');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
version: 1,
|
|
50
|
+
projection: 'independent-content',
|
|
51
|
+
cursor: null,
|
|
52
|
+
identity: normalizedIdentity,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
43
55
|
async function decodeGatewayResponse(response, nowMs) {
|
|
44
56
|
const rawText = await response.text();
|
|
45
57
|
let data = {};
|
|
@@ -405,10 +417,12 @@ export function createBrowserGatewayWorkspaceSyncEngineRemoteServices(options) {
|
|
|
405
417
|
});
|
|
406
418
|
},
|
|
407
419
|
pullWorkspaceContent: (input) => {
|
|
408
|
-
const { previousCursor, ...request } = input;
|
|
420
|
+
const { previousCursor, independentCompletionIdentity, ...request } = input;
|
|
409
421
|
return execute({
|
|
410
422
|
operation: { type: 'content-pull', ...request },
|
|
411
|
-
previousBoundary:
|
|
423
|
+
previousBoundary: independentCompletionIdentity
|
|
424
|
+
? independentContentBoundary(independentCompletionIdentity)
|
|
425
|
+
: v2Boundary(previousCursor),
|
|
412
426
|
applying: true,
|
|
413
427
|
});
|
|
414
428
|
},
|
|
@@ -510,6 +524,10 @@ export function createBrowserGatewayWorkspaceSyncEngineRemoteServices(options) {
|
|
|
510
524
|
}));
|
|
511
525
|
},
|
|
512
526
|
buildV2ApplyCompletionGroup: ({ workspaceId, previousCursor, nextCursor, receipts, }) => completionGroup(workspaceId, receipts, v2Boundary(previousCursor), v2Boundary(nextCursor)),
|
|
527
|
+
buildIndependentContentApplyCompletionGroup: ({ workspaceId, identity, receipts, }) => {
|
|
528
|
+
const boundary = independentContentBoundary(identity);
|
|
529
|
+
return completionGroup(workspaceId, receipts, boundary, boundary);
|
|
530
|
+
},
|
|
513
531
|
buildV2PushApplyCompletionGroup: ({ workspaceId, pushBoundary, receipts, }) => completionGroup(workspaceId, receipts, parseWorkspaceSyncEngineV2PushBoundary(pushBoundary), parseWorkspaceSyncEngineV2PushBoundary(pushBoundary)),
|
|
514
532
|
buildV3ApplyCompletionGroup: ({ workspaceId, apply, receipts }) => {
|
|
515
533
|
const metadata = readReceipts(workspaceId, receipts);
|
|
@@ -24,6 +24,10 @@ export interface WorkspaceSyncEngineV2ApplyOptions {
|
|
|
24
24
|
export interface WorkspaceSyncEngineV3ApplyOptions {
|
|
25
25
|
completionGroup?: WorkspaceSyncEngineTransferCompletionGroup;
|
|
26
26
|
}
|
|
27
|
+
export declare function authoritativeV2ApplyFailureMessage(input: {
|
|
28
|
+
status?: number;
|
|
29
|
+
data?: unknown;
|
|
30
|
+
}): string;
|
|
27
31
|
/**
|
|
28
32
|
* Local-runtime operations used by the workspace sync engine.
|
|
29
33
|
*
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { applyLocalWorkspaceChanges, applyLocalWorkspaceSyncV3CombinedProjection, applyLocalWorkspaceSyncV3Projection, applyLocalWorkspaceSyncV3TaskCommentAudit, completeLocalSyncAuthoritativeRepair, getLocalWorkspaceContentPullObligations, recordLocalWorkspaceContentPullObligationFailures, getLocalTaskMetadataOwnership, getLocalWorkspaceContentManifest, getLocalWorkspaceSyncV3CombinedProjection, getLocalWorkspaceSyncV3Projection, getLocalWorkspaceSyncV3TaskCommentAudit, } from '../cloudSyncApi.js';
|
|
2
2
|
import { prepareLocalTaskRootOutboxRecovery, } from '../v3/localOutboxRecoveryClient.js';
|
|
3
|
+
export function authoritativeV2ApplyFailureMessage(input) {
|
|
4
|
+
const data = input.data && typeof input.data === 'object' && !Array.isArray(input.data)
|
|
5
|
+
? input.data
|
|
6
|
+
: {};
|
|
7
|
+
const detail = String(data.error || '').trim();
|
|
8
|
+
const code = String(data.code || '').trim();
|
|
9
|
+
const status = Number(input.status) || 0;
|
|
10
|
+
return detail
|
|
11
|
+
? `Authoritative V2 push echo apply failed (${status}): ${detail}${code ? ` [${code}]` : ''}`
|
|
12
|
+
: `Authoritative V2 push echo apply failed (${status}).`;
|
|
13
|
+
}
|
|
3
14
|
/**
|
|
4
15
|
* Same-origin HTTP implementation used while the browser owns orchestration.
|
|
5
16
|
*/
|
|
@@ -37,6 +37,12 @@ export type WorkspaceSyncEngineContentPullInput = Parameters<typeof pullWorkspac
|
|
|
37
37
|
* commit against one exact prior boundary.
|
|
38
38
|
*/
|
|
39
39
|
previousCursor: string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Separates an independent content lookup from a V2 feed page. The
|
|
42
|
+
* identity becomes an apply boundary that settles the transfer
|
|
43
|
+
* without advancing or validating the runner's V2 cursor.
|
|
44
|
+
*/
|
|
45
|
+
independentCompletionIdentity?: string;
|
|
40
46
|
};
|
|
41
47
|
type RemoteResult<Result> = Awaited<Result> & {
|
|
42
48
|
transferReceipt?: WorkspaceSyncEngineRemoteTransferReceipt;
|
|
@@ -94,6 +100,11 @@ export interface WorkspaceSyncEngineRemoteServices {
|
|
|
94
100
|
nextCursor: string | null;
|
|
95
101
|
receipts: readonly WorkspaceSyncEngineRemoteTransferReceipt[];
|
|
96
102
|
}): WorkspaceSyncEngineTransferCompletionGroup | undefined;
|
|
103
|
+
buildIndependentContentApplyCompletionGroup(input: {
|
|
104
|
+
workspaceId: string;
|
|
105
|
+
identity: string;
|
|
106
|
+
receipts: readonly WorkspaceSyncEngineRemoteTransferReceipt[];
|
|
107
|
+
}): WorkspaceSyncEngineTransferCompletionGroup | undefined;
|
|
97
108
|
buildV2PushApplyCompletionGroup(input: {
|
|
98
109
|
workspaceId: string;
|
|
99
110
|
pushBoundary: WorkspaceSyncEngineV2PushBoundary;
|
|
@@ -6,7 +6,7 @@ export function createBrowserWorkspaceSyncEngineRemoteServices(resolveCloudAuthU
|
|
|
6
6
|
provisionWorkspace: (input) => provisionWorkspace(resolveCloudAuthUrl, input),
|
|
7
7
|
pushWorkspaceChanges: ({ ownership: _ownership, pushBoundary: _pushBoundary, ...input }) => (pushWorkspaceChanges(resolveCloudAuthUrl, input)),
|
|
8
8
|
pullWorkspaceChanges: (input) => pullWorkspaceChanges(resolveCloudAuthUrl, input),
|
|
9
|
-
pullWorkspaceContent: ({ previousCursor: _previousCursor, ...input }) => (pullWorkspaceContent(resolveCloudAuthUrl, input)),
|
|
9
|
+
pullWorkspaceContent: ({ previousCursor: _previousCursor, independentCompletionIdentity: _independentCompletionIdentity, ...input }) => (pullWorkspaceContent(resolveCloudAuthUrl, input)),
|
|
10
10
|
pullInitiativeFeed: (input) => pullWorkspaceSyncV3Feed(resolveCloudAuthUrl, input),
|
|
11
11
|
pullInitiativeRepair: (input) => pullWorkspaceInitiativeRepair(resolveCloudAuthUrl, input),
|
|
12
12
|
pullCombinedFeed: (input) => pullWorkspaceSyncV3CombinedFeed(resolveCloudAuthUrl, input),
|
|
@@ -15,6 +15,7 @@ export function createBrowserWorkspaceSyncEngineRemoteServices(resolveCloudAuthU
|
|
|
15
15
|
abandonTransferReceipts: () => undefined,
|
|
16
16
|
acknowledgeNonApplyingTransferReceipts: () => undefined,
|
|
17
17
|
buildV2ApplyCompletionGroup: () => undefined,
|
|
18
|
+
buildIndependentContentApplyCompletionGroup: () => undefined,
|
|
18
19
|
buildV2PushApplyCompletionGroup: () => undefined,
|
|
19
20
|
buildV3ApplyCompletionGroup: () => undefined,
|
|
20
21
|
};
|
|
@@ -40,6 +40,18 @@ function v3Boundary(input) {
|
|
|
40
40
|
...(input.coverage ? { coverage: { ...input.coverage } } : {}),
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
function independentContentBoundary(identity) {
|
|
44
|
+
const normalizedIdentity = String(identity || '').trim();
|
|
45
|
+
if (!normalizedIdentity) {
|
|
46
|
+
throw new TypeError('Independent content transfer identity is required.');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
version: 1,
|
|
50
|
+
projection: 'independent-content',
|
|
51
|
+
cursor: null,
|
|
52
|
+
identity: normalizedIdentity,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
43
55
|
function decodeGatewayResult(result, nowMs) {
|
|
44
56
|
const rawText = new TextDecoder().decode(result.body);
|
|
45
57
|
let data = {};
|
|
@@ -96,12 +108,15 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
96
108
|
throw new TypeError('The server sync operation workspace does not match its runner.');
|
|
97
109
|
}
|
|
98
110
|
};
|
|
99
|
-
const abandon = (permitId, errorCode) => (options.permitStore.abandonPermit({
|
|
111
|
+
const abandon = (permitId, errorCode, readResponseUnavailableBeforeApply = false) => (options.permitStore.abandonPermit({
|
|
100
112
|
permitId,
|
|
101
113
|
workspaceId: options.workspaceId,
|
|
102
114
|
coordinatorGeneration: options.coordinatorGeneration,
|
|
103
115
|
serverInstanceId: options.serverInstanceId,
|
|
104
116
|
errorCode,
|
|
117
|
+
...(readResponseUnavailableBeforeApply
|
|
118
|
+
? { readResponseUnavailableBeforeApply: true }
|
|
119
|
+
: {}),
|
|
105
120
|
}));
|
|
106
121
|
const execute = async (input) => {
|
|
107
122
|
assertWorkspace(input.operation.workspaceId);
|
|
@@ -131,7 +146,7 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
131
146
|
});
|
|
132
147
|
}
|
|
133
148
|
catch (error) {
|
|
134
|
-
abandon(permit.permitId, 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED');
|
|
149
|
+
abandon(permit.permitId, 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED', operationClass === 'pull' || operationClass === 'repair');
|
|
135
150
|
throw error;
|
|
136
151
|
}
|
|
137
152
|
const decoded = decodeGatewayResult(gatewayResult, issuedAt.getTime());
|
|
@@ -171,6 +186,12 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
171
186
|
abandon(permit.permitId, 'SYNC_COORDINATOR_APPLYING_REJECTED');
|
|
172
187
|
return result;
|
|
173
188
|
}
|
|
189
|
+
if (!result.ok
|
|
190
|
+
&& completedPermit.upstreamDisposition === 'unknown'
|
|
191
|
+
&& (operationClass === 'pull' || operationClass === 'repair')) {
|
|
192
|
+
abandon(permit.permitId, 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED', true);
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
174
195
|
if (!input.previousBoundary) {
|
|
175
196
|
abandon(permit.permitId, 'SYNC_COORDINATOR_APPLY_BOUNDARY_MISSING');
|
|
176
197
|
throw new Error('A server applying request did not declare its prior boundary.');
|
|
@@ -319,10 +340,12 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
319
340
|
});
|
|
320
341
|
},
|
|
321
342
|
pullWorkspaceContent: (input) => {
|
|
322
|
-
const { previousCursor, ...request } = input;
|
|
343
|
+
const { previousCursor, independentCompletionIdentity, ...request } = input;
|
|
323
344
|
return execute({
|
|
324
345
|
operation: { type: 'content-pull', ...request },
|
|
325
|
-
previousBoundary:
|
|
346
|
+
previousBoundary: independentCompletionIdentity
|
|
347
|
+
? independentContentBoundary(independentCompletionIdentity)
|
|
348
|
+
: v2Boundary(previousCursor),
|
|
326
349
|
applying: true,
|
|
327
350
|
});
|
|
328
351
|
},
|
|
@@ -433,6 +456,10 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
433
456
|
}
|
|
434
457
|
},
|
|
435
458
|
buildV2ApplyCompletionGroup: ({ workspaceId, previousCursor, nextCursor, receipts, }) => completionGroup(workspaceId, receipts, v2Boundary(previousCursor), v2Boundary(nextCursor)),
|
|
459
|
+
buildIndependentContentApplyCompletionGroup: ({ workspaceId, identity, receipts, }) => {
|
|
460
|
+
const boundary = independentContentBoundary(identity);
|
|
461
|
+
return completionGroup(workspaceId, receipts, boundary, boundary);
|
|
462
|
+
},
|
|
436
463
|
buildV2PushApplyCompletionGroup: ({ workspaceId, pushBoundary, receipts, }) => completionGroup(workspaceId, receipts, parseWorkspaceSyncEngineV2PushBoundary(pushBoundary), parseWorkspaceSyncEngineV2PushBoundary(pushBoundary)),
|
|
437
464
|
buildV3ApplyCompletionGroup: ({ workspaceId, apply, receipts }) => {
|
|
438
465
|
const metadata = readReceipts(workspaceId, receipts);
|
|
@@ -2,7 +2,7 @@ import type { LocalWorkspaceSyncRunnerOperations } from '../coordinator/localWor
|
|
|
2
2
|
import { type EnsureWorkspaceReadyResult } from '../syncService.js';
|
|
3
3
|
import type { WorkspaceSyncV2ProjectionExclusion } from '../v3/workspaceSyncV2ProjectionExclusions.js';
|
|
4
4
|
import type { WorkspaceSyncV3CombinedCoverageSelector } from '../v3/workspaceSyncV3CombinedWireContract.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type WorkspaceSyncEngineLocalServices } from './workspaceSyncEngineLocalServices.js';
|
|
6
6
|
import type { WorkspaceSyncEngineRemoteServices } from './workspaceSyncEngineRemoteServices.js';
|
|
7
7
|
import type { WorkspaceSyncEngineServerPushCandidateProvider } from './workspaceSyncEngineServerSnapshotReader.js';
|
|
8
8
|
import type { WorkspaceSyncEngineServerCheckpointPort } from './workspaceSyncEngineServerApplyBoundary.js';
|
|
@@ -46,11 +46,13 @@ export interface WorkspaceSyncEngineServerAttachmentApplyPort {
|
|
|
46
46
|
} | {
|
|
47
47
|
success: false;
|
|
48
48
|
error: string;
|
|
49
|
+
disposition?: 'retryable' | 'terminal-unavailable';
|
|
49
50
|
}>;
|
|
50
51
|
}): Promise<{
|
|
51
52
|
attempted: number;
|
|
52
53
|
resolved: number;
|
|
53
54
|
remaining: number;
|
|
55
|
+
terminal: number;
|
|
54
56
|
nextAttemptAt: string | null;
|
|
55
57
|
lastError: string | null;
|
|
56
58
|
}>;
|