blob: 71bc2a7a8851d59501f87b395876e0cc24a98e7d [file]
import { randomUUID } from 'node:crypto';
import type { Config, ResultRecord, Task } from './contracts.js';
import { validateRealBackendIsolation } from './isolation.js';
import { resolveHeavyTaskMode } from './heavy-task-policy.js';
import { renderHeavyTaskProgressForPrompt } from './heavy-task-progress.js';
import {
authenticateHeadlessStorageWriter,
openHeadlessStorageForWrite,
type HeadlessStorageWriter,
} from './headless-storage.js';
import { backendNeedsIsolation, validateTaskVerification } from './runner.js';
import { budgetExtensionInboxItem } from './task-inbox.js';
import {
runTaskOnceWithStorage,
type RunTaskOnceDeps,
type RunTaskOnceResult,
} from './task-agent-controller.js';
import {
taxonomyFromResultRecord,
type AutonomousDecision,
type AutonomousResultTaxonomy,
type FeedbackObservation,
type SelfCheckObservation,
type TaskEvent,
type TaskInterventionPolicy,
type TaskRunError,
type TaskRunResult,
} from './task-contracts.js';
import type { TaskRunProjection } from './task-run-projection.js';
import type { TaskRunWriter } from './task-run-store.js';
import { taskDefinitionFromTask } from './task-run-adapter.js';
export interface AutonomousLoopBudget {
maxAttempts: number;
maxRuntimeSteps?: number;
maxWallTimeMs?: number;
}
export interface LoopBudgetSnapshot {
attemptsUsed: number;
maxAttempts: number;
runtimeStepsUsed: number;
maxRuntimeSteps?: number;
elapsedMs: number;
maxWallTimeMs?: number;
}
export interface SelfCheckPolicy {
observe(input: SelfCheckInput): SelfCheckOutput | Promise<SelfCheckOutput>;
}
export interface SelfCheckInput {
config: Config;
task: Task;
attempt: RunTaskOnceResult;
budget: LoopBudgetSnapshot;
}
export interface SelfCheckOutput {
summary: string;
details?: Record<string, unknown>;
}
export interface FeedbackPromptInput {
config: Config;
task: Task;
attempt: RunTaskOnceResult;
budget: LoopBudgetSnapshot;
feedback: readonly FeedbackObservation[];
selfCheck?: SelfCheckObservation;
}
export interface AutonomousDecisionInput {
config: Config;
task: Task;
attempt: RunTaskOnceResult;
budget: LoopBudgetSnapshot;
selfCheck?: SelfCheckObservation;
}
export interface AutonomousDecisionPolicyResult {
decision: AutonomousDecision['decision'];
reason?: string;
instructionOverride?: string;
details?: Record<string, unknown>;
}
export type AutonomousDecisionPolicy = (
input: AutonomousDecisionInput,
) => AutonomousDecisionPolicyResult | Promise<AutonomousDecisionPolicyResult>;
export interface RunAutonomousTaskOptions extends RunTaskOnceDeps {
budget: AutonomousLoopBudget;
replayPriorAttemptRuntimeContext?: boolean;
selfCheck?: false | SelfCheckPolicy;
feedbackPrompt?: (input: FeedbackPromptInput) => string;
decision?: AutonomousDecisionPolicy;
}
export interface RunAutonomousTaskResult {
taskRunId: string;
attempts: RunTaskOnceResult[];
projection: TaskRunProjection;
/** Latest authoritative attempt result; loop/cap terminal status lives in projection. */
resultRecord: ResultRecord;
}
export class AutonomousAgentLoop {
constructor(private readonly deps: RunAutonomousTaskOptions) {}
run(config: Config, task: Task): Promise<RunAutonomousTaskResult> {
return runAutonomousTask(config, task, this.deps);
}
}
export async function runAutonomousTask(
config: Config,
task: Task,
options: RunAutonomousTaskOptions,
): Promise<RunAutonomousTaskResult> {
const storage = await openHeadlessStorageForWrite(options.storageRoot);
return runAutonomousTaskWithStorage(config, task, options, storage);
}
export async function runAutonomousTaskWithStorage(
config: Config,
task: Task,
options: RunAutonomousTaskOptions,
storage: HeadlessStorageWriter,
): Promise<RunAutonomousTaskResult> {
storage = authenticateHeadlessStorageWriter(storage);
const now = options.now ?? Date.now;
const newId = options.newId ?? randomUUID;
const taskRunId = options.taskRunId ?? newId();
const taskRunStore = storage.taskRunStore;
const startedAt = now();
const attempts: RunTaskOnceResult[] = [];
await appendTaskEvent(taskRunStore, taskRunId, {
type: 'task_run_created',
id: newId(),
taskRunId,
ts: startedAt,
taskId: task.id,
configId: config.id,
taskDefinition: taskDefinitionFromTask(task),
});
await appendTaskEvent(taskRunStore, taskRunId, {
type: 'task_run_queued',
id: newId(),
taskRunId,
ts: now(),
taskId: task.id,
configId: config.id,
taskDefinition: taskDefinitionFromTask(task),
});
const budgetError = validateBudget(options.budget);
if (budgetError) {
const finishedAt = now();
const resultRecord = syntheticResultRecord(
config,
task,
taskRunId,
'setup_failed',
budgetError,
startedAt,
finishedAt,
);
await appendTaskEvent(
taskRunStore,
taskRunId,
terminalEventFromResultRecord(resultRecord, taskRunId, newId),
);
return { taskRunId, attempts, projection: await taskRunStore.project(taskRunId), resultRecord };
}
try {
if (backendNeedsIsolation(config.backend)) {
validateRealBackendIsolation(options.realBackendIsolation);
if (!options.registerBackends) {
throw new Error(
`@maka/headless: backend "${config.backend}" requires registerBackends to wire an isolated backend factory`,
);
}
}
validateTaskVerification(task);
} catch (error) {
const finishedAt = now();
const resultRecord = syntheticResultRecord(
config,
task,
taskRunId,
'setup_failed',
error instanceof Error ? error.message : String(error),
startedAt,
finishedAt,
);
await appendTaskEvent(
taskRunStore,
taskRunId,
terminalEventFromResultRecord(resultRecord, taskRunId, newId),
);
return { taskRunId, attempts, projection: await taskRunStore.project(taskRunId), resultRecord };
}
let instructionOverride: string | undefined = options.instructionOverride;
let runtimeStepsUsed = 0;
let latestResultRecord: ResultRecord | undefined;
let priorRuntimeContext = options.priorRuntimeContext ? [...options.priorRuntimeContext] : [];
const budgetStartedAt = now();
while (attempts.length < options.budget.maxAttempts) {
const beforeAttemptBudget = budgetSnapshot(
options.budget,
attempts.length,
runtimeStepsUsed,
budgetStartedAt,
now(),
);
if (attempts.length > 0 && isWallTimeExhausted(beforeAttemptBudget)) {
await appendSystemFeedback(
taskRunStore,
taskRunId,
undefined,
now,
newId,
'wall time cap reached before attempt',
{ budget: beforeAttemptBudget },
);
const finishedAt = now();
const resultRecord =
latestResultRecord ??
syntheticResultRecord(
config,
task,
taskRunId,
'budget_exhausted',
'wall time cap reached before attempt',
startedAt,
finishedAt,
);
await appendBudgetTerminal(
taskRunStore,
taskRunId,
now,
newId,
beforeAttemptBudget,
'wall time cap reached before attempt',
options.interventionPolicy,
);
return {
taskRunId,
attempts,
projection: await taskRunStore.project(taskRunId),
resultRecord,
};
}
const attemptNumber = attempts.length + 1;
const attemptId = `${taskRunId}-attempt-${attemptNumber}`;
const attempt = await runTaskOnceWithStorage(
config,
task,
{
...options,
taskRunId,
attemptId,
createTaskRun: false,
closeTaskRun: false,
...(instructionOverride ? { instructionOverride } : {}),
...(priorRuntimeContext.length > 0 ? { priorRuntimeContext } : {}),
},
storage,
);
attempts.push(attempt);
latestResultRecord = attempt.resultRecord;
runtimeStepsUsed += attempt.resultRecord.steps;
if (options.replayPriorAttemptRuntimeContext) {
priorRuntimeContext = [
...priorRuntimeContext,
...attempt.invocations.flatMap((invocation) => invocation.events),
];
}
const afterAttemptBudget = budgetSnapshot(
options.budget,
attempts.length,
runtimeStepsUsed,
budgetStartedAt,
now(),
);
if (attempt.settledByDeadline) {
await appendBudgetTerminal(
taskRunStore,
taskRunId,
now,
newId,
afterAttemptBudget,
'benchmark deadline reached during attempt',
);
return {
taskRunId,
attempts,
projection: await taskRunStore.project(taskRunId),
resultRecord: attempt.resultRecord,
};
}
const selfCheck = isWallTimeExhausted(afterAttemptBudget)
? undefined
: await maybeRecordSelfCheck(
options.selfCheck,
{
config,
task,
attempt,
budget: afterAttemptBudget,
},
taskRunStore,
taskRunId,
attemptId,
now,
newId,
);
const verifierFeedback = await appendVerifierFeedback(
taskRunStore,
taskRunId,
attemptId,
now,
newId,
attempt.resultRecord,
attempt.projection.latestVerifierResult?.id,
attempt.projection.latestScoreResult?.id,
);
if (selfCheck) {
await appendSystemFeedback(
taskRunStore,
taskRunId,
attemptId,
now,
newId,
'self-check recorded as advisory feedback',
{ selfCheckId: selfCheck.id, selfCheckSummary: selfCheck.summary },
);
}
const defaultDecision = defaultDecisionForAttempt(attempt, afterAttemptBudget);
const policyDecision = options.decision
? await options.decision({ config, task, attempt, budget: afterAttemptBudget, selfCheck })
: defaultDecision;
const decision = enforceCaps(policyDecision, attempt, afterAttemptBudget);
const nextInstruction =
decision.instructionOverride ??
options.feedbackPrompt?.({
config,
task,
attempt,
budget: afterAttemptBudget,
feedback: [verifierFeedback],
...(selfCheck ? { selfCheck } : {}),
}) ??
defaultContinuationPrompt(config, task, attempt, selfCheck);
await appendDecision(
taskRunStore,
taskRunId,
attemptId,
now,
newId,
decision,
attempt,
afterAttemptBudget,
selfCheck,
);
if (decision.decision === 'continue' || decision.decision === 'retry') {
instructionOverride = nextInstruction;
continue;
}
if (shouldBudgetTerminal(decision, attempt, afterAttemptBudget)) {
await appendBudgetTerminal(
taskRunStore,
taskRunId,
now,
newId,
afterAttemptBudget,
decision.reason ?? 'loop budget exhausted',
options.interventionPolicy,
);
} else {
await appendTaskEvent(
taskRunStore,
taskRunId,
terminalEventFromResultRecord(attempt.resultRecord, taskRunId, newId, {
verifierResultId: attempt.projection.latestVerifierResult?.id,
scoreResultId: attempt.projection.latestScoreResult?.id,
}),
);
}
return {
taskRunId,
attempts,
projection: await taskRunStore.project(taskRunId),
resultRecord: attempt.resultRecord,
};
}
const exhaustedBudget = budgetSnapshot(
options.budget,
attempts.length,
runtimeStepsUsed,
budgetStartedAt,
now(),
);
if (latestResultRecord?.passed) {
const latestAttempt = attempts[attempts.length - 1];
await appendTaskEvent(
taskRunStore,
taskRunId,
terminalEventFromResultRecord(latestResultRecord, taskRunId, newId, {
verifierResultId: latestAttempt?.projection.latestVerifierResult?.id,
scoreResultId: latestAttempt?.projection.latestScoreResult?.id,
}),
);
return {
taskRunId,
attempts,
projection: await taskRunStore.project(taskRunId),
resultRecord: latestResultRecord,
};
}
await appendBudgetTerminal(
taskRunStore,
taskRunId,
now,
newId,
exhaustedBudget,
'max attempts exhausted',
options.interventionPolicy,
);
const resultRecord =
latestResultRecord ??
syntheticResultRecord(
config,
task,
taskRunId,
'budget_exhausted',
'max attempts exhausted',
startedAt,
now(),
);
return { taskRunId, attempts, projection: await taskRunStore.project(taskRunId), resultRecord };
}
function validateBudget(budget: AutonomousLoopBudget): string | undefined {
if (!Number.isInteger(budget.maxAttempts) || budget.maxAttempts < 1) {
return 'budget.maxAttempts must be a positive integer';
}
if (
budget.maxRuntimeSteps !== undefined &&
(!Number.isInteger(budget.maxRuntimeSteps) || budget.maxRuntimeSteps < 1)
) {
return 'budget.maxRuntimeSteps must be a positive integer when provided';
}
if (
budget.maxWallTimeMs !== undefined &&
(!Number.isInteger(budget.maxWallTimeMs) || budget.maxWallTimeMs < 1)
) {
return 'budget.maxWallTimeMs must be a positive integer when provided';
}
return undefined;
}
function budgetSnapshot(
budget: AutonomousLoopBudget,
attemptsUsed: number,
runtimeStepsUsed: number,
startedAt: number,
currentTime: number,
): LoopBudgetSnapshot {
return {
attemptsUsed,
maxAttempts: budget.maxAttempts,
runtimeStepsUsed,
...(budget.maxRuntimeSteps !== undefined ? { maxRuntimeSteps: budget.maxRuntimeSteps } : {}),
elapsedMs: Math.max(0, currentTime - startedAt),
...(budget.maxWallTimeMs !== undefined ? { maxWallTimeMs: budget.maxWallTimeMs } : {}),
};
}
function isWallTimeExhausted(budget: LoopBudgetSnapshot): boolean {
return budget.maxWallTimeMs !== undefined && budget.elapsedMs >= budget.maxWallTimeMs;
}
function defaultDecisionForAttempt(
attempt: RunTaskOnceResult,
budget: LoopBudgetSnapshot,
): AutonomousDecisionPolicyResult {
const taxonomy = taxonomyFromResultRecord(attempt.resultRecord);
if (attempt.resultRecord.passed && taxonomy === 'passed') {
return { decision: 'stop', reason: 'authoritative verification passed' };
}
if (isNonRetryable(taxonomy)) {
return {
decision: taxonomy === 'aborted' || taxonomy === 'cancelled' ? 'abort' : 'stop',
reason: `${taxonomy} is not retryable`,
};
}
if (budget.attemptsUsed >= budget.maxAttempts) {
return { decision: 'stop', reason: 'max attempts exhausted' };
}
if (budget.maxRuntimeSteps !== undefined && budget.runtimeStepsUsed >= budget.maxRuntimeSteps) {
return { decision: 'stop', reason: 'runtime step cap reached' };
}
if (isWallTimeExhausted(budget)) {
return { decision: 'stop', reason: 'wall time cap reached' };
}
return { decision: 'continue', reason: `${taxonomy} can be retried while budget remains` };
}
function enforceCaps(
decision: AutonomousDecisionPolicyResult,
attempt: RunTaskOnceResult,
budget: LoopBudgetSnapshot,
): AutonomousDecisionPolicyResult {
if (decision.decision !== 'continue' && decision.decision !== 'retry') return decision;
if (attempt.resultRecord.passed)
return { ...decision, decision: 'stop', reason: 'authoritative verification passed' };
if (budget.attemptsUsed >= budget.maxAttempts)
return { ...decision, decision: 'stop', reason: 'max attempts exhausted' };
if (budget.maxRuntimeSteps !== undefined && budget.runtimeStepsUsed >= budget.maxRuntimeSteps) {
return { ...decision, decision: 'stop', reason: 'runtime step cap reached' };
}
if (isWallTimeExhausted(budget))
return { ...decision, decision: 'stop', reason: 'wall time cap reached' };
return decision;
}
function shouldBudgetTerminal(
decision: AutonomousDecisionPolicyResult,
attempt: RunTaskOnceResult,
budget: LoopBudgetSnapshot,
): boolean {
if (attempt.resultRecord.passed) return false;
if (decision.reason?.includes('max attempts') || decision.reason?.includes('cap')) return true;
if (budget.attemptsUsed >= budget.maxAttempts) return true;
if (budget.maxRuntimeSteps !== undefined && budget.runtimeStepsUsed >= budget.maxRuntimeSteps)
return true;
return isWallTimeExhausted(budget);
}
function isNonRetryable(taxonomy: AutonomousResultTaxonomy): boolean {
return (
taxonomy === 'policy_denied' ||
taxonomy === 'blocked' ||
taxonomy === 'aborted' ||
taxonomy === 'cancelled' ||
taxonomy === 'setup_failed' ||
taxonomy === 'infra_failed'
);
}
async function maybeRecordSelfCheck(
policy: false | SelfCheckPolicy | undefined,
input: SelfCheckInput,
store: TaskRunWriter,
taskRunId: string,
attemptId: string,
now: () => number,
newId: () => string,
): Promise<SelfCheckObservation | undefined> {
if (!policy) return undefined;
const output = await policy.observe(input);
const ts = now();
const observation: SelfCheckObservation = {
id: newId(),
taskRunId,
attemptId,
ts,
summary: output.summary.slice(0, 1000),
...(output.details ? { details: output.details } : {}),
};
await appendTaskEvent(store, taskRunId, {
type: 'self_check_observed',
id: newId(),
taskRunId,
ts,
observation,
});
return observation;
}
async function appendVerifierFeedback(
store: TaskRunWriter,
taskRunId: string,
attemptId: string,
now: () => number,
newId: () => string,
record: ResultRecord,
verifierResultId: string | undefined,
scoreResultId: string | undefined,
): Promise<FeedbackObservation> {
const taxonomy = taxonomyFromResultRecord(record);
const ts = now();
const observation: FeedbackObservation = {
id: newId(),
taskRunId,
attemptId,
ts,
source: 'verifier',
summary: record.passed ? 'verification passed' : `verification did not pass: ${taxonomy}`,
details: {
passed: record.passed,
taxonomy,
status: record.status,
exitCode: record.exitCode,
steps: record.steps,
...(record.error ? { error: record.error } : {}),
...(record.errorClass ? { errorClass: record.errorClass } : {}),
...(verifierResultId ? { verifierResultId } : {}),
...(scoreResultId ? { scoreResultId } : {}),
},
};
await appendTaskEvent(store, taskRunId, {
type: 'feedback_observed',
id: newId(),
taskRunId,
ts,
observation,
});
return observation;
}
async function appendSystemFeedback(
store: TaskRunWriter,
taskRunId: string,
attemptId: string | undefined,
now: () => number,
newId: () => string,
summary: string,
details: Record<string, unknown>,
): Promise<void> {
const ts = now();
const observation: FeedbackObservation = {
id: newId(),
taskRunId,
...(attemptId ? { attemptId } : {}),
ts,
source: 'system',
summary,
details,
};
await appendTaskEvent(store, taskRunId, {
type: 'feedback_observed',
id: newId(),
taskRunId,
ts,
observation,
});
}
async function appendDecision(
store: TaskRunWriter,
taskRunId: string,
attemptId: string,
now: () => number,
newId: () => string,
decision: AutonomousDecisionPolicyResult,
attempt: RunTaskOnceResult,
budget: LoopBudgetSnapshot,
selfCheck: SelfCheckObservation | undefined,
): Promise<void> {
const ts = now();
const recorded: AutonomousDecision = {
id: newId(),
taskRunId,
attemptId,
ts,
decision: decision.decision,
...(decision.reason ? { reason: decision.reason } : {}),
details: {
attemptNumber: budget.attemptsUsed,
budget,
latestScoreId: attempt.projection.latestScoreResult?.id,
latestVerifierId: attempt.projection.latestVerifierResult?.id,
latestTaxonomy: taxonomyFromResultRecord(attempt.resultRecord),
selfCheckContributed: Boolean(selfCheck),
...(selfCheck ? { selfCheckId: selfCheck.id } : {}),
...(decision.details ? decision.details : {}),
},
};
await appendTaskEvent(store, taskRunId, {
type: 'autonomous_decision_recorded',
id: newId(),
taskRunId,
ts,
decision: recorded,
});
}
async function appendBudgetTerminal(
store: TaskRunWriter,
taskRunId: string,
now: () => number,
newId: () => string,
budget: LoopBudgetSnapshot,
reason: string,
policy: TaskInterventionPolicy | undefined = undefined,
): Promise<void> {
const ts = now();
if (policy?.mode === 'park' && policy.allowBudgetExtensionRequests) {
const item = budgetExtensionInboxItem({
inboxItemId: newId(),
taskRunId,
reason,
createdAt: ts,
budget: { ...budget },
});
await appendTaskEvent(store, taskRunId, {
type: 'task_inbox_item_recorded',
id: newId(),
taskRunId,
ts,
item,
});
await appendTaskEvent(store, taskRunId, {
type: 'task_run_needs_approval',
id: newId(),
taskRunId,
ts,
reason: 'budget_extension',
inboxItemId: item.inboxItemId,
});
return;
}
await appendTaskEvent(store, taskRunId, {
type: 'task_run_budget_exhausted',
id: newId(),
taskRunId,
ts,
finishedAt: ts,
error: {
message: reason,
class: 'budget_exhausted',
details: { budget },
},
});
}
function terminalEventFromResultRecord(
record: ResultRecord,
taskRunId: string,
newId: () => string,
refs: { verifierResultId?: string; scoreResultId?: string } = {},
): TaskEvent {
const taxonomy = taxonomyFromResultRecord(record);
const result: TaskRunResult = {
passed: record.passed,
taxonomy,
...(refs.verifierResultId ? { verifierResultId: refs.verifierResultId } : {}),
...(refs.scoreResultId ? { scoreResultId: refs.scoreResultId } : {}),
};
const base = { id: newId(), taskRunId, ts: record.finishedAt, finishedAt: record.finishedAt };
if (record.status === 'completed') {
return { type: 'task_run_completed', ...base, result };
}
const error = errorFromResultRecord(record, taxonomy);
switch (taxonomy) {
case 'agent_incomplete':
return { type: 'task_run_incomplete', ...base, error };
case 'blocked':
return { type: 'task_run_blocked', ...base, error };
case 'policy_denied':
return { type: 'task_run_policy_denied', ...base, error };
case 'budget_exhausted':
return { type: 'task_run_budget_exhausted', ...base, error };
case 'aborted':
return { type: 'task_run_aborted', ...base, error };
case 'cancelled':
return { type: 'task_run_cancelled', ...base, error };
case 'passed':
case 'verification_failed':
case 'verification_error':
case 'agent_failed':
case 'invalid_setup':
case 'unsupported_adapter':
case 'isolation_required':
case 'setup_failed':
case 'infra_failed':
return { type: 'task_run_failed', ...base, error };
}
}
function syntheticResultRecord(
config: Config,
task: Task,
taskRunId: string,
taxonomy: AutonomousResultTaxonomy,
message: string,
startedAt: number,
finishedAt: number,
): ResultRecord {
return {
taskId: task.id,
configId: config.id,
sessionId: taskRunId,
runId: taskRunId,
status: 'failed',
passed: false,
exitCode: null,
steps: 0,
durationMs: finishedAt - startedAt,
startedAt,
finishedAt,
error: message,
errorClass: taxonomy,
};
}
function errorFromResultRecord(
record: ResultRecord,
taxonomy: AutonomousResultTaxonomy,
): TaskRunError {
return {
message: record.error ?? errorMessageFromTaxonomy(taxonomy),
...(record.errorClass ? { class: record.errorClass } : { class: taxonomy }),
};
}
function errorMessageFromTaxonomy(taxonomy: AutonomousResultTaxonomy): string {
switch (taxonomy) {
case 'agent_failed':
return 'agent run failed';
case 'agent_incomplete':
return 'agent run incomplete';
case 'invalid_setup':
return 'invalid setup';
case 'unsupported_adapter':
return 'unsupported verifier adapter';
case 'isolation_required':
return 'isolated executor required';
case 'setup_failed':
return 'task setup failed';
case 'infra_failed':
return 'infrastructure failed';
case 'verification_error':
return 'verification errored';
case 'policy_denied':
return 'task run denied by policy';
case 'budget_exhausted':
return 'task run budget exhausted';
case 'aborted':
return 'task run aborted';
case 'blocked':
return 'task run blocked';
case 'cancelled':
return 'task run cancelled';
case 'verification_failed':
return 'verification failed';
case 'passed':
return 'task run failed';
}
}
function defaultContinuationPrompt(
config: Config,
task: Task,
attempt: RunTaskOnceResult,
selfCheck: SelfCheckObservation | undefined,
): string {
const taxonomy = taxonomyFromResultRecord(attempt.resultRecord);
const selfCheckLine = selfCheck ? `\nAdvisory self-check: ${selfCheck.summary}` : '';
const progressBlock = resolveHeavyTaskMode(config, task).enabled
? renderHeavyTaskProgressForPrompt(attempt.projection)
: undefined;
const progressLine = progressBlock ? `\n\n${progressBlock}` : '';
return `${task.instruction}
Previous autonomous attempt did not pass authoritative verification.
Verifier taxonomy: ${taxonomy}.
Verification exit code: ${attempt.resultRecord.exitCode ?? 'none'}.
Continue from that feedback and produce a corrected solution.${selfCheckLine}${progressLine}`;
}
function appendTaskEvent(store: TaskRunWriter, taskRunId: string, event: TaskEvent): Promise<void> {
return store.appendEvent(taskRunId, event);
}