@taskforcehq/taskforce 0.3.327 → 0.3.328
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Taskforce.module.css +24 -53
- package/dist/components/activity/EntityActivityTimeline.js +15 -5
- package/dist/components/context/AttachmentPreviewHost.js +1 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
- package/dist/components/context/ContextAttachmentManager.js +116 -20
- package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
- package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
- package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
- package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
- package/dist/components/features/planning/PlanningModule.js +26 -9
- package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
- package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
- package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +2 -2
- package/dist/components/task/TaskCard.js +37 -4
- package/dist/components/task/TaskContextUpload.d.ts +3 -0
- package/dist/components/task/TaskContextUpload.js +2 -2
- package/dist/components/task/TaskForm.d.ts +4 -0
- package/dist/components/task/TaskForm.js +32 -7
- package/dist/components/task/TaskImageReviews.js +16 -16
- package/dist/components/task/TaskStatusActions.js +11 -2
- package/dist/components/task/TaskWorkstreamPicker.js +2 -1
- package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
- package/dist/components/ui/DocumentTypeIcon.js +8 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
- package/dist/components/ui/TaxonomyDropdown.js +30 -3
- package/dist/components/views/StandaloneLayout.js +62 -9
- package/dist/components/views/WidgetView.js +8 -5
- package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
- package/dist/components/views/panels/PlanningDrawer.js +48 -44
- package/dist/config/envSchema.js +1 -0
- package/dist/config/localServiceCloudTarget.d.ts +10 -0
- package/dist/config/localServiceCloudTarget.js +76 -0
- package/dist/core/AiProfileService.d.ts +1 -0
- package/dist/core/AiProfileService.js +2 -1
- package/dist/core/AttachmentLinkService.js +7 -4
- package/dist/core/DeletedTaskLifecycleService.d.ts +4 -0
- package/dist/core/DeletedTaskLifecycleService.js +21 -0
- package/dist/core/McpTokenService.d.ts +4 -0
- package/dist/core/McpTokenService.js +52 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +5 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +20 -11
- package/dist/core/Taskforce.d.ts +63 -1
- package/dist/core/Taskforce.js +558 -73
- package/dist/core/types.d.ts +2 -1
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
- package/dist/documentReviews/service.d.ts +4 -0
- package/dist/documentReviews/service.js +141 -46
- package/dist/hooks/tasks/useTaskFormActions.d.ts +3 -0
- package/dist/hooks/tasks/useTaskFormActions.js +33 -7
- package/dist/hooks/useTaskforce.d.ts +59 -50
- package/dist/hooks/useTaskforce.js +103 -1
- package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
- package/dist/mcp/canonicalAssetHelpers.js +39 -5
- package/dist/mcp/collaborationRegistrar.js +15 -13
- package/dist/mcp/documentAssetRegistrar.js +16 -9
- package/dist/mcp/localServiceProxy.js +4 -1
- package/dist/mcp/runtime.d.ts +16 -0
- package/dist/mcp/runtime.js +566 -171
- package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
- package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
- package/dist/mcp/taskPlanningRegistrar.js +42 -34
- package/dist/mcp/toolRegistry.js +2 -2
- package/dist/migrations/taskSchemaMigrations.js +35 -0
- package/dist/runtime/appGateDefinitions.d.ts +2 -2
- package/dist/runtime/appGateDefinitions.js +2 -2
- package/dist/server/appAccess.d.ts +12 -0
- package/dist/server/appAccess.js +21 -0
- package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
- package/dist/server/authorizedWorkspaceAccess.js +40 -0
- package/dist/server/cloudCollectionRead.d.ts +25 -0
- package/dist/server/cloudCollectionRead.js +120 -0
- package/dist/server/cloudCollectionReadPool.d.ts +52 -0
- package/dist/server/cloudCollectionReadPool.js +140 -0
- package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
- package/dist/server/cloudCollectionReadWorker.js +36 -0
- package/dist/server/index.d.ts +3 -11
- package/dist/server/index.js +53 -40
- package/dist/server/localServiceLease.d.ts +3 -0
- package/dist/server/localServiceLease.js +9 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +160 -13
- package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
- package/dist/server/routes/durableTaskHttpRouters.js +2 -2
- package/dist/server/routes/localService.js +2 -0
- package/dist/server/routes/primitives.d.ts +2 -0
- package/dist/server/routes/shared.d.ts +24 -0
- package/dist/server/routes/shared.js +114 -18
- package/dist/server/routes/sync.js +22 -0
- package/dist/server/routes/syncV3FeedRoutes.js +4 -2
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +20 -2
- package/dist/server/routes/syncV3OperationRoutes.js +17 -2
- package/dist/server/routes/tasks.js +199 -89
- package/dist/server/routes.js +35 -7
- package/dist/service/localServiceCli.js +36 -25
- package/dist/service/localServiceClientLifecycle.js +67 -6
- package/dist/shared/planningIdentity.d.ts +20 -0
- package/dist/shared/planningIdentity.js +79 -0
- package/dist/storage/postgresAdapter.js +4 -0
- package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
- package/dist/storage/postgresRequestDiagnostics.js +24 -0
- package/dist/storage/workspaceAssetStore.d.ts +32 -0
- package/dist/storage/workspaceAssetStore.js +70 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +16 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +70 -22
- package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +5 -0
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +15 -0
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +26 -3
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
- package/dist/sync/syncApplyHandlers.d.ts +3 -1
- package/dist/sync/syncApplyHandlers.js +29 -0
- package/dist/sync/syncService.js +9 -0
- package/dist/sync/taskSyncPayload.d.ts +1 -0
- package/dist/sync/taskSyncPayload.js +6 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +21 -3
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +17 -4
- package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
- package/dist/sync/v3/localOutboxDispatcher.js +5 -1
- package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +1 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +15 -3
- package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
- package/dist/sync/v3/syncDurabilityStore.d.ts +8 -0
- package/dist/sync/v3/syncDurabilityStore.js +171 -33
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +1 -0
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +18 -2
- package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +1 -9
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +1 -0
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +7 -1
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +15 -3
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +4 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +29 -6
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +29 -7
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +62 -7
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
- package/dist/sync/workspacePullFeed.d.ts +6 -1
- package/dist/sync/workspacePullFeed.js +172 -161
- package/dist/sync/workspaceSyncModel.d.ts +11 -0
- package/dist/sync/workspaceSyncModel.js +27 -2
- package/dist/types.d.ts +8 -0
- package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-MZs7lVED.js} +1 -1
- package/dist/ui/assets/{AiProfilesModule-DPxQeF9N.js → AiProfilesModule-C3i0LOxe.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-DHNYaNg6.js → AnnotatedAttachmentWorkspace-DVZk5Tln.js} +1 -1
- package/dist/ui/assets/AssetTraySortControl-Kf1pelSy.js +1 -0
- package/dist/ui/assets/ContextAttachmentManager-B8boOs6Q.js +3 -0
- package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-DXH4XUsB.js} +4 -4
- package/dist/ui/assets/EntityActivityTimeline-BtSTq0ON.js +1 -0
- package/dist/ui/assets/PlanningModule-BPVyEa-f.js +1 -0
- package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
- package/dist/ui/assets/{PlansPage-BVH_NfgT.js → PlansPage-ChqgxALc.js} +1 -1
- package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +1 -0
- package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
- package/dist/ui/assets/{TaskSettings-xVsxSa0g.js → TaskSettings-g4ECKNOz.js} +1 -1
- package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BazdUwxZ.js} +1 -1
- package/dist/ui/assets/WorkflowManagerModule-DWFspC-o.js +1 -0
- package/dist/ui/assets/index-BUplSsv_.js +7 -0
- package/dist/ui/assets/index-gz2EXuoK.css +1 -0
- package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-BkFLXavV.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.js +1 -1
- package/dist/utils/contextAttachmentUpload.d.ts +2 -0
- package/dist/utils/contextAttachmentUpload.js +7 -0
- package/dist/utils/contextFiles.d.ts +1 -1
- package/dist/utils/contextFiles.js +4 -4
- package/package.json +6 -1
- package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
- package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
- package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
- package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
- package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
- package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
- package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
- package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
- package/dist/ui/assets/index-BzJlVnNS.css +0 -1
- package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* POST /api/taskforce/context-upload/init
|
|
9
9
|
* POST /api/taskforce/context-upload/finalize
|
|
10
10
|
* POST /api/taskforce/context-upload
|
|
11
|
+
* POST /api/taskforce/context-upload/draft/promote
|
|
12
|
+
* POST /api/taskforce/context-upload/draft/discard
|
|
11
13
|
* GET /api/taskforce/context/:filename
|
|
12
14
|
* GET /api/taskforce/documents/:assetId/content
|
|
13
15
|
* GET /api/taskforce/context-link
|
|
@@ -33,7 +35,7 @@ import { parsePlan } from '../../utils/planParser.js';
|
|
|
33
35
|
import { getCanonicalDocumentName, getReadableDocumentName, isGeneratedDocumentName } from '../../utils/documentNames.js';
|
|
34
36
|
import { createWorkspaceAssetAccessGuard } from './assetAccess.js';
|
|
35
37
|
import { parseJsonBody } from './primitives.js';
|
|
36
|
-
import { requestHeader, requestRuntimeMode, resolveErrorStatusCode } from './shared.js';
|
|
38
|
+
import { requestHeader, requestHumanActorRef, requestRuntimeMode, requestUserId, resolveErrorStatusCode, } from './shared.js';
|
|
37
39
|
import { resolveDeploymentConfig } from '../../config/deployment.js';
|
|
38
40
|
import { isAiProfileAvatarStorageKey } from '../../utils/aiProfileAvatarPaths.js';
|
|
39
41
|
import { createTaskHttpDurableRouters } from './durableTaskHttpRouters.js';
|
|
@@ -110,7 +112,7 @@ async function proxyCloudUserProfileAvatar(req, res, relativePath, contentTypeFo
|
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
114
|
export function registerDocumentRoutes(deps) {
|
|
113
|
-
const { addRoute, core, context, requestWorkspaceId, ensureAdminRole, resolveEffectiveObjectStorageConfig, getObjectStorageClient, resolveStorageWorkspaceId, ensureContextUploadAllowed, parseStorageFolderSegments, buildCanonicalWorkspaceAssetRelativePath, inferAssetKindFromPath, appendStorageTelemetry, scanAndRecordDocument, buildTaskAttachmentPayload, workspaceAssetStore, taskAssetStore, normalizeDocumentPathInput, getAssetMetadata, getCanonicalAssetMetadata, documentRetentionDays, appendDocumentPurgeHistory, markAssetDeleted, isAssetDeleted, clearAssetDeleted, ensureWithinDir, sanitizeDownloadFilename, contentTypeForExt, inferInlineDocumentFromPath,
|
|
115
|
+
const { addRoute, core, context, requestWorkspaceId, ensureAdminRole, resolveEffectiveObjectStorageConfig, getObjectStorageClient, resolveStorageWorkspaceId, ensureContextUploadAllowed, parseStorageFolderSegments, buildCanonicalWorkspaceAssetRelativePath, inferAssetKindFromPath, appendStorageTelemetry, scanAndRecordDocument, buildTaskAttachmentPayload, workspaceAssetStore, taskAssetStore, normalizeDocumentPathInput, getAssetMetadata, getCanonicalAssetMetadata, documentRetentionDays, appendDocumentPurgeHistory, markAssetDeleted, isAssetDeleted, clearAssetDeleted, ensureWithinDir, sanitizeDownloadFilename, contentTypeForExt, inferInlineDocumentFromPath, resolveCanonicalContentRead, inferWorkspaceIdFromStoragePath, inferTaskIdFromStoragePath, } = deps;
|
|
114
116
|
const isTaskAttachmentDurable = (req, workspaceId) => (createTaskHttpDurableRouters({ core, context, req, workspaceId }).attachmentLinks.enabled);
|
|
115
117
|
const durableUploadIdentity = (req, workspaceId, taskId) => {
|
|
116
118
|
const normalizedTaskId = String(taskId || '').trim();
|
|
@@ -172,6 +174,69 @@ export function registerDocumentRoutes(deps) {
|
|
|
172
174
|
return String(url.searchParams.get('workspaceId') || '').trim() || requestWorkspaceId(req);
|
|
173
175
|
};
|
|
174
176
|
const { ensureWorkspaceAssetAccess, isCloudAssetRequest } = createWorkspaceAssetAccessGuard({ core, context });
|
|
177
|
+
const readTaskDescriptionDraftUpload = (body) => {
|
|
178
|
+
const draftId = String(body?.draftUploadId || '').trim();
|
|
179
|
+
const purpose = String(body?.uploadPurpose || '').trim();
|
|
180
|
+
if (!draftId && !purpose)
|
|
181
|
+
return null;
|
|
182
|
+
if (purpose !== 'task-description-draft' || !/^[a-zA-Z0-9-]{16,160}$/.test(draftId)) {
|
|
183
|
+
throw Object.assign(new Error('Invalid task Description draft upload identity.'), { statusCode: 400 });
|
|
184
|
+
}
|
|
185
|
+
return { draftId, purpose };
|
|
186
|
+
};
|
|
187
|
+
const recordTaskDescriptionDraftUpload = (input) => {
|
|
188
|
+
const draft = readTaskDescriptionDraftUpload(input.body);
|
|
189
|
+
if (!draft || !workspaceAssetStore)
|
|
190
|
+
return;
|
|
191
|
+
workspaceAssetStore.recordUploadDraft({
|
|
192
|
+
workspaceId: input.workspaceId,
|
|
193
|
+
draftId: draft.draftId,
|
|
194
|
+
assetId: input.assetId,
|
|
195
|
+
storageKey: input.storageKey,
|
|
196
|
+
purpose: draft.purpose,
|
|
197
|
+
createdByUserId: String(requestUserId(input.req) || '').trim() || 'anonymous',
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
const retireOrDeleteTaskDescriptionDraftUploads = async (input) => {
|
|
201
|
+
if (!workspaceAssetStore)
|
|
202
|
+
return { total: 0, promoted: 0, discarded: 0 };
|
|
203
|
+
const records = workspaceAssetStore.listUploadDrafts(input);
|
|
204
|
+
const now = new Date();
|
|
205
|
+
const deletedAt = now.toISOString();
|
|
206
|
+
const purgeAfter = new Date(now.getTime() + documentRetentionDays * 86_400_000).toISOString();
|
|
207
|
+
let promoted = 0;
|
|
208
|
+
let discarded = 0;
|
|
209
|
+
for (const record of records) {
|
|
210
|
+
const links = workspaceAssetStore.listLinksForAsset(record.assetId, record.workspaceId);
|
|
211
|
+
if (links.length > 0) {
|
|
212
|
+
workspaceAssetStore.retireUploadDraft({
|
|
213
|
+
workspaceId: record.workspaceId,
|
|
214
|
+
draftId: record.draftId,
|
|
215
|
+
assetId: record.assetId,
|
|
216
|
+
retiredAt: deletedAt,
|
|
217
|
+
});
|
|
218
|
+
promoted += 1;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const asset = workspaceAssetStore.get(record.assetId, record.workspaceId);
|
|
222
|
+
if (asset && !asset.deletedAt) {
|
|
223
|
+
workspaceAssetStore.assertAssetDeletionAllowed(record.assetId, record.workspaceId);
|
|
224
|
+
workspaceAssetStore.markDeleted(record.assetId, record.workspaceId, deletedAt, purgeAfter);
|
|
225
|
+
core.notifyDocumentMetadataMutation(record.workspaceId, [record.storageKey], 'delete');
|
|
226
|
+
}
|
|
227
|
+
else if (!asset) {
|
|
228
|
+
await getObjectStorageClient()?.deleteObject(record.storageKey);
|
|
229
|
+
}
|
|
230
|
+
workspaceAssetStore.retireUploadDraft({
|
|
231
|
+
workspaceId: record.workspaceId,
|
|
232
|
+
draftId: record.draftId,
|
|
233
|
+
assetId: record.assetId,
|
|
234
|
+
retiredAt: deletedAt,
|
|
235
|
+
});
|
|
236
|
+
discarded += 1;
|
|
237
|
+
}
|
|
238
|
+
return { total: records.length, promoted, discarded };
|
|
239
|
+
};
|
|
175
240
|
const removeDocumentAttachmentFromTaskAttachments = (attachmentsRaw, asset) => {
|
|
176
241
|
const canonicalDocumentPath = `/api/taskforce/documents/${encodeURIComponent(asset.assetId)}/content`;
|
|
177
242
|
const legacyContextPath = `/api/taskforce/context/${encodeURIComponent(asset.storageKey)}`;
|
|
@@ -260,6 +325,13 @@ export function registerDocumentRoutes(deps) {
|
|
|
260
325
|
const relativeToStorage = canonicalAsset.relativePath;
|
|
261
326
|
const relativePath = path.relative(projectRoot, path.join(basePath, relativeToStorage));
|
|
262
327
|
if (objectStorageClient && resolvedObjectStorage.provider === 'r2' && resolvedObjectStorage.r2) {
|
|
328
|
+
recordTaskDescriptionDraftUpload({
|
|
329
|
+
req,
|
|
330
|
+
body,
|
|
331
|
+
workspaceId,
|
|
332
|
+
assetId: canonicalAsset.assetId,
|
|
333
|
+
storageKey: relativeToStorage,
|
|
334
|
+
});
|
|
263
335
|
const uploadUrl = objectStorageClient.createSignedPutUrl(relativeToStorage, resolvedObjectStorage.r2.signedUploadTtlSeconds, String(mimeType || 'application/octet-stream'));
|
|
264
336
|
appendStorageTelemetry('upload.initiated', {
|
|
265
337
|
path: relativeToStorage,
|
|
@@ -345,8 +417,19 @@ export function registerDocumentRoutes(deps) {
|
|
|
345
417
|
provider: 'r2',
|
|
346
418
|
workspaceId: inferWorkspaceIdFromStoragePath(relativeToStorage),
|
|
347
419
|
taskId: durableTaskAttachment ? null : taskId,
|
|
348
|
-
originalName: typeof body?.originalName === 'string' ? body.originalName.trim() : null
|
|
420
|
+
originalName: typeof body?.originalName === 'string' ? body.originalName.trim() : null,
|
|
421
|
+
actorRef: requestHumanActorRef(req),
|
|
349
422
|
});
|
|
423
|
+
const finalizedAsset = workspaceAssetStore?.getByStorageKey(relativeToStorage, workspaceId);
|
|
424
|
+
if (finalizedAsset) {
|
|
425
|
+
recordTaskDescriptionDraftUpload({
|
|
426
|
+
req,
|
|
427
|
+
body,
|
|
428
|
+
workspaceId,
|
|
429
|
+
assetId: finalizedAsset.assetId,
|
|
430
|
+
storageKey: relativeToStorage,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
350
433
|
if (durableTaskAttachment && taskId) {
|
|
351
434
|
attachUploadedAssetThroughDurableBoundary({
|
|
352
435
|
req,
|
|
@@ -456,7 +539,8 @@ export function registerDocumentRoutes(deps) {
|
|
|
456
539
|
provider: 'r2',
|
|
457
540
|
workspaceId,
|
|
458
541
|
taskId: durableTaskAttachment ? null : normalizedTaskId,
|
|
459
|
-
originalName: safeOriginal || actualFilename
|
|
542
|
+
originalName: safeOriginal || actualFilename,
|
|
543
|
+
actorRef: requestHumanActorRef(req),
|
|
460
544
|
});
|
|
461
545
|
appendStorageTelemetry('upload.completed', {
|
|
462
546
|
path: relativeToStorage,
|
|
@@ -476,7 +560,8 @@ export function registerDocumentRoutes(deps) {
|
|
|
476
560
|
provider: 'local',
|
|
477
561
|
workspaceId,
|
|
478
562
|
taskId: durableTaskAttachment ? null : normalizedTaskId,
|
|
479
|
-
originalName: safeOriginal || actualFilename
|
|
563
|
+
originalName: safeOriginal || actualFilename,
|
|
564
|
+
actorRef: requestHumanActorRef(req),
|
|
480
565
|
});
|
|
481
566
|
appendStorageTelemetry('upload.completed', {
|
|
482
567
|
path: relativeToStorage,
|
|
@@ -494,6 +579,16 @@ export function registerDocumentRoutes(deps) {
|
|
|
494
579
|
caption: safeOriginal || actualFilename,
|
|
495
580
|
});
|
|
496
581
|
}
|
|
582
|
+
const uploadedAsset = workspaceAssetStore?.getByStorageKey(relativeToStorage, workspaceId);
|
|
583
|
+
if (uploadedAsset) {
|
|
584
|
+
recordTaskDescriptionDraftUpload({
|
|
585
|
+
req,
|
|
586
|
+
body,
|
|
587
|
+
workspaceId,
|
|
588
|
+
assetId: uploadedAsset.assetId,
|
|
589
|
+
storageKey: relativeToStorage,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
497
592
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
498
593
|
const attachment = buildTaskAttachmentPayload(relativeToStorage, {
|
|
499
594
|
caption: safeOriginal || actualFilename,
|
|
@@ -508,6 +603,40 @@ export function registerDocumentRoutes(deps) {
|
|
|
508
603
|
})
|
|
509
604
|
}));
|
|
510
605
|
});
|
|
606
|
+
addRoute('POST', '/api/taskforce/context-upload/draft/promote', async (req, res) => {
|
|
607
|
+
const body = await parseJsonBody(req);
|
|
608
|
+
const workspaceId = resolveStorageWorkspaceId(req, body);
|
|
609
|
+
const draft = readTaskDescriptionDraftUpload(body);
|
|
610
|
+
if (!workspaceId || !draft) {
|
|
611
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
612
|
+
res.end(JSON.stringify({ error: 'workspaceId and draft upload identity are required.' }));
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const summary = await retireOrDeleteTaskDescriptionDraftUploads({
|
|
616
|
+
workspaceId,
|
|
617
|
+
draftId: draft.draftId,
|
|
618
|
+
createdByUserId: String(requestUserId(req) || '').trim() || 'anonymous',
|
|
619
|
+
});
|
|
620
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
621
|
+
res.end(JSON.stringify({ success: true, ...summary }));
|
|
622
|
+
});
|
|
623
|
+
addRoute('POST', '/api/taskforce/context-upload/draft/discard', async (req, res) => {
|
|
624
|
+
const body = await parseJsonBody(req);
|
|
625
|
+
const workspaceId = resolveStorageWorkspaceId(req, body);
|
|
626
|
+
const draft = readTaskDescriptionDraftUpload(body);
|
|
627
|
+
if (!workspaceId || !draft) {
|
|
628
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
629
|
+
res.end(JSON.stringify({ error: 'workspaceId and draft upload identity are required.' }));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const summary = await retireOrDeleteTaskDescriptionDraftUploads({
|
|
633
|
+
workspaceId,
|
|
634
|
+
draftId: draft.draftId,
|
|
635
|
+
createdByUserId: String(requestUserId(req) || '').trim() || 'anonymous',
|
|
636
|
+
});
|
|
637
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
638
|
+
res.end(JSON.stringify({ success: true, ...summary }));
|
|
639
|
+
});
|
|
511
640
|
addRoute('GET', '/api/taskforce/context/:filename', async (req, res, params) => {
|
|
512
641
|
const resolvedObjectStorage = resolveEffectiveObjectStorageConfig();
|
|
513
642
|
const objectStorageClient = getObjectStorageClient();
|
|
@@ -556,8 +685,8 @@ export function registerDocumentRoutes(deps) {
|
|
|
556
685
|
? `attachment; filename="${resolvedDownloadName.replace(/"/g, '')}"`
|
|
557
686
|
: 'inline';
|
|
558
687
|
const shouldProxyInlineDocument = !forceDownload && (canonicalAsset?.kind === 'document' || inferInlineDocumentFromPath(normalizedRelative));
|
|
559
|
-
if (canonicalAsset
|
|
560
|
-
const response = await
|
|
688
|
+
if (canonicalAsset && !canonicalAsset.deletedAt && (canonicalAsset.kind === 'document' || !filePath)) {
|
|
689
|
+
const response = await resolveCanonicalContentRead({
|
|
561
690
|
asset: canonicalAsset,
|
|
562
691
|
forceDownload,
|
|
563
692
|
requestedFilename
|
|
@@ -567,6 +696,11 @@ export function registerDocumentRoutes(deps) {
|
|
|
567
696
|
res.end('Context file not found');
|
|
568
697
|
return;
|
|
569
698
|
}
|
|
699
|
+
if (response.statusCode === 409) {
|
|
700
|
+
res.writeHead(409, response.headers || { 'Content-Type': 'application/json' });
|
|
701
|
+
res.end(response.body);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
570
704
|
if (forceDownload && response.statusCode === 302 && response.headers?.Location) {
|
|
571
705
|
res.writeHead(302, { Location: response.headers.Location });
|
|
572
706
|
res.end();
|
|
@@ -659,7 +793,7 @@ export function registerDocumentRoutes(deps) {
|
|
|
659
793
|
res.end('Document scan pending');
|
|
660
794
|
return;
|
|
661
795
|
}
|
|
662
|
-
const response = await
|
|
796
|
+
const response = await resolveCanonicalContentRead({
|
|
663
797
|
asset,
|
|
664
798
|
forceDownload,
|
|
665
799
|
requestedFilename
|
|
@@ -669,6 +803,11 @@ export function registerDocumentRoutes(deps) {
|
|
|
669
803
|
res.end(JSON.stringify({ error: 'Document content not found' }));
|
|
670
804
|
return;
|
|
671
805
|
}
|
|
806
|
+
if (response.statusCode === 409) {
|
|
807
|
+
res.writeHead(409, response.headers || { 'Content-Type': 'application/json' });
|
|
808
|
+
res.end(response.body);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
672
811
|
if (forceDownload && response.statusCode === 302 && response.headers?.Location) {
|
|
673
812
|
res.writeHead(302, { Location: response.headers.Location });
|
|
674
813
|
res.end();
|
|
@@ -791,6 +930,12 @@ export function registerDocumentRoutes(deps) {
|
|
|
791
930
|
res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
|
|
792
931
|
return;
|
|
793
932
|
}
|
|
933
|
+
const workspaceId = requestWorkspaceId(req);
|
|
934
|
+
const draftExpiry = new Date(Date.now() - 30 * 86_400_000).toISOString();
|
|
935
|
+
const draftSummary = await retireOrDeleteTaskDescriptionDraftUploads({
|
|
936
|
+
workspaceId,
|
|
937
|
+
createdBefore: draftExpiry,
|
|
938
|
+
});
|
|
794
939
|
const { basePath } = core.getPaths();
|
|
795
940
|
const summary = await purgeExpiredDocuments({
|
|
796
941
|
basePath,
|
|
@@ -804,7 +949,7 @@ export function registerDocumentRoutes(deps) {
|
|
|
804
949
|
provider: resolveEffectiveObjectStorageConfig().provider
|
|
805
950
|
});
|
|
806
951
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
807
|
-
res.end(JSON.stringify({ success: true, ...summary }));
|
|
952
|
+
res.end(JSON.stringify({ success: true, ...summary, draftUploads: draftSummary }));
|
|
808
953
|
});
|
|
809
954
|
addRoute('GET', '/api/taskforce/documents', async (req, res) => {
|
|
810
955
|
const workspaceId = requestWorkspaceIdWithQuery(req);
|
|
@@ -1059,7 +1204,7 @@ export function registerDocumentRoutes(deps) {
|
|
|
1059
1204
|
core.updateTask(targetId, {
|
|
1060
1205
|
attachments: [...(Array.isArray(task.attachments) ? task.attachments : []), payload],
|
|
1061
1206
|
}, workspaceId, {
|
|
1062
|
-
actorRef:
|
|
1207
|
+
actorRef: requestHumanActorRef(req),
|
|
1063
1208
|
saveSource: 'manual',
|
|
1064
1209
|
});
|
|
1065
1210
|
}
|
|
@@ -1124,7 +1269,7 @@ export function registerDocumentRoutes(deps) {
|
|
|
1124
1269
|
else {
|
|
1125
1270
|
const updatedAttachments = removeDocumentAttachmentFromTaskAttachments(task.attachments, asset);
|
|
1126
1271
|
core.updateTask(taskId, { attachments: updatedAttachments }, workspaceId, {
|
|
1127
|
-
actorRef:
|
|
1272
|
+
actorRef: requestHumanActorRef(req),
|
|
1128
1273
|
saveSource: 'manual'
|
|
1129
1274
|
});
|
|
1130
1275
|
}
|
|
@@ -1280,7 +1425,8 @@ export function registerDocumentRoutes(deps) {
|
|
|
1280
1425
|
contentType: 'text/markdown; charset=utf-8',
|
|
1281
1426
|
provider: 'r2',
|
|
1282
1427
|
workspaceId: assetWorkspaceId,
|
|
1283
|
-
originalName: metadata.logicalName || metadata.originalFilename || path.basename(normalizedStoragePath)
|
|
1428
|
+
originalName: metadata.logicalName || metadata.originalFilename || path.basename(normalizedStoragePath),
|
|
1429
|
+
actorRef: requestHumanActorRef(req),
|
|
1284
1430
|
});
|
|
1285
1431
|
clearAssetDeleted(normalizedStoragePath);
|
|
1286
1432
|
const version = workspaceAssetStore
|
|
@@ -1336,7 +1482,8 @@ export function registerDocumentRoutes(deps) {
|
|
|
1336
1482
|
contentType: 'text/markdown; charset=utf-8',
|
|
1337
1483
|
provider: 'local',
|
|
1338
1484
|
workspaceId: assetWorkspaceId,
|
|
1339
|
-
originalName: path.basename(normalizedStoragePath)
|
|
1485
|
+
originalName: path.basename(normalizedStoragePath),
|
|
1486
|
+
actorRef: requestHumanActorRef(req),
|
|
1340
1487
|
});
|
|
1341
1488
|
clearAssetDeleted(normalizedStoragePath);
|
|
1342
1489
|
version = workspaceAssetStore
|
|
@@ -53,6 +53,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
53
53
|
replace(args: {
|
|
54
54
|
taskId: string;
|
|
55
55
|
links: readonly import("../../sync/v3/workspaceSyncV3NestedTaskCodecs.js").WorkspaceSyncV3TaskAttachmentLink[];
|
|
56
|
+
cardCoverAssetId?: string | null;
|
|
56
57
|
}): {
|
|
57
58
|
state: "pending" | "committed";
|
|
58
59
|
route: "local-outbox" | "cloud-acceptance";
|
|
@@ -77,6 +78,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
77
78
|
replace(args: {
|
|
78
79
|
taskId: string;
|
|
79
80
|
links: readonly import("../../sync/v3/workspaceSyncV3NestedTaskCodecs.js").WorkspaceSyncV3TaskAttachmentLink[];
|
|
81
|
+
cardCoverAssetId?: string | null;
|
|
80
82
|
}): {
|
|
81
83
|
state: "pending" | "committed";
|
|
82
84
|
route: "local-outbox" | "cloud-acceptance";
|
|
@@ -4,12 +4,12 @@ import { createDurableTaskHttpRootMutationRouter } from '../../sync/v3/durableTa
|
|
|
4
4
|
import { createDurableTaskCommentMutationRouter } from '../../sync/v3/durableTaskCommentMutationRouter.js';
|
|
5
5
|
import { createDurableTaskChecklistMutationRouter } from '../../sync/v3/durableTaskChecklistMutationRouter.js';
|
|
6
6
|
import { createDurableTaskAttachmentLinksMutationRouter } from '../../sync/v3/durableTaskAttachmentLinksMutationRouter.js';
|
|
7
|
-
import { requestHeader,
|
|
7
|
+
import { requestHeader, requestHumanActorRef, requestRuntimeMode } from './shared.js';
|
|
8
8
|
export function createTaskHttpDurableRouters(input) {
|
|
9
9
|
const { core, context, req, workspaceId } = input;
|
|
10
10
|
const runtimeMode = context.authConfig?.runtimeMode === 'cloud' ? 'cloud' : requestRuntimeMode(req);
|
|
11
11
|
const parentOperationId = requestHeader(req, 'x-taskforce-operation-id', '').trim();
|
|
12
|
-
const actorRef =
|
|
12
|
+
const actorRef = requestHumanActorRef(req);
|
|
13
13
|
const createRootRouter = (operationId = parentOperationId, deferPostCommitNotification) => createDurableTaskHttpRootMutationRouter({
|
|
14
14
|
core,
|
|
15
15
|
runtimeMode,
|
|
@@ -185,6 +185,7 @@ export function registerLocalServiceRoutes(deps) {
|
|
|
185
185
|
endpoint: context.endpoint?.() || null,
|
|
186
186
|
projectIdentity: context.projectIdentity,
|
|
187
187
|
dataIdentity: context.ownershipKey,
|
|
188
|
+
cloudTarget: context.cloudTarget,
|
|
188
189
|
transports: ['streamable-http'],
|
|
189
190
|
health: {
|
|
190
191
|
lifecycle: context.sessions.state(),
|
|
@@ -279,6 +280,7 @@ export function registerLocalServiceRoutes(deps) {
|
|
|
279
280
|
generation: context.generation,
|
|
280
281
|
protocolVersion: context.protocolVersion,
|
|
281
282
|
ownerPid: context.ownerPid,
|
|
283
|
+
cloudTarget: context.cloudTarget,
|
|
282
284
|
sessions: context.sessions.list(),
|
|
283
285
|
...(context.diagnostics?.() || {}),
|
|
284
286
|
});
|
|
@@ -4,6 +4,7 @@ import type { ResolvedObjectStorageConfig } from '../../storage/objectStorage.js
|
|
|
4
4
|
import type { LocalSyncCoordinatorRouteServices } from '../../sync/coordinator/localSyncCoordinatorRouteServices.js';
|
|
5
5
|
import type { WorkspaceSyncServerDataServices } from '../../sync/engine/workspaceSyncServerDataServices.js';
|
|
6
6
|
import type { LocalServiceSessionManager } from '../localServiceSessions.js';
|
|
7
|
+
import type { LocalServiceCloudTarget } from '../../config/localServiceCloudTarget.js';
|
|
7
8
|
export interface LocalServiceRouteContext {
|
|
8
9
|
generation: string;
|
|
9
10
|
protocolVersion: string;
|
|
@@ -11,6 +12,7 @@ export interface LocalServiceRouteContext {
|
|
|
11
12
|
ownerPid: number;
|
|
12
13
|
bootstrapCapability: string;
|
|
13
14
|
ownershipKey: string;
|
|
15
|
+
cloudTarget: LocalServiceCloudTarget;
|
|
14
16
|
projectIdentity: string;
|
|
15
17
|
operationClientId: string;
|
|
16
18
|
sessions: LocalServiceSessionManager;
|
|
@@ -11,6 +11,7 @@ import type { McpAccessTokenAuthResult, McpOAuthAccessTokenAuthResult, McpAccess
|
|
|
11
11
|
import { type RouteContext } from './primitives.js';
|
|
12
12
|
import { type AiProfileBootstrapConfig } from '../../mcp/aiProfileBootstrap.js';
|
|
13
13
|
import type { AiProfileSeatScope } from '../../shared/aiProfileSeatScope.js';
|
|
14
|
+
import { type RequestPerformanceDiagnostics } from '../../storage/postgresRequestDiagnostics.js';
|
|
14
15
|
export declare function resolveErrorStatusCode(error: unknown, fallback?: number): number;
|
|
15
16
|
export declare function buildCloudMcpProfileConnectionId(input: {
|
|
16
17
|
oauth?: McpOAuthAccessTokenAuthResult | null;
|
|
@@ -19,6 +20,7 @@ export declare function buildCloudMcpProfileConnectionId(input: {
|
|
|
19
20
|
}): string | null;
|
|
20
21
|
export declare const BOOTSTRAP_ROUTE_SLOW_THRESHOLD_MS = 1500;
|
|
21
22
|
export declare const BOOTSTRAP_ROUTE_SEVERE_THRESHOLD_MS = 5000;
|
|
23
|
+
export declare const TASKFORCE_PERFORMANCE_TRACE_HEADER = "x-taskforce-performance-trace";
|
|
22
24
|
export type ServerTimingMetric = {
|
|
23
25
|
name: string;
|
|
24
26
|
durationMs: number;
|
|
@@ -31,6 +33,17 @@ export declare function serializeJsonWithTiming(payload: unknown, startedAt?: nu
|
|
|
31
33
|
byteLength: number;
|
|
32
34
|
durationMs: number;
|
|
33
35
|
};
|
|
36
|
+
export declare function runWithPerformanceTrace<T>(req: IncomingMessage, callback: () => T): T;
|
|
37
|
+
export declare function currentPerformanceTraceDiagnostics(): RequestPerformanceDiagnostics | null;
|
|
38
|
+
export declare function performanceTraceServerTimingMetrics(diagnostics: RequestPerformanceDiagnostics | null): ServerTimingMetric[];
|
|
39
|
+
export declare function setPerformanceTraceHeaders(res: ServerResponse, diagnostics: RequestPerformanceDiagnostics | null, responseBytes: number): void;
|
|
40
|
+
export declare function respondWithPerformanceTraceJson(input: {
|
|
41
|
+
req: IncomingMessage;
|
|
42
|
+
res: ServerResponse;
|
|
43
|
+
route: string;
|
|
44
|
+
workspaceId: string;
|
|
45
|
+
load: () => unknown;
|
|
46
|
+
}): void;
|
|
34
47
|
export declare function requestBootstrapTraceId(req: IncomingMessage): string;
|
|
35
48
|
export declare function logSlowBootstrapRoute(input: {
|
|
36
49
|
route: string;
|
|
@@ -47,6 +60,7 @@ export declare function buildCloudMcpRuntimeAuth(input: {
|
|
|
47
60
|
authenticatedViaToken: McpAccessTokenAuthResult | null;
|
|
48
61
|
authenticatedViaOAuth: McpOAuthAccessTokenAuthResult | null;
|
|
49
62
|
userId: string | null;
|
|
63
|
+
aiProfileIdentityAsserted?: boolean;
|
|
50
64
|
} & Required<AiProfileBootstrapConfig>;
|
|
51
65
|
requestHeader: (req: IncomingMessage, name: string, fallback?: string) => string;
|
|
52
66
|
requestId?: string | null;
|
|
@@ -57,6 +71,7 @@ export declare function buildCloudMcpRuntimeAuth(input: {
|
|
|
57
71
|
tokenId?: string | null;
|
|
58
72
|
aiProfileToken?: AiProfileBootstrapConfig['aiProfileToken'];
|
|
59
73
|
aiProfileId?: string | null;
|
|
74
|
+
aiProfileIdentityAsserted?: boolean;
|
|
60
75
|
aiProfileName?: AiProfileBootstrapConfig['aiProfileName'];
|
|
61
76
|
aiProfileIcon?: AiProfileBootstrapConfig['aiProfileIcon'];
|
|
62
77
|
aiProfileColor?: AiProfileBootstrapConfig['aiProfileColor'];
|
|
@@ -79,6 +94,7 @@ export type CloudMcpRequestAuth = {
|
|
|
79
94
|
authenticatedViaOAuth: McpOAuthAccessTokenAuthResult | null;
|
|
80
95
|
userId: string | null;
|
|
81
96
|
aiProfileId?: string | null;
|
|
97
|
+
aiProfileIdentityAsserted?: boolean;
|
|
82
98
|
} & Required<AiProfileBootstrapConfig>;
|
|
83
99
|
export declare function makeAuthenticateCloudMcpRequest(core: TaskforceCore, deps: {
|
|
84
100
|
resolveAuthenticatedUserId: (req: IncomingMessage) => string;
|
|
@@ -101,6 +117,14 @@ export declare function requestRuntimeMode(req: IncomingMessage): 'local' | 'clo
|
|
|
101
117
|
export declare function requestRole(req: IncomingMessage): string;
|
|
102
118
|
export declare function requestSystemRole(req: IncomingMessage): 'system_admin' | 'user';
|
|
103
119
|
export declare function requestUserId(req: IncomingMessage): string;
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the human actor recorded by ordinary HTTP mutations. The standalone
|
|
122
|
+
* server has already replaced this header with authenticated cloud identity
|
|
123
|
+
* when available; local signed-in clients provide the same account id through
|
|
124
|
+
* the request-scoped mutation header. Anonymous sessions retain the legacy
|
|
125
|
+
* generic actor for compatibility.
|
|
126
|
+
*/
|
|
127
|
+
export declare function requestHumanActorRef(req: IncomingMessage): string;
|
|
104
128
|
export declare function makeRequestWorkspaceId(core: TaskforceCore, context: RouteContext): (req: IncomingMessage) => string;
|
|
105
129
|
export declare function makeResolveAuthenticatedUserId(core: TaskforceCore, context: RouteContext, requestWorkspaceId: (req: IncomingMessage) => string): (req: IncomingMessage) => string;
|
|
106
130
|
export declare function makeResolveAuthenticatedMutationUserId(core: TaskforceCore, context: RouteContext, requestWorkspaceId: (req: IncomingMessage) => string): (req: IncomingMessage) => string;
|