@taskforcehq/taskforce 0.3.327 → 0.3.328
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 +24 -53
- package/dist/components/activity/EntityActivityTimeline.js +15 -5
- package/dist/components/context/AttachmentPreviewHost.js +1 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
- package/dist/components/context/ContextAttachmentManager.js +116 -20
- package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
- package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
- package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
- package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
- package/dist/components/features/planning/PlanningModule.js +26 -9
- package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
- package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
- package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +2 -2
- package/dist/components/task/TaskCard.js +37 -4
- package/dist/components/task/TaskContextUpload.d.ts +3 -0
- package/dist/components/task/TaskContextUpload.js +2 -2
- package/dist/components/task/TaskForm.d.ts +4 -0
- package/dist/components/task/TaskForm.js +32 -7
- package/dist/components/task/TaskImageReviews.js +16 -16
- package/dist/components/task/TaskStatusActions.js +11 -2
- package/dist/components/task/TaskWorkstreamPicker.js +2 -1
- package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
- package/dist/components/ui/DocumentTypeIcon.js +8 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
- package/dist/components/ui/TaxonomyDropdown.js +30 -3
- package/dist/components/views/StandaloneLayout.js +62 -9
- package/dist/components/views/WidgetView.js +8 -5
- package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
- package/dist/components/views/panels/PlanningDrawer.js +48 -44
- package/dist/config/envSchema.js +1 -0
- package/dist/config/localServiceCloudTarget.d.ts +10 -0
- package/dist/config/localServiceCloudTarget.js +76 -0
- package/dist/core/AiProfileService.d.ts +1 -0
- package/dist/core/AiProfileService.js +2 -1
- package/dist/core/AttachmentLinkService.js +7 -4
- package/dist/core/DeletedTaskLifecycleService.d.ts +4 -0
- package/dist/core/DeletedTaskLifecycleService.js +21 -0
- package/dist/core/McpTokenService.d.ts +4 -0
- package/dist/core/McpTokenService.js +52 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +5 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +20 -11
- package/dist/core/Taskforce.d.ts +63 -1
- package/dist/core/Taskforce.js +558 -73
- package/dist/core/types.d.ts +2 -1
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
- package/dist/documentReviews/service.d.ts +4 -0
- package/dist/documentReviews/service.js +141 -46
- package/dist/hooks/tasks/useTaskFormActions.d.ts +3 -0
- package/dist/hooks/tasks/useTaskFormActions.js +33 -7
- package/dist/hooks/useTaskforce.d.ts +59 -50
- package/dist/hooks/useTaskforce.js +103 -1
- package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
- package/dist/mcp/canonicalAssetHelpers.js +39 -5
- package/dist/mcp/collaborationRegistrar.js +15 -13
- package/dist/mcp/documentAssetRegistrar.js +16 -9
- package/dist/mcp/localServiceProxy.js +4 -1
- package/dist/mcp/runtime.d.ts +16 -0
- package/dist/mcp/runtime.js +566 -171
- package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
- package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
- package/dist/mcp/taskPlanningRegistrar.js +42 -34
- package/dist/mcp/toolRegistry.js +2 -2
- package/dist/migrations/taskSchemaMigrations.js +35 -0
- package/dist/runtime/appGateDefinitions.d.ts +2 -2
- package/dist/runtime/appGateDefinitions.js +2 -2
- package/dist/server/appAccess.d.ts +12 -0
- package/dist/server/appAccess.js +21 -0
- package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
- package/dist/server/authorizedWorkspaceAccess.js +40 -0
- package/dist/server/cloudCollectionRead.d.ts +25 -0
- package/dist/server/cloudCollectionRead.js +120 -0
- package/dist/server/cloudCollectionReadPool.d.ts +52 -0
- package/dist/server/cloudCollectionReadPool.js +140 -0
- package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
- package/dist/server/cloudCollectionReadWorker.js +36 -0
- package/dist/server/index.d.ts +3 -11
- package/dist/server/index.js +53 -40
- package/dist/server/localServiceLease.d.ts +3 -0
- package/dist/server/localServiceLease.js +9 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +160 -13
- package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
- package/dist/server/routes/durableTaskHttpRouters.js +2 -2
- package/dist/server/routes/localService.js +2 -0
- package/dist/server/routes/primitives.d.ts +2 -0
- package/dist/server/routes/shared.d.ts +24 -0
- package/dist/server/routes/shared.js +114 -18
- package/dist/server/routes/sync.js +22 -0
- package/dist/server/routes/syncV3FeedRoutes.js +4 -2
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +20 -2
- package/dist/server/routes/syncV3OperationRoutes.js +17 -2
- package/dist/server/routes/tasks.js +199 -89
- package/dist/server/routes.js +35 -7
- package/dist/service/localServiceCli.js +36 -25
- package/dist/service/localServiceClientLifecycle.js +67 -6
- package/dist/shared/planningIdentity.d.ts +20 -0
- package/dist/shared/planningIdentity.js +79 -0
- package/dist/storage/postgresAdapter.js +4 -0
- package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
- package/dist/storage/postgresRequestDiagnostics.js +24 -0
- package/dist/storage/workspaceAssetStore.d.ts +32 -0
- package/dist/storage/workspaceAssetStore.js +70 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +16 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +70 -22
- package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +5 -0
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +15 -0
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +26 -3
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
- package/dist/sync/syncApplyHandlers.d.ts +3 -1
- package/dist/sync/syncApplyHandlers.js +29 -0
- package/dist/sync/syncService.js +9 -0
- package/dist/sync/taskSyncPayload.d.ts +1 -0
- package/dist/sync/taskSyncPayload.js +6 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +21 -3
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +17 -4
- package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
- package/dist/sync/v3/localOutboxDispatcher.js +5 -1
- package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +1 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +15 -3
- package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
- package/dist/sync/v3/syncDurabilityStore.d.ts +8 -0
- package/dist/sync/v3/syncDurabilityStore.js +171 -33
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +1 -0
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +18 -2
- package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +1 -9
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +1 -0
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +7 -1
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +15 -3
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +4 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +29 -6
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +29 -7
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +62 -7
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
- package/dist/sync/workspacePullFeed.d.ts +6 -1
- package/dist/sync/workspacePullFeed.js +172 -161
- package/dist/sync/workspaceSyncModel.d.ts +11 -0
- package/dist/sync/workspaceSyncModel.js +27 -2
- package/dist/types.d.ts +8 -0
- package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-MZs7lVED.js} +1 -1
- package/dist/ui/assets/{AiProfilesModule-DPxQeF9N.js → AiProfilesModule-C3i0LOxe.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-DHNYaNg6.js → AnnotatedAttachmentWorkspace-DVZk5Tln.js} +1 -1
- package/dist/ui/assets/AssetTraySortControl-Kf1pelSy.js +1 -0
- package/dist/ui/assets/ContextAttachmentManager-B8boOs6Q.js +3 -0
- package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-DXH4XUsB.js} +4 -4
- package/dist/ui/assets/EntityActivityTimeline-BtSTq0ON.js +1 -0
- package/dist/ui/assets/PlanningModule-BPVyEa-f.js +1 -0
- package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
- package/dist/ui/assets/{PlansPage-BVH_NfgT.js → PlansPage-ChqgxALc.js} +1 -1
- package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +1 -0
- package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
- package/dist/ui/assets/{TaskSettings-xVsxSa0g.js → TaskSettings-g4ECKNOz.js} +1 -1
- package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BazdUwxZ.js} +1 -1
- package/dist/ui/assets/WorkflowManagerModule-DWFspC-o.js +1 -0
- package/dist/ui/assets/index-BUplSsv_.js +7 -0
- package/dist/ui/assets/index-gz2EXuoK.css +1 -0
- package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-BkFLXavV.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.js +1 -1
- package/dist/utils/contextAttachmentUpload.d.ts +2 -0
- package/dist/utils/contextAttachmentUpload.js +7 -0
- package/dist/utils/contextFiles.d.ts +1 -1
- package/dist/utils/contextFiles.js +4 -4
- package/package.json +6 -1
- package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
- package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
- package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
- package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
- package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
- package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
- package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
- package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
- package/dist/ui/assets/index-BzJlVnNS.css +0 -1
- package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import { applyLocalServiceCloudTarget, localServiceCloudTargetsEqual, parseLocalServiceCloudTarget, resolveLocalServiceCloudTarget, } from '../config/localServiceCloudTarget.js';
|
|
3
4
|
import { createStandaloneServer } from '../server/index.js';
|
|
4
5
|
import { inspectLocalServiceDescriptor, redactLocalServiceDescriptor, } from '../server/localServiceLease.js';
|
|
5
6
|
import { acquireLocalServiceOwnershipForRuntime, } from '../server/localServiceOwnershipBootstrap.js';
|
|
6
|
-
import { LOCAL_SERVICE_API_BASE_PATH, LOCAL_SERVICE_PROTOCOL_VERSION, resolveLocalServiceLeaseInput, spawnDetachedLocalService, waitForLocalServiceDescriptor, } from './localServiceClientLifecycle.js';
|
|
7
|
+
import { discoverOrStartLocalService, LOCAL_SERVICE_API_BASE_PATH, LOCAL_SERVICE_PROTOCOL_VERSION, resolveLocalServiceLeaseInput, spawnDetachedLocalService, waitForLocalServiceDescriptor, } from './localServiceClientLifecycle.js';
|
|
7
8
|
import { listSqliteWalHolders } from '../storage/sqliteWalHealth.js';
|
|
8
9
|
const STARTUP_TIMEOUT_MS = 15_000;
|
|
9
10
|
export { LOCAL_SERVICE_PROTOCOL_VERSION } from './localServiceClientLifecycle.js';
|
|
@@ -58,6 +59,13 @@ function reportIncompatibleDescriptor(inspection, error) {
|
|
|
58
59
|
error(`Taskforce service protocol ${inspection.descriptor.protocolVersion} is incompatible with client protocol ${LOCAL_SERVICE_PROTOCOL_VERSION}.`);
|
|
59
60
|
return true;
|
|
60
61
|
}
|
|
62
|
+
function reportCloudTargetMismatch(descriptorTarget, requestedTarget, error) {
|
|
63
|
+
if (localServiceCloudTargetsEqual(descriptorTarget, requestedTarget))
|
|
64
|
+
return false;
|
|
65
|
+
error('Taskforce service is running for a different or unverified cloud target. '
|
|
66
|
+
+ 'Stop it with `taskforce service stop --drain`, then retry.');
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
61
69
|
function defaultRegisterShutdownSignals(handler) {
|
|
62
70
|
process.once('SIGINT', handler);
|
|
63
71
|
process.once('SIGTERM', handler);
|
|
@@ -75,6 +83,8 @@ async function waitForDescriptor(input, expected, options) {
|
|
|
75
83
|
});
|
|
76
84
|
}
|
|
77
85
|
async function runForeground(command, options) {
|
|
86
|
+
const requestedCloudTarget = resolveLocalServiceCloudTarget(options.env);
|
|
87
|
+
applyLocalServiceCloudTarget(options.env, requestedCloudTarget);
|
|
78
88
|
const ownership = await options.acquireOwnership(command.projectRoot, options.env);
|
|
79
89
|
if (!ownership)
|
|
80
90
|
throw new Error('The shared local service requires local runtime mode.');
|
|
@@ -84,6 +94,8 @@ async function runForeground(command, options) {
|
|
|
84
94
|
if (inspection.status === 'live' && inspection.descriptor) {
|
|
85
95
|
if (reportIncompatibleDescriptor(inspection, options.error))
|
|
86
96
|
return 2;
|
|
97
|
+
if (reportCloudTargetMismatch(inspection.descriptor.cloudTarget, requestedCloudTarget, options.error))
|
|
98
|
+
return 2;
|
|
87
99
|
options.log(`Taskforce service is already running at ${inspection.descriptor.endpoint}.`);
|
|
88
100
|
return 0;
|
|
89
101
|
}
|
|
@@ -116,6 +128,7 @@ async function runForeground(command, options) {
|
|
|
116
128
|
ownerPid: process.pid,
|
|
117
129
|
bootstrapCapability: ownership.lease.bootstrapCapability,
|
|
118
130
|
ownershipKey: ownership.lease.identity.ownershipKey,
|
|
131
|
+
cloudTarget: requestedCloudTarget,
|
|
119
132
|
},
|
|
120
133
|
afterShutdown: async () => {
|
|
121
134
|
removeSignals();
|
|
@@ -138,6 +151,7 @@ async function runForeground(command, options) {
|
|
|
138
151
|
endpoint: `http://127.0.0.1:${address.port}`,
|
|
139
152
|
protocolVersion: LOCAL_SERVICE_PROTOCOL_VERSION,
|
|
140
153
|
appVersion: options.appVersion,
|
|
154
|
+
cloudTarget: requestedCloudTarget,
|
|
141
155
|
});
|
|
142
156
|
removeSignals = options.registerShutdownSignals(shutdown);
|
|
143
157
|
server.once('error', (error) => {
|
|
@@ -164,34 +178,28 @@ async function runForeground(command, options) {
|
|
|
164
178
|
}
|
|
165
179
|
}
|
|
166
180
|
async function runStart(command, options) {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
181
|
+
const requestedCloudTarget = resolveLocalServiceCloudTarget(options.env);
|
|
182
|
+
applyLocalServiceCloudTarget(options.env, requestedCloudTarget);
|
|
183
|
+
try {
|
|
184
|
+
const descriptor = await discoverOrStartLocalService({
|
|
185
|
+
projectRoot: command.projectRoot,
|
|
186
|
+
env: options.env,
|
|
187
|
+
executablePath: options.executablePath,
|
|
188
|
+
cliEntryPath: options.cliEntryPath,
|
|
189
|
+
execArgv: options.execArgv,
|
|
190
|
+
inspectDescriptor: options.inspectDescriptor,
|
|
191
|
+
spawnDetached: options.spawnDetached,
|
|
192
|
+
fetch: options.fetch,
|
|
193
|
+
sleep: options.sleep,
|
|
194
|
+
nowMs: options.nowMs,
|
|
195
|
+
});
|
|
196
|
+
options.log(`Taskforce service is ready at ${descriptor.endpoint}.`);
|
|
173
197
|
return 0;
|
|
174
198
|
}
|
|
175
|
-
|
|
176
|
-
options.error(
|
|
177
|
-
return 2;
|
|
178
|
-
}
|
|
179
|
-
options.spawnDetached(options.executablePath, [
|
|
180
|
-
...options.execArgv,
|
|
181
|
-
options.cliEntryPath,
|
|
182
|
-
'service',
|
|
183
|
-
command.projectRoot,
|
|
184
|
-
'--detached-child',
|
|
185
|
-
], options.env);
|
|
186
|
-
const inspection = await waitForDescriptor(leaseInput, 'live', options);
|
|
187
|
-
if (inspection.status !== 'live' || !inspection.descriptor) {
|
|
188
|
-
options.error('Taskforce service did not become ready before the startup deadline.');
|
|
199
|
+
catch (startError) {
|
|
200
|
+
options.error(String(startError?.message || startError));
|
|
189
201
|
return 2;
|
|
190
202
|
}
|
|
191
|
-
if (reportIncompatibleDescriptor(inspection, options.error))
|
|
192
|
-
return 2;
|
|
193
|
-
options.log(`Taskforce service started at ${inspection.descriptor.endpoint}.`);
|
|
194
|
-
return 0;
|
|
195
203
|
}
|
|
196
204
|
async function runStatus(command, options) {
|
|
197
205
|
const leaseInput = resolveLocalServiceLeaseInput(command.projectRoot, options.env);
|
|
@@ -222,9 +230,12 @@ async function runStatus(command, options) {
|
|
|
222
230
|
throw new Error(`HTTP ${response.status}`);
|
|
223
231
|
const payload = await response.json();
|
|
224
232
|
const coordinatorPayload = payload.coordinator;
|
|
233
|
+
const diagnosticCloudTarget = parseLocalServiceCloudTarget(payload.cloudTarget);
|
|
225
234
|
if (payload.ownerPid !== inspection.descriptor.pid
|
|
226
235
|
|| payload.generation !== inspection.descriptor.generation
|
|
227
236
|
|| payload.protocolVersion !== inspection.descriptor.protocolVersion
|
|
237
|
+
|| (inspection.descriptor.cloudTarget
|
|
238
|
+
&& !localServiceCloudTargetsEqual(diagnosticCloudTarget, inspection.descriptor.cloudTarget))
|
|
228
239
|
|| !Array.isArray(payload.sessions)
|
|
229
240
|
|| !coordinatorPayload
|
|
230
241
|
|| typeof coordinatorPayload !== 'object'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
+
import { applyLocalServiceCloudTarget, localServiceCloudTargetsEqual, parseLocalServiceCloudTarget, resolveLocalServiceCloudTarget, } from '../config/localServiceCloudTarget.js';
|
|
4
5
|
import { inspectLocalServiceDescriptor, } from '../server/localServiceLease.js';
|
|
5
6
|
import { resolveLocalDatabaseIdentity } from '../server/localDatabaseIdentity.js';
|
|
6
7
|
export const LOCAL_SERVICE_PROTOCOL_VERSION = '1';
|
|
@@ -39,10 +40,11 @@ export function spawnDetachedLocalService(executablePath, args, env) {
|
|
|
39
40
|
});
|
|
40
41
|
child.unref();
|
|
41
42
|
}
|
|
42
|
-
function buildLocalServiceProcessEnv(env) {
|
|
43
|
+
function buildLocalServiceProcessEnv(env, cloudTarget) {
|
|
43
44
|
const serviceEnv = { ...env };
|
|
44
45
|
for (const key of LOCAL_MCP_CLIENT_ENV_KEYS)
|
|
45
46
|
delete serviceEnv[key];
|
|
47
|
+
applyLocalServiceCloudTarget(serviceEnv, cloudTarget);
|
|
46
48
|
return serviceEnv;
|
|
47
49
|
}
|
|
48
50
|
export async function waitForLocalServiceDescriptor(input, expected, options = {}) {
|
|
@@ -78,15 +80,59 @@ async function validateLocalServiceReadiness(descriptor, fetchFn) {
|
|
|
78
80
|
catch {
|
|
79
81
|
throw new Error('Taskforce shared local service readiness returned invalid JSON.');
|
|
80
82
|
}
|
|
83
|
+
const readinessHasCloudTarget = Object.prototype.hasOwnProperty.call(readiness, 'cloudTarget');
|
|
84
|
+
const readinessCloudTarget = readinessHasCloudTarget
|
|
85
|
+
? parseLocalServiceCloudTarget(readiness.cloudTarget)
|
|
86
|
+
: null;
|
|
87
|
+
const descriptorCloudTarget = descriptor.cloudTarget || null;
|
|
88
|
+
const cloudTargetsMatch = descriptorCloudTarget || readinessCloudTarget
|
|
89
|
+
? localServiceCloudTargetsEqual(descriptorCloudTarget, readinessCloudTarget)
|
|
90
|
+
: true;
|
|
81
91
|
if (readiness.ready !== true
|
|
82
92
|
|| readiness.protocolVersion !== LOCAL_SERVICE_PROTOCOL_VERSION
|
|
83
93
|
|| readiness.protocolVersion !== descriptor.protocolVersion
|
|
84
94
|
|| readiness.generation !== descriptor.generation
|
|
85
95
|
|| readiness.pid !== descriptor.pid
|
|
86
96
|
|| readiness.endpoint !== descriptor.endpoint
|
|
87
|
-
|| readiness.dataIdentity !== descriptor.ownershipKey
|
|
97
|
+
|| readiness.dataIdentity !== descriptor.ownershipKey
|
|
98
|
+
|| (readinessHasCloudTarget && !readinessCloudTarget)
|
|
99
|
+
|| !cloudTargetsMatch) {
|
|
88
100
|
throw new Error('Taskforce shared local service readiness does not match its descriptor.');
|
|
89
101
|
}
|
|
102
|
+
return descriptorCloudTarget;
|
|
103
|
+
}
|
|
104
|
+
async function requestIdleLocalServiceShutdown(descriptor, fetchFn) {
|
|
105
|
+
let response;
|
|
106
|
+
try {
|
|
107
|
+
response = await fetchFn(`${descriptor.endpoint}${LOCAL_SERVICE_API_BASE_PATH}/shutdown`, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
'content-type': 'application/json',
|
|
111
|
+
'x-taskforce-local-service-capability': descriptor.bootstrapCapability,
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
intent: 'administrative-stop',
|
|
115
|
+
mode: 'refuse',
|
|
116
|
+
pid: descriptor.pid,
|
|
117
|
+
generation: descriptor.generation,
|
|
118
|
+
}),
|
|
119
|
+
redirect: 'error',
|
|
120
|
+
signal: AbortSignal.timeout(LOCAL_SERVICE_STARTUP_TIMEOUT_MS),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw new Error('Taskforce service uses a different cloud target and could not be stopped safely. '
|
|
125
|
+
+ `No replacement was started. ${String(error?.message || error)}`);
|
|
126
|
+
}
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
const detail = (await response.text()).slice(0, 500);
|
|
129
|
+
const activeClientHint = response.status === 409
|
|
130
|
+
? ' It may have active MCP clients; close them or run `taskforce service stop --drain`, then retry.'
|
|
131
|
+
: '';
|
|
132
|
+
throw new Error(`Taskforce service uses a different cloud target and refused safe idle replacement (${response.status}).`
|
|
133
|
+
+ activeClientHint
|
|
134
|
+
+ (detail ? ` ${detail}` : ''));
|
|
135
|
+
}
|
|
90
136
|
}
|
|
91
137
|
export async function discoverOrStartLocalService(input) {
|
|
92
138
|
const requestedRoot = path.resolve(input.projectRoot);
|
|
@@ -94,13 +140,25 @@ export async function discoverOrStartLocalService(input) {
|
|
|
94
140
|
const leaseInput = resolveLocalServiceLeaseInput(projectRoot, input.env);
|
|
95
141
|
const inspectDescriptor = input.inspectDescriptor || inspectLocalServiceDescriptor;
|
|
96
142
|
const fetchFn = input.fetch || fetch;
|
|
143
|
+
const requestedCloudTarget = resolveLocalServiceCloudTarget(input.env);
|
|
97
144
|
const current = await inspectDescriptor(leaseInput);
|
|
98
145
|
if (current.status === 'live' && current.descriptor) {
|
|
99
146
|
if (current.descriptor.protocolVersion !== LOCAL_SERVICE_PROTOCOL_VERSION) {
|
|
100
147
|
throw new Error(`Taskforce service protocol ${current.descriptor.protocolVersion} is incompatible with client protocol ${LOCAL_SERVICE_PROTOCOL_VERSION}.`);
|
|
101
148
|
}
|
|
102
|
-
await validateLocalServiceReadiness(current.descriptor, fetchFn);
|
|
103
|
-
|
|
149
|
+
const runningCloudTarget = await validateLocalServiceReadiness(current.descriptor, fetchFn);
|
|
150
|
+
if (localServiceCloudTargetsEqual(runningCloudTarget, requestedCloudTarget)) {
|
|
151
|
+
return current.descriptor;
|
|
152
|
+
}
|
|
153
|
+
await requestIdleLocalServiceShutdown(current.descriptor, fetchFn);
|
|
154
|
+
const stopped = await waitForLocalServiceDescriptor(leaseInput, 'stale', {
|
|
155
|
+
inspectDescriptor,
|
|
156
|
+
sleep: input.sleep,
|
|
157
|
+
nowMs: input.nowMs,
|
|
158
|
+
});
|
|
159
|
+
if (stopped.status !== 'stale') {
|
|
160
|
+
throw new Error('Taskforce service accepted cloud-target replacement but did not release its owner lease. No replacement was started.');
|
|
161
|
+
}
|
|
104
162
|
}
|
|
105
163
|
if (current.status === 'indeterminate') {
|
|
106
164
|
throw new Error('Taskforce service state is indeterminate; refusing to start a competing owner.');
|
|
@@ -111,7 +169,7 @@ export async function discoverOrStartLocalService(input) {
|
|
|
111
169
|
'service',
|
|
112
170
|
projectRoot,
|
|
113
171
|
'--detached-child',
|
|
114
|
-
], buildLocalServiceProcessEnv(input.env));
|
|
172
|
+
], buildLocalServiceProcessEnv(input.env, requestedCloudTarget));
|
|
115
173
|
const inspection = await waitForLocalServiceDescriptor(leaseInput, 'live', {
|
|
116
174
|
inspectDescriptor,
|
|
117
175
|
sleep: input.sleep,
|
|
@@ -123,6 +181,9 @@ export async function discoverOrStartLocalService(input) {
|
|
|
123
181
|
if (inspection.descriptor.protocolVersion !== LOCAL_SERVICE_PROTOCOL_VERSION) {
|
|
124
182
|
throw new Error(`Taskforce service protocol ${inspection.descriptor.protocolVersion} is incompatible with client protocol ${LOCAL_SERVICE_PROTOCOL_VERSION}.`);
|
|
125
183
|
}
|
|
126
|
-
await validateLocalServiceReadiness(inspection.descriptor, fetchFn);
|
|
184
|
+
const startedCloudTarget = await validateLocalServiceReadiness(inspection.descriptor, fetchFn);
|
|
185
|
+
if (!localServiceCloudTargetsEqual(startedCloudTarget, requestedCloudTarget)) {
|
|
186
|
+
throw new Error('Taskforce service started with a different or unverified cloud target; refusing to attach.');
|
|
187
|
+
}
|
|
127
188
|
return inspection.descriptor;
|
|
128
189
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type PlanningIdentityEntityType = 'initiative' | 'workstream';
|
|
2
|
+
export declare const PLANNING_IDENTITY_ICONS: readonly ["Target", "Route", "Flag", "Rocket", "Briefcase", "Folder", "Layers", "Map", "Compass", "Landmark", "BookOpen", "Lightbulb", "Wrench", "Shield", "Bug", "Palette", "Camera", "Music", "Heart", "Globe", "Users", "Sparkles", "FlaskConical", "Code"];
|
|
3
|
+
export type PlanningIdentityIcon = typeof PLANNING_IDENTITY_ICONS[number];
|
|
4
|
+
export declare const PLANNING_IDENTITY_COLORS: readonly ["red", "orange", "amber", "green", "teal", "sky", "blue", "indigo", "violet"];
|
|
5
|
+
export type PlanningIdentityColor = typeof PLANNING_IDENTITY_COLORS[number];
|
|
6
|
+
export declare function normalizePlanningIdentityIcon(value: unknown): PlanningIdentityIcon | null;
|
|
7
|
+
export declare function normalizePlanningIdentityColor(value: unknown): PlanningIdentityColor | null;
|
|
8
|
+
export declare function assertPlanningIdentityIcon(value: unknown): PlanningIdentityIcon | null;
|
|
9
|
+
export declare function assertPlanningIdentityColor(value: unknown): PlanningIdentityColor | null;
|
|
10
|
+
export declare function resolvePlanningIdentity(input: {
|
|
11
|
+
entityType: PlanningIdentityEntityType;
|
|
12
|
+
entityId: string;
|
|
13
|
+
icon?: unknown;
|
|
14
|
+
color?: unknown;
|
|
15
|
+
}): {
|
|
16
|
+
icon: PlanningIdentityIcon;
|
|
17
|
+
color: PlanningIdentityColor | null;
|
|
18
|
+
customized: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare function planningIdentityColorToken(color?: PlanningIdentityColor | null): string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export const PLANNING_IDENTITY_ICONS = Object.freeze([
|
|
2
|
+
'Target',
|
|
3
|
+
'Route',
|
|
4
|
+
'Flag',
|
|
5
|
+
'Rocket',
|
|
6
|
+
'Briefcase',
|
|
7
|
+
'Folder',
|
|
8
|
+
'Layers',
|
|
9
|
+
'Map',
|
|
10
|
+
'Compass',
|
|
11
|
+
'Landmark',
|
|
12
|
+
'BookOpen',
|
|
13
|
+
'Lightbulb',
|
|
14
|
+
'Wrench',
|
|
15
|
+
'Shield',
|
|
16
|
+
'Bug',
|
|
17
|
+
'Palette',
|
|
18
|
+
'Camera',
|
|
19
|
+
'Music',
|
|
20
|
+
'Heart',
|
|
21
|
+
'Globe',
|
|
22
|
+
'Users',
|
|
23
|
+
'Sparkles',
|
|
24
|
+
'FlaskConical',
|
|
25
|
+
'Code',
|
|
26
|
+
]);
|
|
27
|
+
export const PLANNING_IDENTITY_COLORS = Object.freeze([
|
|
28
|
+
'red',
|
|
29
|
+
'orange',
|
|
30
|
+
'amber',
|
|
31
|
+
'green',
|
|
32
|
+
'teal',
|
|
33
|
+
'sky',
|
|
34
|
+
'blue',
|
|
35
|
+
'indigo',
|
|
36
|
+
'violet',
|
|
37
|
+
]);
|
|
38
|
+
const ICON_SET = new Set(PLANNING_IDENTITY_ICONS);
|
|
39
|
+
const COLOR_SET = new Set(PLANNING_IDENTITY_COLORS);
|
|
40
|
+
export function normalizePlanningIdentityIcon(value) {
|
|
41
|
+
if (value === null || value === undefined || value === '')
|
|
42
|
+
return null;
|
|
43
|
+
const normalized = typeof value === 'string' ? value.trim() : '';
|
|
44
|
+
return ICON_SET.has(normalized) ? normalized : null;
|
|
45
|
+
}
|
|
46
|
+
export function normalizePlanningIdentityColor(value) {
|
|
47
|
+
if (value === null || value === undefined || value === '')
|
|
48
|
+
return null;
|
|
49
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
50
|
+
return COLOR_SET.has(normalized) ? normalized : null;
|
|
51
|
+
}
|
|
52
|
+
export function assertPlanningIdentityIcon(value) {
|
|
53
|
+
const normalized = normalizePlanningIdentityIcon(value);
|
|
54
|
+
if (value !== null && value !== undefined && value !== '' && !normalized) {
|
|
55
|
+
throw new Error('Planning identity icon is not supported.');
|
|
56
|
+
}
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
export function assertPlanningIdentityColor(value) {
|
|
60
|
+
const normalized = normalizePlanningIdentityColor(value);
|
|
61
|
+
if (value !== null && value !== undefined && value !== '' && !normalized) {
|
|
62
|
+
throw new Error('Planning identity color is not supported.');
|
|
63
|
+
}
|
|
64
|
+
return normalized;
|
|
65
|
+
}
|
|
66
|
+
export function resolvePlanningIdentity(input) {
|
|
67
|
+
const explicitIcon = normalizePlanningIdentityIcon(input.icon);
|
|
68
|
+
const explicitColor = normalizePlanningIdentityColor(input.color);
|
|
69
|
+
return {
|
|
70
|
+
icon: explicitIcon || (input.entityType === 'initiative' ? 'Target' : 'Route'),
|
|
71
|
+
color: explicitColor,
|
|
72
|
+
customized: Boolean(explicitIcon || explicitColor),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function planningIdentityColorToken(color) {
|
|
76
|
+
return color
|
|
77
|
+
? `var(--planning-identity-${color})`
|
|
78
|
+
: 'var(--planning-progress-empty)';
|
|
79
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { TextDecoder, TextEncoder } from 'util';
|
|
2
2
|
import { Worker } from 'worker_threads';
|
|
3
|
+
import { performance } from 'node:perf_hooks';
|
|
3
4
|
import { assertSynchronousTransactionResult, } from './databaseAdapter.js';
|
|
5
|
+
import { recordPostgresWorkerWait } from './postgresRequestDiagnostics.js';
|
|
4
6
|
const RESPONSE_STATUS_INDEX = 0;
|
|
5
7
|
const RESPONSE_LENGTH_INDEX = 1;
|
|
6
8
|
const RESPONSE_READY = 1;
|
|
@@ -88,7 +90,9 @@ export class PostgresAdapter {
|
|
|
88
90
|
statusBuffer,
|
|
89
91
|
responseBuffer
|
|
90
92
|
});
|
|
93
|
+
const waitStartedAt = performance.now();
|
|
91
94
|
const waitResult = Atomics.wait(status, RESPONSE_STATUS_INDEX, RESPONSE_INITIAL, RESPONSE_TIMEOUT_MS);
|
|
95
|
+
recordPostgresWorkerWait(payload.op, performance.now() - waitStartedAt);
|
|
92
96
|
if (waitResult === 'timed-out') {
|
|
93
97
|
throw new Error('[Taskforce] Postgres worker timed out.');
|
|
94
98
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type RequestPerformanceDiagnostics = {
|
|
2
|
+
postgresQueryCount: number;
|
|
3
|
+
postgresWorkerCallCount: number;
|
|
4
|
+
postgresWorkerWaitMs: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function runWithRequestPerformanceDiagnostics<T>(enabled: boolean, callback: () => T): T;
|
|
7
|
+
export declare function recordPostgresWorkerWait(operation: string, durationMs: number): void;
|
|
8
|
+
export declare function getRequestPerformanceDiagnostics(): RequestPerformanceDiagnostics | null;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
const requestPerformanceStorage = new AsyncLocalStorage();
|
|
3
|
+
export function runWithRequestPerformanceDiagnostics(enabled, callback) {
|
|
4
|
+
if (!enabled)
|
|
5
|
+
return callback();
|
|
6
|
+
return requestPerformanceStorage.run({
|
|
7
|
+
postgresQueryCount: 0,
|
|
8
|
+
postgresWorkerCallCount: 0,
|
|
9
|
+
postgresWorkerWaitMs: 0,
|
|
10
|
+
}, callback);
|
|
11
|
+
}
|
|
12
|
+
export function recordPostgresWorkerWait(operation, durationMs) {
|
|
13
|
+
const diagnostics = requestPerformanceStorage.getStore();
|
|
14
|
+
if (!diagnostics)
|
|
15
|
+
return;
|
|
16
|
+
diagnostics.postgresWorkerCallCount += 1;
|
|
17
|
+
if (operation === 'query')
|
|
18
|
+
diagnostics.postgresQueryCount += 1;
|
|
19
|
+
diagnostics.postgresWorkerWaitMs += Math.max(0, durationMs);
|
|
20
|
+
}
|
|
21
|
+
export function getRequestPerformanceDiagnostics() {
|
|
22
|
+
const diagnostics = requestPerformanceStorage.getStore();
|
|
23
|
+
return diagnostics ? { ...diagnostics } : null;
|
|
24
|
+
}
|
|
@@ -67,11 +67,43 @@ export interface WorkspaceAssetLinkEntry {
|
|
|
67
67
|
updatedAt: string;
|
|
68
68
|
deletedAt: string | null;
|
|
69
69
|
}
|
|
70
|
+
export interface WorkspaceAssetUploadDraftEntry {
|
|
71
|
+
workspaceId: string;
|
|
72
|
+
draftId: string;
|
|
73
|
+
assetId: string;
|
|
74
|
+
storageKey: string;
|
|
75
|
+
purpose: string;
|
|
76
|
+
createdByUserId: string;
|
|
77
|
+
createdAt: string;
|
|
78
|
+
retiredAt: string | null;
|
|
79
|
+
}
|
|
70
80
|
export declare class WorkspaceAssetStore {
|
|
71
81
|
private readonly db;
|
|
72
82
|
private readonly tenantId;
|
|
73
83
|
private supportsTargetTypeCache;
|
|
74
84
|
constructor(db: DatabaseAdapter, tenantId: string);
|
|
85
|
+
recordUploadDraft(input: {
|
|
86
|
+
workspaceId: string;
|
|
87
|
+
draftId: string;
|
|
88
|
+
assetId: string;
|
|
89
|
+
storageKey: string;
|
|
90
|
+
purpose: string;
|
|
91
|
+
createdByUserId: string;
|
|
92
|
+
createdAt?: string;
|
|
93
|
+
}): WorkspaceAssetUploadDraftEntry;
|
|
94
|
+
listUploadDrafts(input: {
|
|
95
|
+
workspaceId: string;
|
|
96
|
+
draftId?: string;
|
|
97
|
+
createdByUserId?: string;
|
|
98
|
+
createdBefore?: string;
|
|
99
|
+
includeRetired?: boolean;
|
|
100
|
+
}): WorkspaceAssetUploadDraftEntry[];
|
|
101
|
+
retireUploadDraft(input: {
|
|
102
|
+
workspaceId: string;
|
|
103
|
+
draftId: string;
|
|
104
|
+
assetId: string;
|
|
105
|
+
retiredAt?: string;
|
|
106
|
+
}): boolean;
|
|
75
107
|
private isPlanningTargetDurablyOwned;
|
|
76
108
|
assertAssetDeletionAllowed(assetIdRaw: string, workspaceIdRaw: string): void;
|
|
77
109
|
private runDurableAssetMutation;
|
|
@@ -114,6 +114,76 @@ export class WorkspaceAssetStore {
|
|
|
114
114
|
this.db = db;
|
|
115
115
|
this.tenantId = tenantId;
|
|
116
116
|
}
|
|
117
|
+
recordUploadDraft(input) {
|
|
118
|
+
const workspaceId = normalizeWorkspaceId(input.workspaceId);
|
|
119
|
+
const draftId = normalizeId(input.draftId);
|
|
120
|
+
const assetId = normalizeId(input.assetId);
|
|
121
|
+
const storageKey = normalizeId(input.storageKey);
|
|
122
|
+
const purpose = normalizeId(input.purpose);
|
|
123
|
+
const createdByUserId = normalizeId(input.createdByUserId, 'anonymous');
|
|
124
|
+
const createdAt = normalizeId(input.createdAt, new Date().toISOString());
|
|
125
|
+
if (!draftId || !assetId || !storageKey || !purpose) {
|
|
126
|
+
throw new Error('Draft upload identity, asset, storage key, and purpose are required.');
|
|
127
|
+
}
|
|
128
|
+
this.db.prepare(`
|
|
129
|
+
INSERT INTO workspace_asset_upload_drafts (
|
|
130
|
+
tenant_id, workspace_id, draft_id, asset_id, storage_key,
|
|
131
|
+
purpose, created_by_user_id, created_at, retired_at
|
|
132
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)
|
|
133
|
+
ON CONFLICT (tenant_id, workspace_id, draft_id, asset_id) DO UPDATE SET
|
|
134
|
+
storage_key = EXCLUDED.storage_key,
|
|
135
|
+
purpose = EXCLUDED.purpose,
|
|
136
|
+
created_by_user_id = EXCLUDED.created_by_user_id
|
|
137
|
+
`).run(this.tenantId, workspaceId, draftId, assetId, storageKey, purpose, createdByUserId, createdAt);
|
|
138
|
+
return {
|
|
139
|
+
workspaceId, draftId, assetId, storageKey, purpose,
|
|
140
|
+
createdByUserId, createdAt, retiredAt: null,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
listUploadDrafts(input) {
|
|
144
|
+
const workspaceId = normalizeWorkspaceId(input.workspaceId);
|
|
145
|
+
const clauses = ['tenant_id = ?', 'workspace_id = ?'];
|
|
146
|
+
const values = [this.tenantId, workspaceId];
|
|
147
|
+
if (input.draftId) {
|
|
148
|
+
clauses.push('draft_id = ?');
|
|
149
|
+
values.push(normalizeId(input.draftId));
|
|
150
|
+
}
|
|
151
|
+
if (input.createdByUserId) {
|
|
152
|
+
clauses.push('created_by_user_id = ?');
|
|
153
|
+
values.push(normalizeId(input.createdByUserId));
|
|
154
|
+
}
|
|
155
|
+
if (input.createdBefore) {
|
|
156
|
+
clauses.push('created_at <= ?');
|
|
157
|
+
values.push(normalizeId(input.createdBefore));
|
|
158
|
+
}
|
|
159
|
+
if (!input.includeRetired)
|
|
160
|
+
clauses.push('retired_at IS NULL');
|
|
161
|
+
const rows = this.db.prepare(`
|
|
162
|
+
SELECT workspace_id, draft_id, asset_id, storage_key, purpose,
|
|
163
|
+
created_by_user_id, created_at, retired_at
|
|
164
|
+
FROM workspace_asset_upload_drafts
|
|
165
|
+
WHERE ${clauses.join(' AND ')}
|
|
166
|
+
ORDER BY created_at ASC, asset_id ASC
|
|
167
|
+
`).all(...values);
|
|
168
|
+
return rows.map((row) => ({
|
|
169
|
+
workspaceId: String(row.workspace_id || ''),
|
|
170
|
+
draftId: String(row.draft_id || ''),
|
|
171
|
+
assetId: String(row.asset_id || ''),
|
|
172
|
+
storageKey: String(row.storage_key || ''),
|
|
173
|
+
purpose: String(row.purpose || ''),
|
|
174
|
+
createdByUserId: String(row.created_by_user_id || ''),
|
|
175
|
+
createdAt: String(row.created_at || ''),
|
|
176
|
+
retiredAt: row.retired_at == null ? null : String(row.retired_at),
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
retireUploadDraft(input) {
|
|
180
|
+
const result = this.db.prepare(`
|
|
181
|
+
UPDATE workspace_asset_upload_drafts
|
|
182
|
+
SET retired_at = COALESCE(retired_at, ?)
|
|
183
|
+
WHERE tenant_id = ? AND workspace_id = ? AND draft_id = ? AND asset_id = ?
|
|
184
|
+
`).run(normalizeId(input.retiredAt, new Date().toISOString()), this.tenantId, normalizeWorkspaceId(input.workspaceId), normalizeId(input.draftId), normalizeId(input.assetId));
|
|
185
|
+
return Number(result.changes || 0) > 0;
|
|
186
|
+
}
|
|
117
187
|
isPlanningTargetDurablyOwned(workspaceId, targetType, targetId) {
|
|
118
188
|
if (targetType !== 'initiative' && targetType !== 'workstream')
|
|
119
189
|
return false;
|
|
@@ -4,6 +4,8 @@ import type { WorkspaceSyncEngineV2PushBoundary } from '../engine/workspaceSyncE
|
|
|
4
4
|
import { type LocalSyncExecutionPermitCleanAbandonResult, type LocalSyncExecutionPermitDrainSnapshot, type LocalSyncExecutionPermit, type LocalSyncExecutionPermitOwnerMode, type LocalSyncExecutionPermitState, type LocalSyncGatewayOperationClass, type LocalSyncRecoveryObligation, type LocalSyncUpstreamDisposition } from './localSyncExecutionPermitTypes.js';
|
|
5
5
|
export declare const LOCAL_SYNC_PERMIT_RETENTION_MIN_AGE_MS: number;
|
|
6
6
|
export declare const LOCAL_SYNC_PERMIT_RETENTION_SOFT_LIMIT = 1000;
|
|
7
|
+
export declare const LOCAL_SYNC_PERMIT_RETENTION_HARD_LIMIT = 10000;
|
|
8
|
+
export declare const LOCAL_SYNC_OBLIGATION_RETENTION_HARD_LIMIT = 2000;
|
|
7
9
|
export declare const LOCAL_SYNC_PERMIT_RETENTION_DELETE_BATCH = 250;
|
|
8
10
|
export declare const LOCAL_SYNC_PERMIT_RETENTION_SWEEP_LIMIT = 5000;
|
|
9
11
|
type LocalSyncPermitCompletionGroupFence = {
|
|
@@ -60,6 +62,12 @@ export declare class LocalSyncExecutionPermitStore {
|
|
|
60
62
|
byState: Partial<Record<LocalSyncExecutionPermitState, number>>;
|
|
61
63
|
recoveryObligations: number;
|
|
62
64
|
localCommitInvariantViolation: boolean;
|
|
65
|
+
history: {
|
|
66
|
+
terminalPermits: number;
|
|
67
|
+
oldestTerminalPermitAt: string | null;
|
|
68
|
+
resolvedRecoveryObligations: number;
|
|
69
|
+
oldestResolvedRecoveryObligationAt: string | null;
|
|
70
|
+
};
|
|
63
71
|
};
|
|
64
72
|
inspectGenerationDrain(input: {
|
|
65
73
|
workspaceId: string;
|
|
@@ -110,6 +118,14 @@ export declare class LocalSyncExecutionPermitStore {
|
|
|
110
118
|
ownerMode: LocalSyncExecutionPermitOwnerMode;
|
|
111
119
|
serverInstanceId: string;
|
|
112
120
|
}): LocalSyncExecutionPermit;
|
|
121
|
+
renewAcceptedApplyCompletion(input: {
|
|
122
|
+
permitId: string;
|
|
123
|
+
workspaceId: string;
|
|
124
|
+
coordinatorGeneration: number;
|
|
125
|
+
serverInstanceId: string;
|
|
126
|
+
ownerMode: LocalSyncExecutionPermitOwnerMode;
|
|
127
|
+
deadlineAt: string;
|
|
128
|
+
}): LocalSyncExecutionPermit;
|
|
113
129
|
completeAcceptedV3Dispatch<T>(input: {
|
|
114
130
|
permitId: string;
|
|
115
131
|
workspaceId: string;
|