@taskforcehq/taskforce 0.3.330 → 0.3.332
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/README.md +7 -2
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +2 -2
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.js +2 -2
- package/dist/components/features/planning/PlanningModule.js +15 -3
- package/dist/components/features/planning/planningMetadata.d.ts +10 -0
- package/dist/components/features/planning/planningMetadata.js +8 -0
- package/dist/components/views/StandaloneLayout.js +35 -1
- package/dist/components/views/panels/PlanningDrawer.d.ts +2 -1
- package/dist/components/views/panels/PlanningDrawer.js +12 -8
- package/dist/config/envSchema.js +7 -0
- package/dist/core/McpTokenService.d.ts +2 -0
- package/dist/core/McpTokenService.js +13 -5
- package/dist/core/Taskforce.d.ts +11 -3
- package/dist/core/Taskforce.js +33 -3
- package/dist/core/types.d.ts +1 -0
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +2 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +28 -9
- package/dist/mcp/httpProtocolRouting.d.ts +1 -0
- package/dist/mcp/httpProtocolRouting.js +8 -5
- package/dist/mcp/runtime.js +24 -5
- package/dist/mcp/taskPlanningRegistrar.js +2 -2
- package/dist/mcp/toolProfiles.js +1 -0
- package/dist/mcp/toolRegistry.js +8 -8
- package/dist/server/buildInfo.d.ts +1 -0
- package/dist/server/buildInfo.js +5 -1
- package/dist/server/cloudMcpCapacityTelemetry.d.ts +68 -0
- package/dist/server/cloudMcpCapacityTelemetry.js +221 -0
- package/dist/server/localWorkspaceSyncServerRuntime.js +13 -5
- package/dist/server/routes/admin.js +14 -2
- package/dist/server/routes/shared.d.ts +3 -0
- package/dist/server/routes/shared.js +280 -240
- package/dist/service/localServiceCli.d.ts +2 -2
- package/dist/service/localServiceCli.js +4 -3
- package/dist/services/executorPhaseAIssuerCore.d.ts +13 -0
- package/dist/services/executorPhaseAIssuerCore.js +107 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +1 -1
- package/dist/services/taskforceAgentMcpBridge.js +35 -5
- package/dist/shared/executorPhaseASchemaValidation.js +1 -1
- package/dist/storage/postgresAdapter.js +3 -1
- package/dist/storage/postgresRequestDiagnostics.d.ts +9 -1
- package/dist/storage/postgresRequestDiagnostics.js +15 -2
- package/dist/storage/postgresWorker.js +13 -3
- package/dist/storage/postgresWorkerProtocol.d.ts +6 -0
- package/dist/storage/workflowStore.d.ts +2 -0
- package/dist/storage/workflowStore.js +39 -2
- package/dist/sync/cloudSyncApi.js +4 -2
- package/dist/sync/collaborationSyncState.js +5 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunner.d.ts +3 -1
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +18 -0
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
- package/dist/sync/syncApplyHandlers.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.js +72 -15
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +7 -7
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +50 -13
- package/dist/sync/v3/localOutboxRecoveryPreparationService.js +8 -2
- package/dist/sync/v3/localTaskCreateOutboxService.d.ts +3 -1
- package/dist/sync/v3/localTaskCreateOutboxService.js +6 -1
- package/dist/sync/v3/localTaskDeleteOutboxService.js +1 -0
- package/dist/sync/v3/localTaskLifecycleOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskLifecycleOutboxService.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.js +6 -2
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskStatusOutboxService.js +20 -5
- package/dist/sync/v3/syncDurabilityStore.d.ts +2 -1
- package/dist/sync/v3/syncDurabilityStore.js +34 -6
- package/dist/sync/v3/workflowRuntimeMutationService.d.ts +5 -0
- package/dist/sync/v3/workflowRuntimeMutationService.js +124 -71
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +25 -3
- package/dist/sync/v3/workspaceSyncV3Feed.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3Feed.js +6 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +49 -18
- package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.js +50 -5
- package/dist/ui/assets/{AiIdentityRosterCard-OcSz1PkK.js → AiIdentityRosterCard-CDMPtnbC.js} +1 -1
- package/dist/ui/assets/{AiProfilesModule-ByhYW5Xi.js → AiProfilesModule-Cww85C4E.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BTM6nIg_.js → AnnotatedAttachmentWorkspace-Dt9TKYSH.js} +1 -1
- package/dist/ui/assets/{AssetTraySortControl-DabEG8L4.js → AssetTraySortControl-CKgapnxu.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-Coa8yVGM.js → ContextAttachmentManager-T-cEVcOZ.js} +2 -2
- package/dist/ui/assets/{DocumentWorkspace-BB0qhdix.js → DocumentWorkspace-HD4MUjAZ.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-CZ8x4UJE.js → EntityActivityTimeline-_V0zfMuh.js} +1 -1
- package/dist/ui/assets/PlanningModule-mTkBygsB.js +1 -0
- package/dist/ui/assets/{PlansPage-D86pZSSl.js → PlansPage-Btiq06VO.js} +1 -1
- package/dist/ui/assets/{TaskContextUpload-BVIkU9yT.js → TaskContextUpload-C00NVUpn.js} +1 -1
- package/dist/ui/assets/{TaskSettings-Cv1mNhv_.js → TaskSettings-Cp-L-4xh.js} +1 -1
- package/dist/ui/assets/{TaskforceAgentsModule-gmH-PD2q.js → TaskforceAgentsModule-Coq9-PUp.js} +2 -2
- package/dist/ui/assets/{WorkflowManagerModule-DbfnHJ-f.js → WorkflowManagerModule-CuqRzMmM.js} +1 -1
- package/dist/ui/assets/index-DSQINwt9.js +7 -0
- package/dist/ui/index.html +1 -1
- package/package.json +3 -1
- package/dist/ui/assets/PlanningModule-pRPjR7v5.js +0 -1
- package/dist/ui/assets/index-CTsDBaef.js +0 -7
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
|
|
2
|
+
export const TASKFORCE_CAPACITY_TRACE_HEADER = 'x-taskforce-capacity-trace';
|
|
3
|
+
function finite(value) {
|
|
4
|
+
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
5
|
+
}
|
|
6
|
+
function millisecondsFromNanoseconds(value) {
|
|
7
|
+
return finite(value) / 1_000_000;
|
|
8
|
+
}
|
|
9
|
+
function percentile(values, rank) {
|
|
10
|
+
if (values.length === 0)
|
|
11
|
+
return 0;
|
|
12
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
13
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil((rank / 100) * sorted.length) - 1));
|
|
14
|
+
return finite(sorted[index]);
|
|
15
|
+
}
|
|
16
|
+
function classifyOutcome(statusCode, aborted) {
|
|
17
|
+
if (aborted)
|
|
18
|
+
return 'timeout_or_abort';
|
|
19
|
+
if (statusCode >= 500)
|
|
20
|
+
return 'server_error';
|
|
21
|
+
if (statusCode === 401 || statusCode === 403 || statusCode === 405 || statusCode === 415 || statusCode === 429) {
|
|
22
|
+
return 'rejected';
|
|
23
|
+
}
|
|
24
|
+
if (statusCode >= 400)
|
|
25
|
+
return 'client_error';
|
|
26
|
+
return 'success';
|
|
27
|
+
}
|
|
28
|
+
function classifyRejection(statusCode) {
|
|
29
|
+
if (statusCode === 429)
|
|
30
|
+
return 'rate_limited';
|
|
31
|
+
if (statusCode === 401 || statusCode === 403)
|
|
32
|
+
return 'auth_or_scope';
|
|
33
|
+
if (statusCode === 405 || statusCode === 415)
|
|
34
|
+
return 'protocol';
|
|
35
|
+
return statusCode >= 400 && statusCode < 500 ? 'other_client' : null;
|
|
36
|
+
}
|
|
37
|
+
function classifyStatus(statusCode) {
|
|
38
|
+
if (statusCode >= 200 && statusCode < 300)
|
|
39
|
+
return '2xx';
|
|
40
|
+
if (statusCode >= 300 && statusCode < 400)
|
|
41
|
+
return '3xx';
|
|
42
|
+
if (statusCode >= 400 && statusCode < 500)
|
|
43
|
+
return '4xx';
|
|
44
|
+
if (statusCode >= 500 && statusCode < 600)
|
|
45
|
+
return '5xx';
|
|
46
|
+
return 'other';
|
|
47
|
+
}
|
|
48
|
+
export class CloudMcpCapacityTelemetry {
|
|
49
|
+
emit;
|
|
50
|
+
eventLoop;
|
|
51
|
+
now;
|
|
52
|
+
cpuUsage;
|
|
53
|
+
memoryUsage;
|
|
54
|
+
windowMs;
|
|
55
|
+
maxSamples;
|
|
56
|
+
activeRequests = 0;
|
|
57
|
+
maxActiveRequests = 0;
|
|
58
|
+
samples = [];
|
|
59
|
+
completedRequests = 0;
|
|
60
|
+
requestClassCounts = {};
|
|
61
|
+
outcomeCounts = {};
|
|
62
|
+
rejectionCounts = {};
|
|
63
|
+
statusClassCounts = {};
|
|
64
|
+
windowStartedAt;
|
|
65
|
+
windowCpuStartedAt;
|
|
66
|
+
flushTimer;
|
|
67
|
+
constructor(options = {}) {
|
|
68
|
+
this.emit = options.emit || ((payload) => console.info(`[Taskforce MCP] ${JSON.stringify(payload)}`));
|
|
69
|
+
this.eventLoop = options.eventLoop || monitorEventLoopDelay({ resolution: 20 });
|
|
70
|
+
this.now = options.now || (() => performance.now());
|
|
71
|
+
this.cpuUsage = options.cpuUsage || process.cpuUsage.bind(process);
|
|
72
|
+
this.memoryUsage = options.memoryUsage || process.memoryUsage.bind(process);
|
|
73
|
+
this.windowMs = Math.max(1_000, options.windowMs || 60_000);
|
|
74
|
+
this.maxSamples = Math.max(10, options.maxSamples || 2_048);
|
|
75
|
+
this.windowStartedAt = this.now();
|
|
76
|
+
this.windowCpuStartedAt = this.cpuUsage();
|
|
77
|
+
this.eventLoop.enable();
|
|
78
|
+
this.flushTimer = setInterval(() => this.flush(), this.windowMs);
|
|
79
|
+
this.flushTimer.unref?.();
|
|
80
|
+
}
|
|
81
|
+
begin(requestClass = 'other') {
|
|
82
|
+
const startedAt = this.now();
|
|
83
|
+
let settled = false;
|
|
84
|
+
let currentClass = requestClass;
|
|
85
|
+
this.activeRequests += 1;
|
|
86
|
+
this.maxActiveRequests = Math.max(this.maxActiveRequests, this.activeRequests);
|
|
87
|
+
return {
|
|
88
|
+
classify: (nextClass) => {
|
|
89
|
+
currentClass = nextClass;
|
|
90
|
+
},
|
|
91
|
+
setTraceHeaders: (res, postgres) => {
|
|
92
|
+
if (res.headersSent)
|
|
93
|
+
return;
|
|
94
|
+
const memory = this.memoryUsage();
|
|
95
|
+
res.setHeader('X-Taskforce-MCP-Active-Requests', String(this.activeRequests));
|
|
96
|
+
res.setHeader('X-Taskforce-MCP-Max-Active-Requests', String(this.maxActiveRequests));
|
|
97
|
+
res.setHeader('X-Taskforce-Event-Loop-P95-Ms', millisecondsFromNanoseconds(this.eventLoop.percentile(95)).toFixed(2));
|
|
98
|
+
res.setHeader('X-Taskforce-Event-Loop-Max-Ms', millisecondsFromNanoseconds(this.eventLoop.max).toFixed(2));
|
|
99
|
+
res.setHeader('X-Taskforce-Process-RSS-Bytes', String(memory.rss));
|
|
100
|
+
res.setHeader('X-Taskforce-Process-Heap-Used-Bytes', String(memory.heapUsed));
|
|
101
|
+
if (postgres) {
|
|
102
|
+
res.setHeader('X-Taskforce-Postgres-Worker-Wait-Ms', postgres.postgresWorkerWaitMs.toFixed(2));
|
|
103
|
+
res.setHeader('X-Taskforce-Postgres-Worker-Wait-Max-Ms', postgres.postgresWorkerWaitMaxMs.toFixed(2));
|
|
104
|
+
res.setHeader('X-Taskforce-Postgres-Pool-Waiting-Max', String(postgres.postgresPoolWaitingMax));
|
|
105
|
+
res.setHeader('X-Taskforce-Postgres-Pool-Total-Max', String(postgres.postgresPoolTotalMax));
|
|
106
|
+
if (postgres.postgresPoolIdleMin !== null) {
|
|
107
|
+
res.setHeader('X-Taskforce-Postgres-Pool-Idle-Min', String(postgres.postgresPoolIdleMin));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
finish: (input) => {
|
|
112
|
+
if (settled)
|
|
113
|
+
return;
|
|
114
|
+
settled = true;
|
|
115
|
+
this.activeRequests = Math.max(0, this.activeRequests - 1);
|
|
116
|
+
const outcome = classifyOutcome(input.statusCode, input.aborted === true);
|
|
117
|
+
this.completedRequests += 1;
|
|
118
|
+
this.requestClassCounts[currentClass] = (this.requestClassCounts[currentClass] || 0) + 1;
|
|
119
|
+
this.outcomeCounts[outcome] = (this.outcomeCounts[outcome] || 0) + 1;
|
|
120
|
+
const rejection = classifyRejection(input.statusCode);
|
|
121
|
+
if (rejection)
|
|
122
|
+
this.rejectionCounts[rejection] = (this.rejectionCounts[rejection] || 0) + 1;
|
|
123
|
+
const statusClass = classifyStatus(input.statusCode);
|
|
124
|
+
this.statusClassCounts[statusClass] = (this.statusClassCounts[statusClass] || 0) + 1;
|
|
125
|
+
this.samples.push({
|
|
126
|
+
requestClass: currentClass,
|
|
127
|
+
outcome,
|
|
128
|
+
durationMs: finite(this.now() - startedAt),
|
|
129
|
+
postgres: input.postgres,
|
|
130
|
+
});
|
|
131
|
+
if (this.samples.length > this.maxSamples)
|
|
132
|
+
this.samples.splice(0, this.samples.length - this.maxSamples);
|
|
133
|
+
this.flushIfDue();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
snapshot() {
|
|
138
|
+
const memory = this.memoryUsage();
|
|
139
|
+
const durations = this.samples.map((sample) => sample.durationMs);
|
|
140
|
+
const workerWaits = this.samples.map((sample) => sample.postgres?.postgresWorkerWaitMs || 0);
|
|
141
|
+
const cpu = this.cpuUsage(this.windowCpuStartedAt);
|
|
142
|
+
return {
|
|
143
|
+
event: 'cloud_mcp_capacity_window',
|
|
144
|
+
windowMs: Math.max(0, Math.round(this.now() - this.windowStartedAt)),
|
|
145
|
+
requests: this.completedRequests,
|
|
146
|
+
activeRequests: this.activeRequests,
|
|
147
|
+
maxActiveRequests: this.maxActiveRequests,
|
|
148
|
+
requestClasses: { ...this.requestClassCounts },
|
|
149
|
+
outcomes: { ...this.outcomeCounts },
|
|
150
|
+
rejections: { ...this.rejectionCounts },
|
|
151
|
+
statusClasses: { ...this.statusClassCounts },
|
|
152
|
+
durationMs: { p50: percentile(durations, 50), p95: percentile(durations, 95), p99: percentile(durations, 99) },
|
|
153
|
+
eventLoopDelayMs: {
|
|
154
|
+
p50: millisecondsFromNanoseconds(this.eventLoop.percentile(50)),
|
|
155
|
+
p95: millisecondsFromNanoseconds(this.eventLoop.percentile(95)),
|
|
156
|
+
p99: millisecondsFromNanoseconds(this.eventLoop.percentile(99)),
|
|
157
|
+
max: millisecondsFromNanoseconds(this.eventLoop.max),
|
|
158
|
+
},
|
|
159
|
+
process: {
|
|
160
|
+
cpuUserMs: finite(cpu.user / 1_000),
|
|
161
|
+
cpuSystemMs: finite(cpu.system / 1_000),
|
|
162
|
+
rssBytes: memory.rss,
|
|
163
|
+
heapUsedBytes: memory.heapUsed,
|
|
164
|
+
heapTotalBytes: memory.heapTotal,
|
|
165
|
+
externalBytes: memory.external,
|
|
166
|
+
},
|
|
167
|
+
postgres: {
|
|
168
|
+
workerWaitMsP95: percentile(workerWaits, 95),
|
|
169
|
+
maxPoolWaiting: Math.max(0, ...this.samples.map((sample) => sample.postgres?.postgresPoolWaitingMax || 0)),
|
|
170
|
+
maxPoolTotal: Math.max(0, ...this.samples.map((sample) => sample.postgres?.postgresPoolTotalMax || 0)),
|
|
171
|
+
minPoolIdle: this.minimumPoolIdle(),
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
flush() {
|
|
176
|
+
if (this.completedRequests === 0 && this.activeRequests === 0)
|
|
177
|
+
return null;
|
|
178
|
+
const payload = this.snapshot();
|
|
179
|
+
this.emit(payload);
|
|
180
|
+
this.samples = [];
|
|
181
|
+
this.completedRequests = 0;
|
|
182
|
+
this.requestClassCounts = {};
|
|
183
|
+
this.outcomeCounts = {};
|
|
184
|
+
this.rejectionCounts = {};
|
|
185
|
+
this.statusClassCounts = {};
|
|
186
|
+
this.maxActiveRequests = this.activeRequests;
|
|
187
|
+
this.windowStartedAt = this.now();
|
|
188
|
+
this.windowCpuStartedAt = this.cpuUsage();
|
|
189
|
+
this.eventLoop.reset();
|
|
190
|
+
return payload;
|
|
191
|
+
}
|
|
192
|
+
shutdown() {
|
|
193
|
+
clearInterval(this.flushTimer);
|
|
194
|
+
this.eventLoop.disable?.();
|
|
195
|
+
}
|
|
196
|
+
flushIfDue() {
|
|
197
|
+
if (this.now() - this.windowStartedAt >= this.windowMs)
|
|
198
|
+
this.flush();
|
|
199
|
+
}
|
|
200
|
+
minimumPoolIdle() {
|
|
201
|
+
const values = this.samples
|
|
202
|
+
.map((sample) => sample.postgres?.postgresPoolIdleMin)
|
|
203
|
+
.filter((value) => typeof value === 'number');
|
|
204
|
+
return values.length > 0 ? Math.min(...values) : null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
let defaultTelemetry = null;
|
|
208
|
+
export function isCloudMcpCapacityTelemetryEnabled() {
|
|
209
|
+
return process.env.TASKFORCE_CLOUD_MCP_CAPACITY_TELEMETRY !== '0';
|
|
210
|
+
}
|
|
211
|
+
export function beginCloudMcpCapacityRequest(requestClass = 'other') {
|
|
212
|
+
if (!isCloudMcpCapacityTelemetryEnabled()) {
|
|
213
|
+
return {
|
|
214
|
+
classify: (_nextClass) => undefined,
|
|
215
|
+
setTraceHeaders: (_res, _postgres) => undefined,
|
|
216
|
+
finish: (_input) => undefined,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
defaultTelemetry ||= new CloudMcpCapacityTelemetry();
|
|
220
|
+
return defaultTelemetry.begin(requestClass);
|
|
221
|
+
}
|
|
@@ -3,7 +3,7 @@ import { LocalSyncCoordinatorStore } from '../sync/coordinator/localSyncCoordina
|
|
|
3
3
|
import { LocalSyncDispatchTracker } from '../sync/coordinator/localSyncDispatchTracker.js';
|
|
4
4
|
import { LocalSyncExecutionPermitStore } from '../sync/coordinator/localSyncExecutionPermitStore.js';
|
|
5
5
|
import { LocalSyncOwnershipDrainBarrier } from '../sync/coordinator/localSyncOwnershipDrainBarrier.js';
|
|
6
|
-
import { LocalWorkspaceSyncRunner, } from '../sync/coordinator/localWorkspaceSyncRunner.js';
|
|
6
|
+
import { canCoordinatorRecoveryPreemptRetry, LocalWorkspaceSyncRunner, } from '../sync/coordinator/localWorkspaceSyncRunner.js';
|
|
7
7
|
import { reconcileSettledV3DispatchRecoveryObligations, } from '../sync/coordinator/localSyncV3DispatchRecoveryReconciler.js';
|
|
8
8
|
import { LocalWorkspaceSyncRunnerStateStore } from '../sync/coordinator/localWorkspaceSyncRunnerState.js';
|
|
9
9
|
import { WorkspaceSyncCredentialBroker, } from '../sync/coordinator/workspaceSyncCredentialBroker.js';
|
|
@@ -518,7 +518,13 @@ export class LocalWorkspaceSyncServerRuntime {
|
|
|
518
518
|
if (!status.checkpoint?.checkpoint.syncEnabled)
|
|
519
519
|
return;
|
|
520
520
|
const retry = status.checkpoint.checkpoint.retry;
|
|
521
|
-
|
|
521
|
+
const coordinatorRecoveryPlan = this.permitStore.inspectAuthoritativeRepairRecoveryObligations(bundle.context.workspaceId);
|
|
522
|
+
const retryWaiting = Boolean(retry && Date.parse(retry.retryAt) > this.now().getTime());
|
|
523
|
+
const recoveryPreemptsRetry = Boolean(retryWaiting
|
|
524
|
+
&& retry
|
|
525
|
+
&& coordinatorRecoveryPlan.total > 0
|
|
526
|
+
&& canCoordinatorRecoveryPreemptRetry(retry.kind));
|
|
527
|
+
if (retryWaiting && retry && !recoveryPreemptsRetry) {
|
|
522
528
|
// Authoritative repair cannot proceed while covered local
|
|
523
529
|
// operations remain active. Keep settling those operations during
|
|
524
530
|
// the repair backoff window so a dependency that becomes due
|
|
@@ -534,9 +540,8 @@ export class LocalWorkspaceSyncServerRuntime {
|
|
|
534
540
|
}
|
|
535
541
|
return;
|
|
536
542
|
}
|
|
537
|
-
const retryDue = Boolean(retry);
|
|
543
|
+
const retryDue = Boolean(retry) && !recoveryPreemptsRetry;
|
|
538
544
|
const wakeEpoch = this.ordinaryWakeEpoch;
|
|
539
|
-
const coordinatorRecoveryPlan = this.permitStore.inspectAuthoritativeRepairRecoveryObligations(bundle.context.workspaceId);
|
|
540
545
|
if (retry?.kind === 'v2-content-push') {
|
|
541
546
|
// This retry exists specifically to publish content required by a
|
|
542
547
|
// blocked durable operation. Re-dispatching that same operation
|
|
@@ -585,7 +590,10 @@ export class LocalWorkspaceSyncServerRuntime {
|
|
|
585
590
|
|| state.coordinatorGeneration !== bundle.context.coordinatorGeneration) {
|
|
586
591
|
return;
|
|
587
592
|
}
|
|
588
|
-
if (
|
|
593
|
+
if (recoveryPreemptsRetry) {
|
|
594
|
+
await bundle.runner.recoverCoordinatorObligationsDue(() => this.permitStore.inspectAuthoritativeRepairRecoveryObligations(bundle.context.workspaceId).total > 0);
|
|
595
|
+
}
|
|
596
|
+
else if (retryDue) {
|
|
589
597
|
await bundle.runner.retryDue();
|
|
590
598
|
}
|
|
591
599
|
else if (wakeSources.size === 1
|
|
@@ -2432,7 +2432,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2432
2432
|
addRoute('OPTIONS', '/mcp', async (_req, res) => {
|
|
2433
2433
|
res.writeHead(204, {
|
|
2434
2434
|
'Access-Control-Allow-Origin': '*',
|
|
2435
|
-
'Access-Control-Allow-Headers': 'authorization, content-type, accept,
|
|
2435
|
+
'Access-Control-Allow-Headers': 'authorization, content-type, accept, mcp-protocol-version, mcp-method, mcp-name, x-taskforce-workspace-id, x-taskforce-mcp-client-id, x-taskforce-ai-profile-token, x-taskforce-ai-name, x-taskforce-ai-icon, x-taskforce-ai-color, x-taskforce-ai-description, x-taskforce-ai-role, x-taskforce-ai-provider, x-taskforce-ai-model, x-taskforce-ai-surface-type',
|
|
2436
2436
|
'Access-Control-Allow-Methods': 'GET, HEAD, POST, DELETE, OPTIONS'
|
|
2437
2437
|
});
|
|
2438
2438
|
res.end();
|
|
@@ -2440,7 +2440,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2440
2440
|
addRoute('OPTIONS', '/mcp/', async (_req, res) => {
|
|
2441
2441
|
res.writeHead(204, {
|
|
2442
2442
|
'Access-Control-Allow-Origin': '*',
|
|
2443
|
-
'Access-Control-Allow-Headers': 'authorization, content-type, accept,
|
|
2443
|
+
'Access-Control-Allow-Headers': 'authorization, content-type, accept, mcp-protocol-version, mcp-method, mcp-name, x-taskforce-workspace-id, x-taskforce-mcp-client-id, x-taskforce-ai-profile-token, x-taskforce-ai-name, x-taskforce-ai-icon, x-taskforce-ai-color, x-taskforce-ai-description, x-taskforce-ai-role, x-taskforce-ai-provider, x-taskforce-ai-model, x-taskforce-ai-surface-type',
|
|
2444
2444
|
'Access-Control-Allow-Methods': 'GET, HEAD, POST, DELETE, OPTIONS'
|
|
2445
2445
|
});
|
|
2446
2446
|
res.end();
|
|
@@ -2474,6 +2474,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2474
2474
|
token_endpoint_auth_methods_supported: ['none'],
|
|
2475
2475
|
code_challenge_methods_supported: ['S256'],
|
|
2476
2476
|
scopes_supported: ['tasks:read', 'tasks:write', 'workspace:read'],
|
|
2477
|
+
authorization_response_iss_parameter_supported: true,
|
|
2477
2478
|
}));
|
|
2478
2479
|
});
|
|
2479
2480
|
addRoute('HEAD', '/.well-known/oauth-authorization-server', async (req, res) => {
|
|
@@ -2489,6 +2490,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2489
2490
|
token_endpoint_auth_methods_supported: ['none'],
|
|
2490
2491
|
code_challenge_methods_supported: ['S256'],
|
|
2491
2492
|
scopes_supported: ['tasks:read', 'tasks:write', 'workspace:read'],
|
|
2493
|
+
authorization_response_iss_parameter_supported: true,
|
|
2492
2494
|
}));
|
|
2493
2495
|
});
|
|
2494
2496
|
addRoute('GET', '/.well-known/oauth-protected-resource', async (req, res) => {
|
|
@@ -2516,6 +2518,12 @@ export function registerAdminRoutes(deps) {
|
|
|
2516
2518
|
return;
|
|
2517
2519
|
const body = await parseJsonBody(req);
|
|
2518
2520
|
try {
|
|
2521
|
+
const applicationType = (() => {
|
|
2522
|
+
const value = String(body?.application_type || 'web').trim().toLowerCase();
|
|
2523
|
+
if (value === 'native' || value === 'web')
|
|
2524
|
+
return value;
|
|
2525
|
+
throw new TaskforceRuleError('application_type must be native or web', 400, 'MCP_OAUTH_APPLICATION_TYPE_INVALID');
|
|
2526
|
+
})();
|
|
2519
2527
|
const provider = (() => {
|
|
2520
2528
|
const explicit = String(body?.provider || '').trim().toLowerCase();
|
|
2521
2529
|
if (explicit === 'chatgpt' || explicit === 'claude' || explicit === 'grok' || explicit === 'gemini' || explicit === 'mistral' || explicit === 'perplexity')
|
|
@@ -2545,6 +2553,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2545
2553
|
provider,
|
|
2546
2554
|
clientName: body?.client_name,
|
|
2547
2555
|
redirectUris: body?.redirect_uris,
|
|
2556
|
+
applicationType,
|
|
2548
2557
|
productClientId: classifyMcpOAuthClientProduct({
|
|
2549
2558
|
provider,
|
|
2550
2559
|
clientName: body?.client_name,
|
|
@@ -2559,6 +2568,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2559
2568
|
grant_types: ['authorization_code'],
|
|
2560
2569
|
response_types: ['code'],
|
|
2561
2570
|
token_endpoint_auth_method: 'none',
|
|
2571
|
+
application_type: client.applicationType,
|
|
2562
2572
|
scope: 'tasks:read tasks:write workspace:read',
|
|
2563
2573
|
}));
|
|
2564
2574
|
}
|
|
@@ -2715,6 +2725,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2715
2725
|
denyTarget.searchParams.set('error', 'access_denied');
|
|
2716
2726
|
if (state)
|
|
2717
2727
|
denyTarget.searchParams.set('state', state);
|
|
2728
|
+
denyTarget.searchParams.set('iss', resolveMcpOauthIssuer(req));
|
|
2718
2729
|
res.writeHead(302, { Location: denyTarget.toString() });
|
|
2719
2730
|
res.end();
|
|
2720
2731
|
return;
|
|
@@ -2745,6 +2756,7 @@ export function registerAdminRoutes(deps) {
|
|
|
2745
2756
|
approveTarget.searchParams.set('code', issued.code);
|
|
2746
2757
|
if (state)
|
|
2747
2758
|
approveTarget.searchParams.set('state', state);
|
|
2759
|
+
approveTarget.searchParams.set('iss', resolveMcpOauthIssuer(req));
|
|
2748
2760
|
res.writeHead(302, { Location: approveTarget.toString() });
|
|
2749
2761
|
res.end();
|
|
2750
2762
|
}
|
|
@@ -14,6 +14,7 @@ import { type McpRuntimeFactory, type TaskforceMcpServerOptions } from '../../mc
|
|
|
14
14
|
import { type AiProfileBootstrapConfig } from '../../mcp/aiProfileBootstrap.js';
|
|
15
15
|
import type { AiProfileSeatScope } from '../../shared/aiProfileSeatScope.js';
|
|
16
16
|
import { type RequestPerformanceDiagnostics } from '../../storage/postgresRequestDiagnostics.js';
|
|
17
|
+
import { beginCloudMcpCapacityRequest } from '../cloudMcpCapacityTelemetry.js';
|
|
17
18
|
export declare function resolveErrorStatusCode(error: unknown, fallback?: number): number;
|
|
18
19
|
export declare function buildCloudMcpProfileConnectionId(input: {
|
|
19
20
|
oauth?: McpOAuthAccessTokenAuthResult | null;
|
|
@@ -128,6 +129,8 @@ export declare function makeHandleCloudMcpRequest(core: TaskforceCore, deps: {
|
|
|
128
129
|
createMcpServer?: McpRuntimeFactory;
|
|
129
130
|
resolveMcpToolPolicy?: CloudMcpToolPolicyResolver;
|
|
130
131
|
acquireMcpRuntime?: AcquireCloudMcpRuntime;
|
|
132
|
+
capacityTelemetryEnabled?: () => boolean;
|
|
133
|
+
beginCapacityRequest?: typeof beginCloudMcpCapacityRequest;
|
|
131
134
|
}): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
132
135
|
export declare function requestHeader(req: IncomingMessage, name: string, fallback?: string): string;
|
|
133
136
|
export declare function requestWorkspaceHeader(req: IncomingMessage): string;
|