@taskforcehq/taskforce 0.3.330 → 0.3.333

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.
Files changed (107) hide show
  1. package/README.md +7 -2
  2. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +2 -2
  3. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.js +2 -2
  4. package/dist/components/features/planning/PlanningModule.js +15 -3
  5. package/dist/components/features/planning/planningMetadata.d.ts +10 -0
  6. package/dist/components/features/planning/planningMetadata.js +8 -0
  7. package/dist/components/views/StandaloneLayout.js +35 -1
  8. package/dist/components/views/panels/PlanningDrawer.d.ts +2 -1
  9. package/dist/components/views/panels/PlanningDrawer.js +12 -8
  10. package/dist/config/envSchema.js +7 -0
  11. package/dist/core/McpTokenService.d.ts +2 -0
  12. package/dist/core/McpTokenService.js +13 -5
  13. package/dist/core/Taskforce.d.ts +11 -3
  14. package/dist/core/Taskforce.js +33 -3
  15. package/dist/core/types.d.ts +1 -0
  16. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +2 -0
  17. package/dist/documentReviews/DocumentReviewCommentStore.js +28 -9
  18. package/dist/mcp/httpProtocolRouting.d.ts +1 -0
  19. package/dist/mcp/httpProtocolRouting.js +8 -5
  20. package/dist/mcp/runtime.js +24 -5
  21. package/dist/mcp/taskPlanningRegistrar.js +2 -2
  22. package/dist/mcp/toolProfiles.js +1 -0
  23. package/dist/mcp/toolRegistry.js +8 -8
  24. package/dist/server/buildInfo.d.ts +1 -0
  25. package/dist/server/buildInfo.js +5 -1
  26. package/dist/server/cloudMcpCapacityTelemetry.d.ts +68 -0
  27. package/dist/server/cloudMcpCapacityTelemetry.js +221 -0
  28. package/dist/server/localWorkspaceSyncServerRuntime.js +13 -5
  29. package/dist/server/routes/admin.js +14 -2
  30. package/dist/server/routes/localService.js +1 -0
  31. package/dist/server/routes/shared.d.ts +3 -0
  32. package/dist/server/routes/shared.js +280 -240
  33. package/dist/server/routes/syncPushRoutes.d.ts +3 -0
  34. package/dist/server/routes/syncPushRoutes.js +8 -2
  35. package/dist/service/localServiceCli.d.ts +2 -2
  36. package/dist/service/localServiceCli.js +4 -3
  37. package/dist/services/executorPhaseAIssuerCore.d.ts +13 -0
  38. package/dist/services/executorPhaseAIssuerCore.js +107 -0
  39. package/dist/services/taskforceAgentMcpBridge.d.ts +1 -1
  40. package/dist/services/taskforceAgentMcpBridge.js +35 -5
  41. package/dist/shared/executorPhaseASchemaValidation.js +1 -1
  42. package/dist/storage/postgresAdapter.js +3 -1
  43. package/dist/storage/postgresRequestDiagnostics.d.ts +9 -1
  44. package/dist/storage/postgresRequestDiagnostics.js +15 -2
  45. package/dist/storage/postgresWorker.js +13 -3
  46. package/dist/storage/postgresWorkerProtocol.d.ts +6 -0
  47. package/dist/storage/workflowStore.d.ts +2 -0
  48. package/dist/storage/workflowStore.js +39 -2
  49. package/dist/sync/cloudSyncApi.js +4 -2
  50. package/dist/sync/collaborationSyncState.js +5 -0
  51. package/dist/sync/coordinator/localWorkspaceSyncRunner.d.ts +3 -1
  52. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +18 -0
  53. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +1 -0
  54. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +7 -6
  55. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +2 -2
  56. package/dist/sync/engine/workspaceSyncEngineLocalApply.d.ts +1 -0
  57. package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +6 -0
  58. package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +34 -1
  59. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +2 -2
  60. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +4 -1
  61. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
  62. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +55 -10
  63. package/dist/sync/syncApplyHandlers.d.ts +1 -0
  64. package/dist/sync/syncApplyHandlers.js +72 -15
  65. package/dist/sync/syncService.d.ts +1 -0
  66. package/dist/sync/syncService.js +5 -2
  67. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +7 -7
  68. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +50 -13
  69. package/dist/sync/v3/localOutboxRecoveryPreparationService.js +8 -2
  70. package/dist/sync/v3/localTaskCreateOutboxService.d.ts +3 -1
  71. package/dist/sync/v3/localTaskCreateOutboxService.js +6 -1
  72. package/dist/sync/v3/localTaskDeleteOutboxService.js +1 -0
  73. package/dist/sync/v3/localTaskLifecycleOutboxService.d.ts +2 -1
  74. package/dist/sync/v3/localTaskLifecycleOutboxService.js +5 -1
  75. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -1
  76. package/dist/sync/v3/localTaskMetadataOutboxService.js +6 -2
  77. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -1
  78. package/dist/sync/v3/localTaskStatusOutboxService.js +20 -5
  79. package/dist/sync/v3/syncDurabilityStore.d.ts +2 -1
  80. package/dist/sync/v3/syncDurabilityStore.js +34 -6
  81. package/dist/sync/v3/workflowRuntimeMutationService.d.ts +5 -0
  82. package/dist/sync/v3/workflowRuntimeMutationService.js +124 -71
  83. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +25 -3
  84. package/dist/sync/v3/workspaceSyncV3Feed.d.ts +1 -0
  85. package/dist/sync/v3/workspaceSyncV3Feed.js +6 -1
  86. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +2 -0
  87. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +49 -18
  88. package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.d.ts +1 -0
  89. package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.js +50 -5
  90. package/dist/ui/assets/{AiIdentityRosterCard-OcSz1PkK.js → AiIdentityRosterCard-Cfvb9Nuv.js} +1 -1
  91. package/dist/ui/assets/{AiProfilesModule-ByhYW5Xi.js → AiProfilesModule-0oR70eFG.js} +1 -1
  92. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BTM6nIg_.js → AnnotatedAttachmentWorkspace-C33iH_r_.js} +1 -1
  93. package/dist/ui/assets/{AssetTraySortControl-DabEG8L4.js → AssetTraySortControl-BAqxUtke.js} +1 -1
  94. package/dist/ui/assets/{ContextAttachmentManager-Coa8yVGM.js → ContextAttachmentManager-BudIbOR9.js} +2 -2
  95. package/dist/ui/assets/{DocumentWorkspace-BB0qhdix.js → DocumentWorkspace-A8dFQyId.js} +1 -1
  96. package/dist/ui/assets/{EntityActivityTimeline-CZ8x4UJE.js → EntityActivityTimeline-g7vtdsx1.js} +1 -1
  97. package/dist/ui/assets/PlanningModule-D-DeQs4F.js +1 -0
  98. package/dist/ui/assets/{PlansPage-D86pZSSl.js → PlansPage-BWmfGi0Z.js} +1 -1
  99. package/dist/ui/assets/{TaskContextUpload-BVIkU9yT.js → TaskContextUpload-BA_lQmS9.js} +1 -1
  100. package/dist/ui/assets/{TaskSettings-Cv1mNhv_.js → TaskSettings-DAlH4YIP.js} +1 -1
  101. package/dist/ui/assets/{TaskforceAgentsModule-gmH-PD2q.js → TaskforceAgentsModule-DmeCf6pJ.js} +2 -2
  102. package/dist/ui/assets/{WorkflowManagerModule-DbfnHJ-f.js → WorkflowManagerModule-p08eDOaa.js} +1 -1
  103. package/dist/ui/assets/index-GHJgcW3j.js +7 -0
  104. package/dist/ui/index.html +1 -1
  105. package/package.json +3 -1
  106. package/dist/ui/assets/PlanningModule-pRPjR7v5.js +0 -1
  107. 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
- if (retry && Date.parse(retry.retryAt) > this.now().getTime()) {
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 (retryDue) {
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, last-event-id, mcp-protocol-version, mcp-session-id, 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',
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, last-event-id, mcp-protocol-version, mcp-session-id, 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',
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
  }
@@ -437,6 +437,7 @@ export function registerLocalServiceRoutes(deps) {
437
437
  workspaceId: session.workspaceId,
438
438
  core: deps.core,
439
439
  serverName: 'taskforce-local-service',
440
+ version: context.appVersion,
440
441
  logStartup: false,
441
442
  runtimeMode: 'local',
442
443
  operationIdentityScope: {
@@ -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;