@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
package/dist/core/Taskforce.js
CHANGED
|
@@ -61,6 +61,7 @@ import { getImageReferenceLabel } from '../utils/imageReferences.js';
|
|
|
61
61
|
import { getCanonicalDocumentName } from '../utils/documentNames.js';
|
|
62
62
|
import { isPathInsideRoot, resolveStorageKeyPathInsideRoot } from '../utils/pathSafety.js';
|
|
63
63
|
import { DEFAULT_TASKFORCE_THEME } from '../utils/theme.js';
|
|
64
|
+
import { normalizeTaskSearchQuery } from '../utils/taskSearch.js';
|
|
64
65
|
import { ensureAssigneeTaskSchema, ensureAnnotatedAttachmentSessionsSchema, ensureAuthIdentitySchema, backfillPlanningAttachmentAssetIdentities, ensureBillingSchema, ensureDocumentReviewSessionsSchema, ensureDocumentReferenceSchema, ensureEntitlementsSchema, ensurePlanningReferenceSchema, ensureScheduleTaskSchema, ensureWorkstreamTaskOrderSchema, ensureTaskAssetsSchema, ensureTaskApproachRemoved, ensureAgentRolesSchema, ensureTaskforceAgentsSchema, ensureTaskforceAgentSkillsSchema, ensureTaskReferenceSchema, ensureTenantSchema, ensureWorkflowSchema, ensureWorkspaceAssetsSchema, ensureWorkspaceSchema, ensureLocalSyncCoordinatorSchema, ensureWorkspaceSyncDurabilitySchema } from '../migrations/taskSchemaMigrations.js';
|
|
65
66
|
import { repairTaskUpdatedAtOnlySyncDrift } from '../migrations/taskUpdatedAtSyncDriftMigration.js';
|
|
66
67
|
export class TaskforceRuleError extends Error {
|
|
@@ -3158,7 +3159,17 @@ export class TaskforceCore {
|
|
|
3158
3159
|
return ['manualComplexityEnabled'];
|
|
3159
3160
|
}
|
|
3160
3161
|
getUserBackedProjectConfigKeys() {
|
|
3161
|
-
return [
|
|
3162
|
+
return [
|
|
3163
|
+
'theme',
|
|
3164
|
+
'jsonBackupEnabled',
|
|
3165
|
+
'checklistDropdownEnabled',
|
|
3166
|
+
'showTaskCardStatusLabel',
|
|
3167
|
+
'showTaskCardDescription',
|
|
3168
|
+
'showTaskCardAttachments',
|
|
3169
|
+
'showTaskCardChecklistProgress',
|
|
3170
|
+
'showTaskCardLatestActivity',
|
|
3171
|
+
'tintTaskCardActivityBackground',
|
|
3172
|
+
];
|
|
3162
3173
|
}
|
|
3163
3174
|
isWorkspaceBackedProjectConfigKey(key) {
|
|
3164
3175
|
return this.getWorkspaceBackedProjectConfigKeys().includes(key);
|
|
@@ -3201,6 +3212,21 @@ export class TaskforceCore {
|
|
|
3201
3212
|
if (typeof source.showTaskCardStatusLabel === 'boolean') {
|
|
3202
3213
|
next.showTaskCardStatusLabel = source.showTaskCardStatusLabel;
|
|
3203
3214
|
}
|
|
3215
|
+
if (typeof source.showTaskCardDescription === 'boolean') {
|
|
3216
|
+
next.showTaskCardDescription = source.showTaskCardDescription;
|
|
3217
|
+
}
|
|
3218
|
+
if (typeof source.showTaskCardAttachments === 'boolean') {
|
|
3219
|
+
next.showTaskCardAttachments = source.showTaskCardAttachments;
|
|
3220
|
+
}
|
|
3221
|
+
if (typeof source.showTaskCardChecklistProgress === 'boolean') {
|
|
3222
|
+
next.showTaskCardChecklistProgress = source.showTaskCardChecklistProgress;
|
|
3223
|
+
}
|
|
3224
|
+
if (typeof source.showTaskCardLatestActivity === 'boolean') {
|
|
3225
|
+
next.showTaskCardLatestActivity = source.showTaskCardLatestActivity;
|
|
3226
|
+
}
|
|
3227
|
+
if (typeof source.tintTaskCardActivityBackground === 'boolean') {
|
|
3228
|
+
next.tintTaskCardActivityBackground = source.tintTaskCardActivityBackground;
|
|
3229
|
+
}
|
|
3204
3230
|
return next;
|
|
3205
3231
|
}
|
|
3206
3232
|
stripPersistedProjectConfigKeys(config) {
|
|
@@ -4019,7 +4045,7 @@ export class TaskforceCore {
|
|
|
4019
4045
|
AND identity.entity_type = 'initiative'
|
|
4020
4046
|
AND identity.entity_id = i.id
|
|
4021
4047
|
WHERE i.tenant_id = ? AND i.workspace_id = ?
|
|
4022
|
-
ORDER BY COALESCE(i.sort_order, 2147483647), i.created_at ASC
|
|
4048
|
+
ORDER BY COALESCE(i.sort_order, 2147483647), i.created_at ASC, i.id ASC
|
|
4023
4049
|
`).all(this.tenantId, normalizedWorkspaceId);
|
|
4024
4050
|
return rows.map((row) => this.rowToInitiative(row));
|
|
4025
4051
|
}
|
|
@@ -4326,7 +4352,7 @@ export class TaskforceCore {
|
|
|
4326
4352
|
AND identity.entity_type = 'workstream'
|
|
4327
4353
|
AND identity.entity_id = w.id
|
|
4328
4354
|
WHERE w.tenant_id = ? AND w.workspace_id = ?
|
|
4329
|
-
ORDER BY COALESCE(w.sort_order, 2147483647), w.created_at ASC
|
|
4355
|
+
ORDER BY COALESCE(w.sort_order, 2147483647), w.created_at ASC, w.id ASC
|
|
4330
4356
|
`).all(this.tenantId, normalizedWorkspaceId);
|
|
4331
4357
|
return rows.map((row) => this.rowToWorkstream(row));
|
|
4332
4358
|
}
|
|
@@ -5493,6 +5519,7 @@ export class TaskforceCore {
|
|
|
5493
5519
|
generateEntityId: (prefix) => this.generateEntityId(prefix),
|
|
5494
5520
|
normalizeWorkspaceId: (workspaceId) => this.normalizeWorkspaceId(workspaceId),
|
|
5495
5521
|
resolveWorkspaceActor: (workspaceId, actorRef) => this.resolveWorkspaceActor(workspaceId, actorRef),
|
|
5522
|
+
resolveWorkspaceActors: (workspaceId, actorRefs) => this.resolveWorkspaceActors(workspaceId, actorRefs),
|
|
5496
5523
|
sanitizeOrderInDay: (value) => this.sanitizeOrderInDay(value),
|
|
5497
5524
|
sanitizeDateOnly: (value) => this.sanitizeDateOnly(value),
|
|
5498
5525
|
normalizeTaskEventActor: (actorRef, workspaceId) => this.normalizeTaskEventActor(actorRef, workspaceId),
|
|
@@ -5572,6 +5599,489 @@ export class TaskforceCore {
|
|
|
5572
5599
|
config: this.readConfig()
|
|
5573
5600
|
};
|
|
5574
5601
|
}
|
|
5602
|
+
listTaskSummaries(workspaceId, archived) {
|
|
5603
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
5604
|
+
const rows = this.db.prepare(`
|
|
5605
|
+
WITH ranked_events AS (
|
|
5606
|
+
SELECT e.entity_id,
|
|
5607
|
+
e.id,
|
|
5608
|
+
e.action,
|
|
5609
|
+
e.actor,
|
|
5610
|
+
e.actor_type,
|
|
5611
|
+
e.details_json,
|
|
5612
|
+
e.created_at,
|
|
5613
|
+
ROW_NUMBER() OVER (
|
|
5614
|
+
PARTITION BY e.entity_id
|
|
5615
|
+
ORDER BY e.created_at DESC, e.id DESC
|
|
5616
|
+
) AS event_rank
|
|
5617
|
+
FROM entity_events e
|
|
5618
|
+
JOIN tasks scoped_task
|
|
5619
|
+
ON scoped_task.tenant_id = e.tenant_id
|
|
5620
|
+
AND scoped_task.workspace_id = e.workspace_id
|
|
5621
|
+
AND scoped_task.id = e.entity_id
|
|
5622
|
+
WHERE e.tenant_id = ? AND e.workspace_id = ?
|
|
5623
|
+
AND e.entity_type = 'task'
|
|
5624
|
+
AND scoped_task.is_archived = ?
|
|
5625
|
+
-- Comment activity is represented by the comment itself; the
|
|
5626
|
+
-- paired audit event is intentionally hidden by taskActivity.
|
|
5627
|
+
AND e.action <> 'task-comment-added'
|
|
5628
|
+
-- Durable text-only autosaves retain an empty task-updated event
|
|
5629
|
+
-- for stable replay identity, but full task activity hides it.
|
|
5630
|
+
-- Match that visibility rule before ranking so an internal event
|
|
5631
|
+
-- cannot displace the latest meaningful card activity. REPLACE is
|
|
5632
|
+
-- intentionally used instead of dialect-specific JSON operators.
|
|
5633
|
+
AND NOT (
|
|
5634
|
+
e.action = 'task-updated'
|
|
5635
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"saveSource":"autosave"%'
|
|
5636
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"changes":{}%'
|
|
5637
|
+
)
|
|
5638
|
+
)
|
|
5639
|
+
SELECT t.id, t.reference_number, t.local_reference_number, t.title, t.status, t.priority,
|
|
5640
|
+
t.complexity, t.type, t.category, t.created_at, t.updated_at, t.completed_at,
|
|
5641
|
+
t.canceled_reason, t.is_archived, t.scheduled_date, t.due_date,
|
|
5642
|
+
t.scheduled_week_key, t.order_in_day, t.workstream_order, t.workstream_id,
|
|
5643
|
+
t.card_cover_asset_id, t.taxonomies, t.created_by, t.assignee,
|
|
5644
|
+
event.id AS latest_event_id,
|
|
5645
|
+
event.action AS latest_event_action,
|
|
5646
|
+
event.actor AS latest_event_actor,
|
|
5647
|
+
event.actor_type AS latest_event_actor_type,
|
|
5648
|
+
event.details_json AS latest_event_details_json,
|
|
5649
|
+
event.created_at AS latest_event_at
|
|
5650
|
+
FROM tasks t
|
|
5651
|
+
LEFT JOIN ranked_events event
|
|
5652
|
+
ON event.entity_id = t.id AND event.event_rank = 1
|
|
5653
|
+
WHERE t.tenant_id = ? AND t.workspace_id = ? AND t.is_archived = ?
|
|
5654
|
+
ORDER BY ${archived ? 't.completed_at DESC, t.created_at DESC' : 't.created_at DESC'}
|
|
5655
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0, this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5656
|
+
const comments = this.db.prepare(`
|
|
5657
|
+
WITH ranked_comments AS (
|
|
5658
|
+
SELECT c.task_id,
|
|
5659
|
+
c.id,
|
|
5660
|
+
c.text,
|
|
5661
|
+
c.author,
|
|
5662
|
+
c.timestamp,
|
|
5663
|
+
COUNT(*) OVER (PARTITION BY c.task_id) AS count,
|
|
5664
|
+
MAX(c.timestamp) OVER (PARTITION BY c.task_id) AS revision,
|
|
5665
|
+
ROW_NUMBER() OVER (
|
|
5666
|
+
PARTITION BY c.task_id
|
|
5667
|
+
ORDER BY c.timestamp DESC, c.id DESC
|
|
5668
|
+
) AS comment_rank
|
|
5669
|
+
FROM comments c
|
|
5670
|
+
JOIN tasks t
|
|
5671
|
+
ON t.tenant_id = c.tenant_id
|
|
5672
|
+
AND t.workspace_id = c.workspace_id
|
|
5673
|
+
AND t.id = c.task_id
|
|
5674
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ? AND t.is_archived = ?
|
|
5675
|
+
)
|
|
5676
|
+
SELECT task_id, id, text, author, timestamp, count, revision
|
|
5677
|
+
FROM ranked_comments
|
|
5678
|
+
WHERE comment_rank = 1
|
|
5679
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5680
|
+
const checklist = this.db.prepare(`
|
|
5681
|
+
SELECT c.task_id, COUNT(*) AS count,
|
|
5682
|
+
SUM(CASE WHEN c.is_completed = 1 THEN 1 ELSE 0 END) AS completed_count,
|
|
5683
|
+
MAX(COALESCE(c.updated_at, c.created_at)) AS revision
|
|
5684
|
+
FROM checklist_items c
|
|
5685
|
+
JOIN tasks t ON t.tenant_id = c.tenant_id AND t.workspace_id = c.workspace_id AND t.id = c.task_id
|
|
5686
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ? AND t.is_archived = ?
|
|
5687
|
+
GROUP BY c.task_id
|
|
5688
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5689
|
+
const attachments = this.db.prepare(`
|
|
5690
|
+
SELECT l.task_id, COUNT(a.asset_id) AS count,
|
|
5691
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN l.asset_id ELSE NULL END) AS cover_asset_id,
|
|
5692
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN a.storage_provider ELSE NULL END) AS cover_storage_provider,
|
|
5693
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN a.storage_key ELSE NULL END) AS cover_storage_key,
|
|
5694
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN a.original_filename ELSE NULL END) AS cover_original_filename,
|
|
5695
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN COALESCE(l.display_name, a.logical_name) ELSE NULL END) AS cover_display_name,
|
|
5696
|
+
MAX(CASE
|
|
5697
|
+
WHEN COALESCE(a.updated_at, '') > COALESCE(l.updated_at, '') THEN a.updated_at
|
|
5698
|
+
ELSE l.updated_at
|
|
5699
|
+
END) AS revision
|
|
5700
|
+
FROM workspace_asset_links l
|
|
5701
|
+
JOIN tasks t ON t.tenant_id = l.tenant_id AND t.workspace_id = l.workspace_id AND t.id = l.task_id
|
|
5702
|
+
JOIN workspace_assets a ON a.tenant_id = l.tenant_id AND a.workspace_id = l.workspace_id AND a.asset_id = l.asset_id AND a.deleted_at IS NULL
|
|
5703
|
+
WHERE l.tenant_id = ? AND l.workspace_id = ? AND t.is_archived = ? AND l.target_type = 'task' AND l.deleted_at IS NULL
|
|
5704
|
+
GROUP BY l.task_id
|
|
5705
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5706
|
+
const relationships = this.db.prepare(`
|
|
5707
|
+
SELECT task_id, COUNT(*) AS count, MAX(updated_at) AS revision
|
|
5708
|
+
FROM (
|
|
5709
|
+
SELECT r.source_task_id AS task_id, r.updated_at
|
|
5710
|
+
FROM task_relationships r
|
|
5711
|
+
JOIN tasks t ON t.tenant_id = r.tenant_id AND t.workspace_id = r.workspace_id AND t.id = r.source_task_id
|
|
5712
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL AND t.is_archived = ?
|
|
5713
|
+
UNION ALL
|
|
5714
|
+
SELECT r.target_task_id AS task_id, r.updated_at
|
|
5715
|
+
FROM task_relationships r
|
|
5716
|
+
JOIN tasks t ON t.tenant_id = r.tenant_id AND t.workspace_id = r.workspace_id AND t.id = r.target_task_id
|
|
5717
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL
|
|
5718
|
+
AND r.target_task_id <> r.source_task_id AND t.is_archived = ?
|
|
5719
|
+
) scoped_relationships
|
|
5720
|
+
GROUP BY task_id
|
|
5721
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0, this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5722
|
+
const countByTask = (entries) => new Map(entries.map((entry) => [String(entry.task_id || ''), Number(entry.count || 0)]));
|
|
5723
|
+
const commentCountByTask = countByTask(comments);
|
|
5724
|
+
const attachmentCountByTask = countByTask(attachments);
|
|
5725
|
+
const commentRevisionByTask = new Map(comments.map((entry) => [String(entry.task_id || ''), String(entry.revision || '')]));
|
|
5726
|
+
const attachmentRevisionByTask = new Map(attachments.map((entry) => [String(entry.task_id || ''), String(entry.revision || '')]));
|
|
5727
|
+
const coverAttachmentByTask = new Map(attachments.flatMap((entry) => {
|
|
5728
|
+
const taskId = String(entry.task_id || '');
|
|
5729
|
+
const assetId = String(entry.cover_asset_id || '').trim();
|
|
5730
|
+
const storageKey = String(entry.cover_storage_key || '').trim();
|
|
5731
|
+
const storageProvider = String(entry.cover_storage_provider || 'local').trim();
|
|
5732
|
+
if (!taskId || !assetId || !storageKey)
|
|
5733
|
+
return [];
|
|
5734
|
+
const isExternalHttpReference = storageProvider === 'external' && /^https?:\/\//i.test(storageKey);
|
|
5735
|
+
return [[taskId, {
|
|
5736
|
+
assetId,
|
|
5737
|
+
path: isExternalHttpReference
|
|
5738
|
+
? storageKey
|
|
5739
|
+
: storageProvider === 'external'
|
|
5740
|
+
? `/api/taskforce/context-link?path=${encodeURIComponent(storageKey)}`
|
|
5741
|
+
: `/api/taskforce/context/${encodeURIComponent(storageKey)}`,
|
|
5742
|
+
...(isExternalHttpReference ? {} : { fsPath: storageKey }),
|
|
5743
|
+
displayName: entry.cover_display_name ? String(entry.cover_display_name) : undefined,
|
|
5744
|
+
originalFilename: entry.cover_original_filename ? String(entry.cover_original_filename) : undefined,
|
|
5745
|
+
linkRole: 'image',
|
|
5746
|
+
taskId,
|
|
5747
|
+
}]];
|
|
5748
|
+
}));
|
|
5749
|
+
const checklistByTask = new Map(checklist.map((entry) => [String(entry.task_id || ''), {
|
|
5750
|
+
total: Number(entry.count || 0),
|
|
5751
|
+
completed: Number(entry.completed_count || 0),
|
|
5752
|
+
revision: String(entry.revision || ''),
|
|
5753
|
+
}]));
|
|
5754
|
+
const relationshipCountByTask = countByTask(relationships);
|
|
5755
|
+
const relationshipRevisionByTask = new Map(relationships.map((entry) => [
|
|
5756
|
+
String(entry.task_id || ''),
|
|
5757
|
+
String(entry.revision || ''),
|
|
5758
|
+
]));
|
|
5759
|
+
const latestActivityByTask = new Map();
|
|
5760
|
+
const latestActivityRevisionByTask = new Map();
|
|
5761
|
+
const considerLatestActivity = (taskId, item) => {
|
|
5762
|
+
const existing = latestActivityByTask.get(taskId);
|
|
5763
|
+
const nextTimestamp = Date.parse(String(item.timestamp || ''));
|
|
5764
|
+
const existingTimestamp = existing ? Date.parse(String(existing.timestamp || '')) : Number.NaN;
|
|
5765
|
+
const isNewer = !existing
|
|
5766
|
+
|| (Number.isFinite(nextTimestamp) && !Number.isFinite(existingTimestamp))
|
|
5767
|
+
|| (Number.isFinite(nextTimestamp) && Number.isFinite(existingTimestamp) && nextTimestamp > existingTimestamp)
|
|
5768
|
+
|| (nextTimestamp === existingTimestamp && item.id.localeCompare(existing.id) > 0);
|
|
5769
|
+
if (!isNewer)
|
|
5770
|
+
return;
|
|
5771
|
+
latestActivityByTask.set(taskId, item);
|
|
5772
|
+
latestActivityRevisionByTask.set(taskId, `${item.timestamp}:${item.id}`);
|
|
5773
|
+
};
|
|
5774
|
+
for (const entry of comments) {
|
|
5775
|
+
const taskId = String(entry.task_id || '').trim();
|
|
5776
|
+
const commentId = String(entry.id || '').trim();
|
|
5777
|
+
const timestamp = String(entry.timestamp || '');
|
|
5778
|
+
if (!taskId || !commentId)
|
|
5779
|
+
continue;
|
|
5780
|
+
considerLatestActivity(taskId, {
|
|
5781
|
+
id: `comment:${commentId}`,
|
|
5782
|
+
type: 'comment',
|
|
5783
|
+
timestamp,
|
|
5784
|
+
comment: {
|
|
5785
|
+
text: String(entry.text || ''),
|
|
5786
|
+
author: String(entry.author || 'user').trim() || 'user',
|
|
5787
|
+
},
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
for (const row of rows) {
|
|
5791
|
+
const taskId = String(row.id || '').trim();
|
|
5792
|
+
const eventId = String(row.latest_event_id || '').trim();
|
|
5793
|
+
const timestamp = String(row.latest_event_at || '');
|
|
5794
|
+
if (!taskId || !eventId)
|
|
5795
|
+
continue;
|
|
5796
|
+
let details = {};
|
|
5797
|
+
try {
|
|
5798
|
+
const parsed = row.latest_event_details_json ? JSON.parse(String(row.latest_event_details_json)) : {};
|
|
5799
|
+
details = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
5800
|
+
}
|
|
5801
|
+
catch {
|
|
5802
|
+
details = {};
|
|
5803
|
+
}
|
|
5804
|
+
considerLatestActivity(taskId, {
|
|
5805
|
+
id: `event:${eventId}`,
|
|
5806
|
+
type: 'event',
|
|
5807
|
+
timestamp,
|
|
5808
|
+
event: {
|
|
5809
|
+
action: String(row.latest_event_action || ''),
|
|
5810
|
+
actor: String(row.latest_event_actor || 'system').trim() || 'system',
|
|
5811
|
+
actorType: row.latest_event_actor_type === 'human' || row.latest_event_actor_type === 'ai'
|
|
5812
|
+
? row.latest_event_actor_type
|
|
5813
|
+
: 'system',
|
|
5814
|
+
details,
|
|
5815
|
+
},
|
|
5816
|
+
});
|
|
5817
|
+
}
|
|
5818
|
+
return rows.map((row) => {
|
|
5819
|
+
const id = String(row.id || '').trim();
|
|
5820
|
+
const scheduledDate = this.sanitizeDateOnly(row.scheduled_date);
|
|
5821
|
+
const dueDate = this.sanitizeDateOnly(row.due_date);
|
|
5822
|
+
const referenceNumber = this.sanitizeTaskReferenceNumber(row.reference_number);
|
|
5823
|
+
const localReferenceNumber = this.sanitizeTaskReferenceNumber(row.local_reference_number);
|
|
5824
|
+
let taxonomies = {};
|
|
5825
|
+
try {
|
|
5826
|
+
taxonomies = JSON.parse(row.taxonomies || '{}');
|
|
5827
|
+
}
|
|
5828
|
+
catch {
|
|
5829
|
+
taxonomies = {};
|
|
5830
|
+
}
|
|
5831
|
+
const checklistCounts = checklistByTask.get(id) || { total: 0, completed: 0, revision: '' };
|
|
5832
|
+
const commentCount = commentCountByTask.get(id) || 0;
|
|
5833
|
+
const attachmentCount = attachmentCountByTask.get(id) || 0;
|
|
5834
|
+
const relationshipCount = relationshipCountByTask.get(id) || 0;
|
|
5835
|
+
const detailRevision = JSON.stringify([
|
|
5836
|
+
String(row.updated_at || row.created_at || ''),
|
|
5837
|
+
commentCount,
|
|
5838
|
+
commentRevisionByTask.get(id) || '',
|
|
5839
|
+
checklistCounts.total,
|
|
5840
|
+
checklistCounts.completed,
|
|
5841
|
+
checklistCounts.revision,
|
|
5842
|
+
attachmentCount,
|
|
5843
|
+
attachmentRevisionByTask.get(id) || '',
|
|
5844
|
+
relationshipCount,
|
|
5845
|
+
relationshipRevisionByTask.get(id) || '',
|
|
5846
|
+
latestActivityRevisionByTask.get(id) || '',
|
|
5847
|
+
]);
|
|
5848
|
+
return {
|
|
5849
|
+
id,
|
|
5850
|
+
referenceNumber,
|
|
5851
|
+
localReferenceNumber,
|
|
5852
|
+
referenceLabel: deriveTaskReferenceLabel({ referenceNumber, localReferenceNumber }),
|
|
5853
|
+
title: String(row.title || ''),
|
|
5854
|
+
status: this.normalizeStatusValue(row.status),
|
|
5855
|
+
priority: this.normalizePriorityValue(row.priority),
|
|
5856
|
+
complexity: Number(row.complexity || 3),
|
|
5857
|
+
type: String(row.type || 'task'),
|
|
5858
|
+
category: String(row.category || 'default'),
|
|
5859
|
+
createdAt: String(row.created_at || ''),
|
|
5860
|
+
updatedAt: row.updated_at ? String(row.updated_at) : undefined,
|
|
5861
|
+
completedAt: row.completed_at ? String(row.completed_at) : null,
|
|
5862
|
+
canceledReason: row.canceled_reason ? String(row.canceled_reason) : null,
|
|
5863
|
+
isArchived: row.is_archived === 1 || row.is_archived === true,
|
|
5864
|
+
scheduledDate,
|
|
5865
|
+
dueDate,
|
|
5866
|
+
scheduledWeekKey: row.scheduled_week_key || (scheduledDate ? this.dateOnlyToIsoWeekKey(scheduledDate) : null),
|
|
5867
|
+
orderInDay: this.sanitizeOrderInDay(row.order_in_day),
|
|
5868
|
+
workstreamOrder: this.sanitizeOrderInDay(row.workstream_order),
|
|
5869
|
+
workstreamId: this.sanitizeWorkstreamId(row.workstream_id),
|
|
5870
|
+
cardCoverAssetId: typeof row.card_cover_asset_id === 'string' && row.card_cover_asset_id.trim()
|
|
5871
|
+
? row.card_cover_asset_id.trim()
|
|
5872
|
+
: null,
|
|
5873
|
+
cardCoverAttachment: coverAttachmentByTask.get(id) || null,
|
|
5874
|
+
taxonomies,
|
|
5875
|
+
createdBy: this.normalizeCreatedBy(row.created_by, normalizedWorkspaceId, { preserveGenericAi: true }),
|
|
5876
|
+
assignee: this.sanitizeAssignee(row.assignee, normalizedWorkspaceId),
|
|
5877
|
+
isSummary: true,
|
|
5878
|
+
commentCount,
|
|
5879
|
+
checklistCount: checklistCounts.total,
|
|
5880
|
+
completedChecklistCount: checklistCounts.completed,
|
|
5881
|
+
attachmentCount,
|
|
5882
|
+
relationshipCount,
|
|
5883
|
+
latestActivity: latestActivityByTask.get(id) || null,
|
|
5884
|
+
detailRevision,
|
|
5885
|
+
};
|
|
5886
|
+
});
|
|
5887
|
+
}
|
|
5888
|
+
listActiveTaskSummaries(workspaceId) {
|
|
5889
|
+
return { tasks: this.listTaskSummaries(workspaceId, false) };
|
|
5890
|
+
}
|
|
5891
|
+
listArchiveSummaries(workspaceId) {
|
|
5892
|
+
return { archived: this.listTaskSummaries(workspaceId, true) };
|
|
5893
|
+
}
|
|
5894
|
+
getTaskDetailRevision(id, workspaceId) {
|
|
5895
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
5896
|
+
const taskId = String(id || '').trim();
|
|
5897
|
+
if (!taskId)
|
|
5898
|
+
return null;
|
|
5899
|
+
const row = this.db.prepare(`
|
|
5900
|
+
WITH target_task AS (
|
|
5901
|
+
SELECT id, created_at, updated_at
|
|
5902
|
+
FROM tasks
|
|
5903
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
5904
|
+
),
|
|
5905
|
+
comment_stats AS (
|
|
5906
|
+
SELECT COUNT(*) AS count, MAX(c.timestamp) AS revision
|
|
5907
|
+
FROM comments c
|
|
5908
|
+
JOIN target_task t ON t.id = c.task_id
|
|
5909
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ?
|
|
5910
|
+
),
|
|
5911
|
+
latest_comment AS (
|
|
5912
|
+
SELECT c.id, c.timestamp
|
|
5913
|
+
FROM comments c
|
|
5914
|
+
JOIN target_task t ON t.id = c.task_id
|
|
5915
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ?
|
|
5916
|
+
ORDER BY c.timestamp DESC, c.id DESC
|
|
5917
|
+
LIMIT 1
|
|
5918
|
+
),
|
|
5919
|
+
checklist_stats AS (
|
|
5920
|
+
SELECT COUNT(*) AS count,
|
|
5921
|
+
SUM(CASE WHEN c.is_completed = 1 THEN 1 ELSE 0 END) AS completed_count,
|
|
5922
|
+
MAX(COALESCE(c.updated_at, c.created_at)) AS revision
|
|
5923
|
+
FROM checklist_items c
|
|
5924
|
+
JOIN target_task t ON t.id = c.task_id
|
|
5925
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ?
|
|
5926
|
+
),
|
|
5927
|
+
attachment_stats AS (
|
|
5928
|
+
SELECT COUNT(a.asset_id) AS count,
|
|
5929
|
+
MAX(CASE
|
|
5930
|
+
WHEN COALESCE(a.updated_at, '') > COALESCE(l.updated_at, '') THEN a.updated_at
|
|
5931
|
+
ELSE l.updated_at
|
|
5932
|
+
END) AS revision
|
|
5933
|
+
FROM workspace_asset_links l
|
|
5934
|
+
JOIN target_task t ON t.id = l.task_id
|
|
5935
|
+
JOIN workspace_assets a
|
|
5936
|
+
ON a.tenant_id = l.tenant_id
|
|
5937
|
+
AND a.workspace_id = l.workspace_id
|
|
5938
|
+
AND a.asset_id = l.asset_id
|
|
5939
|
+
AND a.deleted_at IS NULL
|
|
5940
|
+
WHERE l.tenant_id = ? AND l.workspace_id = ?
|
|
5941
|
+
AND l.target_type = 'task' AND l.deleted_at IS NULL
|
|
5942
|
+
),
|
|
5943
|
+
relationship_rows AS (
|
|
5944
|
+
SELECT r.updated_at
|
|
5945
|
+
FROM task_relationships r
|
|
5946
|
+
JOIN target_task t ON t.id = r.source_task_id
|
|
5947
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL
|
|
5948
|
+
UNION ALL
|
|
5949
|
+
SELECT r.updated_at
|
|
5950
|
+
FROM task_relationships r
|
|
5951
|
+
JOIN target_task t ON t.id = r.target_task_id
|
|
5952
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL
|
|
5953
|
+
AND r.target_task_id <> r.source_task_id
|
|
5954
|
+
),
|
|
5955
|
+
relationship_stats AS (
|
|
5956
|
+
SELECT COUNT(*) AS count, MAX(updated_at) AS revision
|
|
5957
|
+
FROM relationship_rows
|
|
5958
|
+
),
|
|
5959
|
+
latest_event AS (
|
|
5960
|
+
SELECT e.id, e.created_at
|
|
5961
|
+
FROM entity_events e
|
|
5962
|
+
JOIN target_task t ON t.id = e.entity_id
|
|
5963
|
+
WHERE e.tenant_id = ? AND e.workspace_id = ?
|
|
5964
|
+
AND e.entity_type = 'task'
|
|
5965
|
+
AND e.action <> 'task-comment-added'
|
|
5966
|
+
AND NOT (
|
|
5967
|
+
e.action = 'task-updated'
|
|
5968
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"saveSource":"autosave"%'
|
|
5969
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"changes":{}%'
|
|
5970
|
+
)
|
|
5971
|
+
ORDER BY e.created_at DESC, e.id DESC
|
|
5972
|
+
LIMIT 1
|
|
5973
|
+
)
|
|
5974
|
+
SELECT t.created_at, t.updated_at,
|
|
5975
|
+
comments.count AS comment_count, comments.revision AS comment_revision,
|
|
5976
|
+
latest_comment.id AS latest_comment_id,
|
|
5977
|
+
latest_comment.timestamp AS latest_comment_at,
|
|
5978
|
+
checklist.count AS checklist_count,
|
|
5979
|
+
checklist.completed_count AS completed_checklist_count,
|
|
5980
|
+
checklist.revision AS checklist_revision,
|
|
5981
|
+
attachments.count AS attachment_count,
|
|
5982
|
+
attachments.revision AS attachment_revision,
|
|
5983
|
+
relationships.count AS relationship_count,
|
|
5984
|
+
relationships.revision AS relationship_revision,
|
|
5985
|
+
latest_event.id AS latest_event_id,
|
|
5986
|
+
latest_event.created_at AS latest_event_at
|
|
5987
|
+
FROM target_task t
|
|
5988
|
+
CROSS JOIN comment_stats comments
|
|
5989
|
+
CROSS JOIN checklist_stats checklist
|
|
5990
|
+
CROSS JOIN attachment_stats attachments
|
|
5991
|
+
CROSS JOIN relationship_stats relationships
|
|
5992
|
+
LEFT JOIN latest_comment ON 1 = 1
|
|
5993
|
+
LEFT JOIN latest_event ON 1 = 1
|
|
5994
|
+
`).get(this.tenantId, normalizedWorkspaceId, taskId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId);
|
|
5995
|
+
if (!row)
|
|
5996
|
+
return null;
|
|
5997
|
+
const latestCommentId = String(row.latest_comment_id || '').trim();
|
|
5998
|
+
const latestCommentAt = String(row.latest_comment_at || '');
|
|
5999
|
+
const latestEventId = String(row.latest_event_id || '').trim();
|
|
6000
|
+
const latestEventAt = String(row.latest_event_at || '');
|
|
6001
|
+
const commentTimestamp = Date.parse(latestCommentAt);
|
|
6002
|
+
const eventTimestamp = Date.parse(latestEventAt);
|
|
6003
|
+
const commentActivityId = latestCommentId ? `comment:${latestCommentId}` : '';
|
|
6004
|
+
const eventActivityId = latestEventId ? `event:${latestEventId}` : '';
|
|
6005
|
+
const latestActivityRevision = !latestEventId
|
|
6006
|
+
|| (latestCommentId && ((Number.isFinite(commentTimestamp) && !Number.isFinite(eventTimestamp))
|
|
6007
|
+
|| commentTimestamp > eventTimestamp
|
|
6008
|
+
|| (commentTimestamp === eventTimestamp && commentActivityId.localeCompare(eventActivityId) > 0)))
|
|
6009
|
+
? (latestCommentId ? `${latestCommentAt}:${commentActivityId}` : '')
|
|
6010
|
+
: `${latestEventAt}:${eventActivityId}`;
|
|
6011
|
+
return JSON.stringify([
|
|
6012
|
+
String(row.updated_at || row.created_at || ''),
|
|
6013
|
+
Number(row.comment_count || 0),
|
|
6014
|
+
String(row.comment_revision || ''),
|
|
6015
|
+
Number(row.checklist_count || 0),
|
|
6016
|
+
Number(row.completed_checklist_count || 0),
|
|
6017
|
+
String(row.checklist_revision || ''),
|
|
6018
|
+
Number(row.attachment_count || 0),
|
|
6019
|
+
String(row.attachment_revision || ''),
|
|
6020
|
+
Number(row.relationship_count || 0),
|
|
6021
|
+
String(row.relationship_revision || ''),
|
|
6022
|
+
latestActivityRevision,
|
|
6023
|
+
]);
|
|
6024
|
+
}
|
|
6025
|
+
getTaskDetailSnapshot(id, workspaceId) {
|
|
6026
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
6027
|
+
return this.db.transaction(() => {
|
|
6028
|
+
if (this.db.provider === 'postgres') {
|
|
6029
|
+
this.db.exec('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
|
|
6030
|
+
}
|
|
6031
|
+
const task = this.getTask(id, normalizedWorkspaceId);
|
|
6032
|
+
if (!task)
|
|
6033
|
+
return null;
|
|
6034
|
+
const detailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6035
|
+
return detailRevision ? { ...task, detailRevision } : null;
|
|
6036
|
+
})();
|
|
6037
|
+
}
|
|
6038
|
+
updateTaskIfDetailRevisionMatches(id, expectedDetailRevision, updates, workspaceId, options) {
|
|
6039
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
6040
|
+
const updated = this.db.transaction(() => {
|
|
6041
|
+
if (this.db.provider === 'postgres') {
|
|
6042
|
+
this.db.exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
|
|
6043
|
+
}
|
|
6044
|
+
const currentDetailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6045
|
+
if (currentDetailRevision === null)
|
|
6046
|
+
return null;
|
|
6047
|
+
if (currentDetailRevision !== expectedDetailRevision) {
|
|
6048
|
+
throw new TaskforceRuleError('Task changed after this editor was loaded. Reload the latest task before saving.', 409, 'TASK_DETAIL_REVISION_CONFLICT', { currentDetailRevision });
|
|
6049
|
+
}
|
|
6050
|
+
const task = this.updateTask(id, updates, normalizedWorkspaceId, {
|
|
6051
|
+
actorRef: options?.actorRef,
|
|
6052
|
+
saveSource: options?.saveSource,
|
|
6053
|
+
persistBackup: false,
|
|
6054
|
+
emitRealtime: false,
|
|
6055
|
+
});
|
|
6056
|
+
if (!task)
|
|
6057
|
+
return null;
|
|
6058
|
+
const detailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6059
|
+
return detailRevision ? { ...task, detailRevision } : null;
|
|
6060
|
+
})();
|
|
6061
|
+
if (updated) {
|
|
6062
|
+
this.persistBackup();
|
|
6063
|
+
this.emitRealtimeMutation({
|
|
6064
|
+
workspaceId: normalizedWorkspaceId,
|
|
6065
|
+
entityType: 'task',
|
|
6066
|
+
changeType: 'upsert',
|
|
6067
|
+
entityIds: [updated.id],
|
|
6068
|
+
});
|
|
6069
|
+
}
|
|
6070
|
+
return updated;
|
|
6071
|
+
}
|
|
6072
|
+
assertTaskDetailRevisionForUpdate(id, expectedDetailRevision, workspaceId) {
|
|
6073
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
6074
|
+
const lockSql = this.db.provider === 'postgres'
|
|
6075
|
+
? `SELECT id FROM tasks WHERE tenant_id = ? AND workspace_id = ? AND id = ? FOR UPDATE`
|
|
6076
|
+
: `SELECT id FROM tasks WHERE tenant_id = ? AND workspace_id = ? AND id = ?`;
|
|
6077
|
+
const task = this.db.prepare(lockSql).get(this.tenantId, normalizedWorkspaceId, id);
|
|
6078
|
+
if (!task)
|
|
6079
|
+
throw new TaskforceRuleError('Task not found.', 404, 'TASK_NOT_FOUND');
|
|
6080
|
+
const currentDetailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6081
|
+
if (currentDetailRevision !== expectedDetailRevision) {
|
|
6082
|
+
throw new TaskforceRuleError('Task changed after this editor was loaded. Reload the latest task before saving.', 409, 'TASK_DETAIL_REVISION_CONFLICT', { currentDetailRevision });
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
5575
6085
|
/**
|
|
5576
6086
|
* Filter-pushdown listing used by read-only surfaces that don't need the
|
|
5577
6087
|
* full task graph (MCP list_tasks, CLI summaries). Avoids the per-task
|
|
@@ -5750,41 +6260,53 @@ export class TaskforceCore {
|
|
|
5750
6260
|
};
|
|
5751
6261
|
});
|
|
5752
6262
|
}
|
|
5753
|
-
searchTaskSummaries(query, scope = 'all', workspaceId) {
|
|
6263
|
+
searchTaskSummaries(query, scope = 'all', workspaceId, options) {
|
|
5754
6264
|
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
5755
|
-
const normalizedQuery = String(query || '')
|
|
5756
|
-
if (!normalizedQuery)
|
|
6265
|
+
const normalizedQuery = normalizeTaskSearchQuery(String(query || ''));
|
|
6266
|
+
if (!normalizedQuery.raw)
|
|
5757
6267
|
return [];
|
|
5758
|
-
const exactReferenceNumber = parseTaskReference(normalizedQuery);
|
|
5759
|
-
const exactLocalReferenceNumber = parseLocalTaskReference(normalizedQuery);
|
|
5760
|
-
const
|
|
6268
|
+
const exactReferenceNumber = parseTaskReference(normalizedQuery.raw);
|
|
6269
|
+
const exactLocalReferenceNumber = parseLocalTaskReference(normalizedQuery.raw);
|
|
6270
|
+
const searchableText = `
|
|
6271
|
+
COALESCE(title, '') || ' ' ||
|
|
6272
|
+
COALESCE(description, '') || ' ' ||
|
|
6273
|
+
COALESCE(id, '') || ' ' ||
|
|
6274
|
+
CASE WHEN reference_number IS NULL THEN '' ELSE 'T-' || CAST(reference_number AS TEXT) END || ' ' ||
|
|
6275
|
+
CASE WHEN local_reference_number IS NULL THEN '' ELSE 'LT-' || CAST(local_reference_number AS TEXT) END || ' ' ||
|
|
6276
|
+
COALESCE(category, '') || ' ' ||
|
|
6277
|
+
COALESCE(type, '') || ' ' ||
|
|
6278
|
+
COALESCE(CAST(priority AS TEXT), '') || ' ' ||
|
|
6279
|
+
COALESCE(status, '') || ' ' ||
|
|
6280
|
+
COALESCE(assignee, '') || ' ' ||
|
|
6281
|
+
COALESCE(canceled_reason, '')
|
|
6282
|
+
`;
|
|
6283
|
+
const escapedTerms = normalizedQuery.tokens.map((token) => (`%${token.replace(/[\\%_]/g, '\\$&')}%`));
|
|
5761
6284
|
const clauses = [
|
|
5762
6285
|
'tenant_id = ?',
|
|
5763
6286
|
'workspace_id = ?',
|
|
5764
|
-
|
|
6287
|
+
...escapedTerms.map(() => `LOWER(${searchableText}) LIKE ? ESCAPE '\\'`),
|
|
5765
6288
|
];
|
|
5766
6289
|
const params = [
|
|
5767
6290
|
this.tenantId,
|
|
5768
6291
|
normalizedWorkspaceId,
|
|
5769
|
-
|
|
5770
|
-
term,
|
|
5771
|
-
term,
|
|
5772
|
-
exactReferenceNumber || -1,
|
|
5773
|
-
exactLocalReferenceNumber || -1,
|
|
6292
|
+
...escapedTerms,
|
|
5774
6293
|
];
|
|
5775
6294
|
if (scope === 'active')
|
|
5776
6295
|
clauses.push('is_archived = 0');
|
|
5777
6296
|
if (scope === 'archive')
|
|
5778
6297
|
clauses.push('is_archived = 1');
|
|
6298
|
+
const limit = Math.max(1, Math.min(51, Math.trunc(options?.limit ?? 50)));
|
|
6299
|
+
const offset = Math.max(0, Math.trunc(options?.offset ?? 0));
|
|
5779
6300
|
const rows = this.db.prepare(`
|
|
5780
|
-
SELECT id, reference_number, local_reference_number, title, status, assignee, category, priority
|
|
6301
|
+
SELECT id, reference_number, local_reference_number, title, status, assignee, category, priority, is_archived
|
|
5781
6302
|
FROM tasks
|
|
5782
6303
|
WHERE ${clauses.join(' AND ')}
|
|
5783
6304
|
ORDER BY
|
|
5784
6305
|
CASE WHEN id = ? OR reference_number = ? OR local_reference_number = ? THEN 0 ELSE 1 END,
|
|
5785
|
-
created_at DESC
|
|
5786
|
-
|
|
5787
|
-
|
|
6306
|
+
created_at DESC,
|
|
6307
|
+
id DESC
|
|
6308
|
+
LIMIT ? OFFSET ?
|
|
6309
|
+
`).all(...params, normalizedQuery.raw, exactReferenceNumber || -1, exactLocalReferenceNumber || -1, limit, offset);
|
|
5788
6310
|
return rows.map((row) => {
|
|
5789
6311
|
const referenceNumber = this.sanitizeTaskReferenceNumber(row.reference_number);
|
|
5790
6312
|
const localReferenceNumber = this.sanitizeTaskReferenceNumber(row.local_reference_number);
|
|
@@ -5798,6 +6320,7 @@ export class TaskforceCore {
|
|
|
5798
6320
|
assignee: String(row.assignee || '').trim() || null,
|
|
5799
6321
|
category: String(row.category || ''),
|
|
5800
6322
|
priority: this.normalizePriorityValue(row.priority),
|
|
6323
|
+
isArchived: row.is_archived === 1 || row.is_archived === true,
|
|
5801
6324
|
};
|
|
5802
6325
|
});
|
|
5803
6326
|
}
|
|
@@ -7229,6 +7752,20 @@ export class TaskforceCore {
|
|
|
7229
7752
|
runTaskMetadataOwnershipTransaction: (input) => this.runTaskMetadataOwnershipTransaction(input),
|
|
7230
7753
|
};
|
|
7231
7754
|
}
|
|
7755
|
+
buildUnresolvedWorkspaceActor(raw) {
|
|
7756
|
+
const normalized = raw.toLowerCase();
|
|
7757
|
+
const unresolvedKind = (normalized.startsWith('ai-profile-') || this.isKnownAiModelName(normalized)) ? 'ai' : 'human';
|
|
7758
|
+
return {
|
|
7759
|
+
id: raw,
|
|
7760
|
+
kind: unresolvedKind,
|
|
7761
|
+
label: this.formatActorLabel(raw),
|
|
7762
|
+
username: this.normalizeAiProfileUsername(raw, raw),
|
|
7763
|
+
icon: unresolvedKind === 'ai' ? GENERIC_AI_ACTOR_ICON : 'User',
|
|
7764
|
+
color: unresolvedKind === 'ai' ? GENERIC_AI_ACTOR_COLOR : '#22c55e',
|
|
7765
|
+
isNamed: true,
|
|
7766
|
+
meta: { unresolved: true }
|
|
7767
|
+
};
|
|
7768
|
+
}
|
|
7232
7769
|
resolveWorkspaceActor(workspaceId, actorRef) {
|
|
7233
7770
|
const raw = String(actorRef || '').trim();
|
|
7234
7771
|
const normalized = raw.toLowerCase();
|
|
@@ -7258,17 +7795,81 @@ export class TaskforceCore {
|
|
|
7258
7795
|
if (workspaceUser) {
|
|
7259
7796
|
return this.workspaceUserToActor(workspaceUser);
|
|
7260
7797
|
}
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7798
|
+
return this.buildUnresolvedWorkspaceActor(raw);
|
|
7799
|
+
}
|
|
7800
|
+
resolveWorkspaceActors(workspaceId, actorRefs) {
|
|
7801
|
+
const rawActorRefs = [...new Set(actorRefs
|
|
7802
|
+
.map((actorRef) => String(actorRef || '').trim())
|
|
7803
|
+
.filter(Boolean))];
|
|
7804
|
+
const resolvedActors = new Map();
|
|
7805
|
+
const lookupActorRefs = [];
|
|
7806
|
+
for (const raw of rawActorRefs) {
|
|
7807
|
+
const normalized = raw.toLowerCase();
|
|
7808
|
+
if (normalized === 'unassigned' || normalized === 'none' || normalized === 'null') {
|
|
7809
|
+
resolvedActors.set(raw, null);
|
|
7810
|
+
}
|
|
7811
|
+
else if (this.isHumanActorReference(raw)) {
|
|
7812
|
+
resolvedActors.set(raw, this.buildHumanActorFallback());
|
|
7813
|
+
}
|
|
7814
|
+
else if (this.isGenericAiActorReference(raw)) {
|
|
7815
|
+
resolvedActors.set(raw, this.buildGenericAiActor());
|
|
7816
|
+
}
|
|
7817
|
+
else {
|
|
7818
|
+
lookupActorRefs.push(raw);
|
|
7819
|
+
}
|
|
7820
|
+
}
|
|
7821
|
+
if (lookupActorRefs.length === 0)
|
|
7822
|
+
return resolvedActors;
|
|
7823
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
7824
|
+
const aiProfileRows = this.db.prepare(`
|
|
7825
|
+
SELECT *
|
|
7826
|
+
FROM ai_profiles
|
|
7827
|
+
WHERE tenant_id = ?
|
|
7828
|
+
AND workspace_id = ?
|
|
7829
|
+
ORDER BY COALESCE(last_active_at, updated_at, created_at) DESC, id ASC
|
|
7830
|
+
`).all(this.tenantId, normalizedWorkspaceId);
|
|
7831
|
+
const aiProfileRowsById = new Map(aiProfileRows
|
|
7832
|
+
.map((row) => [String(row.id || '').trim(), row])
|
|
7833
|
+
.filter(([id]) => Boolean(id)));
|
|
7834
|
+
const aiProfileRowsByAlias = new Map();
|
|
7835
|
+
for (const row of aiProfileRows) {
|
|
7836
|
+
const aliases = new Set([String(row.name || '').trim().toLowerCase(), String(row.username || '').trim().toLowerCase()]);
|
|
7837
|
+
for (const alias of aliases) {
|
|
7838
|
+
if (!alias)
|
|
7839
|
+
continue;
|
|
7840
|
+
const matches = aiProfileRowsByAlias.get(alias) ?? [];
|
|
7841
|
+
matches.push(row);
|
|
7842
|
+
aiProfileRowsByAlias.set(alias, matches);
|
|
7843
|
+
}
|
|
7844
|
+
}
|
|
7845
|
+
const workspaceUsersById = new Map(this.listWorkspaceUsers(normalizedWorkspaceId)
|
|
7846
|
+
.map((user) => [user.userId, user]));
|
|
7847
|
+
for (const raw of lookupActorRefs) {
|
|
7848
|
+
const directAiProfileRow = aiProfileRowsById.get(raw);
|
|
7849
|
+
if (directAiProfileRow) {
|
|
7850
|
+
resolvedActors.set(raw, this.aiProfileToActor(this.aiProfileRowToRecord(directAiProfileRow)));
|
|
7851
|
+
continue;
|
|
7852
|
+
}
|
|
7853
|
+
const aliasMatches = aiProfileRowsByAlias.get(raw.toLowerCase()) ?? [];
|
|
7854
|
+
const aliasRowsById = new Map(aliasMatches
|
|
7855
|
+
.map((row) => [String(row.id || '').trim(), row])
|
|
7856
|
+
.filter(([id]) => Boolean(id)));
|
|
7857
|
+
const canonicalAliasIds = [...new Set(aliasMatches
|
|
7858
|
+
.map((row) => this.resolveCanonicalAiProfileId(aliasRowsById, String(row.id || '').trim()))
|
|
7859
|
+
.filter(Boolean))];
|
|
7860
|
+
if (canonicalAliasIds.length === 1) {
|
|
7861
|
+
const canonicalAiProfileRow = aiProfileRowsById.get(canonicalAliasIds[0]);
|
|
7862
|
+
if (canonicalAiProfileRow) {
|
|
7863
|
+
resolvedActors.set(raw, this.aiProfileToActor(this.aiProfileRowToRecord(canonicalAiProfileRow)));
|
|
7864
|
+
continue;
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
const workspaceUser = workspaceUsersById.get(raw);
|
|
7868
|
+
resolvedActors.set(raw, workspaceUser
|
|
7869
|
+
? this.workspaceUserToActor(workspaceUser)
|
|
7870
|
+
: this.buildUnresolvedWorkspaceActor(raw));
|
|
7871
|
+
}
|
|
7872
|
+
return resolvedActors;
|
|
7272
7873
|
}
|
|
7273
7874
|
listAssignableActors(workspaceId) {
|
|
7274
7875
|
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
@@ -9716,9 +10317,11 @@ export class TaskforceCore {
|
|
|
9716
10317
|
return null;
|
|
9717
10318
|
const patchFields = Object.keys(patch);
|
|
9718
10319
|
const archivedNestedContentWrite = options?.allowArchivedNestedContentSyncWrite === true
|
|
9719
|
-
&& patchFields.some((field) => [
|
|
10320
|
+
&& patchFields.some((field) => [
|
|
10321
|
+
'comments', 'attachments', 'checklistItems', 'cardCoverAssetId',
|
|
10322
|
+
].includes(field))
|
|
9720
10323
|
&& patchFields.every((field) => [
|
|
9721
|
-
'id', 'comments', 'attachments', 'checklistItems',
|
|
10324
|
+
'id', 'comments', 'attachments', 'checklistItems', 'cardCoverAssetId',
|
|
9722
10325
|
].includes(field));
|
|
9723
10326
|
if (existing.isArchived && !archivedNestedContentWrite) {
|
|
9724
10327
|
throw new TaskforceRuleError('Archived tasks are read-only. Unarchive first to make changes.', 409, 'TASK_ARCHIVED_READ_ONLY');
|