import type { GthConfig } from '#src/config.js'; import type { AgentResolvers, GthAgentFactory, GthCommand } from '#src/core/types.js'; import type { GthAdvertisedTools, GthRunStats } from '#src/core/types.js'; import { type GthRunRecap } from '#src/core/runRecap.js'; import type { GthTerminationReason } from '#src/core/terminationReason.js'; import { type SingleShotResumability } from '#src/runtime/resumeHint.js'; /** * Result of a {@link runSingleShot} run: the pass/fail contract callers such as `ask`/`exec` have * always used (`ok`), plus the SUT's answer text and run stats (GS2-16's {@link GthRunStats}) that * were already computed internally but previously discarded. Extends `GthRunStats` rather than * restating `tokensInput`/`tokensOutput`/`tools` as parallel fields. */ export interface SingleShotResult extends GthRunStats { /** `true` when the run completed without error, `false` when it failed. */ ok: boolean; /** * BATCH-32 — what the agent ADVERTISED to the model for this run: the full pre-`allowedTools` * inventory, the tools the allow-list removed, and the count of nameless ones. * * A sibling of the run stats rather than a field inside them: `GthRunStats` is the per-turn tally * the accumulator folds messages into, and this is fixed at `init`. `undefined` means no * inventory was observed (an agent that does not report one), which is a different answer from an * inventory listing no tools. */ advertisedTools?: GthAdvertisedTools; /** The SUT's full answer text (`runner.processMessages()`'s return value). Empty on failure. */ answer: string; /** * [[EXT-159]] — why the run ended, as a value. * * The structural twin of the line this runtime prints: a caller (and a test) reads the * classification here rather than matching the prose on the console, so no user-facing string is * the only carrier of it. `null` means no site classified the ending, which by this taxonomy's * contract is a site we missed — never a stand-in for an ordinary finish, which is recorded as * `completed`. */ terminationReason: GthTerminationReason | null; /** * [[EXT-178]] — the end-of-run recap, when one was requested and produced; `null` otherwise. * * **The fact, not the prose.** The same principle [[EXT-159]] states for the termination reason * and [[EXT-158]] for the outstanding-work value: the classification a caller (and a test) reads * is this object, so no user-facing string is the only carrier of it. An embedder that wants to * render a recap its own way — or to ask whether the run reported itself complete — reads * `recap.complete` and `recap.work` rather than matching the sentence on the console. * * `null` collapses every "no recap" outcome deliberately; see `requestRunRecap` for why the * failures are a maintainer's question and not a user's. */ recap: GthRunRecap | null; /** * GS2-106 — the conversation this run was recorded under, with its stable run id; absent when the * run was not recorded (history off, or the store could not be written). What a caller needs to * tell a person how to come back to this run; the continue hint prints it for `ask` and `exec`. */ conversation?: { conversationId: number; runId: string | null; }; /** * GS2-106 — whether this conversation can be continued, and if not why: the fact behind the * continue hint, so a caller or a test reads the value rather than the line on stderr. Present * only when the caller asked for the hint ({@link SingleShotOptions.announceResumeHint}). */ resumability?: SingleShotResumability; } /** Options that qualify a {@link runSingleShot} run without changing how it behaves. */ export interface SingleShotOptions { /** * GS2-95 — the name of the command the USER typed, for the run header (`eval`, `batch`, * `workflow`, `gth-batch`). Omit it and the header names `command`, which is right for every * caller whose verb IS its name (`ask`, `exec`). * * It is deliberately NOT `command`: that argument selects the agent's mode prompt, so a caller * that renamed itself through it would change which system prompt its runs execute under. */ displayCommand?: string; /** * [[EXT-158]] — announce, at the end of the run, that the agent's own checklist still had * non-completed items. **Scope (d), decided here rather than in a red cell.** * * ## WHY THIS IS OPT-IN, AND WHY THE DEFAULT IS THE QUIET ONE * * This runtime has two very different classes of caller. `gth ask` and `gth exec` are a person * running a verb and reading what comes back — the surface this notice exists for, and they set * this. `gth batch`, `gth eval` and `gth workflow` drive it as a HARNESS, hundreds of cells at a * time, folding each run into a report; a per-cell sentence about an unfinished checklist is * noise to a reader who is looking at aggregate outcomes, and it appears on precisely the cells * that PASSED, since the notice speaks only for the `completed` ending. * * A default-on flag with an opt-out would have put that output into every harness whose author * never heard of this node, and the first anyone would know of it is a changed report. Default-off * means the failure direction is a surface that stays silent until someone adds one line — which * is a missing notice rather than a changed contract, and it is recoverable by the person who * notices. * * **This is about the notice, never the detection.** The fact is recorded on every run through * every path; `runner.getOutstandingWork()` answers for a batch cell exactly as it does for * `gth ask`. Only whether a sentence is printed is what this decides. * * Note also that {@link displayRunEndReport}, which draws this notice, writes through * `displayNotice` and so goes to **stderr** — so even switched on it cannot change the stdout a * caller parses, nor the `answer` this function returns, which is what `gth batch`'s cases assert * on. */ announceOutstandingWork?: boolean; /** * [[EXT-178]] — request an end-of-run recap on this run, subject to the user's `recap` rung. * * ## SCOPE (4), DECIDED HERE AND NOT IN A RED CELL — AND WHY IT IS A SECOND FLAG * * The surface table is the one {@link announceOutstandingWork} sets out, with the same split: * `gth ask` and `gth exec` are a person running a verb and reading what comes back, and they set * this; `gth batch`, `gth eval` and `gth workflow` drive this runtime as a harness and do not. * * **It is a separate flag rather than a second meaning for that one, because the two differ in * the thing that matters most about a default.** The notice is deterministic, free and bounded; a * recap is a model call — the user's tokens, the user's latency, the user's money — per cell. * Folding them together would mean a harness author who once opted into a free sentence had * thereby opted a thousand-cell suite into a thousand extra model calls, with no line in their * own code to point at. Two flags make the expensive one its own, visible decision. * * Excluded elsewhere by the same structure — a surface that does not ask does not get one: * * - `review` / `pr` end with a full written verdict that already says what was examined and what * was found. A recap *of* a review is the banner problem in its purest form. * - ACP and AG-UI are embedders whose client owns the end-of-turn surface. Spending a model call * server-side for a paragraph the protocol has no field for, and that the client may already be * writing itself, is not ours to decide; [[EXT-158]]'s notice travels there as structured data, * which is the right shape for a protocol. * * **Neither the rung nor this flag changes what a caller parses.** The recap is rendered through * `displayNotice`, which writes to **stderr**, and the `answer` this function returns is * untouched — the property [[EXT-158]] was careful to keep and that `gth batch`'s cases assert * on. The value itself reaches a caller as `SingleShotResult.recap`. */ announceRunRecap?: boolean; /** * GS2-106 — print the continue hint after the run (`gth ask --resume "…"`), subject to * the user's `output.resumeHint` rung. Opt-in for the reason {@link announceOutstandingWork} is: * `ask` and `exec` set it; the harnesses do not, and their cells could not be resumed anyway. The * gate itself, and why, is documented in `runtime/resumeHint.ts`. */ announceResumeHint?: boolean; /** * GS2-106 — the fan-out surface driving this run (`batch`, `eval`, `gth-batch`, `workflow`), * recorded on the conversation as its `origin`. A direct `ask`/`exec` omits it. * * An explicit option rather than something inferred from {@link displayCommand}: that one names * the header, and a header is free to change its wording, while this decides whether every resume * surface refuses the row. The two happen to hold the same words today; nothing should rely on it. */ origin?: string; /** * GS2-106 — continue a recorded conversation instead of starting one: the conversation the * caller's resume seam already resolved and checked (`ask --resume`, `exec --resume`, * `gth history resume`). The run re-enters `threadId` from its checkpoint and appends `content` * as a fresh user message; the turn is recorded under `conversationId`. */ resume?: SingleShotResume; } /** * GS2-106 — a conversation a single-shot run continues. Resolved and checked BEFORE the run by the * resume seam, which is also where a refusal is said; this runtime only re-enters it. */ export interface SingleShotResume { conversationId: number; /** The LangGraph thread whose checkpoint holds the conversation's state. */ threadId: string; } /** * Ask a question and get an answer from the LLM. * * This is the shared, non-interactive single-shot runtime behind both the conversational * `ask` command and the scripted `exec` command (prompt-as-script). The `command` argument * is forwarded to the agent so it can pick the right mode prompt (e.g. exec-mode for `exec`). * * @param source - The source of the question (used for file naming) * @param _preamble - Deprecated/ignored (BATCH-13): the agent composes the system prompt itself; * see the body comment. Retained positionally so existing callers need no change. * @param content - The content of the question * @param config - The resolved config * @param resolvers - Optional agent resolvers (tools/middleware) * @param command - The originating command (defaults to `ask`); selects the agent mode prompt * @param agentFactory - Optional backend factory (B5). When omitted the runner uses its built-in * lean {@link @gaunt-sloth/core!core/GthLangChainAgent.GthLangChainAgent | GthLangChainAgent} default. The app layer passes `resolveAgentFactory(config, * 'lean')`, which resolves to the same agent through the shared backend seam. * @param options - GS2-95: `displayCommand` names the run in the header when the caller's own name * differs from the `command` it runs under (`gth eval` runs cases in `ask` mode). Header only — * it never reaches the mode prompt. * @returns A {@link SingleShotResult}: `ok` is `true` when the run completed without error, `false` * when it failed (so callers such as `exec` can set a non-zero exit code); `answer`/`tokensInput`/ * `tokensOutput`/`tools` carry the SUT's answer text and run stats for callers that need them * (e.g. `gth batch`/`gth eval`). */ export declare function runSingleShot(source: string, _preamble: string, content: string, config: GthConfig, resolvers?: AgentResolvers, command?: GthCommand, agentFactory?: GthAgentFactory, options?: SingleShotOptions): Promise;