@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/server/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createRoutes, matchRoute } from './routes.js';
|
|
|
16
16
|
import { formatApiErrorResponse } from './routes/primitives.js';
|
|
17
17
|
import { logSlowBootstrapRoute } from './routes/shared.js';
|
|
18
18
|
import { applyCorsHeaders, getAllowedOriginsFromEnv } from './cors.js';
|
|
19
|
-
import { applyPrefixedRateLimitHeaders, applyRateLimitHeaders, InMemoryRateLimiter, resolveRateLimitKey } from './rateLimit.js';
|
|
19
|
+
import { applyPrefixedRateLimitHeaders, applyRateLimitHeaders, InMemoryRateLimiter, isWorkspaceSyncFeedRead, resolveRateLimitKey, } from './rateLimit.js';
|
|
20
20
|
import { isRequestAuthorized, resolveAuthConfigFromEnv, resolveRequestAccess } from './auth.js';
|
|
21
21
|
import { resolveAuthorizedWorkspaceAccess, resolveDisabledAccessError, } from './authorizedWorkspaceAccess.js';
|
|
22
22
|
export { resolveAuthorizedWorkspaceAccess, resolveDisabledAccessError, isWorkspaceAccessRecoveryRoute, } from './authorizedWorkspaceAccess.js';
|
|
@@ -178,6 +178,12 @@ export const TASKFORCE_PROXY_ORIGIN_PROTO_HEADER = 'x-taskforce-origin-proto';
|
|
|
178
178
|
export const TASKFORCE_PROXY_ORIGIN_BASE_URL_HEADER = 'x-taskforce-origin-base-url';
|
|
179
179
|
export const TASKFORCE_PROXY_ORIGIN_RUNTIME_HEADER = 'x-taskforce-origin-runtime';
|
|
180
180
|
export const TASKFORCE_PROXY_CLOUD_ENVIRONMENT_HEADER = 'x-taskforce-cloud-environment';
|
|
181
|
+
export function sanitizeQaRunIdHeader(raw) {
|
|
182
|
+
const normalized = String(Array.isArray(raw) ? raw[0] : raw || '')
|
|
183
|
+
.trim()
|
|
184
|
+
.replace(/[^A-Za-z0-9._-]+/g, '-');
|
|
185
|
+
return normalized.slice(0, 128) || '-';
|
|
186
|
+
}
|
|
181
187
|
function shouldLogRefreshDiagnostic(pathname) {
|
|
182
188
|
return REFRESH_DIAGNOSTIC_PATH_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(prefix));
|
|
183
189
|
}
|
|
@@ -658,10 +664,13 @@ export function createStandaloneServer(options) {
|
|
|
658
664
|
: (runtimeMode === 'cloud');
|
|
659
665
|
const rateLimitLimit = Number.parseInt(process.env.TASKFORCE_API_RATE_LIMIT_MAX_REQUESTS || '', 10) || 120;
|
|
660
666
|
const rateLimitWindowMs = Number.parseInt(process.env.TASKFORCE_API_RATE_LIMIT_WINDOW_MS || '', 10) || 60_000;
|
|
667
|
+
const syncFeedRateLimit = Number.parseInt(process.env.TASKFORCE_SYNC_FEED_RATE_LIMIT_MAX_REQUESTS || '', 10) || 600;
|
|
668
|
+
const syncFeedRateLimitWindowMs = Number.parseInt(process.env.TASKFORCE_SYNC_FEED_RATE_LIMIT_WINDOW_MS || '', 10) || 60_000;
|
|
661
669
|
const webhookRateLimitMaxRequests = Number.parseInt(process.env.TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_MAX_REQUESTS || '', 10) || 600;
|
|
662
670
|
const webhookRateLimitWindowMs = Number.parseInt(process.env.TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_WINDOW_MS || '', 10) || 60_000;
|
|
663
671
|
const maxPayloadBytes = Number.parseInt(process.env.TASKFORCE_MAX_PAYLOAD_BYTES || '', 10) || 1_048_576;
|
|
664
672
|
const rateLimiter = new InMemoryRateLimiter(rateLimitLimit, rateLimitWindowMs);
|
|
673
|
+
const syncFeedRateLimiter = new InMemoryRateLimiter(syncFeedRateLimit, syncFeedRateLimitWindowMs);
|
|
665
674
|
const webhookRateLimiter = new InMemoryRateLimiter(webhookRateLimitMaxRequests, webhookRateLimitWindowMs);
|
|
666
675
|
const resolveAuthRateLimitSettings = () => {
|
|
667
676
|
const auth = core.getGlobalSettings().auth || {};
|
|
@@ -765,7 +774,8 @@ export function createStandaloneServer(options) {
|
|
|
765
774
|
const requestId = String(req.headers['x-request-id'] || req.headers['x-railway-request-id'] || '').trim() || '-';
|
|
766
775
|
const workspaceId = String(req.headers['x-taskforce-workspace-id'] || '').trim() || '-';
|
|
767
776
|
const userId = String(req.headers['x-taskforce-user-id'] || '').trim() || '-';
|
|
768
|
-
|
|
777
|
+
const qaRunId = sanitizeQaRunIdHeader(req.headers['x-taskforce-qa-run-id']);
|
|
778
|
+
console.info(`[Taskforce RefreshDiag] method=${method} path=${pathname} status=${statusCode} durationMs=${durationMs} requestId=${requestId} workspaceId=${workspaceId} userId=${userId} qaRunId=${qaRunId}`);
|
|
769
779
|
};
|
|
770
780
|
// Resolve static files directory at runtime
|
|
771
781
|
// Standalone UI source now lives in apps/app and builds to dist/ui.
|
|
@@ -992,7 +1002,9 @@ export function createStandaloneServer(options) {
|
|
|
992
1002
|
});
|
|
993
1003
|
const rateLimitResult = isBillingWebhook
|
|
994
1004
|
? webhookRateLimiter.check(key)
|
|
995
|
-
:
|
|
1005
|
+
: isWorkspaceSyncFeedRead(req.method, url.pathname)
|
|
1006
|
+
? syncFeedRateLimiter.check(key)
|
|
1007
|
+
: rateLimiter.check(key);
|
|
996
1008
|
if (isBillingWebhook) {
|
|
997
1009
|
applyPrefixedRateLimitHeaders(res, rateLimitResult, 'X-Webhook-RateLimit');
|
|
998
1010
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type LocalServiceCloudTarget } from '../config/localServiceCloudTarget.js';
|
|
2
|
+
import { type LocalServiceRuntimeContract } from '../config/localServiceRuntimeContract.js';
|
|
2
3
|
import { type WindowsAclCommandRunner } from './localServiceWindowsAcl.js';
|
|
3
4
|
export interface LocalServiceDescriptor {
|
|
4
5
|
schemaVersion: 1;
|
|
@@ -11,6 +12,7 @@ export interface LocalServiceDescriptor {
|
|
|
11
12
|
bootstrapCapability: string;
|
|
12
13
|
publishedAt: string;
|
|
13
14
|
cloudTarget?: LocalServiceCloudTarget | null;
|
|
15
|
+
runtimeContract?: LocalServiceRuntimeContract | null;
|
|
14
16
|
}
|
|
15
17
|
export interface LocalServiceLeaseIdentityInput {
|
|
16
18
|
projectRoot: string;
|
|
@@ -30,6 +32,7 @@ export interface LocalServiceDescriptorPublication {
|
|
|
30
32
|
protocolVersion: string;
|
|
31
33
|
appVersion: string;
|
|
32
34
|
cloudTarget: LocalServiceCloudTarget;
|
|
35
|
+
runtimeContract: LocalServiceRuntimeContract;
|
|
33
36
|
}
|
|
34
37
|
export interface LocalServiceLease {
|
|
35
38
|
readonly identity: LocalServiceLeaseIdentity;
|
|
@@ -4,6 +4,7 @@ import * as net from 'net';
|
|
|
4
4
|
import * as os from 'os';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { parseLocalServiceCloudTarget, } from '../config/localServiceCloudTarget.js';
|
|
7
|
+
import { parseLocalServiceRuntimeContract, } from '../config/localServiceRuntimeContract.js';
|
|
7
8
|
import { resolveLocalSyncProcessLockIdentity } from './localDatabaseIdentity.js';
|
|
8
9
|
import { ensureWindowsUserOnlyDirectoryAcl, } from './localServiceWindowsAcl.js';
|
|
9
10
|
const DESCRIPTOR_SCHEMA_VERSION = 1;
|
|
@@ -168,8 +169,14 @@ function parseDescriptor(value, ownershipKey) {
|
|
|
168
169
|
if (value.cloudTarget != null && !cloudTarget) {
|
|
169
170
|
throw new Error('Local service descriptor cloud target is invalid.');
|
|
170
171
|
}
|
|
172
|
+
const runtimeContract = value.runtimeContract == null
|
|
173
|
+
? null
|
|
174
|
+
: parseLocalServiceRuntimeContract(value.runtimeContract);
|
|
175
|
+
if (value.runtimeContract != null && !runtimeContract) {
|
|
176
|
+
throw new Error('Local service descriptor runtime contract is invalid.');
|
|
177
|
+
}
|
|
171
178
|
assertLoopbackEndpoint(value.endpoint);
|
|
172
|
-
return { ...value, cloudTarget };
|
|
179
|
+
return { ...value, cloudTarget, runtimeContract };
|
|
173
180
|
}
|
|
174
181
|
function readOptionalDescriptor(identity) {
|
|
175
182
|
try {
|
|
@@ -371,6 +378,7 @@ export async function acquireLocalServiceLease(input, dependencies = {}) {
|
|
|
371
378
|
bootstrapCapability,
|
|
372
379
|
publishedAt: (dependencies.now?.() || new Date()).toISOString(),
|
|
373
380
|
cloudTarget: publication.cloudTarget,
|
|
381
|
+
runtimeContract: publication.runtimeContract,
|
|
374
382
|
};
|
|
375
383
|
publishJsonFileAtomically(identity.descriptorPath, descriptor, platform);
|
|
376
384
|
return descriptor;
|
|
@@ -15,6 +15,7 @@ import { createWorkspaceSyncEngineServerRunnerOperations } from '../sync/engine/
|
|
|
15
15
|
import { workspaceSyncTransferEvidenceEvent } from '../sync/engine/workspaceSyncTransferEvidence.js';
|
|
16
16
|
import { createWorkspaceSyncEngineServerPushCandidateProvider, createWorkspaceSyncEngineServerSnapshotReader, } from '../sync/engine/workspaceSyncEngineServerSnapshotReader.js';
|
|
17
17
|
import { readWorkspaceSyncLocalOwnershipSnapshot } from '../sync/v3/workspaceSyncLocalOwnershipSnapshot.js';
|
|
18
|
+
import { resolveRequestedCombinedFeedCoverage } from '../sync/v3/combinedFeedCapability.js';
|
|
18
19
|
import { resolveWorkspaceSyncV3CombinedWireContract, } from '../sync/v3/workspaceSyncV3CombinedWireContract.js';
|
|
19
20
|
import { LocalWorkspaceSyncServerOutboxDispatcher, } from '../sync/v3/localWorkspaceSyncServerOutboxDispatcher.js';
|
|
20
21
|
import { readWorkspaceSyncV3LocalOutboxStatus, } from '../sync/v3/workspaceSyncV3LocalOutboxStatus.js';
|
|
@@ -950,15 +951,21 @@ export class LocalWorkspaceSyncServerRuntime {
|
|
|
950
951
|
if (combined.owner === 'v3' && !combinedContract) {
|
|
951
952
|
throw Object.assign(new Error('The persisted combined-feed ownership contract is invalid.'), { code: 'SYNC_RUNNER_V3_OWNERSHIP_INVALID' });
|
|
952
953
|
}
|
|
954
|
+
const desiredCombinedCoverage = combined.owner === 'v3'
|
|
955
|
+
? resolveRequestedCombinedFeedCoverage(this.options.env || process.env)
|
|
956
|
+
: null;
|
|
957
|
+
const desiredCombinedContract = desiredCombinedCoverage
|
|
958
|
+
? resolveWorkspaceSyncV3CombinedWireContract(desiredCombinedCoverage)
|
|
959
|
+
: null;
|
|
953
960
|
return {
|
|
954
961
|
initiativeFeedActivationEnabled: ownership.initiativeProjection.owner === 'v3',
|
|
955
962
|
initiativeProjection: ownership.initiativeProjection,
|
|
956
963
|
combinedFeedActivationEnabled: combined.owner === 'v3',
|
|
957
964
|
combinedProjection: combined,
|
|
958
|
-
...(combinedContract
|
|
965
|
+
...(combinedContract && desiredCombinedContract
|
|
959
966
|
? {
|
|
960
|
-
combinedFeedCoverage:
|
|
961
|
-
combinedFeedExcludedProjections:
|
|
967
|
+
combinedFeedCoverage: desiredCombinedContract.coverage,
|
|
968
|
+
combinedFeedExcludedProjections: desiredCombinedContract.excludedProjections,
|
|
962
969
|
}
|
|
963
970
|
: {}),
|
|
964
971
|
};
|
|
@@ -26,5 +26,6 @@ export declare function resolveRateLimitKey(params: {
|
|
|
26
26
|
sessionId?: string | null;
|
|
27
27
|
userId?: string | null;
|
|
28
28
|
}): string;
|
|
29
|
+
export declare function isWorkspaceSyncFeedRead(method: string | undefined, pathname: string): boolean;
|
|
29
30
|
export declare function applyRateLimitHeaders(res: ServerResponse, result: RateLimitResult): void;
|
|
30
31
|
export declare function applyPrefixedRateLimitHeaders(res: ServerResponse, result: RateLimitResult, prefix: string): void;
|
package/dist/server/rateLimit.js
CHANGED
|
@@ -97,6 +97,10 @@ export function resolveRateLimitKey(params) {
|
|
|
97
97
|
}
|
|
98
98
|
return `ip:${ip}:${method}:${route}`;
|
|
99
99
|
}
|
|
100
|
+
export function isWorkspaceSyncFeedRead(method, pathname) {
|
|
101
|
+
return String(method || 'GET').trim().toUpperCase() === 'GET'
|
|
102
|
+
&& pathname === '/api/taskforce/sync/v3/workspace/feed';
|
|
103
|
+
}
|
|
100
104
|
export function applyRateLimitHeaders(res, result) {
|
|
101
105
|
res.setHeader('X-RateLimit-Limit', String(result.limit));
|
|
102
106
|
res.setHeader('X-RateLimit-Remaining', String(result.remaining));
|
|
@@ -68,23 +68,34 @@ export function createRealtimeSyncWsManager(core, authConfig, logger = console,
|
|
|
68
68
|
}
|
|
69
69
|
connections.delete(ws);
|
|
70
70
|
};
|
|
71
|
-
const
|
|
71
|
+
const resolveSubscriptionAccess = (state, workspaceId) => {
|
|
72
72
|
if (!workspaceId)
|
|
73
|
-
return
|
|
73
|
+
return 'forbidden';
|
|
74
74
|
if (!authConfig.enabled || !authConfig.requiredForApi)
|
|
75
|
-
return
|
|
75
|
+
return 'allowed';
|
|
76
76
|
if (!state.authenticated || !state.userId || state.userId === 'anonymous')
|
|
77
|
-
return
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
return 'forbidden';
|
|
78
|
+
try {
|
|
79
|
+
const resolved = core.resolveUserAccess(state.userId, workspaceId, state.role);
|
|
80
|
+
return resolved?.workspaceId === workspaceId ? 'allowed' : 'forbidden';
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger.warn(`[Taskforce] WebSocket access check unavailable workspace=${workspaceId}: ${String(error instanceof Error ? error.message : error)}`);
|
|
84
|
+
return 'unavailable';
|
|
85
|
+
}
|
|
80
86
|
};
|
|
81
87
|
const closeUnauthorized = (ws, reason) => {
|
|
82
88
|
logger.warn(`[Taskforce] WebSocket rejected: ${reason}`);
|
|
83
89
|
ws.close(1008, reason);
|
|
84
90
|
};
|
|
85
91
|
const subscribeWorkspace = (ws, state, workspaceId) => {
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
const accessDecision = resolveSubscriptionAccess(state, workspaceId);
|
|
93
|
+
if (accessDecision !== 'allowed') {
|
|
94
|
+
send(ws, {
|
|
95
|
+
type: 'taskforce:subscription-error',
|
|
96
|
+
workspaceId,
|
|
97
|
+
reason: accessDecision === 'unavailable' ? 'temporarily-unavailable' : 'forbidden'
|
|
98
|
+
});
|
|
88
99
|
return;
|
|
89
100
|
}
|
|
90
101
|
state.workspaceIds.add(workspaceId);
|
|
@@ -132,8 +143,13 @@ export function createRealtimeSyncWsManager(core, authConfig, logger = console,
|
|
|
132
143
|
send(ws, { type: 'taskforce:replay-error', reason: 'workspace-required' });
|
|
133
144
|
return;
|
|
134
145
|
}
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
const accessDecision = resolveSubscriptionAccess(state, workspaceId);
|
|
147
|
+
if (accessDecision !== 'allowed') {
|
|
148
|
+
send(ws, {
|
|
149
|
+
type: 'taskforce:replay-error',
|
|
150
|
+
workspaceId,
|
|
151
|
+
reason: accessDecision === 'unavailable' ? 'temporarily-unavailable' : 'forbidden'
|
|
152
|
+
});
|
|
137
153
|
return;
|
|
138
154
|
}
|
|
139
155
|
state.workspaceIds.add(workspaceId);
|
|
@@ -191,8 +207,16 @@ export function createRealtimeSyncWsManager(core, authConfig, logger = console,
|
|
|
191
207
|
connections.set(ws, state);
|
|
192
208
|
logger.log(`[Taskforce] WebSocket client connected user=${state.userId}`);
|
|
193
209
|
const initialWorkspaceId = parseWorkspaceId(access.workspaceId);
|
|
194
|
-
if (initialWorkspaceId
|
|
195
|
-
state
|
|
210
|
+
if (initialWorkspaceId) {
|
|
211
|
+
const accessDecision = resolveSubscriptionAccess(state, initialWorkspaceId);
|
|
212
|
+
if (accessDecision === 'unavailable') {
|
|
213
|
+
ws.close(1013, 'access-unavailable');
|
|
214
|
+
removeConnection(ws);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (accessDecision === 'allowed') {
|
|
218
|
+
state.workspaceIds.add(initialWorkspaceId);
|
|
219
|
+
}
|
|
196
220
|
}
|
|
197
221
|
ws.on('message', (rawData) => {
|
|
198
222
|
const payload = parseJson(rawData);
|
|
@@ -1953,6 +1953,7 @@ export function registerAdminRoutes(deps) {
|
|
|
1953
1953
|
workspaceAssetStore,
|
|
1954
1954
|
publishedContentResolver,
|
|
1955
1955
|
repairMissingAssetLinks: true,
|
|
1956
|
+
repairMissingContentFiles: body?.repairMissingContentFiles === true,
|
|
1956
1957
|
aiProfiles: typeof core.listAiProfiles === 'function'
|
|
1957
1958
|
? core.listAiProfiles(workspaceId, { includeArchived: true }) || []
|
|
1958
1959
|
: [],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY,
|
|
1
|
+
import { TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY, } from '../../runtime/appGates.js';
|
|
2
|
+
import { resolveNodeAppGateAccess } from '../../runtime/nodeAppGates.js';
|
|
2
3
|
import { AgentRoleDefinitionValidationError } from '../../shared/agentRoleDefinition.js';
|
|
3
4
|
import { AgentRoleConflictError, AgentRoleNotFoundError, AgentRoleRetiredError, AgentRoleStore, } from '../../storage/agentRoleStore.js';
|
|
4
5
|
import { parseJsonBody } from './primitives.js';
|
|
@@ -128,7 +129,7 @@ export function registerAgentRolesRoutes(deps) {
|
|
|
128
129
|
const guard = (req, res) => {
|
|
129
130
|
if (!ensureAppAccess(req, res))
|
|
130
131
|
return false;
|
|
131
|
-
const gate =
|
|
132
|
+
const gate = resolveNodeAppGateAccess(TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY);
|
|
132
133
|
if (gate.allowed)
|
|
133
134
|
return true;
|
|
134
135
|
writeJson(res, 403, {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY,
|
|
1
|
+
import { TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY, } from '../../runtime/appGates.js';
|
|
2
|
+
import { resolveNodeAppGateAccess } from '../../runtime/nodeAppGates.js';
|
|
2
3
|
import { compileTaskforceAgentSkillPrompt } from '../../shared/taskforceAgentSkill.js';
|
|
3
4
|
import { TaskforceAgentStore } from '../../storage/taskforceAgentStore.js';
|
|
4
5
|
import { TaskforceAgentSkillConflictError, TaskforceAgentSkillStore, } from '../../storage/taskforceAgentSkillStore.js';
|
|
@@ -73,7 +74,7 @@ export function registerAgentSkillsRoutes(deps) {
|
|
|
73
74
|
const ensureFeatureAccess = (req, res) => {
|
|
74
75
|
if (!ensureAppAccess(req, res))
|
|
75
76
|
return false;
|
|
76
|
-
const gate =
|
|
77
|
+
const gate = resolveNodeAppGateAccess(TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY);
|
|
77
78
|
if (gate.allowed)
|
|
78
79
|
return true;
|
|
79
80
|
writeJson(res, 403, {
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
* POST /api/taskforce/agents/model-gateway/test
|
|
12
12
|
* PATCH /api/taskforce/agents/:agentId/conversations/:conversationId
|
|
13
13
|
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
14
15
|
import { assignedAiProfileColor, normalizeAiProfileColor } from '../../shared/aiProfileColors.js';
|
|
15
16
|
import { BedrockAnthropicMessagesModelGateway } from '../../services/bedrockAnthropicMessagesModelGateway.js';
|
|
16
17
|
import { BedrockMantleModelGateway } from '../../services/bedrockMantleModelGateway.js';
|
|
17
18
|
import { BedrockModelGateway } from '../../services/bedrockModelGateway.js';
|
|
19
|
+
import { CodexAppServerModelGateway } from '../../services/codexAppServerModelGateway.js';
|
|
18
20
|
import { saveAiProfileAvatarAssets } from '../aiProfileAvatarAssets.js';
|
|
19
21
|
import { ModelGatewayConfigurationError, normalizePersistedModelGatewayMessages } from '../../services/modelGateway.js';
|
|
20
22
|
import { BedrockAvatarImageProvider, buildTaskforceAgentAvatarPrompt, } from '../../services/taskforceAgentAvatarImageProvider.js';
|
|
@@ -25,10 +27,11 @@ import { TaskforceAgentSkillStore } from '../../storage/taskforceAgentSkillStore
|
|
|
25
27
|
import { WorkflowStore } from '../../storage/workflowStore.js';
|
|
26
28
|
import { WorkspaceSyncDurabilityStore } from '../../sync/v3/syncDurabilityStore.js';
|
|
27
29
|
import { projectLegacyTaskforceAgentResponse } from '../../compat/taskforceAgentDefinitionCompat.js';
|
|
28
|
-
import { TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY,
|
|
30
|
+
import { TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY, } from '../../runtime/appGates.js';
|
|
31
|
+
import { resolveNodeAppGateAccess } from '../../runtime/nodeAppGates.js';
|
|
29
32
|
import { DEFAULT_TASKFORCE_AGENT_MODEL_KEY, DEFAULT_TASKFORCE_AGENT_PROVIDER_KEY, getTaskforceAgentModel, isTaskforceAgentModelVisible, labelForTaskforceAgentModel, labelForTaskforceAgentProvider, normalizeTaskforceAgentModelKey, normalizeTaskforceAgentModelSelection, normalizeTaskforceAgentProviderKey, } from '../../shared/taskforceAgentModels.js';
|
|
30
33
|
import { buildTaskforceAgentRunContext } from '../../shared/taskforceAgentRunContext.js';
|
|
31
|
-
import { inspectAgentDefinition } from '../../shared/taskforceAgentDefinition.js';
|
|
34
|
+
import { inspectAgentDefinition, serializeCanonicalJson } from '../../shared/taskforceAgentDefinition.js';
|
|
32
35
|
import { buildResolvedTaskforceAgentSystemPrompt, resolveTaskforceAgentRuntimeConfiguration, TaskforceAgentRuntimeResolutionError, } from '../../shared/taskforceAgentRuntimeResolution.js';
|
|
33
36
|
import { serializeTaskforceAgentExecutionSnapshot } from '../../shared/taskforceAgentExecutionSnapshot.js';
|
|
34
37
|
import { getTaskReferenceLabel } from '../../utils/taskReferences.js';
|
|
@@ -113,6 +116,11 @@ function normalizeMessages(value, fallbackPrompt) {
|
|
|
113
116
|
return { error: 'Prompt must be 4,000 characters or less.' };
|
|
114
117
|
return { messages: [{ role: 'user', content: prompt }] };
|
|
115
118
|
}
|
|
119
|
+
function buildManagedAgentExecutionRequestId(input) {
|
|
120
|
+
return createHash('sha256')
|
|
121
|
+
.update(serializeCanonicalJson(input))
|
|
122
|
+
.digest('hex');
|
|
123
|
+
}
|
|
116
124
|
function toGatewayMessages(messages) {
|
|
117
125
|
return messages.map((message) => ({
|
|
118
126
|
role: message.role,
|
|
@@ -122,7 +130,13 @@ function toGatewayMessages(messages) {
|
|
|
122
130
|
function requestsWorkflowStepCompletion(messages) {
|
|
123
131
|
const latestUserMessage = [...messages].reverse().find((message) => message.role === 'user');
|
|
124
132
|
const content = String(latestUserMessage?.content || '').trim().toLowerCase();
|
|
125
|
-
return /^(?:please\s+|(?:can|could|would)\s+you\s+)?(?:perform\s+and\s+complete|complete|finish)\s+(?:your|the|this)\s+(?:current\s+)?workflow\s+step[.!?]*$/.test(content);
|
|
133
|
+
return /^(?:(?:hey|hi|hello)\b[^,!?]{0,80},?\s+)?(?:please\s+|(?:can|could|would)\s+you\s+)?(?:perform\s+and\s+complete|complete|finish)\s+(?:your|the|this)\s+(?:current\s+)?workflow\s+step(?:\s+(?:on|for)\s+(?:task\s+)?(?:t-\d+|[a-z0-9][a-z0-9-]{0,119}))?[.!?]*$/.test(content);
|
|
134
|
+
}
|
|
135
|
+
function extractLatestExplicitTaskReference(messages) {
|
|
136
|
+
const latestUserMessage = [...messages].reverse().find((message) => message.role === 'user');
|
|
137
|
+
const matches = String(latestUserMessage?.content || '').match(/\bT-\d+\b/gi) || [];
|
|
138
|
+
const references = [...new Set(matches.map((match) => match.toUpperCase()))];
|
|
139
|
+
return references.length === 1 ? references[0] : null;
|
|
126
140
|
}
|
|
127
141
|
function normalizeTaskId(value) {
|
|
128
142
|
const taskId = String(value || '').trim();
|
|
@@ -144,6 +158,18 @@ function resolveTaskContextTask(core, workspaceId, taskId) {
|
|
|
144
158
|
return core.getTask(taskId, workspaceId)
|
|
145
159
|
|| (typeof core.resolveTaskIdentifier === 'function' ? core.resolveTaskIdentifier(taskId, workspaceId) : null);
|
|
146
160
|
}
|
|
161
|
+
function workflowToolCallsTargetSameTask(core, workspaceId, left, right) {
|
|
162
|
+
const resolveCanonicalId = (value) => {
|
|
163
|
+
const taskId = normalizeTaskId(value);
|
|
164
|
+
if (!taskId)
|
|
165
|
+
return null;
|
|
166
|
+
const task = resolveTaskContextTask(core, workspaceId, taskId);
|
|
167
|
+
return normalizeTaskId(task?.id)?.toLowerCase() || taskId.toLowerCase();
|
|
168
|
+
};
|
|
169
|
+
const leftTaskId = resolveCanonicalId(left);
|
|
170
|
+
const rightTaskId = resolveCanonicalId(right);
|
|
171
|
+
return Boolean(leftTaskId && rightTaskId && leftTaskId === rightTaskId);
|
|
172
|
+
}
|
|
147
173
|
function resolveTaskScopeContext(core, workspaceId, input) {
|
|
148
174
|
const taskId = String(input.taskId || '').trim();
|
|
149
175
|
if (!taskId)
|
|
@@ -244,6 +270,12 @@ function createDefaultModelGateway(selection) {
|
|
|
244
270
|
}
|
|
245
271
|
const source = selection?.modelSource || model?.source || 'bedrock';
|
|
246
272
|
const runtime = selection?.runtime || model?.runtime || 'bedrock-runtime';
|
|
273
|
+
if (source === 'subscription_dev' && runtime === 'subscription-dev') {
|
|
274
|
+
if (selection?.connectionScope !== 'local_dev') {
|
|
275
|
+
throw new ModelGatewayConfigurationError('Subscription-dev models require local_dev connection scope.', 'MODEL_SOURCE_UNAVAILABLE');
|
|
276
|
+
}
|
|
277
|
+
return new CodexAppServerModelGateway();
|
|
278
|
+
}
|
|
247
279
|
if (source !== 'bedrock') {
|
|
248
280
|
throw new ModelGatewayConfigurationError('Selected model source is not configured yet.', 'MODEL_SOURCE_NOT_IMPLEMENTED');
|
|
249
281
|
}
|
|
@@ -693,7 +725,7 @@ export function registerAgentsRoutes(deps) {
|
|
|
693
725
|
const ensureTaskforceAgentsAccess = (req, res) => {
|
|
694
726
|
if (!ensureAppAccess(req, res))
|
|
695
727
|
return false;
|
|
696
|
-
const gate =
|
|
728
|
+
const gate = resolveNodeAppGateAccess(TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY);
|
|
697
729
|
if (gate.allowed)
|
|
698
730
|
return true;
|
|
699
731
|
writeJson(res, 403, {
|
|
@@ -1149,9 +1181,30 @@ export function registerAgentsRoutes(deps) {
|
|
|
1149
1181
|
}
|
|
1150
1182
|
}
|
|
1151
1183
|
const savedConversationTaskId = savedConversation?.taskId ? normalizeTaskId(savedConversation.taskId) : null;
|
|
1152
|
-
const
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1184
|
+
const explicitTaskReference = extractLatestExplicitTaskReference(normalizedMessages.messages);
|
|
1185
|
+
const explicitTask = explicitTaskReference
|
|
1186
|
+
? resolveTaskContextTask(core, workspaceId, explicitTaskReference)
|
|
1187
|
+
: null;
|
|
1188
|
+
if (explicitTaskReference
|
|
1189
|
+
&& !explicitTask
|
|
1190
|
+
&& requestsWorkflowStepCompletion(normalizedMessages.messages)) {
|
|
1191
|
+
writeJson(res, 404, {
|
|
1192
|
+
success: false,
|
|
1193
|
+
error: `Task ${explicitTaskReference} was not found.`,
|
|
1194
|
+
code: 'TASK_NOT_FOUND',
|
|
1195
|
+
});
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
const taskId = normalizeTaskId(explicitTask?.id)
|
|
1199
|
+
|| (explicitTaskReference ? null : hasTaskId ? bodyTaskId : savedConversationTaskId);
|
|
1200
|
+
const taskReference = taskId
|
|
1201
|
+
? normalizeTaskContextValue(getTaskReferenceLabel(explicitTask)
|
|
1202
|
+
|| (explicitTask ? explicitTaskReference : null)
|
|
1203
|
+
|| body?.taskReference)
|
|
1204
|
+
: '';
|
|
1205
|
+
const taskTitle = taskId
|
|
1206
|
+
? normalizeTaskContextValue(explicitTask?.title || body?.taskTitle)
|
|
1207
|
+
: '';
|
|
1155
1208
|
if (resolvedRuntimeConfiguration) {
|
|
1156
1209
|
const definition = resolvedRuntimeConfiguration.definition;
|
|
1157
1210
|
const draftDiffers = ((body?.providerKey !== undefined
|
|
@@ -1272,6 +1325,13 @@ export function registerAgentsRoutes(deps) {
|
|
|
1272
1325
|
allowedMutationTaskId: taskId ?? null,
|
|
1273
1326
|
readOnly: !savedAgent,
|
|
1274
1327
|
toolPolicy: resolvedRuntimeConfiguration?.definition.toolPolicy,
|
|
1328
|
+
executionRequestId: buildManagedAgentExecutionRequestId({
|
|
1329
|
+
workspaceId,
|
|
1330
|
+
agentId: savedAgent?.id || agentId || null,
|
|
1331
|
+
conversationId,
|
|
1332
|
+
taskId: taskId ?? null,
|
|
1333
|
+
messages: normalizedMessages.messages,
|
|
1334
|
+
}),
|
|
1275
1335
|
system: scopedAgentSystem,
|
|
1276
1336
|
});
|
|
1277
1337
|
const executionSnapshot = savedAgent && resolvedRuntimeConfiguration
|
|
@@ -1304,21 +1364,40 @@ export function registerAgentsRoutes(deps) {
|
|
|
1304
1364
|
&& mcpBridge?.availableToolNames.includes('complete_workflow_step')
|
|
1305
1365
|
&& requestsWorkflowStepCompletion(gatewayInput.messages));
|
|
1306
1366
|
let workflowCompletionCorrectionSent = false;
|
|
1367
|
+
let reservedWorkflowCompletionUsed = false;
|
|
1307
1368
|
while (mcpBridge) {
|
|
1308
1369
|
if (requestedToolCall) {
|
|
1309
|
-
|
|
1370
|
+
const activeToolCall = requestedToolCall;
|
|
1371
|
+
const workflowStepAlreadyCompleted = mcpToolResults.some((toolResult) => (toolResult.name === 'complete_workflow_step' && toolResult.ok));
|
|
1372
|
+
const workflowStepLoaded = activeToolCall.name === 'complete_workflow_step'
|
|
1373
|
+
&& mcpToolResults.some((toolResult) => (toolResult.name === 'get_task_workflow'
|
|
1374
|
+
&& toolResult.ok
|
|
1375
|
+
&& workflowToolCallsTargetSameTask(core, workspaceId, toolResult.arguments?.taskId, activeToolCall.arguments?.taskId)));
|
|
1376
|
+
const canUseReservedWorkflowCompletion = Boolean(workflowCompletionRequested
|
|
1377
|
+
&& !workflowStepAlreadyCompleted
|
|
1378
|
+
&& !reservedWorkflowCompletionUsed
|
|
1379
|
+
&& activeToolCall.name === 'complete_workflow_step'
|
|
1380
|
+
&& workflowStepLoaded
|
|
1381
|
+
&& unresolvedFailedMutations.size === 0);
|
|
1382
|
+
if (mcpToolResults.length >= MAX_MCP_TOOL_ITERATIONS
|
|
1383
|
+
&& !canUseReservedWorkflowCompletion)
|
|
1310
1384
|
break;
|
|
1311
|
-
|
|
1312
|
-
|
|
1385
|
+
if (mcpToolResults.length >= MAX_MCP_TOOL_ITERATIONS) {
|
|
1386
|
+
reservedWorkflowCompletionUsed = true;
|
|
1387
|
+
}
|
|
1388
|
+
const blocksWorkflowCompletion = activeToolCall.name === 'complete_workflow_step'
|
|
1389
|
+
&& (!workflowStepLoaded || unresolvedFailedMutations.size > 0);
|
|
1313
1390
|
const unresolvedMutationNames = [...new Set(unresolvedFailedMutations.values())];
|
|
1314
1391
|
const toolResult = blocksWorkflowCompletion
|
|
1315
1392
|
? {
|
|
1316
|
-
name:
|
|
1317
|
-
arguments:
|
|
1393
|
+
name: activeToolCall.name,
|
|
1394
|
+
arguments: activeToolCall.arguments,
|
|
1318
1395
|
ok: false,
|
|
1319
|
-
text:
|
|
1396
|
+
text: !workflowStepLoaded
|
|
1397
|
+
? 'Taskforce refused workflow completion because the current workflow step was not loaded successfully. Call get_task_workflow first, perform its instructions, then retry completion.'
|
|
1398
|
+
: `Taskforce refused workflow completion because these earlier changes failed: ${unresolvedMutationNames.join(', ')}. Retry the failed changes successfully before completing the step.`,
|
|
1320
1399
|
}
|
|
1321
|
-
: await mcpBridge.executeToolCall(
|
|
1400
|
+
: await mcpBridge.executeToolCall(activeToolCall);
|
|
1322
1401
|
mcpToolResults.push(toolResult);
|
|
1323
1402
|
if (isTaskforceAgentMutationToolName(toolResult.name)
|
|
1324
1403
|
&& toolResult.name !== 'complete_workflow_step') {
|
|
@@ -1349,6 +1428,17 @@ export function registerAgentsRoutes(deps) {
|
|
|
1349
1428
|
throw error;
|
|
1350
1429
|
}
|
|
1351
1430
|
requestedToolCall = mcpBridge.parseToolCall(result.text);
|
|
1431
|
+
if (toolResult.name === 'complete_workflow_step'
|
|
1432
|
+
&& toolResult.ok
|
|
1433
|
+
&& requestedToolCall
|
|
1434
|
+
&& mcpToolResults.length >= MAX_MCP_TOOL_ITERATIONS) {
|
|
1435
|
+
result = {
|
|
1436
|
+
...result,
|
|
1437
|
+
text: 'Workflow step completed successfully.',
|
|
1438
|
+
};
|
|
1439
|
+
requestedToolCall = null;
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1352
1442
|
continue;
|
|
1353
1443
|
}
|
|
1354
1444
|
const workflowStepCompleted = mcpToolResults.some((toolResult) => (toolResult.name === 'complete_workflow_step' && toolResult.ok));
|
|
@@ -1358,7 +1448,7 @@ export function registerAgentsRoutes(deps) {
|
|
|
1358
1448
|
role: 'user',
|
|
1359
1449
|
content: unresolvedFailedMutations.size > 0
|
|
1360
1450
|
? `The workflow step is not complete because these Taskforce changes failed: ${[...new Set(unresolvedFailedMutations.values())].join(', ')}. Retry those exact changes successfully before calling complete_workflow_step. If they cannot succeed, respond exactly WORKFLOW_STEP_INCOMPLETE: followed by the reason.`
|
|
1361
|
-
: 'The workflow step is not authoritatively complete.
|
|
1451
|
+
: 'The workflow step is not authoritatively complete. Call get_task_workflow first if it has not succeeded in this response, perform the returned step instructions, then call complete_workflow_step. Otherwise respond exactly WORKFLOW_STEP_INCOMPLETE: followed by the reason. Do not claim completion without successful tool results.',
|
|
1362
1452
|
});
|
|
1363
1453
|
result = await gateway.generate({
|
|
1364
1454
|
...gatewayInput,
|
|
@@ -1370,7 +1460,19 @@ export function registerAgentsRoutes(deps) {
|
|
|
1370
1460
|
break;
|
|
1371
1461
|
}
|
|
1372
1462
|
if (requestedToolCall) {
|
|
1373
|
-
|
|
1463
|
+
mcpToolResults.push({
|
|
1464
|
+
name: requestedToolCall.name,
|
|
1465
|
+
arguments: requestedToolCall.arguments,
|
|
1466
|
+
ok: false,
|
|
1467
|
+
text: 'Taskforce did not execute this call because the per-message tool limit was reached.',
|
|
1468
|
+
});
|
|
1469
|
+
result = {
|
|
1470
|
+
...result,
|
|
1471
|
+
text: workflowCompletionRequested
|
|
1472
|
+
? 'WORKFLOW_STEP_INCOMPLETE: Taskforce reached the per-message tool limit before finishing the workflow step. Review Tool Activity and retry.'
|
|
1473
|
+
: 'Taskforce reached the per-message tool limit. Review Tool Activity and retry.',
|
|
1474
|
+
};
|
|
1475
|
+
requestedToolCall = null;
|
|
1374
1476
|
}
|
|
1375
1477
|
if (workflowCompletionRequested && !mcpToolResults.some((toolResult) => (toolResult.name === 'complete_workflow_step' && toolResult.ok))) {
|
|
1376
1478
|
const incompletePrefix = 'WORKFLOW_STEP_INCOMPLETE:';
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* DELETE /api/taskforce/annotated-attachments/sessions/:id
|
|
12
12
|
* GET /api/taskforce/annotated-attachments/sessions/:id/payload
|
|
13
13
|
*/
|
|
14
|
-
import { ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY,
|
|
14
|
+
import { ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY, } from '../../runtime/appGates.js';
|
|
15
|
+
import { resolveNodeAppGateAccess } from '../../runtime/nodeAppGates.js';
|
|
15
16
|
import { parseJsonBody } from './primitives.js';
|
|
16
17
|
import { resolveErrorStatusCode } from './shared.js';
|
|
17
18
|
import { getImageReferenceLabel, parseImageReference } from '../../utils/imageReferences.js';
|
|
@@ -37,7 +38,7 @@ export function registerAnnotatedAttachmentRoutes(deps) {
|
|
|
37
38
|
const ensureAnnotatedAttachmentAccess = (req, res, workspaceId) => {
|
|
38
39
|
if (!ensureAppAccess(req, res))
|
|
39
40
|
return false;
|
|
40
|
-
const gate =
|
|
41
|
+
const gate = resolveNodeAppGateAccess(ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY);
|
|
41
42
|
if (gate.allowed)
|
|
42
43
|
return true;
|
|
43
44
|
writeJson(res, 403, {
|
|
@@ -60,6 +60,7 @@ import { buildGithubAuthUrl, exchangeGithubCode } from '../auth/providers/github
|
|
|
60
60
|
import { hasValidSession, createClearSessionCookie, createSessionTokenCookie, resolveRequestAccess, resolveSessionCookieAttributesForRequest, } from '../auth.js';
|
|
61
61
|
import { sendEmail } from '../email.js';
|
|
62
62
|
import { resolveDeploymentConfig } from '../../config/deployment.js';
|
|
63
|
+
import { resolveNodeAppGateAccessMap } from '../../runtime/nodeAppGates.js';
|
|
63
64
|
import { resolveTaskMetadataActivationCapability } from '../../sync/v3/taskMetadataActivation.js';
|
|
64
65
|
import { resolveTaskCreateActivationCapability } from '../../sync/v3/taskCreateActivation.js';
|
|
65
66
|
import { resolveTaskStatusActivationCapability } from '../../sync/v3/taskStatusActivation.js';
|
|
@@ -401,7 +402,7 @@ export function registerAuthRoutes(deps) {
|
|
|
401
402
|
: 'Reset your Taskforce password';
|
|
402
403
|
const text = kind === 'verify-email'
|
|
403
404
|
? `Welcome to Taskforce.\n\nYou're almost in. Enter this verification token on the verification screen to activate your account:\n\n${token}\n\nPrefer the faster route? Open the link below to verify your email automatically:\n${link}\n\nIf you didn't create a Taskforce account, you can safely ignore this email.`
|
|
404
|
-
: `
|
|
405
|
+
: `Enter this password reset token on the reset screen:\n\n${token}\n\nOr reset your Taskforce password using this link:\n${link}\n\nIf you did not request this reset, you can ignore this message.`;
|
|
405
406
|
const result = await sendEmail({
|
|
406
407
|
to: email,
|
|
407
408
|
subject,
|
|
@@ -470,6 +471,7 @@ export function registerAuthRoutes(deps) {
|
|
|
470
471
|
authSource: runtimeContract.authSource,
|
|
471
472
|
workspaceMode: runtimeContract.workspaceMode,
|
|
472
473
|
workspaceSwitchingEnabled: runtimeContract.workspaceSwitchingEnabled,
|
|
474
|
+
appGates: resolveNodeAppGateAccessMap(process.env),
|
|
473
475
|
syncV3
|
|
474
476
|
}
|
|
475
477
|
}));
|
|
@@ -10,8 +10,9 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
10
10
|
actorRef: string;
|
|
11
11
|
root: {
|
|
12
12
|
enabled: boolean;
|
|
13
|
+
hasCommittedOperation(): boolean;
|
|
13
14
|
create(candidate: unknown): import("../../core/Taskforce.js").TaskItem;
|
|
14
|
-
update(taskIdInput: string, candidate: unknown, saveSource: "manual" | "autosave", activityAction?: "task-updated" | "task-schedule-changed"): import("../../core/Taskforce.js").TaskItem;
|
|
15
|
+
update(taskIdInput: string, candidate: unknown, saveSource: "manual" | "autosave", activityAction?: "task-updated" | "task-schedule-changed", expectedDetailRevision?: string): import("../../core/Taskforce.js").TaskItem;
|
|
15
16
|
runBatch<T>(routeInput: string, request: unknown, apply: (context: {
|
|
16
17
|
runChild<R>(mutation: () => R): R;
|
|
17
18
|
deferPostCommitNotification(notify: () => void): void;
|
|
@@ -41,6 +42,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
41
42
|
replace(args: {
|
|
42
43
|
taskId: string;
|
|
43
44
|
items: readonly Partial<import("../../core/Taskforce.js").ChecklistItem>[];
|
|
45
|
+
expectedDetailRevision?: string;
|
|
44
46
|
}): {
|
|
45
47
|
state: "pending" | "committed";
|
|
46
48
|
route: "local-outbox" | "cloud-acceptance";
|
|
@@ -54,6 +56,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
54
56
|
taskId: string;
|
|
55
57
|
links: readonly import("../../sync/v3/workspaceSyncV3NestedTaskCodecs.js").WorkspaceSyncV3TaskAttachmentLink[];
|
|
56
58
|
cardCoverAssetId?: string | null;
|
|
59
|
+
expectedDetailRevision?: string;
|
|
57
60
|
}): {
|
|
58
61
|
state: "pending" | "committed";
|
|
59
62
|
route: "local-outbox" | "cloud-acceptance";
|
|
@@ -66,6 +69,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
66
69
|
replace(args: {
|
|
67
70
|
taskId: string;
|
|
68
71
|
items: readonly Partial<import("../../core/Taskforce.js").ChecklistItem>[];
|
|
72
|
+
expectedDetailRevision?: string;
|
|
69
73
|
}): {
|
|
70
74
|
state: "pending" | "committed";
|
|
71
75
|
route: "local-outbox" | "cloud-acceptance";
|
|
@@ -79,6 +83,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
79
83
|
taskId: string;
|
|
80
84
|
links: readonly import("../../sync/v3/workspaceSyncV3NestedTaskCodecs.js").WorkspaceSyncV3TaskAttachmentLink[];
|
|
81
85
|
cardCoverAssetId?: string | null;
|
|
86
|
+
expectedDetailRevision?: string;
|
|
82
87
|
}): {
|
|
83
88
|
state: "pending" | "committed";
|
|
84
89
|
route: "local-outbox" | "cloud-acceptance";
|
|
@@ -134,6 +139,7 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
134
139
|
}>, options?: {
|
|
135
140
|
saveSource?: "manual" | "autosave";
|
|
136
141
|
atomicItemFailures?: boolean;
|
|
142
|
+
expectedDetailRevision?: string;
|
|
137
143
|
}): {
|
|
138
144
|
preview: import("../../core/Taskforce.js").BulkUpdateFieldsPreview;
|
|
139
145
|
results: import("../../core/Taskforce.js").TaskItem[];
|
|
@@ -149,8 +155,9 @@ export declare function createTaskHttpDurableRouters(input: {
|
|
|
149
155
|
};
|
|
150
156
|
createRootRouter: (operationId?: string, deferPostCommitNotification?: (notify: () => void) => void) => {
|
|
151
157
|
enabled: boolean;
|
|
158
|
+
hasCommittedOperation(): boolean;
|
|
152
159
|
create(candidate: unknown): import("../../core/Taskforce.js").TaskItem;
|
|
153
|
-
update(taskIdInput: string, candidate: unknown, saveSource: "manual" | "autosave", activityAction?: "task-updated" | "task-schedule-changed"): import("../../core/Taskforce.js").TaskItem;
|
|
160
|
+
update(taskIdInput: string, candidate: unknown, saveSource: "manual" | "autosave", activityAction?: "task-updated" | "task-schedule-changed", expectedDetailRevision?: string): import("../../core/Taskforce.js").TaskItem;
|
|
154
161
|
runBatch<T>(routeInput: string, request: unknown, apply: (context: {
|
|
155
162
|
runChild<R>(mutation: () => R): R;
|
|
156
163
|
deferPostCommitNotification(notify: () => void): void;
|