@@ -49,6 +49,14 @@ export interface HarnessOrchestratorOptions {
4949 checkpoint ?: OrchestratorCheckpointMode ;
5050 opencodeTranscript ?: string ;
5151 onExecutorOutput ?: ( event : { stream : "stdout" | "stderr" ; text : string } ) => void ;
52+ onProgress ?: ( event : HarnessProgressEvent ) => void ;
53+ }
54+
55+ export interface HarnessProgressEvent {
56+ at : string ;
57+ phase : "context" | "plan" | "sandbox" | "execute" | "collect" | "evaluate" | "decision" | "write" ;
58+ message : string ;
59+ loop ?: number ;
5260}
5361
5462export interface AgentExecutorInput {
@@ -65,6 +73,7 @@ export interface AgentExecutorInput {
6573 executorCommand ?: string ;
6674 dryRun ?: boolean ;
6775 onExecutorOutput ?: ( event : { stream : "stdout" | "stderr" ; text : string } ) => void ;
76+ onProgress ?: ( event : HarnessProgressEvent ) => void ;
6877}
6978
7079export interface AgentExecutorResult {
@@ -171,21 +180,29 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
171180 const executorName = options . executor ?? "mock" ;
172181 const maxLoops = Math . max ( 1 , options . maxLoops ?? 1 ) ;
173182 const root = path . resolve ( repo ) ;
183+ const progress = ( phase : HarnessProgressEvent [ "phase" ] , message : string , loop ?: number ) => {
184+ options . onProgress ?.( { at : new Date ( ) . toISOString ( ) , phase, message, loop } ) ;
185+ } ;
174186
187+ progress ( "context" , `building repository context for ${ root } ` ) ;
175188 const preContext = await buildContextPackage ( root ) ;
189+ progress ( "context" , "writing context package" ) ;
176190 const contextWrite = writeContextPackage ( preContext ) ;
177191 const executor = createAgentExecutor ( executorName ) ;
192+ progress ( "plan" , "writing task run and edit boundary" ) ;
178193 const taskRun = writeTaskRun ( preContext , task , { base, type : options . type ?? "auto" , tokenBudget : options . tokenBudget , preserveTrace : true } ) ;
179194 const dir = path . join ( root , ".agent-context" , "orchestrator" , taskRun . runId ) ;
180195 mkdirSync ( dir , { recursive : true } ) ;
181196 const checkpoint = createCheckpoint ( root , taskRun . runId , taskRun . dir , options . checkpoint ?? "none" ) ;
182197 const sandbox = createSandboxAdapter ( options . checkpoint ?? "none" ) ;
198+ progress ( "sandbox" , `preparing ${ options . checkpoint ?? "none" } sandbox` ) ;
183199 const sandboxHandle = await sandbox . prepare ( taskRun . runId , root ) ;
184200 let sandboxDiscarded = sandboxHandle . mode === "host" ;
185201 const iterations : OrchestratorIterationReport [ ] = [ ] ;
186202 let previousDecision : HarnessOrchestratorReport [ "decision" ] | undefined ;
187203 let latestContext = preContext ;
188204 if ( sandboxHandle . mode === "git-worktree" ) {
205+ progress ( "context" , "building context inside git-worktree sandbox" ) ;
189206 latestContext = await buildContextPackage ( sandboxHandle . root ) ;
190207 writeContextPackage ( latestContext ) ;
191208 writeTaskRun ( latestContext , task , { base, type : options . type ?? "auto" , tokenBudget : options . tokenBudget , preserveTrace : true } ) ;
@@ -200,7 +217,9 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
200217
201218 try {
202219 for ( let loopIndex = 1 ; loopIndex <= maxLoops ; loopIndex += 1 ) {
220+ progress ( "plan" , `starting loop ${ loopIndex } of ${ maxLoops } ` , loopIndex ) ;
203221 if ( loopIndex > 1 || previousDecision ?. action === "repack" ) {
222+ progress ( "context" , "refreshing context for next loop" , loopIndex ) ;
204223 latestContext = await buildContextPackage ( sandboxHandle . root ) ;
205224 writeContextPackage ( latestContext ) ;
206225 writeTaskRun ( latestContext , task , { base, type : options . type ?? "auto" , tokenBudget : options . tokenBudget , preserveTrace : true } ) ;
@@ -211,6 +230,7 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
211230 mkdirSync ( iterationDir , { recursive : true } ) ;
212231 const prompt = buildExecutorPrompt ( latestContext , taskRun , executorName , options , previousDecision , loopIndex ) ;
213232 const promptFile = write ( path . join ( iterationDir , "prompt.md" ) , prompt ) ;
233+ progress ( "execute" , `launching ${ executorName } executor` , loopIndex ) ;
214234 const executorResult = await executor ( {
215235 repo : sandboxHandle . root ,
216236 hostRepo : root ,
@@ -224,9 +244,12 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
224244 agent : options . agent ,
225245 executorCommand : options . executorCommand ,
226246 dryRun : options . dryRun ,
227- onExecutorOutput : options . onExecutorOutput
247+ onExecutorOutput : options . onExecutorOutput ,
248+ onProgress : options . onProgress
228249 } ) ;
250+ progress ( "collect" , `${ executorName } executor finished with exit code ${ executorResult . exitCode ?? "unknown" } ` , loopIndex ) ;
229251
252+ progress ( "collect" , "normalizing executor events" , loopIndex ) ;
230253 const normalized = normalizeAgentEvents ( {
231254 executor : executorName ,
232255 stdout : executorResult . stdout ,
@@ -246,6 +269,7 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
246269 appendExecutorTrace ( root , taskRun . runId , executorName , executorResult , loopIndex , normalized . warnings ) ;
247270 mirrorTraceForEvaluation ( root , sandboxHandle . root , taskRun . runId ) ;
248271
272+ progress ( "evaluate" , "running hallucination, regression, impact, policy, and verify checks" , loopIndex ) ;
249273 const postContext = await buildContextPackage ( sandboxHandle . root ) ;
250274 const hallucination = buildHallucinationReport ( postContext , { base, traceId : taskRun . runId , task } ) ;
251275 writeHallucinationReport ( postContext , hallucination ) ;
@@ -298,6 +322,7 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
298322 } else {
299323 latestDecision = decision ;
300324 }
325+ progress ( "decision" , `decision: ${ latestDecision . action } ` , loopIndex ) ;
301326
302327 const iterationFiles = writeIterationArtifacts ( root , iterationDir , {
303328 runId : taskRun . runId ,
@@ -438,6 +463,7 @@ export async function runHarnessOrchestrator(repo: string, task: string, options
438463 ] ;
439464 report . artifacts . orchestratorFiles = files . map ( ( file ) => path . relative ( root , file ) . replaceAll ( "\\" , "/" ) ) ;
440465 writeFileSync ( path . join ( dir , "orchestrator.json" ) , `${ JSON . stringify ( report , null , 2 ) } \n` , "utf8" ) ;
466+ progress ( "write" , `wrote orchestrator report to ${ path . relative ( root , path . join ( dir , "orchestrator.md" ) ) . replaceAll ( "\\" , "/" ) } ` ) ;
441467
442468 return { report, files } ;
443469 } finally {
0 commit comments