@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
|
@@ -6,11 +6,42 @@ import { buildTaskActivityJournalEntry, serializeTaskActivityForSync, } from './
|
|
|
6
6
|
import { fingerprintWorkspaceSyncPayload, WORKSPACE_SYNC_CONTRACT_VERSION, } from './syncDurabilityStore.js';
|
|
7
7
|
import { buildWorkflowRuntimeEventId, buildWorkflowRuntimeTaskActivityId, } from './workflowRuntimeOperationIdentity.js';
|
|
8
8
|
import { fingerprintWorkflowRuntimePayload, serializeWorkflowExecutionEventSyncPayload, serializeWorkflowExecutionRootSyncPayload, validateWorkflowRuntimeAuthoritativeResult, validateWorkflowRuntimeMutationCommand, } from './workflowRuntimeSync.js';
|
|
9
|
+
export class WorkflowRuntimeSemanticReplayError extends Error {
|
|
10
|
+
code = 'WORKFLOW_RUNTIME_SEMANTIC_REPLAY_REQUIRED';
|
|
11
|
+
statusCode = 409;
|
|
12
|
+
constructor() {
|
|
13
|
+
super('This workflow command was already completed by another synchronized runtime.');
|
|
14
|
+
this.name = 'WorkflowRuntimeSemanticReplayError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function hasEquivalentCompletedStep(input) {
|
|
18
|
+
if (input.command.kind !== 'complete-step')
|
|
19
|
+
return false;
|
|
20
|
+
const events = input.workflowStore.listExecutionEvents(input.workspaceId, input.command.executionId);
|
|
21
|
+
const completed = events.find((event) => (event.idempotencyKey === `${input.command.idempotencyKey}:step-completed`));
|
|
22
|
+
const terminal = events.find((event) => (event.idempotencyKey === `${input.command.idempotencyKey}:step-started`
|
|
23
|
+
|| event.idempotencyKey === `${input.command.idempotencyKey}:execution-completed`));
|
|
24
|
+
if (!completed || !terminal)
|
|
25
|
+
return false;
|
|
26
|
+
const evidence = completed.details.evidence;
|
|
27
|
+
const evidenceCommentId = evidence
|
|
28
|
+
&& typeof evidence === 'object'
|
|
29
|
+
&& !Array.isArray(evidence)
|
|
30
|
+
&& evidence.type === 'comment'
|
|
31
|
+
? String(evidence.commentId || '')
|
|
32
|
+
: '';
|
|
33
|
+
return completed.actorId === input.command.actorId
|
|
34
|
+
&& terminal.actorId === input.command.actorId
|
|
35
|
+
&& completed.stepId === input.command.expectedStepId
|
|
36
|
+
&& evidenceCommentId === (input.command.evidenceCommentId || '');
|
|
37
|
+
}
|
|
9
38
|
export function prepareWorkflowRuntimeMutation(input) {
|
|
10
39
|
const before = input.workflowStore.getExecutionStateSnapshot(input.workspaceId, input.command.executionId);
|
|
11
40
|
if (!before)
|
|
12
41
|
throw new Error('Workflow execution not found.');
|
|
13
42
|
if (before.execution.status !== input.command.expectedStatus) {
|
|
43
|
+
if (hasEquivalentCompletedStep(input))
|
|
44
|
+
throw new WorkflowRuntimeSemanticReplayError();
|
|
14
45
|
throw new Error('Workflow execution has advanced beyond the expected status.');
|
|
15
46
|
}
|
|
16
47
|
if (input.command.kind === 'complete-step' && input.command.taskTarget.taskId !== before.execution.taskId) {
|
|
@@ -191,13 +222,25 @@ export function runDurableWorkflowRuntimeMutation(input) {
|
|
|
191
222
|
response: JSON.parse(reservation.responseJson),
|
|
192
223
|
};
|
|
193
224
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
225
|
+
let prepared;
|
|
226
|
+
try {
|
|
227
|
+
prepared = prepareWorkflowRuntimeMutation({
|
|
228
|
+
workflowStore,
|
|
229
|
+
workspaceId: input.workspaceId,
|
|
230
|
+
clientId: input.clientId,
|
|
231
|
+
operationId: input.operationId,
|
|
232
|
+
command,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
input.store.releaseFailedOperationReservation({
|
|
237
|
+
identity,
|
|
238
|
+
requestFingerprint,
|
|
239
|
+
leaseOwner: reservation.leaseOwner,
|
|
240
|
+
now: input.now,
|
|
241
|
+
});
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
201
244
|
const { before, willReassignTask, taskActivityId, eventIds } = prepared;
|
|
202
245
|
const readTaskActivity = (result) => {
|
|
203
246
|
const activity = result.taskActivity || input.core.getTaskEventForSync({
|
|
@@ -262,73 +305,83 @@ export function runDurableWorkflowRuntimeMutation(input) {
|
|
|
262
305
|
},
|
|
263
306
|
})),
|
|
264
307
|
];
|
|
265
|
-
const accepted =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
308
|
+
const accepted = (() => {
|
|
309
|
+
try {
|
|
310
|
+
return input.store.acceptReservedOperationBatch({
|
|
311
|
+
identity,
|
|
312
|
+
requestFingerprint,
|
|
313
|
+
leaseOwner: reservation.leaseOwner,
|
|
314
|
+
now: input.now,
|
|
315
|
+
entries,
|
|
316
|
+
applyMutation: () => applyPreparedWorkflowRuntimeMutation({
|
|
317
|
+
core: input.core,
|
|
318
|
+
workflowStore,
|
|
319
|
+
workspaceId: input.workspaceId,
|
|
320
|
+
command,
|
|
321
|
+
prepared,
|
|
322
|
+
now: input.now,
|
|
323
|
+
}),
|
|
324
|
+
buildResponse: (result, revisions) => {
|
|
325
|
+
const executionPayload = serializeWorkflowExecutionRootSyncPayload(result.executionResult.execution);
|
|
326
|
+
const eventPayloads = result.executionResult.events.map(serializeWorkflowExecutionEventSyncPayload);
|
|
327
|
+
const projectionRevisions = [
|
|
328
|
+
{
|
|
329
|
+
projection: 'workflow-execution/root-v1',
|
|
330
|
+
...revisionFor(revisions, 'workflow-execution', command.executionId),
|
|
331
|
+
canonicalFingerprint: fingerprintWorkflowRuntimePayload(executionPayload),
|
|
332
|
+
},
|
|
333
|
+
...result.newEvents.map((event) => {
|
|
334
|
+
const payload = serializeWorkflowExecutionEventSyncPayload(event);
|
|
335
|
+
return {
|
|
336
|
+
projection: 'workflow-execution-event/full-v1',
|
|
337
|
+
...revisionFor(revisions, 'workflow-execution-event', event.id),
|
|
338
|
+
canonicalFingerprint: fingerprintWorkflowRuntimePayload(payload),
|
|
339
|
+
};
|
|
340
|
+
}),
|
|
341
|
+
...(result.task ? [{
|
|
342
|
+
projection: 'task/root-v1',
|
|
343
|
+
...revisionFor(revisions, 'task', result.task.id),
|
|
344
|
+
canonicalFingerprint: fingerprintWorkspaceSyncPayload(serializeTaskForSync(result.task)),
|
|
345
|
+
}, {
|
|
346
|
+
projection: 'entity-event/full-v1',
|
|
347
|
+
...revisionFor(revisions, 'entity-event', readTaskActivity(result).id),
|
|
348
|
+
canonicalFingerprint: fingerprintWorkspaceSyncPayload(serializeTaskActivityForSync(readTaskActivity(result))),
|
|
349
|
+
}] : []),
|
|
350
|
+
].sort((left, right) => BigInt(left.sequence) < BigInt(right.sequence) ? -1 : 1);
|
|
351
|
+
const authoritative = validateWorkflowRuntimeAuthoritativeResult({
|
|
352
|
+
execution: executionPayload,
|
|
353
|
+
events: eventPayloads,
|
|
354
|
+
acceptedEventIds: result.newEvents.map((event) => event.id),
|
|
355
|
+
...(result.task ? {
|
|
356
|
+
task: {
|
|
357
|
+
payload: serializeTaskForSync(result.task),
|
|
358
|
+
activity: serializeTaskActivityForSync(readTaskActivity(result)),
|
|
359
|
+
},
|
|
360
|
+
} : {}),
|
|
361
|
+
revisions: projectionRevisions,
|
|
362
|
+
replayed: result.executionResult.replayed,
|
|
363
|
+
});
|
|
364
|
+
const executionRevision = revisionFor(revisions, 'workflow-execution', command.executionId);
|
|
290
365
|
return {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
366
|
+
success: true,
|
|
367
|
+
contractVersion: WORKSPACE_SYNC_CONTRACT_VERSION,
|
|
368
|
+
workspaceId: input.workspaceId,
|
|
369
|
+
clientId: input.clientId,
|
|
370
|
+
operationId: input.operationId,
|
|
371
|
+
acceptedSequence: executionRevision.sequence,
|
|
372
|
+
operationHighWatermark: revisions.at(-1)?.sequence || executionRevision.sequence,
|
|
373
|
+
authoritative,
|
|
294
374
|
};
|
|
295
|
-
}
|
|
296
|
-
...(result.task ? [{
|
|
297
|
-
projection: 'task/root-v1',
|
|
298
|
-
...revisionFor(revisions, 'task', result.task.id),
|
|
299
|
-
canonicalFingerprint: fingerprintWorkspaceSyncPayload(serializeTaskForSync(result.task)),
|
|
300
|
-
}, {
|
|
301
|
-
projection: 'entity-event/full-v1',
|
|
302
|
-
...revisionFor(revisions, 'entity-event', readTaskActivity(result).id),
|
|
303
|
-
canonicalFingerprint: fingerprintWorkspaceSyncPayload(serializeTaskActivityForSync(readTaskActivity(result))),
|
|
304
|
-
}] : []),
|
|
305
|
-
].sort((left, right) => BigInt(left.sequence) < BigInt(right.sequence) ? -1 : 1);
|
|
306
|
-
const authoritative = validateWorkflowRuntimeAuthoritativeResult({
|
|
307
|
-
execution: executionPayload,
|
|
308
|
-
events: eventPayloads,
|
|
309
|
-
acceptedEventIds: result.newEvents.map((event) => event.id),
|
|
310
|
-
...(result.task ? {
|
|
311
|
-
task: {
|
|
312
|
-
payload: serializeTaskForSync(result.task),
|
|
313
|
-
activity: serializeTaskActivityForSync(readTaskActivity(result)),
|
|
314
|
-
},
|
|
315
|
-
} : {}),
|
|
316
|
-
revisions: projectionRevisions,
|
|
317
|
-
replayed: result.executionResult.replayed,
|
|
375
|
+
},
|
|
318
376
|
});
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
operationHighWatermark: revisions.at(-1)?.sequence || executionRevision.sequence,
|
|
328
|
-
authoritative,
|
|
329
|
-
};
|
|
330
|
-
},
|
|
331
|
-
});
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
if (hasEquivalentCompletedStep({ workflowStore, workspaceId: input.workspaceId, command })) {
|
|
380
|
+
throw new WorkflowRuntimeSemanticReplayError();
|
|
381
|
+
}
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
332
385
|
const notify = (callback) => {
|
|
333
386
|
if (!callback)
|
|
334
387
|
return;
|
|
@@ -13,7 +13,7 @@ import { enqueueLocalTaskCommentReconciliation } from './localTaskCommentOutboxS
|
|
|
13
13
|
import { buildWorkspaceSyncV3TaskCommentReconcileProvenance, indexWorkspaceSyncV3TaskCommentProvenanceEvidence, } from './taskCommentReconcileProvenance.js';
|
|
14
14
|
import { serializeTaskRootForSync, } from './workspaceSyncV3TaskRootProjection.js';
|
|
15
15
|
import { prepareWorkspaceSyncV3WorkflowRuntimeApply } from './workspaceSyncV3WorkflowRuntimeApply.js';
|
|
16
|
-
import { serializeWorkflowExecutionEventSyncPayload, serializeWorkflowExecutionRootSyncPayload, serializeWorkflowPublishedDefinitionSyncPayload, } from './workflowRuntimeSync.js';
|
|
16
|
+
import { serializeWorkflowExecutionEventSyncPayload, serializeWorkflowExecutionRootSyncPayload, serializeWorkflowPublishedDefinitionSyncPayload, validateWorkflowExecutionEventSyncPayload, } from './workflowRuntimeSync.js';
|
|
17
17
|
import { serializeWorkstreamTaskOrderForSync } from './workstreamTaskOrderSync.js';
|
|
18
18
|
import { validateWorkspaceSyncV3WorkstreamTaskOrderFeedEntry } from './workspaceSyncV3WorkstreamTaskOrderProjection.js';
|
|
19
19
|
function combinedRepairBaselineMarker(page) {
|
|
@@ -198,6 +198,9 @@ function applyEntries(input) {
|
|
|
198
198
|
store: new WorkflowStore(input.core.getDatabaseAdapter(), input.tenantId),
|
|
199
199
|
workspaceId: input.workspaceId,
|
|
200
200
|
entries: input.entries,
|
|
201
|
+
replaceAuthoritativeHistoryExecutionIds: input.allowAuthoritativeRevisionReset
|
|
202
|
+
? input.authoritativeWorkflowRecoveryExecutionIds
|
|
203
|
+
: undefined,
|
|
201
204
|
});
|
|
202
205
|
return store.runWorkspaceDomainCoexistenceTransaction({
|
|
203
206
|
tenantId: input.tenantId,
|
|
@@ -1011,7 +1014,7 @@ export function applyWorkspaceSyncV3CombinedRepairPage(input) {
|
|
|
1011
1014
|
: [];
|
|
1012
1015
|
const selectRecoveryEntries = (entries) => {
|
|
1013
1016
|
const byTarget = new Map(entries.map((entry) => [`${entry.domain}\0${entry.entityId}`, entry]));
|
|
1014
|
-
|
|
1017
|
+
const selected = recoveryTargets.map((target) => {
|
|
1015
1018
|
const entry = byTarget.get(`${target.domain}\0${target.entityId}`);
|
|
1016
1019
|
if (!entry) {
|
|
1017
1020
|
if (!target.required)
|
|
@@ -1023,12 +1026,28 @@ export function applyWorkspaceSyncV3CombinedRepairPage(input) {
|
|
|
1023
1026
|
|| (target.domain === 'task-attachment-links'
|
|
1024
1027
|
&& (entry.projection === 'task-attachment-links/full-v1'
|
|
1025
1028
|
|| entry.projection === 'task-attachment-links/full-v2'))
|
|
1026
|
-
|| (target.domain === 'entity-event' && entry.projection === 'entity-event/full-v1')
|
|
1029
|
+
|| (target.domain === 'entity-event' && entry.projection === 'entity-event/full-v1')
|
|
1030
|
+
|| (target.domain === 'workflow-execution'
|
|
1031
|
+
&& entry.projection === 'workflow-execution/root-v1'));
|
|
1027
1032
|
if (!supportedTarget) {
|
|
1028
1033
|
throw new Error('Targeted workspace sync recovery does not support this projection.');
|
|
1029
1034
|
}
|
|
1030
1035
|
return entry;
|
|
1031
1036
|
}).filter((entry) => entry !== null);
|
|
1037
|
+
const workflowExecutionIds = new Set(selected
|
|
1038
|
+
.filter((entry) => entry.projection === 'workflow-execution/root-v1')
|
|
1039
|
+
.map((entry) => entry.entityId));
|
|
1040
|
+
if (workflowExecutionIds.size > 0) {
|
|
1041
|
+
for (const entry of entries) {
|
|
1042
|
+
if (entry.projection !== 'workflow-execution-event/full-v1')
|
|
1043
|
+
continue;
|
|
1044
|
+
const payload = validateWorkflowExecutionEventSyncPayload(entry.payload);
|
|
1045
|
+
if (!workflowExecutionIds.has(payload.event.workflowExecutionId))
|
|
1046
|
+
continue;
|
|
1047
|
+
selected.push(entry);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return selected;
|
|
1032
1051
|
};
|
|
1033
1052
|
const projections = coveredProjections(input.page);
|
|
1034
1053
|
let notices = notifications();
|
|
@@ -1056,6 +1075,9 @@ export function applyWorkspaceSyncV3CombinedRepairPage(input) {
|
|
|
1056
1075
|
authoritativeEntityEventRecoveryIds: new Set(recoveryTargets
|
|
1057
1076
|
.filter((target) => target.domain === 'entity-event')
|
|
1058
1077
|
.map((target) => target.entityId)),
|
|
1078
|
+
authoritativeWorkflowRecoveryExecutionIds: new Set(recoveryTargets
|
|
1079
|
+
.filter((target) => target.domain === 'workflow-execution')
|
|
1080
|
+
.map((target) => target.entityId)),
|
|
1059
1081
|
} : {}),
|
|
1060
1082
|
});
|
|
1061
1083
|
},
|
|
@@ -34,6 +34,7 @@ export interface WorkspaceSyncV3FeedProjectionInput {
|
|
|
34
34
|
rows: readonly WorkspaceSyncJournalRow[];
|
|
35
35
|
cycleHighWatermark: string;
|
|
36
36
|
followingRow?: WorkspaceSyncJournalRow | null;
|
|
37
|
+
workspaceId: string;
|
|
37
38
|
}
|
|
38
39
|
export type WorkspaceSyncV3FeedOperationProjector = (input: WorkspaceSyncV3FeedProjectionInput) => WorkspaceSyncV3FeedEntry[];
|
|
39
40
|
export interface WorkspaceSyncV3FeedCoveragePolicy {
|
|
@@ -207,7 +207,12 @@ export function buildWorkspaceSyncV3Feed(input) {
|
|
|
207
207
|
throw new WorkspaceSyncV3FeedOperationTooLargeError(`Workspace sync operation ${clientId}/${operationId} exceeds ${MAX_OPERATION_ROWS} journal rows.`);
|
|
208
208
|
}
|
|
209
209
|
const groupRows = rows.slice(offset, end);
|
|
210
|
-
const groupEntries = projectOperation({
|
|
210
|
+
const groupEntries = projectOperation({
|
|
211
|
+
rows: groupRows,
|
|
212
|
+
cycleHighWatermark: String(high),
|
|
213
|
+
followingRow: rows[end] || null,
|
|
214
|
+
workspaceId: input.workspaceId,
|
|
215
|
+
});
|
|
211
216
|
if (entries.length + groupEntries.length > pageSize) {
|
|
212
217
|
if (!entries.length)
|
|
213
218
|
throw new WorkspaceSyncV3FeedOperationTooLargeError(`Covered operation ${clientId}/${operationId} exceeds the feed entry limit.`);
|
|
@@ -49,9 +49,11 @@ export declare function projectWorkspaceSyncV3NestedTaskOperation(input: {
|
|
|
49
49
|
rows: readonly WorkspaceSyncJournalRow[];
|
|
50
50
|
cycleHighWatermark: string;
|
|
51
51
|
followingRow?: WorkspaceSyncJournalRow | null;
|
|
52
|
+
workspaceId: string;
|
|
52
53
|
}): WorkspaceSyncV3FeedEntry[] | null;
|
|
53
54
|
export declare function projectWorkspaceSyncV3NestedTaskV2Operation(input: {
|
|
54
55
|
rows: readonly WorkspaceSyncJournalRow[];
|
|
55
56
|
cycleHighWatermark: string;
|
|
56
57
|
followingRow?: WorkspaceSyncJournalRow | null;
|
|
58
|
+
workspaceId: string;
|
|
57
59
|
}): WorkspaceSyncV3FeedEntry[] | null;
|
|
@@ -225,6 +225,34 @@ function projectNestedDependentRows(rows, attachmentProjection) {
|
|
|
225
225
|
return [projected];
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
|
+
function assertOperationEventWorkspace(events, workspaceId) {
|
|
229
|
+
if (events.some((event) => event.workspaceId !== workspaceId)) {
|
|
230
|
+
throw new Error('Nested task operation activity belongs to another workspace.');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function assertTaskOperationActivities(events, taskId) {
|
|
234
|
+
let membershipStarted = false;
|
|
235
|
+
let taskActivityCount = 0;
|
|
236
|
+
for (const event of events) {
|
|
237
|
+
if (event.entityType === 'task') {
|
|
238
|
+
if (membershipStarted) {
|
|
239
|
+
throw new Error('Task operation activity cannot follow workstream membership activity.');
|
|
240
|
+
}
|
|
241
|
+
if (event.entityId !== taskId || event.taskId !== taskId) {
|
|
242
|
+
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
243
|
+
}
|
|
244
|
+
taskActivityCount += 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
membershipStarted = true;
|
|
248
|
+
if (event.entityType !== 'workstream' || event.details?.taskId !== taskId) {
|
|
249
|
+
throw new Error('Task operation secondary activity does not describe workstream membership.');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (taskActivityCount < 1) {
|
|
253
|
+
throw new Error('Task operation must contain primary task activity before workstream membership.');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
228
256
|
function isSupportedNestedOnlyOperation(rows) {
|
|
229
257
|
const first = rows[0];
|
|
230
258
|
if (!first)
|
|
@@ -247,7 +275,7 @@ function isSupportedNestedOnlyOperation(rows) {
|
|
|
247
275
|
* root. Apply still requires referenced parents to exist on the receiver; feed
|
|
248
276
|
* only needs to project the nested rows.
|
|
249
277
|
*/
|
|
250
|
-
function projectNestedOnlyOperation(rows, attachmentProjection) {
|
|
278
|
+
function projectNestedOnlyOperation(rows, attachmentProjection, workspaceId) {
|
|
251
279
|
if (!isSupportedNestedOnlyOperation(rows)) {
|
|
252
280
|
throw new Error('Nested-only task operation is not an allowed repair or reconciliation shape.');
|
|
253
281
|
}
|
|
@@ -259,6 +287,10 @@ function projectNestedOnlyOperation(rows, attachmentProjection) {
|
|
|
259
287
|
throw new Error('Nested task operation dependent identity does not match its task root.');
|
|
260
288
|
}
|
|
261
289
|
}
|
|
290
|
+
const rawEvents = rows
|
|
291
|
+
.filter((row) => row.domain === 'entity-event')
|
|
292
|
+
.map((row) => canonicalEntityEventPayload(parsePayload(row)));
|
|
293
|
+
assertOperationEventWorkspace(rawEvents, workspaceId);
|
|
262
294
|
const projected = projectNestedDependentRows(rows, attachmentProjection);
|
|
263
295
|
const events = projected
|
|
264
296
|
.filter((entry) => entry.domain === 'entity-event')
|
|
@@ -268,13 +300,7 @@ function projectNestedOnlyOperation(rows, attachmentProjection) {
|
|
|
268
300
|
}
|
|
269
301
|
if (taskScoped.length > 0 && events.length > 0) {
|
|
270
302
|
const taskId = taskScoped[0].entityId;
|
|
271
|
-
|
|
272
|
-
if (primaryEvent && (primaryEvent.entityType !== 'task' || primaryEvent.entityId !== taskId)) {
|
|
273
|
-
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
274
|
-
}
|
|
275
|
-
if (secondaryEvents.some((event) => (event.entityType !== 'workstream' || event.details?.taskId !== taskId))) {
|
|
276
|
-
throw new Error('Task operation secondary activity does not describe workstream membership.');
|
|
277
|
-
}
|
|
303
|
+
assertTaskOperationActivities(events, taskId);
|
|
278
304
|
}
|
|
279
305
|
if (events.length > 0 && projected.at(-1)?.domain !== 'entity-event') {
|
|
280
306
|
throw new Error('Nested task operation must end with entity activity.');
|
|
@@ -293,7 +319,7 @@ function projectNestedTaskOperation(input, attachmentProjection) {
|
|
|
293
319
|
const [first, ...dependents] = input.rows;
|
|
294
320
|
if (first && projectionByDomain.has(first.domain)
|
|
295
321
|
&& input.rows.every((row) => projectionByDomain.has(row.domain))) {
|
|
296
|
-
return projectNestedOnlyOperation(input.rows, attachmentProjection);
|
|
322
|
+
return projectNestedOnlyOperation(input.rows, attachmentProjection, input.workspaceId);
|
|
297
323
|
}
|
|
298
324
|
if (!first || !['task', 'initiative', 'workstream'].includes(first.domain)) {
|
|
299
325
|
throw new Error('Nested task operation must begin with its v3-owned parent root.');
|
|
@@ -302,10 +328,13 @@ function projectNestedTaskOperation(input, attachmentProjection) {
|
|
|
302
328
|
throw new Error('Nested task operation contains an unsupported dependent row.');
|
|
303
329
|
}
|
|
304
330
|
assertNestedDependentOrder(dependents);
|
|
331
|
+
const rawEventRows = dependents.filter((row) => row.domain === 'entity-event');
|
|
332
|
+
const rawEvents = rawEventRows.map((row) => canonicalEntityEventPayload(parsePayload(row)));
|
|
333
|
+
assertOperationEventWorkspace(rawEvents, input.workspaceId);
|
|
305
334
|
const projectedDependents = projectNestedDependentRows(dependents, attachmentProjection);
|
|
306
335
|
const eventRows = projectedDependents.filter((entry) => entry.domain === 'entity-event');
|
|
307
|
-
const
|
|
308
|
-
|
|
336
|
+
const sentinelOnly = eventRows.length === 0 && rawEvents.length > 0
|
|
337
|
+
&& rawEvents.every((event) => event.action === 'sync-v3-repair-baseline');
|
|
309
338
|
const nestedBodiesOnly = eventRows.length === 0
|
|
310
339
|
&& projectedDependents.length > 0
|
|
311
340
|
&& projectedDependents.every((entry) => entry.domain !== 'entity-event');
|
|
@@ -324,17 +353,19 @@ function projectNestedTaskOperation(input, attachmentProjection) {
|
|
|
324
353
|
}
|
|
325
354
|
const events = eventRows.map((entry) => validateWorkspaceSyncV3EntityEventPayload(entry.payload));
|
|
326
355
|
const [primaryEvent, ...secondaryEvents] = events;
|
|
327
|
-
if (primaryEvent && (primaryEvent.entityType !== first.domain || primaryEvent.entityId !== first.entityId)) {
|
|
328
|
-
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
329
|
-
}
|
|
330
356
|
if (new Set(events.map((event) => event.id)).size !== events.length) {
|
|
331
357
|
throw new Error('Nested task operation contains duplicate entity-event identities.');
|
|
332
358
|
}
|
|
333
|
-
if (first.domain
|
|
334
|
-
|
|
359
|
+
if (first.domain === 'task' && events.length > 0) {
|
|
360
|
+
assertTaskOperationActivities(events, first.entityId);
|
|
335
361
|
}
|
|
336
|
-
if (first.domain
|
|
337
|
-
|
|
362
|
+
else if (first.domain !== 'task') {
|
|
363
|
+
if (primaryEvent && (primaryEvent.entityType !== first.domain || primaryEvent.entityId !== first.entityId)) {
|
|
364
|
+
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
365
|
+
}
|
|
366
|
+
if (secondaryEvents.length > 0) {
|
|
367
|
+
throw new Error('Planning-root operations cannot contain secondary entity activity.');
|
|
368
|
+
}
|
|
338
369
|
}
|
|
339
370
|
let parent;
|
|
340
371
|
if (first.domain === 'task') {
|
|
@@ -14,4 +14,5 @@ export declare function prepareWorkspaceSyncV3WorkflowRuntimeApply(input: {
|
|
|
14
14
|
store: WorkflowStore;
|
|
15
15
|
workspaceId: string;
|
|
16
16
|
entries: WorkspaceSyncV3FeedEntry[];
|
|
17
|
+
replaceAuthoritativeHistoryExecutionIds?: ReadonlySet<string>;
|
|
17
18
|
}): WorkspaceSyncV3WorkflowRuntimeApplyPlan;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalWorkspaceSyncJson } from './canonicalWorkspaceSyncJson.js';
|
|
2
|
-
import { validateWorkflowExecutionEventSyncPayload, validateWorkflowExecutionRootSyncPayload, validateWorkflowPublishedDefinitionSyncPayload, validateWorkflowRuntimeEventHistory, } from './workflowRuntimeSync.js';
|
|
2
|
+
import { serializeWorkflowPublishedDefinitionSyncPayload, validateWorkflowExecutionEventSyncPayload, validateWorkflowExecutionRootSyncPayload, validateWorkflowPublishedDefinitionSyncPayload, validateWorkflowRuntimeEventHistory, } from './workflowRuntimeSync.js';
|
|
3
3
|
import { validateWorkspaceSyncV3WorkflowRuntimeFeedEntry } from './workspaceSyncV3WorkflowRuntimeProjection.js';
|
|
4
4
|
const WORKFLOW_PROJECTIONS = new Set([
|
|
5
5
|
'workflow-published-definition/full-v2',
|
|
@@ -9,6 +9,19 @@ const WORKFLOW_PROJECTIONS = new Set([
|
|
|
9
9
|
function compareEvents(left, right) {
|
|
10
10
|
return canonicalWorkspaceSyncJson(left) === canonicalWorkspaceSyncJson(right);
|
|
11
11
|
}
|
|
12
|
+
function executionIdentity(root) {
|
|
13
|
+
const execution = root.execution;
|
|
14
|
+
return canonicalWorkspaceSyncJson({
|
|
15
|
+
id: execution.id,
|
|
16
|
+
tenantId: execution.tenantId,
|
|
17
|
+
workspaceId: execution.workspaceId,
|
|
18
|
+
taskId: execution.taskId,
|
|
19
|
+
workflowTemplateId: execution.workflowTemplateId,
|
|
20
|
+
workflowVersionId: execution.workflowVersionId,
|
|
21
|
+
createdByActorId: execution.createdByActorId || null,
|
|
22
|
+
createdAt: execution.createdAt,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
12
25
|
function mergeEventHistory(input) {
|
|
13
26
|
const local = [...input.local].sort((left, right) => left.sequenceNumber - right.sequenceNumber);
|
|
14
27
|
const incoming = [...input.incoming].sort((left, right) => left.sequenceNumber - right.sequenceNumber);
|
|
@@ -47,6 +60,19 @@ function derivedTemplate(definition, existing) {
|
|
|
47
60
|
deletedAt: null,
|
|
48
61
|
};
|
|
49
62
|
}
|
|
63
|
+
function installedPublishedDefinition(store, workspaceId, versionId) {
|
|
64
|
+
const version = store.getVersion(workspaceId, versionId);
|
|
65
|
+
if (!version || version.status !== 'published')
|
|
66
|
+
return null;
|
|
67
|
+
const template = store.getTemplate(workspaceId, version.workflowTemplateId);
|
|
68
|
+
if (!template)
|
|
69
|
+
return null;
|
|
70
|
+
return serializeWorkflowPublishedDefinitionSyncPayload({
|
|
71
|
+
template,
|
|
72
|
+
version,
|
|
73
|
+
steps: store.listSteps(workspaceId, version.id),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
50
76
|
/**
|
|
51
77
|
* Builds complete workflow aggregates before any revision is accepted. A root
|
|
52
78
|
* entry is the atomic install trigger; definition and event entries only carry
|
|
@@ -56,6 +82,7 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
56
82
|
const workflowEntries = new Set(input.entries.filter((entry) => WORKFLOW_PROJECTIONS.has(entry.projection)));
|
|
57
83
|
const definitions = new Map();
|
|
58
84
|
const roots = new Map();
|
|
85
|
+
const rootEntries = new Map();
|
|
59
86
|
const eventsByExecution = new Map();
|
|
60
87
|
for (const entry of workflowEntries) {
|
|
61
88
|
validateWorkspaceSyncV3WorkflowRuntimeFeedEntry(entry);
|
|
@@ -68,10 +95,23 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
68
95
|
}
|
|
69
96
|
else if (entry.projection === 'workflow-execution/root-v1') {
|
|
70
97
|
const root = validateWorkflowExecutionRootSyncPayload(entry.payload);
|
|
71
|
-
|
|
72
|
-
|
|
98
|
+
const existing = roots.get(root.execution.id);
|
|
99
|
+
const existingEntry = rootEntries.get(root.execution.id);
|
|
100
|
+
if (existing && existingEntry) {
|
|
101
|
+
if (executionIdentity(existing) !== executionIdentity(root)) {
|
|
102
|
+
throw new Error(`Workflow execution root identity changes in one apply batch: ${root.execution.id}.`);
|
|
103
|
+
}
|
|
104
|
+
const revision = BigInt(entry.entityRevision);
|
|
105
|
+
const existingRevision = BigInt(existingEntry.entityRevision);
|
|
106
|
+
if (revision === existingRevision
|
|
107
|
+
&& canonicalWorkspaceSyncJson(existing) !== canonicalWorkspaceSyncJson(root)) {
|
|
108
|
+
throw new Error(`Workflow execution root revision conflicts in one apply batch: ${root.execution.id}.`);
|
|
109
|
+
}
|
|
110
|
+
if (revision <= existingRevision)
|
|
111
|
+
continue;
|
|
73
112
|
}
|
|
74
113
|
roots.set(root.execution.id, root);
|
|
114
|
+
rootEntries.set(root.execution.id, entry);
|
|
75
115
|
}
|
|
76
116
|
else {
|
|
77
117
|
const event = validateWorkflowExecutionEventSyncPayload(entry.payload).event;
|
|
@@ -87,6 +127,7 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
87
127
|
}
|
|
88
128
|
const assignments = new Map();
|
|
89
129
|
for (const [executionId, root] of roots) {
|
|
130
|
+
const replaceAuthoritativeHistory = Boolean(input.replaceAuthoritativeHistoryExecutionIds?.has(executionId));
|
|
90
131
|
const local = input.store.getAssignmentForExecutionSync(input.workspaceId, executionId);
|
|
91
132
|
const definition = definitions.get(root.execution.workflowVersionId)
|
|
92
133
|
|| (local ? {
|
|
@@ -97,12 +138,13 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
97
138
|
},
|
|
98
139
|
version: local.version,
|
|
99
140
|
steps: local.steps,
|
|
100
|
-
} : null)
|
|
141
|
+
} : null)
|
|
142
|
+
|| installedPublishedDefinition(input.store, input.workspaceId, root.execution.workflowVersionId);
|
|
101
143
|
if (!definition) {
|
|
102
144
|
throw new Error(`Workflow execution ${executionId} is missing its published definition.`);
|
|
103
145
|
}
|
|
104
146
|
const merged = mergeEventHistory({
|
|
105
|
-
local: local?.events || [],
|
|
147
|
+
local: replaceAuthoritativeHistory ? [] : local?.events || [],
|
|
106
148
|
incoming: eventsByExecution.get(executionId) || [],
|
|
107
149
|
});
|
|
108
150
|
const execution = merged.localExtension && local
|
|
@@ -131,12 +173,15 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
131
173
|
return;
|
|
132
174
|
if (entry.projection !== 'workflow-execution/root-v1')
|
|
133
175
|
return;
|
|
176
|
+
if (rootEntries.get(entry.entityId) !== entry)
|
|
177
|
+
return;
|
|
134
178
|
const assignment = assignments.get(entry.entityId);
|
|
135
179
|
if (!assignment)
|
|
136
180
|
throw new Error(`Workflow execution apply plan is missing ${entry.entityId}.`);
|
|
137
181
|
input.store.installAssignmentForSync(input.workspaceId, assignment, {
|
|
138
182
|
authority: 'cloud-execution',
|
|
139
183
|
expectedTaskId: assignment.execution.taskId,
|
|
184
|
+
replaceAuthoritativeHistory: input.replaceAuthoritativeHistoryExecutionIds?.has(entry.entityId),
|
|
140
185
|
});
|
|
141
186
|
},
|
|
142
187
|
};
|
package/dist/ui/assets/{AiIdentityRosterCard-OcSz1PkK.js → AiIdentityRosterCard-CDMPtnbC.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as i}from"./vendor-react-CKJs5o3c.js";import{t as r}from"./index-
|
|
1
|
+
import{j as i}from"./vendor-react-CKJs5o3c.js";import{t as r}from"./index-DSQINwt9.js";const t=[{name:"Blue",value:"#4269d0"},{name:"Orange",value:"#efb118"},{name:"Red",value:"#ff725c"},{name:"Cyan",value:"#6cc5b0"},{name:"Green",value:"#3ca951"},{name:"Pink",value:"#ff8ab7"},{name:"Purple",value:"#a463f2"},{name:"Light blue",value:"#97bbf5"},{name:"Brown",value:"#9c6b4e"},{name:"Gray",value:"#9498a0"}];function N(e){const s=String(e||"ai").trim().toLowerCase();let a=2166136261;for(let l=0;l<s.length;l+=1)a^=s.charCodeAt(l),a=Math.imul(a,16777619);return t[(a>>>0)%t.length].value}function b(e){const[s,a=""]=e.split("#"),n=s.includes("?")?"&":"?";return`${s}${n}taskforceCloud=1${a?`#${a}`:""}`}function $(e){return/^https?:\/\//i.test(e)?e:b(e)}function j({name:e,username:s,subtitle:a,avatar:n,selected:l,disabled:o=!1,onSelect:u,ariaLabel:c,avatarVariant:f="portrait",avatarClassName:m="",avatarStyle:d,className:p="",topUtility:h,bottomUtility:v,footer:P}){return i.jsxs("button",{type:"button",className:`${r.aiProfileGroup} ${l?r.aiProfileGroupSelected:""} ${p}`.trim(),onClick:u,disabled:o,"aria-label":c,"aria-pressed":l,children:[h,v,i.jsxs("span",{className:r.aiProfileGroupHeader,children:[i.jsx("span",{className:`${r.aiProfileGroupAvatar} ${f==="logo"?r.aiProfileGroupAvatarLogo:""} ${m}`.trim(),style:d,"aria-hidden":"true",children:n}),i.jsxs("span",{className:r.aiProfileGroupIdentity,children:[i.jsx("span",{className:r.aiProfileName,children:e}),s?i.jsxs("span",{className:r.aiProfileHandle,children:["@",s]}):null,i.jsx("span",{className:r.aiProfileRole,children:a}),P]})]})]})}export{j as A,N as a,t as b,$ as r,b as w};
|