import type { GthConfig } from '#src/config.js'; import type { AgentResolvers, GthAgentFactory, GthCommand } from '#src/core/types.js'; import type { SingleShotOptions } from '#src/runtime/singleShot.js'; import type { GthAdvertisedTools, GthRunStats } from '#src/core/types.js'; import type { GthTerminationReason } from '#src/core/terminationReason.js'; /** * One turn's result inside a {@link runConversation} run: the per-turn `ok`/`answer` plus that * turn's run stats (GS2-16 {@link GthRunStats} — token usage + invoked tools), captured PER TURN (a * per-invoke delta, not the cumulative conversation total). `ok` is `false` when that turn's agent * invocation failed (`error` set, `answer` empty). Extends `GthRunStats` rather than restating its * fields — same shape {@link @gaunt-sloth/core!runtime/singleShot.SingleShotResult | SingleShotResult} uses. */ export interface ConversationTurnResult extends GthRunStats { /** `true` when this turn completed without error, `false` when it failed. */ ok: boolean; /** This turn's full answer text (`runner.processMessages()`'s return value). Empty on failure. */ answer: string; /** Set when `ok` is `false`: why this turn failed. */ error?: string; /** * BATCH-32 — what the agent advertised to the model (`gth eval`'s coverage denominator). * * **Conversation-level, not a per-turn delta** — the one field here that is not. A conversation * builds its agent ONCE, so the inventory is fixed for the whole run and the same value appears * on every turn. It is repeated rather than returned separately because this function's contract * is one result per turn; a caller that unions across turns gets the right answer either way. */ advertisedTools?: GthAdvertisedTools; /** * [[EXT-159]] — why this turn ended, as a value. * * Per turn, like every other field here, and read before the next turn resets it. It is the * structural carrier the console line beside it is rendered FROM, so a consumer — a multi-turn * eval asking whether a run stopped because the provider faulted or because the model handed back * — reads the classification instead of parsing `error`. `null` means no site classified the * ending, which is a site we missed rather than a turn that went well. */ terminationReason: GthTerminationReason | null; } /** * Run a scripted MULTI-TURN conversation and return one {@link ConversationTurnResult} per turn. * * This is the **conversational** counterpart to {@link @gaunt-sloth/core!runtime/singleShot.runSingleShot | runSingleShot} * (which is stateless — a fresh agent per call). It builds the agent + resolves tools ONCE, then * runs each turn against the ACCUMULATED message history so cross-turn "memory" / identity behaviour * is real, and cleans up ONCE at the end (reusing runSingleShot's cleanup discipline — the resolvers * are the caller's to tear down, exactly as with runSingleShot). * * **History mechanism = stateless replay of the growing message array.** Messages accumulate as * `[user1, ai1, user2, ai2, …]`: per turn a `HumanMessage(user)` is appended, the agent runs on the * WHOLE array, and its answer is appended as an `AIMessage` so the next turn sees it. The system * prompt is NOT seeded here — the agent composes it via `createAgent({ systemPrompt })` (BATCH-13; * see `runSingleShot`), so the replayed array carries only human/assistant turns. Before each * turn the runner's thread is rotated ({@link GthAgentRunner.resetThread}) so the checkpointer starts * empty and the replayed array is the sole history (no `add_messages` double-append). This mirrors * the AG-UI server's "client is the source of truth for history — it sends the full message list * every turn" model and reuses the existing `processMessages` + `resetThread` machinery with no new * agent surface. **Known limitation (unverified pending a live pass):** replay carries prior * *answers* (as `AIMessage` text) but NOT prior tool-call / tool-result messages — a checkpointer- * thread approach (send only the new message, let `add_messages` accumulate) would preserve those. * * **Per-turn tool capture (GS2-16):** `processMessages` resets the analytics tally at its top, so * `getRunStats()` read right after each turn returns THAT turn's tool/token delta (not cumulative). * * A turn that fails is recorded (`ok:false`, `error`) and the conversation STOPS (later turns depend * on the broken context), so the returned array may be shorter than `userMessages` — the caller * (`gth eval`'s runner) fails the un-run turns. * * @param source - The source label (used for output/session-file naming), e.g. `EVAL-`. * @param _preamble - Deprecated/ignored (BATCH-13): the agent composes the system prompt itself (via * `createAgent({ systemPrompt })`); seeding it here too produced a second system message that * `@langchain/anthropic` rejects. Retained positionally so existing callers need no change. * @param userMessages - The ordered user turns to send (one conversation). * @param config - The resolved config. * @param resolvers - Optional agent resolvers (tools/middleware); the caller owns their cleanup. * @param command - The originating command (defaults to `ask`); selects the agent mode prompt. * @param agentFactory - Optional backend factory (B5); omitted = the runner's lean default, which * is also what the shared backend seam resolves to. * @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 its conversations in `ask` mode). * Header only — it never reaches the mode prompt. * @returns One {@link ConversationTurnResult} per turn attempted, in turn order. */ export declare function runConversation(source: string, _preamble: string, userMessages: string[], config: GthConfig, resolvers?: AgentResolvers, command?: GthCommand, agentFactory?: GthAgentFactory, options?: SingleShotOptions): Promise;