@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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LocalServiceRuntimeContract {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
coverageVersion: number | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function hydrateSourceCheckoutServiceEnvironment(input: {
|
|
7
|
+
env: NodeJS.ProcessEnv;
|
|
8
|
+
projectRoot: string;
|
|
9
|
+
packageRoot: string;
|
|
10
|
+
}): string | null;
|
|
11
|
+
export declare function resolveLocalServiceRuntimeContract(env: NodeJS.ProcessEnv): LocalServiceRuntimeContract;
|
|
12
|
+
export declare function parseLocalServiceRuntimeContract(value: unknown): LocalServiceRuntimeContract | null;
|
|
13
|
+
export declare function localServiceRuntimeContractsEqual(left: LocalServiceRuntimeContract | null | undefined, right: LocalServiceRuntimeContract | null | undefined): boolean;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { parseTaskforceCloudEnvironment } from './cloudEnvironment.js';
|
|
5
|
+
const SERVICE_ENV_EXACT_KEYS = new Set([
|
|
6
|
+
'CF_ACCESS_CLIENT_ID',
|
|
7
|
+
'CF_ACCESS_CLIENT_SECRET',
|
|
8
|
+
'TASKFORCE_CF_ACCESS_CLIENT_ID',
|
|
9
|
+
'TASKFORCE_CF_ACCESS_CLIENT_SECRET',
|
|
10
|
+
'TASKFORCE_REALTIME_SYNC',
|
|
11
|
+
'TASKFORCE_RUNTIME_MODE',
|
|
12
|
+
]);
|
|
13
|
+
function isServiceEnvironmentKey(key) {
|
|
14
|
+
if (SERVICE_ENV_EXACT_KEYS.has(key))
|
|
15
|
+
return true;
|
|
16
|
+
if (key.startsWith('TASKFORCE_E2E_') || key.startsWith('TASKFORCE_PLAYWRIGHT_'))
|
|
17
|
+
return false;
|
|
18
|
+
return key.startsWith('TASKFORCE_SYNC_')
|
|
19
|
+
|| key.startsWith('TASKFORCE_LOCAL_SYNC_')
|
|
20
|
+
|| key.startsWith('VITE_TASKFORCE_');
|
|
21
|
+
}
|
|
22
|
+
function isContractKey(key) {
|
|
23
|
+
return key === 'TASKFORCE_RUNTIME_MODE'
|
|
24
|
+
|| key === 'TASKFORCE_REALTIME_SYNC'
|
|
25
|
+
|| key.startsWith('TASKFORCE_SYNC_')
|
|
26
|
+
|| key.startsWith('TASKFORCE_LOCAL_SYNC_');
|
|
27
|
+
}
|
|
28
|
+
function stripQuotes(value) {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
if (trimmed.length >= 2) {
|
|
31
|
+
const first = trimmed[0];
|
|
32
|
+
const last = trimmed[trimmed.length - 1];
|
|
33
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
34
|
+
return trimmed.slice(1, -1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return trimmed;
|
|
38
|
+
}
|
|
39
|
+
function isInside(parent, child) {
|
|
40
|
+
const relative = path.relative(parent, child);
|
|
41
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
42
|
+
}
|
|
43
|
+
function loadTrustedEnvFile(filePath, packageRoot, loaded, visited) {
|
|
44
|
+
const resolved = fs.realpathSync(filePath);
|
|
45
|
+
if (!isInside(packageRoot, resolved) || !path.basename(resolved).startsWith('.env')) {
|
|
46
|
+
throw new Error('Taskforce source-checkout environment includes must remain trusted .env files inside the package root.');
|
|
47
|
+
}
|
|
48
|
+
if (visited.has(resolved))
|
|
49
|
+
return;
|
|
50
|
+
visited.add(resolved);
|
|
51
|
+
const lines = fs.readFileSync(resolved, 'utf8').split(/\r?\n/);
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
55
|
+
continue;
|
|
56
|
+
const include = trimmed.match(/^(?:\.|source)\s+([^\s]+)$/);
|
|
57
|
+
if (include) {
|
|
58
|
+
const includePath = path.resolve(path.dirname(resolved), stripQuotes(include[1]));
|
|
59
|
+
loadTrustedEnvFile(includePath, packageRoot, loaded, visited);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const normalized = trimmed.startsWith('export ') ? trimmed.slice(7).trim() : trimmed;
|
|
63
|
+
const separator = normalized.indexOf('=');
|
|
64
|
+
if (separator <= 0)
|
|
65
|
+
continue;
|
|
66
|
+
const key = normalized.slice(0, separator).trim();
|
|
67
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(key) || !isServiceEnvironmentKey(key))
|
|
68
|
+
continue;
|
|
69
|
+
loaded.set(key, stripQuotes(normalized.slice(separator + 1)));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function hydrateSourceCheckoutServiceEnvironment(input) {
|
|
73
|
+
const projectRoot = fs.realpathSync(input.projectRoot);
|
|
74
|
+
const packageRoot = fs.realpathSync(input.packageRoot);
|
|
75
|
+
if (projectRoot !== packageRoot)
|
|
76
|
+
return null;
|
|
77
|
+
const environment = parseTaskforceCloudEnvironment(input.env.TASKFORCE_CLOUD_ENVIRONMENT);
|
|
78
|
+
if (environment !== 'performance' && environment !== 'staging')
|
|
79
|
+
return null;
|
|
80
|
+
const envFile = path.join(packageRoot, `.env.${environment}`);
|
|
81
|
+
if (!fs.existsSync(envFile))
|
|
82
|
+
return null;
|
|
83
|
+
const explicitKeys = new Set(Object.keys(input.env));
|
|
84
|
+
const loaded = new Map();
|
|
85
|
+
loadTrustedEnvFile(envFile, packageRoot, loaded, new Set());
|
|
86
|
+
for (const [key, value] of loaded) {
|
|
87
|
+
if (!explicitKeys.has(key))
|
|
88
|
+
input.env[key] = value;
|
|
89
|
+
}
|
|
90
|
+
return envFile;
|
|
91
|
+
}
|
|
92
|
+
export function resolveLocalServiceRuntimeContract(env) {
|
|
93
|
+
const effectiveEnv = {
|
|
94
|
+
...env,
|
|
95
|
+
TASKFORCE_RUNTIME_MODE: String(env.TASKFORCE_RUNTIME_MODE || 'local').trim().toLowerCase(),
|
|
96
|
+
};
|
|
97
|
+
const entries = Object.entries(effectiveEnv)
|
|
98
|
+
.filter(([key, value]) => isContractKey(key) && value !== undefined)
|
|
99
|
+
.map(([key, value]) => [key, String(value)])
|
|
100
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
101
|
+
const fingerprint = createHash('sha256')
|
|
102
|
+
.update(JSON.stringify(entries))
|
|
103
|
+
.digest('hex');
|
|
104
|
+
const rawCoverageVersion = Number.parseInt(String(env.TASKFORCE_SYNC_V3_COMBINED_FEED_COVERAGE_VERSION || ''), 10);
|
|
105
|
+
return {
|
|
106
|
+
schemaVersion: 1,
|
|
107
|
+
fingerprint,
|
|
108
|
+
coverageVersion: Number.isSafeInteger(rawCoverageVersion) && rawCoverageVersion > 0
|
|
109
|
+
? rawCoverageVersion
|
|
110
|
+
: null,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function parseLocalServiceRuntimeContract(value) {
|
|
114
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
115
|
+
return null;
|
|
116
|
+
const record = value;
|
|
117
|
+
if (record.schemaVersion !== 1
|
|
118
|
+
|| typeof record.fingerprint !== 'string'
|
|
119
|
+
|| !/^[a-f0-9]{64}$/.test(record.fingerprint)
|
|
120
|
+
|| (record.coverageVersion !== null
|
|
121
|
+
&& (!Number.isSafeInteger(record.coverageVersion) || Number(record.coverageVersion) < 1))) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
schemaVersion: 1,
|
|
126
|
+
fingerprint: record.fingerprint,
|
|
127
|
+
coverageVersion: record.coverageVersion === null ? null : Number(record.coverageVersion),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function localServiceRuntimeContractsEqual(left, right) {
|
|
131
|
+
return Boolean(left && right
|
|
132
|
+
&& left.schemaVersion === right.schemaVersion
|
|
133
|
+
&& left.fingerprint === right.fingerprint
|
|
134
|
+
&& left.coverageVersion === right.coverageVersion);
|
|
135
|
+
}
|
|
@@ -931,15 +931,27 @@ function reconcileSyncedAiProfileConnectionBindings(deps, workspaceId, profileId
|
|
|
931
931
|
.sort()
|
|
932
932
|
: [];
|
|
933
933
|
const clientId = String(connection.clientId || '').trim() || null;
|
|
934
|
-
|
|
935
|
-
|
|
934
|
+
// Credential-derived bindings are authentication state owned by the cloud
|
|
935
|
+
// runtime. Synced profile metadata must never create, remove, or reassign
|
|
936
|
+
// them; doing so would let a stale replica change write attribution.
|
|
937
|
+
const syncedBindingIds = bindingIds.filter((bindingId) => !isCredentialOwnedConnectionBindingId(bindingId));
|
|
938
|
+
if (syncedBindingIds.length === 0) {
|
|
939
|
+
deps.db.prepare(`DELETE FROM ai_profile_connection_bindings
|
|
940
|
+
WHERE tenant_id = ? AND workspace_id = ? AND profile_id = ?
|
|
941
|
+
AND connection_id NOT LIKE 'oauth:%'
|
|
942
|
+
AND connection_id NOT LIKE 'oauth-client:%'
|
|
943
|
+
AND connection_id NOT LIKE 'token:%'`)
|
|
936
944
|
.run(deps.tenantId, workspaceId, profileId);
|
|
937
945
|
return;
|
|
938
946
|
}
|
|
939
|
-
const placeholders =
|
|
947
|
+
const placeholders = syncedBindingIds.map(() => '?').join(', ');
|
|
940
948
|
deps.db.prepare(`DELETE FROM ai_profile_connection_bindings
|
|
941
|
-
WHERE tenant_id = ? AND workspace_id = ? AND profile_id = ?
|
|
942
|
-
|
|
949
|
+
WHERE tenant_id = ? AND workspace_id = ? AND profile_id = ?
|
|
950
|
+
AND connection_id NOT LIKE 'oauth:%'
|
|
951
|
+
AND connection_id NOT LIKE 'oauth-client:%'
|
|
952
|
+
AND connection_id NOT LIKE 'token:%'
|
|
953
|
+
AND connection_id NOT IN (${placeholders})`)
|
|
954
|
+
.run(deps.tenantId, workspaceId, profileId, ...syncedBindingIds);
|
|
943
955
|
const now = new Date().toISOString();
|
|
944
956
|
const upsert = deps.db.prepare(`INSERT INTO ai_profile_connection_bindings
|
|
945
957
|
(tenant_id, workspace_id, connection_id, profile_id, client_id, created_at, updated_at)
|
|
@@ -948,7 +960,7 @@ function reconcileSyncedAiProfileConnectionBindings(deps, workspaceId, profileId
|
|
|
948
960
|
profile_id = excluded.profile_id,
|
|
949
961
|
client_id = excluded.client_id,
|
|
950
962
|
updated_at = excluded.updated_at`);
|
|
951
|
-
for (const bindingId of
|
|
963
|
+
for (const bindingId of syncedBindingIds) {
|
|
952
964
|
upsert.run(deps.tenantId, workspaceId, bindingId, profileId, clientId, now, now);
|
|
953
965
|
}
|
|
954
966
|
}
|
|
@@ -1282,6 +1294,11 @@ function normalizeConnectionBindingId(deps, value) {
|
|
|
1282
1294
|
}
|
|
1283
1295
|
return normalized;
|
|
1284
1296
|
}
|
|
1297
|
+
function isCredentialOwnedConnectionBindingId(connectionId) {
|
|
1298
|
+
return connectionId.startsWith('oauth:')
|
|
1299
|
+
|| connectionId.startsWith('oauth-client:')
|
|
1300
|
+
|| connectionId.startsWith('token:');
|
|
1301
|
+
}
|
|
1285
1302
|
function parseProviderMetadataRow(row) {
|
|
1286
1303
|
if (typeof row.provider_metadata_json !== 'string' || !row.provider_metadata_json.trim())
|
|
1287
1304
|
return {};
|
|
@@ -1304,10 +1321,29 @@ export function bindAiProfileConnectionService(deps, input) {
|
|
|
1304
1321
|
if (!target)
|
|
1305
1322
|
throw deps.createRuleError(`Profile ${profileId} not found`, 404, 'AI_PROFILE_NOT_FOUND');
|
|
1306
1323
|
const now = new Date().toISOString();
|
|
1307
|
-
deps.db.prepare(`INSERT INTO ai_profile_connection_bindings
|
|
1324
|
+
deps.db.prepare(`INSERT INTO ai_profile_connection_bindings
|
|
1325
|
+
(tenant_id, workspace_id, connection_id, profile_id, client_id, created_at, updated_at)
|
|
1308
1326
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1309
|
-
ON CONFLICT (tenant_id, workspace_id, connection_id) DO
|
|
1327
|
+
ON CONFLICT (tenant_id, workspace_id, connection_id) DO NOTHING`)
|
|
1310
1328
|
.run(deps.tenantId, workspaceId, connectionId, profileId, clientId, now, now);
|
|
1329
|
+
const existing = deps.db.prepare(`SELECT b.profile_id, p.archived_at
|
|
1330
|
+
FROM ai_profile_connection_bindings b
|
|
1331
|
+
LEFT JOIN ai_profiles p ON p.tenant_id = b.tenant_id AND p.workspace_id = b.workspace_id AND p.id = b.profile_id
|
|
1332
|
+
WHERE b.tenant_id = ? AND b.workspace_id = ? AND b.connection_id = ? LIMIT 1`)
|
|
1333
|
+
.get(deps.tenantId, workspaceId, connectionId);
|
|
1334
|
+
if (!existing?.profile_id) {
|
|
1335
|
+
throw deps.createRuleError('Failed to establish the MCP connection binding.', 500, 'AI_PROFILE_CONNECTION_BIND_FAILED');
|
|
1336
|
+
}
|
|
1337
|
+
if (existing && existing.profile_id !== profileId && !existing.archived_at) {
|
|
1338
|
+
throw deps.createRuleError('This MCP connection is already bound to a different active AI profile. Reconnect or use an authorized profile recovery flow.', 409, 'AI_PROFILE_CONNECTION_CONFLICT', { workspaceId, connectionId, existingProfileId: existing.profile_id || null, requestedProfileId: profileId });
|
|
1339
|
+
}
|
|
1340
|
+
const updateResult = deps.db.prepare(`UPDATE ai_profile_connection_bindings
|
|
1341
|
+
SET profile_id = ?, client_id = ?, updated_at = ?
|
|
1342
|
+
WHERE tenant_id = ? AND workspace_id = ? AND connection_id = ? AND profile_id = ?`)
|
|
1343
|
+
.run(profileId, clientId, now, deps.tenantId, workspaceId, connectionId, existing.profile_id);
|
|
1344
|
+
if (updateResult.changes !== 1) {
|
|
1345
|
+
throw deps.createRuleError('The MCP connection binding changed concurrently. Retry after refreshing the intended profile.', 409, 'AI_PROFILE_CONNECTION_CONFLICT', { workspaceId, connectionId, expectedProfileId: existing.profile_id || null, requestedProfileId: profileId });
|
|
1346
|
+
}
|
|
1311
1347
|
const rows = deps.db.prepare(`SELECT * FROM ai_profiles WHERE tenant_id = ? AND workspace_id = ?`).all(deps.tenantId, workspaceId);
|
|
1312
1348
|
for (const row of rows) {
|
|
1313
1349
|
const metadata = parseProviderMetadataRow(row);
|
|
@@ -1351,6 +1387,8 @@ export function getAiProfileByConnectionService(deps, input) {
|
|
|
1351
1387
|
const record = hydrateAiProfileRecord(deps, binding);
|
|
1352
1388
|
return input.seatScope !== undefined && (record.seatScope ?? null) !== input.seatScope ? null : record;
|
|
1353
1389
|
}
|
|
1390
|
+
if (isCredentialOwnedConnectionBindingId(connectionId))
|
|
1391
|
+
return null;
|
|
1354
1392
|
const legacyRows = deps.db.prepare(`SELECT * FROM ai_profiles WHERE tenant_id = ? AND workspace_id = ?`).all(deps.tenantId, workspaceId)
|
|
1355
1393
|
.filter((row) => {
|
|
1356
1394
|
const record = hydrateAiProfileRecord(deps, row);
|
|
@@ -8,6 +8,7 @@ export interface DeletedTaskLifecycleServiceDeps {
|
|
|
8
8
|
generateEntityId: (prefix: string) => string;
|
|
9
9
|
normalizeWorkspaceId: (workspaceId: string | undefined) => string;
|
|
10
10
|
resolveWorkspaceActor: (workspaceId: string, actorRef: unknown) => TaskActor | null;
|
|
11
|
+
resolveWorkspaceActors: (workspaceId: string, actorRefs: unknown[]) => Map<string, TaskActor | null>;
|
|
11
12
|
sanitizeOrderInDay: (value: unknown) => number | null;
|
|
12
13
|
sanitizeDateOnly: (value: unknown) => string | null;
|
|
13
14
|
normalizeTaskEventActor: (actorRef: unknown, workspaceId: string) => {
|
|
@@ -119,6 +119,41 @@ export function mapDeletedTaskRowService(deps, row) {
|
|
|
119
119
|
actorProfile: actor === 'system' ? null : deps.resolveWorkspaceActor(workspaceId, actor)
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
+
function mapDeletedTaskRowsService(deps, rows) {
|
|
123
|
+
const actorProfiles = new Map();
|
|
124
|
+
const actorRefsByWorkspace = new Map();
|
|
125
|
+
for (const row of rows) {
|
|
126
|
+
const workspaceId = deps.normalizeWorkspaceId(typeof row.workspace_id === 'string' ? row.workspace_id : undefined);
|
|
127
|
+
const actorRef = String(row.actor || 'system').trim() || 'system';
|
|
128
|
+
if (actorRef === 'system') {
|
|
129
|
+
actorProfiles.set(`${workspaceId}\u0000${actorRef}`, null);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const workspaceActorRefs = actorRefsByWorkspace.get(workspaceId) ?? new Set();
|
|
133
|
+
workspaceActorRefs.add(actorRef);
|
|
134
|
+
actorRefsByWorkspace.set(workspaceId, workspaceActorRefs);
|
|
135
|
+
}
|
|
136
|
+
for (const [workspaceId, actorRefs] of actorRefsByWorkspace) {
|
|
137
|
+
const resolvedWorkspaceActors = deps.resolveWorkspaceActors(workspaceId, [...actorRefs]);
|
|
138
|
+
for (const actorRef of actorRefs) {
|
|
139
|
+
actorProfiles.set(`${workspaceId}\u0000${actorRef}`, resolvedWorkspaceActors.get(actorRef) ?? null);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const cachedDeps = {
|
|
143
|
+
...deps,
|
|
144
|
+
resolveWorkspaceActor: (workspaceId, actorRef) => {
|
|
145
|
+
const cacheKey = `${workspaceId}\u0000${String(actorRef || '').trim()}`;
|
|
146
|
+
if (actorProfiles.has(cacheKey))
|
|
147
|
+
return actorProfiles.get(cacheKey) ?? null;
|
|
148
|
+
const actorProfile = deps.resolveWorkspaceActor(workspaceId, actorRef);
|
|
149
|
+
actorProfiles.set(cacheKey, actorProfile);
|
|
150
|
+
return actorProfile;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
return rows
|
|
154
|
+
.map((row) => mapDeletedTaskRowService(cachedDeps, row))
|
|
155
|
+
.filter((row) => Boolean(row));
|
|
156
|
+
}
|
|
122
157
|
function deletedTaskListSnapshotSelect() {
|
|
123
158
|
const snapshotPairs = DELETED_TASK_LIST_SNAPSHOT_FIELDS
|
|
124
159
|
.map((field) => `'${field}', snapshot -> '${field}'`)
|
|
@@ -220,9 +255,7 @@ export function listDeletedTasksService(deps, workspaceId) {
|
|
|
220
255
|
`;
|
|
221
256
|
const rows = deps.db.prepare(listSql).all(deps.tenantId, normalizedWorkspaceId);
|
|
222
257
|
return {
|
|
223
|
-
deleted: rows
|
|
224
|
-
.map((row) => mapDeletedTaskRowService(deps, row))
|
|
225
|
-
.filter((row) => Boolean(row))
|
|
258
|
+
deleted: mapDeletedTaskRowsService(deps, rows)
|
|
226
259
|
};
|
|
227
260
|
}
|
|
228
261
|
export function listDeletedTasksReadOnlyService(deps, workspaceId) {
|
|
@@ -240,9 +273,7 @@ export function listDeletedTasksReadOnlyService(deps, workspaceId) {
|
|
|
240
273
|
`;
|
|
241
274
|
const rows = deps.db.prepare(listSql).all(deps.tenantId, normalizedWorkspaceId);
|
|
242
275
|
return {
|
|
243
|
-
deleted: rows
|
|
244
|
-
.map((row) => mapDeletedTaskRowService(deps, row))
|
|
245
|
-
.filter((row) => Boolean(row))
|
|
276
|
+
deleted: mapDeletedTaskRowsService(deps, rows)
|
|
246
277
|
.filter((row) => row.deletedAt >= cutoff || durableDeleteOwnedTaskIds.has(row.taskId))
|
|
247
278
|
};
|
|
248
279
|
}
|
|
@@ -8,7 +8,7 @@ export declare class DurableTaskAttachmentLinksApplyError extends Error {
|
|
|
8
8
|
constructor(message: string, code: 'TASK_ATTACHMENT_ASSET_NOT_READY' | 'TASK_ATTACHMENT_ASSET_INVALID' | 'TASK_ATTACHMENT_LINK_INVALID', statusCode: number, assetId?: string | undefined);
|
|
9
9
|
}
|
|
10
10
|
export interface DurableTaskAttachmentLinksCore {
|
|
11
|
-
getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'adoptReadableReferenceNumber'>;
|
|
11
|
+
getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'adoptReadableReferenceNumber' | 'restoreDeleted'>;
|
|
12
12
|
getWorkspaceAssetDispatchReadiness(assetId: string, workspaceId: string): {
|
|
13
13
|
ready: boolean;
|
|
14
14
|
retryable: boolean;
|
|
@@ -16,7 +16,7 @@ export interface DurableTaskAttachmentLinksCore {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
export interface DurableWorkspaceTaskAttachmentLinksCore extends DurableTaskAttachmentLinksCore {
|
|
19
|
-
getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'listLinksForWorkspace' | 'listAllNormalizedByWorkspace' | 'adoptReadableReferenceNumber'>;
|
|
19
|
+
getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'listLinksForWorkspace' | 'listAllNormalizedByWorkspace' | 'adoptReadableReferenceNumber' | 'restoreDeleted'>;
|
|
20
20
|
}
|
|
21
21
|
export declare function resolveTaskCardCoverAssetIdForLinks(input: {
|
|
22
22
|
currentCardCoverAssetId?: string | null;
|
|
@@ -29,6 +29,7 @@ export declare function resolveTaskAttachmentLinksForDurableSync(core: DurableTa
|
|
|
29
29
|
requireDispatchReady?: boolean;
|
|
30
30
|
adoptAuthoritativeReferenceNumber?: boolean;
|
|
31
31
|
ignoreAssetMetadataSnapshots?: boolean;
|
|
32
|
+
restoreDeletedExternalAssetForAuthoritativeSync?: boolean;
|
|
32
33
|
}): AttachmentItem[];
|
|
33
34
|
export declare function normalizeTaskAttachmentDescriptorsForDurableSync(core: DurableTaskAttachmentLinksCore, workspaceId: string, attachments: readonly AttachmentItem[], options?: {
|
|
34
35
|
requireDispatchReady?: boolean;
|
|
@@ -53,6 +53,12 @@ function canonicalRole(asset, requested) {
|
|
|
53
53
|
}
|
|
54
54
|
return expected;
|
|
55
55
|
}
|
|
56
|
+
function canonicalPersistedRole(asset, persisted) {
|
|
57
|
+
// Historical external links predate the reference-only invariant. Repair
|
|
58
|
+
// projections must publish the current canonical shape, while mutation
|
|
59
|
+
// inputs continue through canonicalRole and remain strictly validated.
|
|
60
|
+
return asset.storageProvider === 'external' ? 'reference' : canonicalRole(asset, persisted);
|
|
61
|
+
}
|
|
56
62
|
function referenceFields(asset) {
|
|
57
63
|
if (!asset.referenceNumber)
|
|
58
64
|
return { referenceNumber: null, referenceLabel: null };
|
|
@@ -83,9 +89,13 @@ function assertReady(core, workspaceId, asset, options) {
|
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
function canonicalLink(asset, link, fallbackOrder) {
|
|
92
|
+
// Historical task links could persist an external asset as an attachment.
|
|
93
|
+
// External assets are references by contract, so repair/export must emit the
|
|
94
|
+
// canonical role rather than reproducing an un-installable legacy link.
|
|
95
|
+
const role = asset.storageProvider === 'external' ? 'reference' : link.role;
|
|
86
96
|
return {
|
|
87
97
|
assetId: asset.assetId,
|
|
88
|
-
role
|
|
98
|
+
role,
|
|
89
99
|
order: Number.isSafeInteger(link.sortOrder) && Number(link.sortOrder) >= 0
|
|
90
100
|
? Number(link.sortOrder)
|
|
91
101
|
: fallbackOrder,
|
|
@@ -107,7 +117,7 @@ export function listTaskAttachmentLinksForDurableSync(core, workspaceId, taskId)
|
|
|
107
117
|
fail(`Task attachment asset is deleted: ${link.assetId}`, 'TASK_ATTACHMENT_ASSET_INVALID', 400);
|
|
108
118
|
}
|
|
109
119
|
assertReady(core, workspaceId, asset);
|
|
110
|
-
return canonicalLink(asset, link, index);
|
|
120
|
+
return canonicalLink(asset, { ...link, role: canonicalPersistedRole(asset, link.role) }, index);
|
|
111
121
|
})
|
|
112
122
|
.sort((left, right) => left.order - right.order
|
|
113
123
|
|| (left.assetId < right.assetId ? -1 : left.assetId > right.assetId ? 1 : 0)
|
|
@@ -139,7 +149,7 @@ export function listWorkspaceTaskAttachmentLinksForDurableSync(core, workspaceId
|
|
|
139
149
|
fail(`Task attachment asset is deleted: ${link.assetId}`, 'TASK_ATTACHMENT_ASSET_INVALID', 400);
|
|
140
150
|
}
|
|
141
151
|
assertReady(core, workspaceId, asset);
|
|
142
|
-
return canonicalLink(asset, link, index);
|
|
152
|
+
return canonicalLink(asset, { ...link, role: canonicalPersistedRole(asset, link.role) }, index);
|
|
143
153
|
}).sort((left, right) => left.order - right.order
|
|
144
154
|
|| (left.assetId < right.assetId ? -1 : left.assetId > right.assetId ? 1 : 0)
|
|
145
155
|
|| (left.role < right.role ? -1 : left.role > right.role ? 1 : 0))
|
|
@@ -156,6 +166,14 @@ export function resolveTaskAttachmentLinksForDurableSync(core, workspaceId, link
|
|
|
156
166
|
if (!asset) {
|
|
157
167
|
fail(`Task attachment asset is unavailable because it has not reached this runtime: ${link.assetId}`, 'TASK_ATTACHMENT_ASSET_NOT_READY', 425, link.assetId);
|
|
158
168
|
}
|
|
169
|
+
if (asset.deletedAt
|
|
170
|
+
&& asset.storageProvider === 'external'
|
|
171
|
+
&& options?.restoreDeletedExternalAssetForAuthoritativeSync === true) {
|
|
172
|
+
// External pointers do not travel through binary content hydration.
|
|
173
|
+
// An active authoritative link is the lifecycle proof needed to
|
|
174
|
+
// revive the existing local placeholder inside the apply transaction.
|
|
175
|
+
asset = store.restoreDeleted(asset.assetId, workspaceId, asset.updatedAt) || asset;
|
|
176
|
+
}
|
|
159
177
|
assertReady(core, workspaceId, asset, options);
|
|
160
178
|
// Replicated apply may align independently hydrated local D-/I- numbers
|
|
161
179
|
// to cloud authority. Ordinary local/cloud mutation writers must never
|
|
@@ -175,8 +193,8 @@ export function resolveTaskAttachmentLinksForDurableSync(core, workspaceId, link
|
|
|
175
193
|
const displayName = String(link.displayName
|
|
176
194
|
|| (options?.ignoreAssetMetadataSnapshots === true ? '' : link.originalFilename)
|
|
177
195
|
|| (options?.ignoreAssetMetadataSnapshots === true ? '' : link.caption)
|
|
178
|
-
|| asset.originalFilename
|
|
179
|
-
|| asset.logicalName).trim() || null;
|
|
196
|
+
|| (options?.ignoreAssetMetadataSnapshots === true ? '' : asset.originalFilename)
|
|
197
|
+
|| (options?.ignoreAssetMetadataSnapshots === true ? '' : asset.logicalName)).trim() || null;
|
|
180
198
|
const projected = canonicalLink(asset, { role, displayName, sortOrder: index }, index);
|
|
181
199
|
if (options?.ignoreAssetMetadataSnapshots !== true
|
|
182
200
|
&& ((link.referenceNumber ?? null) !== (projected.referenceNumber ?? null)
|
|
@@ -187,12 +205,18 @@ export function resolveTaskAttachmentLinksForDurableSync(core, workspaceId, link
|
|
|
187
205
|
path: canonicalAssetPath(asset),
|
|
188
206
|
assetId: asset.assetId,
|
|
189
207
|
linkRole: role,
|
|
190
|
-
...(
|
|
208
|
+
...(options?.ignoreAssetMetadataSnapshots !== true
|
|
209
|
+
&& asset.kind === 'document'
|
|
210
|
+
&& asset.storageProvider !== 'external'
|
|
211
|
+
&& projected.caption !== undefined
|
|
191
212
|
? { caption: projected.caption } : {}),
|
|
192
213
|
...(projected.displayName !== undefined ? { displayName: projected.displayName } : {}),
|
|
193
|
-
...(
|
|
194
|
-
|
|
195
|
-
...(
|
|
214
|
+
...(options?.ignoreAssetMetadataSnapshots !== true && projected.originalFilename !== undefined
|
|
215
|
+
? { originalFilename: projected.originalFilename } : {}),
|
|
216
|
+
...(options?.ignoreAssetMetadataSnapshots !== true && projected.referenceNumber !== undefined
|
|
217
|
+
? { referenceNumber: projected.referenceNumber } : {}),
|
|
218
|
+
...(options?.ignoreAssetMetadataSnapshots !== true && projected.referenceLabel !== undefined
|
|
219
|
+
? { referenceLabel: projected.referenceLabel } : {}),
|
|
196
220
|
};
|
|
197
221
|
});
|
|
198
222
|
}
|
package/dist/core/Taskforce.d.ts
CHANGED
|
@@ -95,6 +95,17 @@ export interface AuditActivityItem {
|
|
|
95
95
|
event: TaskEvent;
|
|
96
96
|
}
|
|
97
97
|
export type TaskActivityItem = CommentActivityItem | AuditActivityItem;
|
|
98
|
+
export type TaskActivitySummaryItem = {
|
|
99
|
+
id: string;
|
|
100
|
+
type: 'comment';
|
|
101
|
+
timestamp: string;
|
|
102
|
+
comment: Pick<Comment, 'text' | 'author' | 'actor'>;
|
|
103
|
+
} | {
|
|
104
|
+
id: string;
|
|
105
|
+
type: 'event';
|
|
106
|
+
timestamp: string;
|
|
107
|
+
event: Pick<TaskEvent, 'action' | 'details' | 'actor' | 'actorType' | 'actorProfile'>;
|
|
108
|
+
};
|
|
98
109
|
export interface EntityAuditActivityItem {
|
|
99
110
|
id: string;
|
|
100
111
|
type: 'event';
|
|
@@ -153,6 +164,7 @@ export interface TaskItem {
|
|
|
153
164
|
taskEvents?: TaskEvent[];
|
|
154
165
|
attachments?: AttachmentItem[];
|
|
155
166
|
cardCoverAssetId?: string | null;
|
|
167
|
+
cardCoverAttachment?: Exclude<AttachmentItem, string> | null;
|
|
156
168
|
/** @deprecated Use status='done'. Safe to remove after v0.4.x migration gate. */
|
|
157
169
|
completed?: boolean;
|
|
158
170
|
/** @deprecated Use status='cancelled'. Safe to remove after v0.4.x migration gate. */
|
|
@@ -171,7 +183,27 @@ export interface TaskItem {
|
|
|
171
183
|
workstreamOrder?: number | null;
|
|
172
184
|
workstreamId?: string | null;
|
|
173
185
|
checklistItems?: ChecklistItem[];
|
|
174
|
-
|
|
186
|
+
/** True when this object came from a collection read and omits the full task graph. */
|
|
187
|
+
isSummary?: boolean;
|
|
188
|
+
commentCount?: number;
|
|
189
|
+
checklistCount?: number;
|
|
190
|
+
completedChecklistCount?: number;
|
|
191
|
+
attachmentCount?: number;
|
|
192
|
+
relationshipCount?: number;
|
|
193
|
+
/** Compact newest comment or audit event carried by lightweight collection reads. */
|
|
194
|
+
latestActivity?: TaskActivitySummaryItem | null;
|
|
195
|
+
detailRevision?: string;
|
|
196
|
+
}
|
|
197
|
+
export type TaskSummary = Omit<TaskItem, 'description' | 'comments' | 'activity' | 'taskEvents' | 'attachments' | 'checklistItems'> & {
|
|
198
|
+
isSummary: true;
|
|
199
|
+
commentCount: number;
|
|
200
|
+
checklistCount: number;
|
|
201
|
+
completedChecklistCount: number;
|
|
202
|
+
attachmentCount: number;
|
|
203
|
+
relationshipCount: number;
|
|
204
|
+
latestActivity: TaskActivitySummaryItem | null;
|
|
205
|
+
detailRevision: string;
|
|
206
|
+
};
|
|
175
207
|
export type AttachmentItem = string | {
|
|
176
208
|
path: string;
|
|
177
209
|
fsPath?: string;
|
|
@@ -351,12 +383,23 @@ export interface TaskFile {
|
|
|
351
383
|
manualComplexityEnabled?: boolean;
|
|
352
384
|
checklistDropdownEnabled?: boolean;
|
|
353
385
|
showTaskCardStatusLabel?: boolean;
|
|
386
|
+
showTaskCardDescription?: boolean;
|
|
387
|
+
showTaskCardAttachments?: boolean;
|
|
388
|
+
showTaskCardChecklistProgress?: boolean;
|
|
389
|
+
showTaskCardLatestActivity?: boolean;
|
|
390
|
+
tintTaskCardActivityBackground?: boolean;
|
|
354
391
|
taxonomies?: any[];
|
|
355
392
|
};
|
|
356
393
|
}
|
|
357
394
|
export interface ArchiveFile {
|
|
358
395
|
archived: TaskItem[];
|
|
359
396
|
}
|
|
397
|
+
export interface TaskSummaryFile {
|
|
398
|
+
tasks: TaskSummary[];
|
|
399
|
+
}
|
|
400
|
+
export interface ArchiveSummaryFile {
|
|
401
|
+
archived: TaskSummary[];
|
|
402
|
+
}
|
|
360
403
|
export interface BulkCreateTaskError {
|
|
361
404
|
index: number;
|
|
362
405
|
message: string;
|
|
@@ -997,6 +1040,7 @@ export interface TaskforceSyncCapable {
|
|
|
997
1040
|
repairLegacyBinding?: boolean;
|
|
998
1041
|
}): AiProfileRecord | null;
|
|
999
1042
|
resolveWorkspaceActor(workspaceId: string, actorRef: unknown): TaskActor | null;
|
|
1043
|
+
resolveWorkspaceActors(workspaceId: string, actorRefs: unknown[]): Map<string, TaskActor | null>;
|
|
1000
1044
|
}
|
|
1001
1045
|
export type TaskRelationshipInputType = 'depends_on' | 'blocks' | 'related_to' | 'supersedes';
|
|
1002
1046
|
export type TaskRelationshipKind = 'dependency' | 'related' | 'supersedes';
|
|
@@ -1146,6 +1190,7 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
|
|
|
1146
1190
|
requireDispatchReady?: boolean;
|
|
1147
1191
|
adoptAuthoritativeReferenceNumber?: boolean;
|
|
1148
1192
|
ignoreAssetMetadataSnapshots?: boolean;
|
|
1193
|
+
restoreDeletedExternalAssetForAuthoritativeSync?: boolean;
|
|
1149
1194
|
}): AttachmentItem[];
|
|
1150
1195
|
private normalizeEntitlementState;
|
|
1151
1196
|
private upsertCanonicalEntitlementRecord;
|
|
@@ -1445,6 +1490,16 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
|
|
|
1445
1490
|
private getSyncReconciliationServiceDeps;
|
|
1446
1491
|
private getWorkspaceLifecycleServiceDeps;
|
|
1447
1492
|
listTasks(workspaceId?: string): TaskFile;
|
|
1493
|
+
private listTaskSummaries;
|
|
1494
|
+
listActiveTaskSummaries(workspaceId?: string): TaskSummaryFile;
|
|
1495
|
+
listArchiveSummaries(workspaceId?: string): ArchiveSummaryFile;
|
|
1496
|
+
getTaskDetailRevision(id: string, workspaceId?: string): string | null;
|
|
1497
|
+
getTaskDetailSnapshot(id: string, workspaceId?: string): TaskItem | null;
|
|
1498
|
+
updateTaskIfDetailRevisionMatches(id: string, expectedDetailRevision: string, updates: Partial<TaskItem>, workspaceId?: string, options?: {
|
|
1499
|
+
actorRef?: string;
|
|
1500
|
+
saveSource?: 'manual' | 'autosave';
|
|
1501
|
+
}): TaskItem | null;
|
|
1502
|
+
assertTaskDetailRevisionForUpdate(id: string, expectedDetailRevision: string, workspaceId?: string): void;
|
|
1448
1503
|
/**
|
|
1449
1504
|
* Filter-pushdown listing used by read-only surfaces that don't need the
|
|
1450
1505
|
* full task graph (MCP list_tasks, CLI summaries). Avoids the per-task
|
|
@@ -1480,7 +1535,10 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
|
|
|
1480
1535
|
title: string;
|
|
1481
1536
|
status: TaskStatus;
|
|
1482
1537
|
}>;
|
|
1483
|
-
searchTaskSummaries(query: string, scope?: 'all' | 'active' | 'archive', workspaceId?: string
|
|
1538
|
+
searchTaskSummaries(query: string, scope?: 'all' | 'active' | 'archive', workspaceId?: string, options?: {
|
|
1539
|
+
limit?: number;
|
|
1540
|
+
offset?: number;
|
|
1541
|
+
}): Array<Record<string, unknown>>;
|
|
1484
1542
|
getDataVersion(): number;
|
|
1485
1543
|
getDatabaseProvider(): DatabaseProvider;
|
|
1486
1544
|
isDatabaseHealthy(): boolean;
|
|
@@ -1805,7 +1863,9 @@ export declare class TaskforceCore implements TaskforceSyncCapable {
|
|
|
1805
1863
|
sessionId: string;
|
|
1806
1864
|
}): boolean;
|
|
1807
1865
|
private getAiProfileServiceDeps;
|
|
1866
|
+
private buildUnresolvedWorkspaceActor;
|
|
1808
1867
|
resolveWorkspaceActor(workspaceId: string, actorRef: unknown): TaskActor | null;
|
|
1868
|
+
resolveWorkspaceActors(workspaceId: string, actorRefs: unknown[]): Map<string, TaskActor | null>;
|
|
1809
1869
|
listAssignableActors(workspaceId: string): TaskActor[];
|
|
1810
1870
|
addAdminAuditLog(input: {
|
|
1811
1871
|
action: string;
|