@pixelbyte-software/pixcode 1.42.2 → 1.42.3
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/assets/{index-CMeiCqQf.js → index-BnaWRV1a.js} +84 -84
- package/dist/index.html +1 -1
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +86 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -1
- package/dist-server/server/modules/orchestration/tasks/task-run-graph.js +158 -0
- package/dist-server/server/modules/orchestration/tasks/task-run-graph.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +6 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +93 -25
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/taskmaster-run-graph.mjs +55 -0
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +94 -0
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +10 -0
- package/server/modules/orchestration/tasks/task-run-graph.ts +219 -0
- package/server/modules/orchestration/workflows/workflow-runner.ts +7 -1
- package/server/routes/taskmaster.js +90 -23
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import type { WorkflowNodeRun, WorkflowRun } from '@/modules/orchestration/workflows/workflow.types.js';
|
|
2
|
+
import { workflowStore } from '@/modules/orchestration/workflows/workflow-store.js';
|
|
3
|
+
import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
|
|
4
|
+
import type { OrchestrationTask } from '@/modules/orchestration/tasks/orchestration-task.types.js';
|
|
5
|
+
|
|
6
|
+
export const PIXCODE_TASK_RUN_GRAPH_PROTOCOL = 'pixcode.task-run-graph.v1';
|
|
7
|
+
|
|
8
|
+
export type TaskRunGraphCriterionStatus = 'pending' | 'passed' | 'failed';
|
|
9
|
+
|
|
10
|
+
export interface TaskRunGraphCriterion {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
status: TaskRunGraphCriterionStatus;
|
|
14
|
+
source: 'taskmaster' | 'workflow';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TaskRunGraphRunSummary {
|
|
18
|
+
id: string;
|
|
19
|
+
workflowId: string;
|
|
20
|
+
status: WorkflowRun['status'];
|
|
21
|
+
startedAt: number;
|
|
22
|
+
finishedAt?: number;
|
|
23
|
+
taskmasterId?: string;
|
|
24
|
+
orchestrationTaskId?: string;
|
|
25
|
+
changedFiles: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TaskRunGraph {
|
|
29
|
+
protocol: typeof PIXCODE_TASK_RUN_GRAPH_PROTOCOL;
|
|
30
|
+
projectId: string;
|
|
31
|
+
taskmasterId?: string;
|
|
32
|
+
orchestrationTaskId?: string;
|
|
33
|
+
workflowRuns: TaskRunGraphRunSummary[];
|
|
34
|
+
changedFiles: string[];
|
|
35
|
+
acceptanceCriteria: TaskRunGraphCriterion[];
|
|
36
|
+
status: {
|
|
37
|
+
totalRuns: number;
|
|
38
|
+
completedRuns: number;
|
|
39
|
+
failedRuns: number;
|
|
40
|
+
passedCriteria: number;
|
|
41
|
+
failedCriteria: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readString(value: unknown): string | undefined {
|
|
46
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readRecord(value: unknown): Record<string, unknown> | undefined {
|
|
50
|
+
return value && typeof value === 'object' ? value as Record<string, unknown> : undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function uniqueStrings(values: Array<string | undefined>): string[] {
|
|
54
|
+
return [...new Set(values.filter((value): value is string => Boolean(value?.trim())))]
|
|
55
|
+
.sort((a, b) => a.localeCompare(b));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function taskStatusPassed(status: unknown): boolean {
|
|
59
|
+
const normalized = String(status ?? '').toLocaleLowerCase('en');
|
|
60
|
+
return normalized === 'done' || normalized === 'completed';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function taskStatusFailed(status: unknown): boolean {
|
|
64
|
+
const normalized = String(status ?? '').toLocaleLowerCase('en');
|
|
65
|
+
return normalized === 'failed' || normalized === 'blocked' || normalized === 'cancelled' || normalized === 'canceled';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function criterionStatus(status: unknown): TaskRunGraphCriterionStatus {
|
|
69
|
+
if (taskStatusPassed(status)) return 'passed';
|
|
70
|
+
if (taskStatusFailed(status)) return 'failed';
|
|
71
|
+
return 'pending';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function taskmasterAcceptanceCriteria(taskmasterTask: Record<string, unknown> | undefined): TaskRunGraphCriterion[] {
|
|
75
|
+
if (!taskmasterTask) return [];
|
|
76
|
+
|
|
77
|
+
const criteria: TaskRunGraphCriterion[] = [];
|
|
78
|
+
const testStrategy = readString(taskmasterTask.testStrategy) ?? readString(taskmasterTask.test_strategy);
|
|
79
|
+
if (testStrategy) {
|
|
80
|
+
criteria.push({
|
|
81
|
+
id: 'test-strategy',
|
|
82
|
+
label: testStrategy,
|
|
83
|
+
status: criterionStatus(taskmasterTask.status),
|
|
84
|
+
source: 'taskmaster',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const subtasks = Array.isArray(taskmasterTask.subtasks) ? taskmasterTask.subtasks : [];
|
|
89
|
+
subtasks.forEach((subtask, index) => {
|
|
90
|
+
const record = readRecord(subtask);
|
|
91
|
+
if (!record) return;
|
|
92
|
+
const title = readString(record.title);
|
|
93
|
+
if (!title) return;
|
|
94
|
+
criteria.push({
|
|
95
|
+
id: `subtask-${readString(record.id) ?? index + 1}`,
|
|
96
|
+
label: title,
|
|
97
|
+
status: criterionStatus(record.status),
|
|
98
|
+
source: 'taskmaster',
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return criteria;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function metadataTaskmasterId(run: WorkflowRun): string | undefined {
|
|
106
|
+
return readString(run.metadata?.taskmasterId)
|
|
107
|
+
?? readString(readRecord(run.metadata?.taskGraph)?.taskmasterId)
|
|
108
|
+
?? readString(readRecord(run.metadata?.replay)?.taskmasterId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function metadataOrchestrationTaskId(run: WorkflowRun): string | undefined {
|
|
112
|
+
return readString(run.metadata?.orchestrationTaskId)
|
|
113
|
+
?? readString(readRecord(run.metadata?.taskGraph)?.orchestrationTaskId);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function artifactChangedFiles(node: WorkflowNodeRun): string[] {
|
|
117
|
+
const files: string[] = [];
|
|
118
|
+
|
|
119
|
+
if (node.handoffArtifact?.changedFiles) {
|
|
120
|
+
files.push(...node.handoffArtifact.changedFiles);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const artifact of node.artifacts ?? []) {
|
|
124
|
+
const data = readRecord(artifact.data);
|
|
125
|
+
const metadata = readRecord(artifact.metadata);
|
|
126
|
+
const candidates = [
|
|
127
|
+
readString(metadata?.path),
|
|
128
|
+
readString(metadata?.file),
|
|
129
|
+
readString(data?.path),
|
|
130
|
+
readString(data?.file),
|
|
131
|
+
];
|
|
132
|
+
files.push(...candidates.filter((value): value is string => Boolean(value)));
|
|
133
|
+
|
|
134
|
+
for (const key of ['files', 'changedFiles']) {
|
|
135
|
+
const value = data?.[key] ?? metadata?.[key];
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
files.push(...value.map((entry) => readString(entry)).filter((entry): entry is string => Boolean(entry)));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return uniqueStrings(files);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function changedFilesFromWorkflowRun(run: WorkflowRun): string[] {
|
|
146
|
+
return uniqueStrings(run.nodeRuns.flatMap((node) => artifactChangedFiles(node)));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function workflowRunMatchesTask(run: WorkflowRun, task: OrchestrationTask | undefined, taskmasterId: string | undefined): boolean {
|
|
150
|
+
const runTaskmasterId = metadataTaskmasterId(run);
|
|
151
|
+
const runOrchestrationTaskId = metadataOrchestrationTaskId(run);
|
|
152
|
+
if (taskmasterId && runTaskmasterId === taskmasterId) return true;
|
|
153
|
+
if (task?.id && runOrchestrationTaskId === task.id) return true;
|
|
154
|
+
if (task?.workflowRunIds?.includes(run.id)) return true;
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function runSummary(run: WorkflowRun): TaskRunGraphRunSummary {
|
|
159
|
+
return {
|
|
160
|
+
id: run.id,
|
|
161
|
+
workflowId: run.workflowId,
|
|
162
|
+
status: run.status,
|
|
163
|
+
startedAt: run.startedAt,
|
|
164
|
+
finishedAt: run.finishedAt,
|
|
165
|
+
taskmasterId: metadataTaskmasterId(run),
|
|
166
|
+
orchestrationTaskId: metadataOrchestrationTaskId(run),
|
|
167
|
+
changedFiles: changedFilesFromWorkflowRun(run),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function buildTaskRunGraph({
|
|
172
|
+
projectId,
|
|
173
|
+
taskmasterId,
|
|
174
|
+
taskmasterTask,
|
|
175
|
+
}: {
|
|
176
|
+
projectId: string;
|
|
177
|
+
taskmasterId?: string;
|
|
178
|
+
taskmasterTask?: Record<string, unknown>;
|
|
179
|
+
}): TaskRunGraph {
|
|
180
|
+
const orchestrationTask = taskmasterId
|
|
181
|
+
? orchestrationTaskService.list(projectId).find((task) => task.taskmasterId === taskmasterId)
|
|
182
|
+
: undefined;
|
|
183
|
+
const workflowRuns = workflowStore
|
|
184
|
+
.listRuns()
|
|
185
|
+
.filter((run) => workflowRunMatchesTask(run, orchestrationTask, taskmasterId))
|
|
186
|
+
.map(runSummary);
|
|
187
|
+
const workflowCriteria: TaskRunGraphCriterion[] = workflowRuns.map((run) => ({
|
|
188
|
+
id: `run-${run.id}`,
|
|
189
|
+
label: `Workflow ${run.workflowId} ${run.status}`,
|
|
190
|
+
status: run.status === 'completed' ? 'passed' : run.status === 'failed' || run.status === 'canceled' ? 'failed' : 'pending',
|
|
191
|
+
source: 'workflow',
|
|
192
|
+
}));
|
|
193
|
+
const acceptanceCriteria = [
|
|
194
|
+
...taskmasterAcceptanceCriteria(taskmasterTask),
|
|
195
|
+
...(orchestrationTask?.acceptanceCriteria ?? []),
|
|
196
|
+
...workflowCriteria,
|
|
197
|
+
];
|
|
198
|
+
const changedFiles = uniqueStrings([
|
|
199
|
+
...(orchestrationTask?.changedFiles ?? []),
|
|
200
|
+
...workflowRuns.flatMap((run) => run.changedFiles),
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
protocol: PIXCODE_TASK_RUN_GRAPH_PROTOCOL,
|
|
205
|
+
projectId,
|
|
206
|
+
taskmasterId,
|
|
207
|
+
orchestrationTaskId: orchestrationTask?.id,
|
|
208
|
+
workflowRuns,
|
|
209
|
+
changedFiles,
|
|
210
|
+
acceptanceCriteria,
|
|
211
|
+
status: {
|
|
212
|
+
totalRuns: workflowRuns.length,
|
|
213
|
+
completedRuns: workflowRuns.filter((run) => run.status === 'completed').length,
|
|
214
|
+
failedRuns: workflowRuns.filter((run) => run.status === 'failed' || run.status === 'canceled').length,
|
|
215
|
+
passedCriteria: acceptanceCriteria.filter((criterion) => criterion.status === 'passed').length,
|
|
216
|
+
failedCriteria: acceptanceCriteria.filter((criterion) => criterion.status === 'failed').length,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
workspaceTargetMetadata,
|
|
29
29
|
} from '@/modules/orchestration/workflows/workspace-target.js';
|
|
30
30
|
import { workflowStore } from '@/modules/orchestration/workflows/workflow-store.js';
|
|
31
|
+
import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
|
|
31
32
|
// @ts-ignore — plain-JS service
|
|
32
33
|
import {
|
|
33
34
|
getDefaultProviderModel,
|
|
@@ -1215,7 +1216,7 @@ class WorkflowRunner {
|
|
|
1215
1216
|
const runtimeWorkflow = expandWorkflowForRun(workflow, metadata);
|
|
1216
1217
|
validateWorkflow(runtimeWorkflow);
|
|
1217
1218
|
const workspaceTarget = resolveWorkflowWorkspace(metadata);
|
|
1218
|
-
const runMetadata = {
|
|
1219
|
+
const runMetadata: Record<string, unknown> = {
|
|
1219
1220
|
...metadata,
|
|
1220
1221
|
projectPath: workspaceTarget.projectPath,
|
|
1221
1222
|
selectedProjectPath: workspaceTarget.selectedProjectPath,
|
|
@@ -1232,6 +1233,10 @@ class WorkflowRunner {
|
|
|
1232
1233
|
metadata: runMetadata,
|
|
1233
1234
|
};
|
|
1234
1235
|
workflowStore.setRun(run);
|
|
1236
|
+
const orchestrationTaskId = readString(runMetadata.orchestrationTaskId);
|
|
1237
|
+
if (orchestrationTaskId) {
|
|
1238
|
+
orchestrationTaskService.linkWorkflowRun(orchestrationTaskId, run);
|
|
1239
|
+
}
|
|
1235
1240
|
void this.execute(runtimeWorkflow, run);
|
|
1236
1241
|
return run;
|
|
1237
1242
|
}
|
|
@@ -1591,6 +1596,7 @@ class WorkflowRunner {
|
|
|
1591
1596
|
} finally {
|
|
1592
1597
|
run.finishedAt = run.finishedAt ?? Date.now();
|
|
1593
1598
|
workflowStore.setRun(run);
|
|
1599
|
+
orchestrationTaskService.updateFromWorkflowRun(run);
|
|
1594
1600
|
notifyWorkflowRunFinished(run);
|
|
1595
1601
|
this.cancelingRuns.delete(run.id);
|
|
1596
1602
|
}
|
|
@@ -16,6 +16,9 @@ import express from 'express';
|
|
|
16
16
|
import crossSpawn from 'cross-spawn';
|
|
17
17
|
|
|
18
18
|
import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
|
|
19
|
+
import { buildTaskRunGraph } from '@/modules/orchestration/tasks/task-run-graph.js';
|
|
20
|
+
import { workflowRunner } from '@/modules/orchestration/workflows/workflow-runner.js';
|
|
21
|
+
import { workflowStore } from '@/modules/orchestration/workflows/workflow-store.js';
|
|
19
22
|
|
|
20
23
|
import { extractProjectDirectory } from '../projects.js';
|
|
21
24
|
import {
|
|
@@ -167,6 +170,21 @@ function taskMasterExecutionDescription(task) {
|
|
|
167
170
|
].filter(Boolean).join('\n\n');
|
|
168
171
|
}
|
|
169
172
|
|
|
173
|
+
function taskGraphForTask(projectId, task) {
|
|
174
|
+
return buildTaskRunGraph({
|
|
175
|
+
projectId,
|
|
176
|
+
taskmasterId: String(task.id),
|
|
177
|
+
taskmasterTask: task,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function attachTaskGraph(projectId, task) {
|
|
182
|
+
return {
|
|
183
|
+
...task,
|
|
184
|
+
taskGraph: taskGraphForTask(projectId, task),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
170
188
|
function buildTaskMasterQueueSummary(projectName, projectPath, tasks) {
|
|
171
189
|
const normalized = tasks.map((task) => ({
|
|
172
190
|
...task,
|
|
@@ -389,22 +407,26 @@ router.delete('/install/:jobId', async (req, res) => {
|
|
|
389
407
|
router.get('/tasks/:projectName', async (req, res) => {
|
|
390
408
|
try {
|
|
391
409
|
const { projectName } = req.params;
|
|
410
|
+
const projectId = typeof req.query.projectId === 'string' && req.query.projectId.trim()
|
|
411
|
+
? req.query.projectId.trim()
|
|
412
|
+
: projectName;
|
|
392
413
|
|
|
393
414
|
const { projectPath, transformedTasks, currentTag } = await readTaskMasterTasks(projectName);
|
|
415
|
+
const tasksWithGraph = transformedTasks.map((task) => attachTaskGraph(projectId, task));
|
|
394
416
|
|
|
395
417
|
res.json({
|
|
396
418
|
projectName,
|
|
397
419
|
projectPath,
|
|
398
|
-
tasks:
|
|
420
|
+
tasks: tasksWithGraph,
|
|
399
421
|
currentTag,
|
|
400
|
-
totalTasks:
|
|
422
|
+
totalTasks: tasksWithGraph.length,
|
|
401
423
|
tasksByStatus: {
|
|
402
|
-
pending:
|
|
403
|
-
'in-progress':
|
|
404
|
-
done:
|
|
405
|
-
review:
|
|
406
|
-
deferred:
|
|
407
|
-
cancelled:
|
|
424
|
+
pending: tasksWithGraph.filter(t => t.status === 'pending').length,
|
|
425
|
+
'in-progress': tasksWithGraph.filter(t => t.status === 'in-progress').length,
|
|
426
|
+
done: tasksWithGraph.filter(t => t.status === 'done').length,
|
|
427
|
+
review: tasksWithGraph.filter(t => t.status === 'review').length,
|
|
428
|
+
deferred: tasksWithGraph.filter(t => t.status === 'deferred').length,
|
|
429
|
+
cancelled: tasksWithGraph.filter(t => t.status === 'cancelled').length
|
|
408
430
|
},
|
|
409
431
|
timestamp: new Date().toISOString()
|
|
410
432
|
});
|
|
@@ -459,6 +481,9 @@ router.get('/queue/:projectName', async (req, res) => {
|
|
|
459
481
|
router.get('/task/:projectName/:taskId', async (req, res) => {
|
|
460
482
|
try {
|
|
461
483
|
const { projectName, taskId } = req.params;
|
|
484
|
+
const projectId = typeof req.query.projectId === 'string' && req.query.projectId.trim()
|
|
485
|
+
? req.query.projectId.trim()
|
|
486
|
+
: projectName;
|
|
462
487
|
const { projectPath, transformedTasks } = await readTaskMasterTasks(projectName);
|
|
463
488
|
const task = transformedTasks.find((candidate) => String(candidate.id) === String(taskId));
|
|
464
489
|
if (!task) {
|
|
@@ -472,7 +497,8 @@ router.get('/task/:projectName/:taskId', async (req, res) => {
|
|
|
472
497
|
success: true,
|
|
473
498
|
projectName,
|
|
474
499
|
projectPath,
|
|
475
|
-
task,
|
|
500
|
+
task: attachTaskGraph(projectId, task),
|
|
501
|
+
taskGraph: taskGraphForTask(projectId, task),
|
|
476
502
|
execution: {
|
|
477
503
|
supportsProvider: true,
|
|
478
504
|
supportsModel: true,
|
|
@@ -505,6 +531,7 @@ router.post('/execute/:projectName/:taskId', async (req, res) => {
|
|
|
505
531
|
: '';
|
|
506
532
|
const model = typeof req.body?.model === 'string' ? req.body.model : undefined;
|
|
507
533
|
const permissionMode = typeof req.body?.permissionMode === 'string' ? req.body.permissionMode : undefined;
|
|
534
|
+
const workflowId = typeof req.body?.workflowId === 'string' ? req.body.workflowId : undefined;
|
|
508
535
|
const fallbackProvider = typeof req.body?.fallbackProvider === 'string' ? req.body.fallbackProvider : undefined;
|
|
509
536
|
const workerSlot = Number.isInteger(req.body?.workerSlot) ? req.body.workerSlot : undefined;
|
|
510
537
|
const isolation = ['host', 'worktree', 'docker'].includes(req.body?.isolation)
|
|
@@ -544,15 +571,49 @@ router.post('/execute/:projectName/:taskId', async (req, res) => {
|
|
|
544
571
|
},
|
|
545
572
|
});
|
|
546
573
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
574
|
+
let dispatchedTask;
|
|
575
|
+
let workflowRun;
|
|
576
|
+
if (workflowId) {
|
|
577
|
+
const workflow = workflowStore.getWorkflow(workflowId);
|
|
578
|
+
if (!workflow) {
|
|
579
|
+
return res.status(404).json({
|
|
580
|
+
success: false,
|
|
581
|
+
error: 'Workflow not found',
|
|
582
|
+
message: `Workflow "${workflowId}" was not found`
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
workflowRun = workflowRunner.start(workflow, taskMasterExecutionDescription(task), {
|
|
586
|
+
projectId,
|
|
587
|
+
projectName,
|
|
588
|
+
projectPath,
|
|
589
|
+
selectedProjectPath: projectPath,
|
|
590
|
+
taskmasterId: String(task.id),
|
|
591
|
+
taskmasterTaskTitle: task.title,
|
|
592
|
+
orchestrationTaskId: orchestrationTask.id,
|
|
593
|
+
workflowName: workflow.name,
|
|
594
|
+
settings: {
|
|
595
|
+
isolation,
|
|
596
|
+
keepWorkspace: true,
|
|
597
|
+
baseRef: 'HEAD',
|
|
598
|
+
},
|
|
599
|
+
taskGraph: {
|
|
600
|
+
taskmasterId: String(task.id),
|
|
601
|
+
orchestrationTaskId: orchestrationTask.id,
|
|
602
|
+
source: 'taskmaster',
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
dispatchedTask = orchestrationTaskService.linkWorkflowRun(orchestrationTask.id, workflowRun) ?? orchestrationTask;
|
|
606
|
+
} else {
|
|
607
|
+
dispatchedTask = await orchestrationTaskService.dispatch(orchestrationTask.id, {
|
|
608
|
+
adapterId,
|
|
609
|
+
isolation,
|
|
610
|
+
projectPath,
|
|
611
|
+
model,
|
|
612
|
+
permissionMode,
|
|
613
|
+
fallbackProvider,
|
|
614
|
+
workerSlot,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
556
617
|
|
|
557
618
|
res.json({
|
|
558
619
|
success: true,
|
|
@@ -565,8 +626,11 @@ router.post('/execute/:projectName/:taskId', async (req, res) => {
|
|
|
565
626
|
permissionMode,
|
|
566
627
|
fallbackProvider,
|
|
567
628
|
workerSlot,
|
|
629
|
+
workflowId,
|
|
568
630
|
},
|
|
569
|
-
task: dispatchedTask
|
|
631
|
+
task: dispatchedTask,
|
|
632
|
+
run: workflowRun,
|
|
633
|
+
taskGraph: taskGraphForTask(projectId, task),
|
|
570
634
|
});
|
|
571
635
|
} catch (error) {
|
|
572
636
|
console.error('TaskMaster execute error:', error);
|
|
@@ -591,14 +655,17 @@ router.post('/sync-orchestration/:projectName', async (req, res) => {
|
|
|
591
655
|
? req.body.projectId.trim()
|
|
592
656
|
: projectName;
|
|
593
657
|
|
|
594
|
-
const syncedTasks = transformedTasks.map((task) =>
|
|
595
|
-
|
|
658
|
+
const syncedTasks = transformedTasks.map((task) => {
|
|
659
|
+
const taskGraph = taskGraphForTask(projectId, task);
|
|
660
|
+
return orchestrationTaskService.upsertFromTaskMaster({
|
|
596
661
|
projectId,
|
|
597
662
|
taskmasterId: String(task.id),
|
|
598
663
|
title: task.title,
|
|
599
664
|
description: task.description,
|
|
600
|
-
|
|
601
|
-
|
|
665
|
+
acceptanceCriteria: taskGraph.acceptanceCriteria,
|
|
666
|
+
changedFiles: taskGraph.changedFiles,
|
|
667
|
+
});
|
|
668
|
+
});
|
|
602
669
|
|
|
603
670
|
res.json({
|
|
604
671
|
success: true,
|