/** * design/96 §C (S2) — **Goal 模式**:LLM 自报完成 + core 校验续跑(CC goal 对标:目标 + 完成判定 + 续跑)。 * `runGoal` 是 `runRepairLoop`/`verifyCompleted` 的 SIBLING —— thin composition over `runner.runTask`,零 * Runner core 改动。详 `design/96-references/GOAL-IMPLEMENTER-SPEC.md`(v3,codex r1+r2 异源对抗收敛)。 * * 🔴 **完成判定双闸(reward-hack 红线)**:① LLM 自报完成 = 调内置 `declare_done` 工具(或 `submit_output`,有 * outputSchema 时)= **机器信号**,非泛化 `completed`;② core `doneCheck`(机械/oracle 校验)。**AND**:仅二者 * 都满足才停 —— LLM 自报但 doneCheck 否决 → 续跑(把 reason 围栏喂回),这是防"模型说我完成了就算数"的核心。 * * 🔴 **G1 脱钩(不可绕)**:`status:"achieved"` = 双闸过 → **停迭代 + SURFACE**,`accepted` 恒 false。runGoal * **无任何 commit/accept 副作用**(纯 loop,同 runRepairLoop SAFE-tier 永不 `fixed`)。要 accept,caller 自走 * design/77 Gate-1 out-of-process oracle 隔离边界,不在 goal helper 开后门。 */ import type { Runner } from "../core/runner/runtask.js"; import type { CheckpointToken } from "../core/checkpoint-store.js"; import type { TaskResult, TaskSpec } from "../core/types.js"; /** Read-only snapshot a `doneCheck` sees after one iteration. */ export interface GoalTurnState { /** This iteration's full {@link TaskResult}. */ readonly result: TaskResult; /** 1-based iteration index. */ readonly iteration: number; /** The session threaded through the whole goal (so a doneCheck can read the session/working tree). */ readonly sessionId: string; /** Cumulative tokens (own + nested) across iterations — observe-only (the budget gate is in the engine). */ readonly cumulativeTokens: number; } export interface GoalVerdict { /** core verification passed? (AND-ed with the LLM self-report — the double gate). */ readonly done: boolean; /** When not done: the specific feedback fed back to the LLM (fenced as untrusted data). */ readonly reason: string; } /** The caller's HONEST declaration of how `doneCheck` verifies — surfaced on the result so a * `self_report_only` "achieved" is never mistaken for a mechanically-verified one (G1, reward-hack honesty). */ export type GoalVerificationKind = "mechanical" | "self_report_only"; export interface GoalSpec { /** The goal (injected as iteration 1's objective + GOAL_COMPLETION_GUIDANCE via the trusted goalMode flag). */ objective: string; /** * 🔴 The CORE completion gate (REQUIRED). The LLM self-report (gate 1) is necessary but NOT sufficient: the * goal stops as `achieved` only when gate 1 AND `doneCheck.done`. Provide a PURE read-check (it may read the * session/working tree but must have no side effects — it can be cancelled/timed out). A throw/reject * fail-closes to `failed`/`goal.donecheck_error` (never treated as achieved). Core ships no default — * supplying it forces the caller to confront the reward-hack red line. */ doneCheck: (state: GoalTurnState, signal: AbortSignal) => Promise; /** Honest declaration of `doneCheck`'s substance (§3.2). `self_report_only` ⇒ surfaced on the result. */ verificationKind: GoalVerificationKind; /** Hard iteration cap (≥1) — exceeding without the double gate → `max_iterations` (NOT achieved). */ maxIterations: number; /** Per-iteration base spec (model/tools/toolPolicy/principal/env/outputSchema/…). `objective` is overridden * per iteration; `sessionId` is minted to thread the whole goal if absent; `signal` is set by the engine. */ taskSpec: Omit; /** Cumulative token ceiling (own + nested); reaching it stops with `budget`. */ budgetTokens?: number; /** Whole-goal wall-clock ceiling (ms); reaching it stops with `budget`. */ totalTimeoutMs?: number; /** Cancels the whole goal (folded into each iteration's signal + the doneCheck). */ signal?: AbortSignal; /** * design/73 §1 — aggregation key of the `TaskOutcome` emitted at the goal's terminal state (only when * `verificationKind:"mechanical"` — the red line). Default: `goal:`, * deterministic per objective. Set it when the "same task" spans differently-worded objectives. */ taskSignature?: string; } export type GoalStatus = "achieved" | "max_iterations" | "budget" | "blocked" | "suspended" | "needs_review" | "failed" | "aborted"; export interface GoalResult { readonly status: GoalStatus; /** Last iteration's result; absent on a preflight-terminate (a stop BEFORE iteration 1 ran). */ readonly result?: TaskResult; readonly iterations: number; /** The last `doneCheck` verdict (present once a doneCheck ran). */ readonly lastVerdict?: GoalVerdict; /** The caller's declared verification kind (always present — replaces the ambiguous "verifiedBy"). */ readonly verificationKind: GoalVerificationKind; /** 🔴 G1 explicit: runGoal NEVER accepts the artifact. Always false. `achieved` = "stopped + awaiting * review"; the caller decides acceptance (via a Gate-1 boundary), never runGoal. */ readonly accepted: false; readonly cumulativeTokens: number; /** Carried up on suspended/needs_review so the caller can resume the underlying task. */ readonly checkpointToken?: CheckpointToken; /** Set on `failed` (incl. `goal.donecheck_error`). */ readonly errorCode?: string; } /** Reserved name of the goal-completion signal tool (injected by `runGoal` when there is no outputSchema). */ export declare const DECLARE_DONE_TOOL_NAME = "DeclareDone"; /** * Run a goal to a double-gated completion (or a bound). Iterates `runner.runTask` on ONE session: each * iteration the LLM works and signals completion (declare_done / submit_output); `doneCheck` then verifies. * Only `declare_done` AND `doneCheck.done` → `achieved` (surfaced, NEVER auto-accepted, G1). A rejected * doneCheck feeds its reason back (fenced) and continues. Bounded by maxIterations / budget / wall-clock / * cancel; a durable pause (suspended/needs_review) is a hard boundary that surfaces the checkpointToken. */ export declare function runGoal(runner: Runner, spec: GoalSpec): Promise; //# sourceMappingURL=goal.d.ts.map