import { type Harness, type Json, type Turn } from "@vendoai/core"; import { type VendoToolSearchConfig } from "./tool-search.js"; import { type LanguageModel } from "ai"; /** * The per-turn knobs — the TYPE is the whole declaration. * * There was a `Harness.optionsSchema` here too, restating these knobs as zod. It * was never parsed: nothing in the stack validates a harness's options schema, * and the one path that could have (`HarnessTurns.stream` → `runtime.run`) is * typed `` and forwards no options at all. So the schema was a second, * unenforced copy of this interface, and a caller reaching `Turn.options` is * `runtime.run({ options })` — typed, in-process, and already checked by tsc. * Where a value's range genuinely matters the check lives at the function that * needs it ({@link contextWindowTokens}), which is the only place either the * per-turn or the deployment door was ever checked. */ export interface VendoHarnessOptions { model?: LanguageModel; maxSteps?: number; /** The shipped loop's context knobs, per turn. They were declared on the loop * and on `createAgent` but not here, and this file passed `maxSteps` alone — * so a deployment on the default harness (which is every deployment whose * store can serve harness turns) could not reach the history window or the * token budget at all. */ historyWindow?: number; contextTokenBudget?: number; maxOutputTokens?: number; maxRetries?: number; /** Override the window this seat is assumed to have. The BYO escape for a * model {@link contextWindowTokens}'s table cannot name. */ contextWindowTokens?: number; } export interface VendoHarnessDeps { /** * An explicit system prompt, for a host driving this harness outside our * composition. Set, it WINS over `turn.system`; unset — the normal case, and * what `harness: vendo()` builds — the deployment's assembled prompt arrives on * the turn instead. It cannot arrive here: this value is constructed once at * boot, and the prompt is venue-gated and carries the guard's directions, so it * needs the turn's `RunContext`. */ system?: string | (() => string | undefined | Promise); maxSteps?: number; /** The deployment's defaults for the loop's context knobs; a per-turn option of * the same name wins. `maxSteps` stays above for back-compat and reads the * same either way. */ historyWindow?: number; contextTokenBudget?: number; maxOutputTokens?: number; /** How many times the SDK re-issues a failed provider call ({@link * DEFAULT_MAX_RETRIES}); `0` spends nothing. */ maxRetries?: number; /** The window this deployment's seat is assumed to have, when the shipped * table is wrong about it. Q1a: this lives on the harness and nowhere else — * it is a fact about a model, not a product decision a host composes. */ contextWindowTokens?: number; /** * vendo()'s tool-search strategy: the loadout cap and the `find_tools` hand * ({@link VendoToolSearchConfig}). Composition passes it when it constructs * the default harness; a host-constructed `vendo()` receives it through the * composed adapter slot instead, like `claudeCode()`'s sandbox. Unset both * ways = every projected tool offered and no search — the strategy is the * brain's, so a brain given none has none. */ toolSearch?: VendoToolSearchConfig; /** * The CLOSED toolbox. Set, the equipped set is EXACTLY this list: a string * equips that registry tool (guarded, via `turn.tools.call`, same as today); a * {@link HarnessHand} is the harness's own hand, invisible to every other * consumer. No discovery rail (`find_tools` is not mounted — a fixed loadout has * nothing to discover), no `vendo_*` always-active exemption (the list is * total), no `hire_subagent` unless named. Unset = today's behaviour, unchanged. * * This is what lets a specialist BE `vendo()` plus configuration rather than a * second copy of the loop: the step cap, the seat resolution, `wireErrorMessage` * and the system precedence are the ones above, not a fork of them. */ tools?: readonly (string | HarnessHand)[]; } /** * A tool the harness itself provides — the other half of a closed loadout. * * `execute` receives the TURN, which is what lets a hand be declared once at boot * (where a `Harness` value is built, with no run in sight) while its effects are * per-run: `turn.workspace` is this run's files and `turn.state` is this run's * scratch. A hand never reaches the registry, so nothing else can discover it and * the guard has nothing to decide about it — a hand that touches host data does it * by calling `turn.tools.call` like anyone else. */ export interface HarnessHand { /** What the model calls it. Never `vendo_`-prefixed: those names are the * product's, and the loadout rail treats them as always-active. */ name: string; description: string; /** JSON Schema, the same dialect a `ToolListing.inputSchema` carries. */ inputSchema: Record; execute(input: Json, turn: Turn): Promise; } export declare function vendo(deps?: VendoHarnessDeps): Harness; //# sourceMappingURL=vendo.d.ts.map