LoopResult,
LoopStatus,
LoopStrategy,
+ RoundSnapshot,
SandboxInstance,
TaskSpec,
} from './types.js'
iterationBudget?: number
/** Maximum number of implement↔critic rounds. */
maxRounds?: number
- /** Optional callback invoked after each round completes. */
- onRoundComplete?: (round: number, findings: Finding[]) => void
/** When true, run one extra actor attempt if post-loop validation fails. */
postLoopValidationRetry?: boolean
/** Abort signal for cooperative cancellation (kills in-flight agent subprocesses). */
budget: number
/** Maximum number of rounds. */
maxRounds: number
- /** Optional round-complete callback (no-op if not provided). */
- onRoundComplete: (round: number, findings: Finding[]) => void
}
/** Result of a single implement↔critic round. */
strategy: LoopStrategy,
opts?: RefinementLoopOptions
): Promise<LoopResult> {
- const { baseBranch, budget, maxRounds, onRoundComplete } = resolveLoopOptions(opts)
+ const { baseBranch, budget, maxRounds } = resolveLoopOptions(opts)
const signal = opts?.signal
const validate = strategy.validate ?? ((cwd: string, s: TaskSpec) => runValidation(cwd, s))
const ctx: LoopContext = { baseBranch, sandbox, signal, spec, strategy }
const seenKeys = new Set<string>()
+ const roundHistory: RoundSnapshot[] = []
let failureReason: string | undefined
let lastFindings: Finding[] = []
let status: LoopStatus = 'exhausted'
const result = await executeRound(ctx, round, budget, lastFindings)
+ roundHistory.push(buildRoundSnapshot(result, round))
+
const earlyExit = checkEarlyExit(spec, round, result, totalCommits)
if (earlyExit !== null) {
totalCommits = earlyExit.totalCommits
totalCommits += result.commits
previousFindingsCount = nonLowFindings.length
- onRoundComplete(round, findings)
if (strategy.shouldConverge?.(findings, round, totalCommits)) {
lastFindings = findings
status = 'converged'
} else if (roundsCompleted < maxRounds) {
const result = await executeRound(ctx, roundsCompleted + 1, budget, lastFindings)
+ roundHistory.push(buildRoundSnapshot(result, roundsCompleted + 1))
if (result.commits > 0) {
totalCommits += result.commits
if (await validate(sandbox.worktreePath, spec)) {
totalCommits = await resetToBestState(sandbox.worktreePath, bestSha, totalCommits, baseBranch)
}
- return { baseBranch, failureReason, lastFindings, roundsCompleted, status, totalCommits }
+ return {
+ baseBranch,
+ failureReason,
+ lastFindings,
+ roundHistory,
+ roundsCompleted,
+ status,
+ totalCommits,
+ }
+}
+
+/**
+ *
+ * @param result
+ * @param round
+ */
+function buildRoundSnapshot (result: RoundResult, round: number): RoundSnapshot {
+ return {
+ commits: result.commits,
+ findings: result.findings ?? [],
+ round,
+ status:
+ result.findings === null
+ ? 'critic_errored'
+ : result.findings.length > 0
+ ? 'has_findings'
+ : 'no_findings',
+ }
}
/**
baseBranch: opts?.baseBranch ?? GIT_BASE_BRANCH,
budget: opts?.iterationBudget ?? AGENT_ITERATION_BUDGET,
maxRounds: opts?.maxRounds ?? AGENT_MAX_CRITIC_ROUNDS,
- onRoundComplete: opts?.onRoundComplete ?? (() => undefined),
}
}
failureReason?: string
/** Outstanding findings from the last round. */
lastFindings: Finding[]
- /** Number of rounds completed. */
+ /**
+ * Complete findings history across all rounds.
+ * Authoritative source — `lastFindings` is kept for backward compatibility.
+ */
+ roundHistory: RoundSnapshot[]
+ /** Number of main-loop rounds completed (excludes post-loop validation retry). */
roundsCompleted: number
/** Termination status. */
status: LoopStatus
validate?: (cwd: string, spec: TaskSpec) => Promise<boolean>
}
+/** Snapshot of a single implement↔critic round. */
+export interface RoundSnapshot {
+ /** Number of commits the actor produced this round. */
+ commits: number
+ /** Findings from the critic (empty array if critic errored). */
+ findings: Finding[]
+ /** 1-indexed round number. */
+ round: number
+ /** Outcome of the critic phase for this round. */
+ status: 'critic_errored' | 'has_findings' | 'no_findings'
+}
+
/** Type alias for a sandcastle sandbox instance. */
export type SandboxInstance = Awaited<ReturnType<typeof sandcastle.createSandbox>>
id: string
/** Label names associated with the task (platform-specific, optional). */
labels?: string[]
+ /** Raw planner agent output that produced this task selection. */
+ plannerOutput?: string
/** Task title. */
title: string
}