@pixelbyte-software/pixcode 1.42.1 → 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.
Files changed (31) hide show
  1. package/dist/assets/{index-C97kIvXz.js → index-BnaWRV1a.js} +182 -182
  2. package/dist/index.html +1 -1
  3. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +86 -0
  4. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -1
  5. package/dist-server/server/modules/orchestration/tasks/task-run-graph.js +158 -0
  6. package/dist-server/server/modules/orchestration/tasks/task-run-graph.js.map +1 -0
  7. package/dist-server/server/modules/orchestration/workflows/workflow-fallback-policy.js +114 -0
  8. package/dist-server/server/modules/orchestration/workflows/workflow-fallback-policy.js.map +1 -0
  9. package/dist-server/server/modules/orchestration/workflows/workflow-replay.js +177 -0
  10. package/dist-server/server/modules/orchestration/workflows/workflow-replay.js.map +1 -0
  11. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +53 -7
  12. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
  13. package/dist-server/server/modules/orchestration/workflows/workflow-trace.js +74 -0
  14. package/dist-server/server/modules/orchestration/workflows/workflow-trace.js.map +1 -1
  15. package/dist-server/server/modules/orchestration/workflows/workflow.routes.js +88 -0
  16. package/dist-server/server/modules/orchestration/workflows/workflow.routes.js.map +1 -1
  17. package/dist-server/server/routes/taskmaster.js +93 -25
  18. package/dist-server/server/routes/taskmaster.js.map +1 -1
  19. package/package.json +1 -1
  20. package/scripts/smoke/taskmaster-run-graph.mjs +55 -0
  21. package/scripts/smoke/workflow-fallback-replay.mjs +56 -0
  22. package/server/modules/orchestration/tasks/orchestration-task.service.ts +94 -0
  23. package/server/modules/orchestration/tasks/orchestration-task.types.ts +10 -0
  24. package/server/modules/orchestration/tasks/task-run-graph.ts +219 -0
  25. package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -0
  26. package/server/modules/orchestration/workflows/workflow-replay.ts +254 -0
  27. package/server/modules/orchestration/workflows/workflow-runner.ts +112 -7
  28. package/server/modules/orchestration/workflows/workflow-trace.ts +76 -0
  29. package/server/modules/orchestration/workflows/workflow.routes.ts +107 -0
  30. package/server/modules/orchestration/workflows/workflow.types.ts +5 -0
  31. package/server/routes/taskmaster.js +90 -23
@@ -1,4 +1,5 @@
1
1
  import type { WorkflowContextPacket } from '@/modules/orchestration/workflows/context-packet.js';
2
+ import type { WorkflowFallbackTrigger } from '@/modules/orchestration/workflows/workflow-fallback-policy.js';
2
3
  import type { WorkflowHandoffArtifact } from '@/modules/orchestration/workflows/handoff-artifact.js';
3
4
 
4
5
  export type WorkflowRunStatus = 'queued' | 'running' | 'completed' | 'failed' | 'canceled';
@@ -21,6 +22,8 @@ export interface WorkflowNode {
21
22
  isolation?: 'host' | 'worktree' | 'docker';
22
23
  timeoutMs?: number;
23
24
  internal?: boolean;
25
+ fallbackTrigger?: WorkflowFallbackTrigger;
26
+ fallbackSourceNodeId?: string;
24
27
  }
25
28
 
26
29
  export interface Workflow {
@@ -44,6 +47,8 @@ export interface WorkflowNodeRun {
44
47
  timeoutMs?: number;
45
48
  stage?: string;
46
49
  internal?: boolean;
50
+ fallbackTrigger?: WorkflowFallbackTrigger;
51
+ fallbackSourceNodeId?: string;
47
52
  status: WorkflowNodeStatus;
48
53
  a2aTaskId?: string;
49
54
  startedAt?: number;
@@ -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: transformedTasks,
420
+ tasks: tasksWithGraph,
399
421
  currentTag,
400
- totalTasks: transformedTasks.length,
422
+ totalTasks: tasksWithGraph.length,
401
423
  tasksByStatus: {
402
- pending: transformedTasks.filter(t => t.status === 'pending').length,
403
- 'in-progress': transformedTasks.filter(t => t.status === 'in-progress').length,
404
- done: transformedTasks.filter(t => t.status === 'done').length,
405
- review: transformedTasks.filter(t => t.status === 'review').length,
406
- deferred: transformedTasks.filter(t => t.status === 'deferred').length,
407
- cancelled: transformedTasks.filter(t => t.status === 'cancelled').length
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
- const dispatchedTask = await orchestrationTaskService.dispatch(orchestrationTask.id, {
548
- adapterId,
549
- isolation,
550
- projectPath,
551
- model,
552
- permissionMode,
553
- fallbackProvider,
554
- workerSlot,
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
- orchestrationTaskService.upsertFromTaskMaster({
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,