import { type CreateAgentSessionOptions, ModelRegistry, ModelRuntime, type ToolDefinition } from "@earendil-works/pi-coding-agent"; import type { Static, TSchema } from "typebox"; import { type AgentHistoryEntry } from "./agent-history.js"; import { type ExecutorRegistry, type ExecutorUsage, type WorkflowExecutor } from "./executor.js"; import { type ModelTierConfig, type RankableModel } from "./model-tier-config.js"; import { type StructuredOutputCapture } from "./structured-output.js"; /** * Last-resort structured-output recovery: extract a JSON block from prose, coerce * it toward the schema, and accept it only if it then validates. Never fabricates * — returns undefined unless the parsed value genuinely satisfies the schema. */ export declare function extractValidated(text: string, schema: TSchema): T | undefined; /** * The last assistant message's terminal metadata (stopReason/errorMessage). The pi * SDK does NOT throw provider usage/quota limits — it records them as an assistant * message with stopReason "error" and an errorMessage. This is the only place that * metadata is observable to the workflow layer. */ export declare function lastAssistantError(messages: unknown[]): { stopReason?: string; errorMessage?: string; } | undefined; /** * If the subagent's turn ended in a provider usage/quota/rate-limit error, throw a * PROVIDER_USAGE_LIMIT WorkflowError carrying the real provider message + reset hint. * Gated on stopReason === "error" so a successful turn whose text merely mentions * "rate limit" is never misclassified. recoverable:false so the run checkpoints * (paused) rather than being retried into the same wall or collapsed to a silent null. */ export declare function throwIfProviderLimit(messages: unknown[], label?: string): void; /** Minimal session surface resolveStructuredOutput needs (real session or a test double). */ export interface StructuredSession { prompt(text: string): Promise; setActiveToolsByName?(names: string[]): void; messages: unknown[]; } /** * Resolve a schema agent's result. If the tool was called, return the captured * value. Otherwise re-prompt up to maxSchemaRetries (tools restricted to * structured_output), then try strict schema-validated prose extraction, else * throw SCHEMA_NONCOMPLIANCE (non-recoverable — surfaced, never a silent null). * Module-level with an injected `lastText` so it is unit-testable. */ export declare function resolveStructuredOutput(session: StructuredSession, capture: StructuredOutputCapture, schema: TSchema, options: { maxSchemaRetries?: number; signal?: AbortSignal; label?: string; }, lastText: (messages: unknown[]) => string): Promise; /** * Resolve which concrete model spec a subagent should use. Precedence, most * specific first: * 1. options.model — an explicit per-agent model (also carries agentType / * phase model, which the workflow layer folds into options.model). * 2. options.tier — resolved via the model-tiers config, falling back to the * session's main model when the tier has no configured entry. * 3. DEFAULT TIER — when neither is set but the user has a model-tiers config, * untagged agents default to the "medium" tier so a configured tier set * actually affects the whole workflow (not just agents the script tagged). * Fresh-install medium == the session model, so this is a no-op until the * user customizes tiers via /workflows-models. * Returns undefined when nothing applies, so the session default is used. * * `loadConfig` is injectable for testing; it defaults to reading from disk. */ export declare function resolveAgentModelSpec(options: { model?: string; tier?: string; }, mainModel: string | undefined, loadConfig?: () => ModelTierConfig | null, onTierWithoutConfig?: (tier: string) => void): string | undefined; export interface WorkflowAgentOptions { cwd?: string; /** Extra tools available to the subagent in addition to the structured output tool. */ tools?: ToolDefinition[]; /** * Extra tool NAMES to deny in the subagent session, on top of the always-on * defaults ({@link DEFAULT_EXCLUDED_SUBAGENT_TOOLS}). Lets the host exclude * other recursive-orchestration tools it registers (e.g. a pi-subagents tool) * so a workflow subagent can't fan out through them either (#107). */ excludeTools?: string[]; /** Override any createAgentSession option (model, modelRuntime, resourceLoader, etc.). */ session?: Partial; /** Extra system guidance prepended to every subagent task. */ instructions?: string; /** * The session's main model (`provider/modelId`). Used as a fallback when * resolving opts.tier and no model-tiers.json config exists. Without this, * a workflow using `{ tier: "small" }` would log a warning and fall through * to the session default when no config is saved yet. */ mainModel?: string; /** * Shared model registry from the host Pi session. When provided, subagents * resolve tier/model specs against the same registry the main session uses, * including dynamically-registered providers such as ollama-cloud. Without * this, the agent builds an isolated registry from disk and may miss models * that are only available via extension registration. */ modelRegistry?: ModelRegistry; /** Registry used to dispatch external executors. */ executorRegistry?: ExecutorRegistry; /** * Persist each subagent transcript as a real pi session file under the * standard sessions directory (keyed by the runner's project cwd), instead * of the default in-memory session that is discarded when the run ends. * Default: false (current behavior). */ persistAgentSessions?: boolean; } /** * The ModelRuntime behind a registry facade. pi's ModelRegistry does not expose * its runtime publicly, so reach into the private field (stable since 0.80.8); * subagent sessions need it to share the host session's exact catalog and auth * (createAgentSession takes modelRuntime, not a registry, since 0.80.8). * * Exported so the test suite can pin this pi-internals contract: the cast means * neither tsc nor mock-based tests would notice pi renaming the field, and the * runtime consequence is silent (subagents fall back to a default runtime and * extension-registered providers vanish from routing). */ export declare function runtimeOf(registry: ModelRegistry): ModelRuntime | undefined; /** * List the user's currently available models (those with auth configured) with * the minimal fields tier ranking needs: canonical spec, output price, and * context window. This is the single place the SDK `Model` is projected into * the SDK-agnostic `RankableModel`. Best-effort: returns [] if the registry * can't be built (or while the disk-backed fallback is still initializing). */ export declare function listAvailableModels(registry?: ModelRegistry): RankableModel[]; /** * List the user's currently available models as `provider/modelId` specs. Used * to tell the workflow author which models it may route agents to. Best-effort: * returns [] if the registry can't be built. */ export declare function listAvailableModelSpecs(registry?: ModelRegistry): string[]; /** Real token/cost usage for a single subagent run, read from the SDK session. */ export type AgentUsage = ExecutorUsage; /** * Map session stats to an AgentUsage, or undefined when the provider reported * no usage at all (all-zero stats). Returning undefined — instead of a zero * breakdown — lets displays fall back to their scalar token count, so setups * on non-reporting providers render the same as before the split existed. */ export declare function usageFromStats(stats: { tokens: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number; }; cost: number; }): AgentUsage | undefined; export interface AgentRunOptions { label?: string; /** Executor to use for this call. Omitted means the Pi implementation. */ executor?: WorkflowExecutor; /** * Display name recorded on the persisted session (session_info entry) when * `persistAgentSessions` is enabled, so transcripts are identifiable in * session pickers (e.g. `workflow: