ORCHESTRATOR: produce the task DAG.
(goal: string, signal?: AbortSignal)
| 94 | |
| 95 | /** ORCHESTRATOR: produce the task DAG. */ |
| 96 | async decompose(goal: string, signal?: AbortSignal): Promise<TaskGraph> { |
| 97 | const raw = this.opts.planner |
| 98 | ? await this.opts.planner(goal, signal) |
| 99 | : await this.planViaModel(goal, signal); |
| 100 | |
| 101 | const nodes = new Map<TaskId, TaskNode>(); |
| 102 | for (const t of raw.tasks) { |
| 103 | nodes.set(t.id, { |
| 104 | id: t.id, |
| 105 | kind: t.kind, |
| 106 | title: t.title, |
| 107 | instruction: t.instruction, |
| 108 | targetFiles: t.targetFiles ?? [], |
| 109 | contextFiles: t.contextFiles ?? [], |
| 110 | contextSymbols: t.contextSymbols, |
| 111 | dependsOn: t.dependsOn ?? [], |
| 112 | role: defaultRoleForKind(t.kind), |
| 113 | visualReview: t.visualReview ?? false, |
| 114 | status: 'pending', |
| 115 | attempts: 0, |
| 116 | }); |
| 117 | } |
| 118 | |
| 119 | const graph: TaskGraph = { nodes, goal, acyclic: false }; |
| 120 | const order = topoSort(graph); |
| 121 | if (!order) { |
| 122 | throw new Error('Planner produced a cyclic task graph; cannot schedule. Re-plan with acyclic dependencies.'); |
| 123 | } |
| 124 | graph.acyclic = true; |
| 125 | logger.info('Decomposed goal into DAG', { tasks: nodes.size, order }); |
| 126 | return graph; |
| 127 | } |
| 128 | |
| 129 | /** Ask the planning model for the DAG as JSON. */ |
| 130 | private async planViaModel(goal: string, signal?: AbortSignal): Promise<RawPlan> { |
no test coverage detected