/** * Research command — thin handler over the Research Capability * (tech-plan §2c, §3, §8). * * Research runs an asynchronous create→poll lifecycle server-side and * costs 4-250 credits per request. The handler shows a wait disclaimer * before invoke, sets up a Ctrl-C signal handler that prints the * request_id + resume command, and returns the full unbudgeted * envelope — `--max-chars` is NOT an option here: the dispatcher seam * (index.ts) applies the whole-envelope budget via RESEARCH_LADDER * after this returns (ADR-0007). * * The Adapter's `invoke()` owns the full lifecycle (state-file resume, * POST, poll loop, completion/failure/404 handling); shared execution * wraps it with cache + zero-retry. * * Provider selection, capability support, configuration, Adapter * construction, and adapter.research agreement live in `src/index.ts`. * * Cache stores the full report; the budget is a dispatcher-seam projection. */ import type { CommandContext, CommandResult } from "../command-invocation.js"; import type { ResearchCapability } from "../capabilities/research.js"; import type { ProviderId } from "../providers/types.js"; import type { ExecutionDependencies } from "../lib/execution.js"; import type { ContextSourceContent, ParsedContextText } from "../lib/context-file.js"; import { type LadderRule } from "../lib/output-budget.js"; export interface ResearchOptions { readonly model?: "mini" | "pro" | "auto"; readonly outputLength?: "short" | "standard" | "long"; readonly citationFormat?: "numbered" | "mla" | "apa" | "chicago"; readonly domain?: string; /** Polling timeout in seconds. Default 300. */ readonly timeout?: number; readonly noCache?: boolean; /** * Local-context plan, Ticket 3 (DESIGN D5): resume-bearing context * flags consumed by `buildResearchResumeCommand` only — the wire * request builder ignores this field (the D2.5 mutation derives * from `ResearchHandlerDependencies.context`, not from here). */ readonly context?: ResearchResumeContext; } /** Local-context plan, DESIGN D1: `--context-mode` values (research only). */ export type ResearchContextMode = "organize" | "bias" | "both"; /** * Local-context plan, Ticket 3 (DESIGN D5): the resume-command view of * the context flags. `path` is the original `--context` value (file * sources only); `mode` records ONLY an explicitly-set * `--context-mode` — undefined (the organize default) stays omitted, * matching `buildResearchResumeCommand`'s set-values-only convention. */ export interface ResearchResumeContext { readonly source: "file" | "stdin"; readonly path?: string; readonly mode?: ResearchContextMode; } /** * Local-context plan, Ticket 2: what the handler threads into * `research()` for `--context` / `--context-stdin`. The source is read * (`readContextSource`) and parsed (`parseContextText`) exactly ONCE * in `handleResearch`, BEFORE `executeWithFallback` — stdin drains on * the first read, so a per-fallback-attempt read would hand the retry * an empty string, silently mutate the request, and hash to a * different async-job state file (DESIGN D5's second-paid-job trap). * `research()` consumes this field only (D4 remap + D5 envelope field) * and never re-reads the source. */ export interface ResearchContextInput { readonly mode: ResearchContextMode; readonly content: ContextSourceContent; readonly parsed: ParsedContextText; } /** * Dependencies injected by `src/index.ts` after Provider selection, * capability support check, configuration check, Adapter construction, * and adapter.research agreement. */ export interface ResearchHandlerDependencies { readonly capability: ResearchCapability; readonly execution: ExecutionDependencies; /** * SIGINT registrar factory. Production wires * `process.on("SIGINT", ...)` inside the handler so each per-attempt * entry installs a listener and each `finally` removes it (Review * Fix 3). Tests inject a recorder factory so they can capture the * state-file path + canonical resume command for the active * attempt, trigger the listener, and prove loser cleanup ran. * * The factory receives the per-attempt state-file path and the * provider-specific resume command (both bound to the candidate * capability inside `research()`) and returns a registrar that * accepts a `print` closure (already bound to the same values) and * returns a teardown. */ readonly registerInterrupt?: (stateFilePath: string, resumeCommand: string) => (print: () => void) => () => void; /** * Local-context plan, Ticket 2 (DESIGN D3/D5): parsed local context * from `--context` / `--context-stdin`, read + parsed once in the * handler before the fallback executor runs. Absent → byte-identical * pre-context behavior (no remap, no envelope field). */ readonly context?: ResearchContextInput; } export declare function validateModel(value: unknown): "mini" | "pro" | "auto" | undefined; export declare function validateOutputLength(value: unknown): "short" | "standard" | "long" | undefined; export declare function validateCitationFormat(value: unknown): "numbered" | "mla" | "apa" | "chicago" | undefined; /** * Format the Ctrl-C interrupt message. Pure and exported so tests verify * the request_id and resume command appear in the output without * simulating a real SIGINT. The message goes to stderr. */ export declare function formatInterruptMessage(requestId: string, resumeCommand: string): string; /** * Build a canonical shell-safe resume command that names the Provider * the user actually wants to resume AND every identity-bearing request * option they actually set. The Provider id is threaded from * `capability.run.cacheIdentity(request).provider` so the command * matches the on-disk state file (the identity hash is keyed on the * Provider, the credential fingerprint, and the full request — see * `lib/async-job-state.ts`). * * Without `--provider`, a successful Tavily fallback to Exa would * (today) print `scoutline research ""`, which resumes polling on * the *default* Provider — creating a second paid job when only one * already exists (Review Fix 3). Without the identity-bearing * options, a re-run that omits `--model pro` would compute a * different state-file hash and create a fresh job as well. * * Only options that were ACTUALLY set appear in the command — * `undefined` values are omitted so the resume uses the user's own * defaults rather than picking values that were never requested. */ export declare function buildResearchResumeCommand(query: string, options: ResearchOptions, providerId: ProviderId): string; /** The research Output Budget ladder (ordered; see ADR-0007 T4). */ export declare const RESEARCH_LADDER: readonly [LadderRule, LadderRule]; /** * Output Budget T4: rebuild the text presentations from a budgeted * projection so -O compact/markdown/refs/tty reflect the shrunken * envelope (sources — the citations block — survive longest there * too). Thin passthrough over `buildResearchPresentations`. */ export declare function rebuildBudgetedResearchPresentations(sections: readonly { heading: string; body: string; }[], sources: readonly { title?: string; url?: string; }[]): Readonly>>; export declare function research(query: string, options: ResearchOptions | undefined, deps: ResearchHandlerDependencies, _context?: CommandContext): Promise; export declare const RESEARCH_HELP: string; //# sourceMappingURL=research.d.ts.map