/** * The leaf executor: the ONE place farketari launches a model. Every AI * step in the algorithm is a WorkOrder (standing instructions for the * work kind + the resolved model backend) plus a task (the specific * situation); this runs it as a Claude Code session in the given working * directory and returns the session's final message text. * * Role implementations stay thin compositions over this: they phrase the * task, call claudeStep, and (where the algorithm needs data back) parse * the final text. Model routing is entirely the order's: order.model * names the model, and a backend with a baseUrl (the easy tier's router) * is reached by pointing the session's ANTHROPIC_BASE_URL at it. */ import type { WorkOrder } from "./prompts.js"; /** * A mutable handle a ROLE owns to chain consecutive steps into ONE * conversation. Steps are one-shot by default; a role passes the same * handle to a tight group of back-to-back, same-model steps (the * acceptance author's four authoring steps) so later steps inherit the * earlier steps' context instead of re-surveying the repository. The * executor resumes `id` when set and writes the session's id back after * every SUCCESSFUL step; the owner clears `id` to open a fresh * conversation (a new slice must never inherit the previous one's). */ export interface SessionHandle { id?: string; /** * The model id the session under `id` belongs to. A session is tied to its * provider, so the handle resumes ONLY when the next step's model matches; * a step on a different model (a lane crossing tiers — e.g. a backend lane * stepping from intermediate work into a `hard` domain-design) starts a * fresh chain for that model instead of resuming the wrong provider's * session. Written back alongside `id` after every successful step. */ model?: string; } /** * The signature role implementations depend on — injectable so tests * drive the algorithm with a fake executor instead of real sessions. * `session` is optional: omitted (the default) every step is one-shot. */ export type StepExecutor = (order: WorkOrder, task: string, workingDir: string, session?: SessionHandle) => Promise; /** True when a step stopped because it ran out of turns, not because the * work failed — the caller may treat its result as partial. */ export declare function isTurnLimitFailure(detail: string): boolean; /** Failure texts that indicate the provider hiccuped, not the work. */ export declare function isTransientSessionFailure(detail: string): boolean; /** * The retry policy around ONE step's session, factored out so it is unit- * tested without launching a model: `attempt` is what claudeStep passes as * runSession, `wait` is injectable so tests skip the real backoff. * * Two distinct failure classes, checked in this order: * - turn-limit exhaustion (never "transient"): the step wandered after mostly * finishing — retry ONCE from a fresh session (undefined) over the changed * tree, which finishes the residual work far more often than it re-wanders. * - a provider hiccup: retry up to TRANSIENT_RETRIES times after a backoff, * keeping the same session (a lane must not lose its context to a blip). * Anything else, or exhausting either budget, throws. */ export declare function runWithRetries(kind: string, attempt: (session: SessionHandle | undefined) => Promise, session: SessionHandle | undefined, wait?: (ms: number) => Promise): Promise; export declare const claudeStep: StepExecutor; /** * The id to resume, or undefined for a fresh session. Resume ONLY a * same-model session: a handle carried across a model switch (a multi-tier * lane crossing tiers) must never resume the previous provider's session. */ export declare function resumeIdFor(handle: SessionHandle | undefined, model: string): string | undefined;