import type { PermissionPhase, TaskOptions } from '../harness/runtime.ts'; import type { Trace } from '../harness/trace.ts'; import type { ScmAdapter } from '../scm/types.ts'; import type { TaskDriver, TaskProgress, TaskTerminalStatus } from './tasks/driver.ts'; type Finish = (status: string, extra?: { reason?: string; message?: string; [key: string]: unknown }) => Promise; type Deliver = (status: TaskTerminalStatus, body: string) => Promise; export interface TaskDriverExecutionInput { driver: TaskDriver; phase: PermissionPhase; writable: boolean; repo: string; scm: ScmAdapter; projectId: string; changeRequestNumber: number; trace: Trace; signal: AbortSignal; options: TaskOptions; repairAttempts: () => number; repairBudget: number; resumed: { pausePhase?: string } | null; progress: TaskProgress; guardCycle: (phaseLabel: string) => Promise; beginTask: (name: string) => Promise; seed: () => Promise>; finish: Finish; deliver: Deliver; hasWorkspaceChanges: () => Promise; publish: (kind: string) => Promise; headSha: () => string; saveRepairAttempts: () => Promise; publishApprovalPlan: (body: string) => Promise; } export async function runTaskDriver(input: TaskDriverExecutionInput): Promise<{ handled: true; result: unknown } | { handled: false }> { for (;;) { const decision = await input.driver.decide({ phase: input.phase, writable: input.writable, headSha: input.headSha(), repo: input.repo, scm: input.scm, projectId: input.projectId, changeRequestNumber: input.changeRequestNumber, trace: input.trace, signal: input.signal, options: input.options, repairAttempts: input.repairAttempts(), repairBudget: input.repairBudget, resumed: input.resumed, progress: input.progress, guardCycle: input.guardCycle, }); if (decision.kind === 'complete') return { handled: true, result: await input.deliver(decision.status, decision.body) }; if (decision.kind === 'blocked') { return { handled: true, result: await input.finish('needs_human', { reason: decision.reason, ...(decision.failureCode ? { failure_code: decision.failureCode } : {}) }) }; } if (decision.mode === 'repair' && decision.countsAsRepairAttempt) { await input.saveRepairAttempts(); } await input.beginTask(decision.agent.name); const turn = await decision.agent.run( decision.evidence === undefined ? input.options : { ...input.options, evidence: decision.evidence }, { ...await input.seed(), ...decision.seed }, ); if (turn.outcome === 'unfinished') { return { handled: true, result: await input.finish(turn.status, { reason: turn.reason }) }; } if (decision.artifact) input.trace.save(decision.artifact, turn.trace ?? turn); if (decision.mode === 'plan') return { handled: true, result: await input.publishApprovalPlan(turn.body) }; if (decision.mode === 'diagnose') { return { handled: true, result: await input.deliver(decision.status, decision.heading + '\n\n' + turn.body + '\n\n' + decision.trailer) }; } const changed = await input.hasWorkspaceChanges(); const writtenBack = changed && input.writable; if (writtenBack) await input.publish(decision.writebackKind); input.progress.turns.push({ body: turn.body, writtenBack, sha: input.headSha() }); } }