import { hasAllCriteriaPass, isFinalRunCompletionCandidate } from "./goal-status.js"; import { createGoalsAction } from "./plan-missing-recovery.js"; import type { UlwLoopToolkitSurface } from "./surface.js"; import type { UlwLoopItem, UlwLoopPlan } from "./types.js"; /** * Status is the one call every agent already makes between steps, so it is * also the cheapest place to answer "what now?" without a second round trip. */ export function statusNextActions(plan: UlwLoopPlan, surface: UlwLoopToolkitSurface = "lazycodex"): readonly string[] { const actions: string[] = []; const active = plan.goals.find((goal) => goal.id === plan.activeGoalId); if (plan.goals.length === 0) actions.push(`No goals yet: bootstrap with \`${createGoalsAction(surface)}\`.`); else if (active !== undefined) actions.push(...activeGoalActions(plan, active, surface)); for (const goal of plan.goals.filter((candidate) => candidate.status === "review_blocked")) actions.push( surface === "omo-senpi" ? `${goal.id} is review_blocked: capture the reviewer verdict with \`agentToolkit.recordReviewBlockers({ goalId: "${goal.id}", title: "", objective: "<objective>", evidence: "<verdict>" })\`.` : `${goal.id} is review_blocked: capture the reviewer verdict with \`omo-agent-toolkit ulw-loop record-review-blockers --goal-id ${goal.id} --title "<title>" --objective "<objective>" --evidence "<verdict>" --codex-goal-json '<get_goal json>'\`.`, ); if (plan.evidenceLayoutVersion !== 2 || active === undefined) actions.push("plan is evidence-layout v1; artifacts go under .omo/evidence/"); return actions; } function activeGoalActions(plan: UlwLoopPlan, active: UlwLoopItem, surface: UlwLoopToolkitSurface): readonly string[] { const unresolved = active.successCriteria.filter((criterion) => criterion.status !== "pass"); if (unresolved.length > 0) return [ surface === "omo-senpi" ? `${active.id} has ${unresolved.length} unresolved criterion(s) (${unresolved.map((criterion) => criterion.id).join(", ")}): record proof with \`agentToolkit.recordEvidence({ goalId: "${active.id}", criterionId: "<id>", status: "pass", evidence: "<observable proof>" })\`.` : `${active.id} has ${unresolved.length} unresolved criterion(s) (${unresolved.map((criterion) => criterion.id).join(", ")}): record proof with \`omo-agent-toolkit ulw-loop record-evidence --goal-id ${active.id} --criterion-id <id> --status pass --evidence "<observable proof>"\`.`, ]; if (hasAllCriteriaPass(active) && isFinalRunCompletionCandidate(plan, active)) return [ surface === "omo-senpi" ? `${active.id} passes every criterion and is the final story: complete the driver goal, then call \`agentToolkit.checkpoint({ goalId: "${active.id}", printTemplate: true })\` to build the final quality gate. Use status().result.currentAttemptDir for all quality-gate artifacts.` : `${active.id} passes every criterion and is the final story: update_goal complete, then checkpoint --print-template to build the final quality gate. Use status --json's currentAttemptDir for all quality-gate artifacts.`, ]; return [ surface === "omo-senpi" ? `${active.id} passes every criterion: close it with \`agentToolkit.checkpoint({ goalId: "${active.id}", status: "complete", evidence: "<proof>" })\`. Put quality-gate artifacts under status().result.currentAttemptDir.` : `${active.id} passes every criterion: close it with \`omo-agent-toolkit ulw-loop checkpoint --goal-id ${active.id} --status complete --evidence "<proof>" --codex-goal-json '<get_goal json>'\`. Put quality-gate artifacts under the currentAttemptDir shown by status --json.`, ]; }