/** * The single owner of compile-pipeline caching and orchestration. * * Before this module, "how does compilation caching work" required reading * four places: parseCache.ts (parse memo), commands.ts (three module * globals + compileMany + ensureCompiledClosure), precompile.ts (config * grouping + cross-config assert), and the directory branch of compile(). * A BuildSession now owns all of that state and logic; commands.ts keeps * thin delegates for its existing exports. * * Contract (spec "Boundary constraints"): buildCompiledClosure remains a * pure exported function — compileSource (lib/compiler/compile.ts) calls * it directly for in-memory sandbox compiles and must not be affected. * * PR 2 adds `freshness`/the manifest here. Until then every compile is a * full compile (the historical behavior). */ import { AgencyConfig } from "../config.js"; import { SymbolTable } from "../symbolTable.js"; import { type Freshness } from "./manifestTracker.js"; import { type ImportStrategy } from "../importStrategy.js"; export type CompileOptions = { ts?: boolean; symbolTable?: SymbolTable; importStrategy?: ImportStrategy; /** Suppress the per-file `input → output (in Nms)` progress line. */ quiet?: boolean; /** Test-harness only. Honors `import test { … }`. Never set outside the * test runner / analysis paths — kept off AgencyConfig so agent source * cannot enable it. */ allowTestImports?: boolean; /** Skip policy. "incremental" (default) consults and records the * manifest; "force" recompiles everything but rewrites it (--force); * "always" is internal (allowTestImports / --ts / caller-supplied * importStrategy) and touches nothing. */ freshness?: Freshness; }; export type CompileRequest = { /** Files or directories. One entry = the legacy per-entry path * (covers-check closure, stdlib carve-out); many = one union closure. */ entries: string[]; /** Single non-directory entry only: explicit output path. */ outputFile?: string; } & CompileOptions; /** * A set of entry files sharing one effective config. The canonical config * key is NOT part of this type: it is the session's own integrity * mechanism (the cross-config assert compares it, and PR 2 makes it a * manifest field), so the session derives it — a caller-supplied key that * canonicalized differently would silently weaken the assert. */ export type CompileGroup = { /** Base-config marker or the local-`agency.json` dir, for error messages. */ label: string; config: AgencyConfig; /** Absolute `.agency` entry paths. */ files: string[]; }; export declare function createBuildSession(): BuildSession; export declare class BuildSession { private compiledFiles; private currentClosure; private tracker; /** * The one public compile entry point: callers declare WHAT to build * (entries + options); the session decides how. A single entry keeps the * legacy per-entry closure semantics (covers-check reuse, stdlib * carve-out); multiple entries compile under ONE union closure, where * closure errors THROW (`CompileClosureError`) so programmatic callers * can attach context. Parse/typecheck failures inside per-file compiles * keep their exit behavior. Returns the output path for a single * non-directory entry, null otherwise. */ compile(config: AgencyConfig, request: CompileRequest): string | null; /** Compile config-heterogeneous groups (one union closure each), after * asserting no module is reachable from groups whose configs differ — * compiled output is config-dependent and a sibling `.js` is a single * slot per module, so a shared module would be last-writer-wins. * Throws `CompileClosureError` naming the module and group labels. */ compileGroups(groups: CompileGroup[], options?: { quiet?: boolean; allowTestImports?: boolean; freshness?: Freshness; }): void; /** Drop all cached state (watch-mode rebuild boundary). */ reset(): void; private setClosure; /** BFS over the closure import graph. Stdlib entries have no closure — * their deps are [] by construction (stdlibHash covers them). */ private transitiveDeps; /** * Internal variant of compileMany keeping the prebuilt-closure handoff * (`options.closure`) used by compileGroups; the public method omits it — * the closure MUST cover every file, a cache-coherence contract no * external caller should have to carry. */ private compileManyImpl; /** * Build the import-closure analysis once per compile session — at the * outermost call, before any recursive per-file compile runs. The * recursive children reuse the cached closure to get per-module init * plans without re-parsing. * * "Outermost call" = no `options.symbolTable` (passed by recursive * children). When the outermost call's entry file changes (e.g. the * `agency test` runner iterates several .test.json fixtures in one * process), the cached closure no longer covers the new entry's * imports so we rebuild and drop the stale `compiledFiles` set. * Without that drop, downstream codegen would look up plans for * modules that aren't in the closure and emit an empty init plan. * * Stdlib files compile under their own entry (e.g., when a user runs * `agency compile std/...`) but most user code reaches them via * `import "std::..."`, which the closure walker intentionally skips. * Avoid building a closure rooted at a stdlib file — its imports are * structured differently and we don't need the analysis there. */ private ensureCompiledClosure; private compileDirectory; private compileEntry; } export declare function readFile(inputFile: string): string; export declare function findCrossConfigConflicts(groups: { label: string; configKey: string; modules: string[]; }[]): { module: string; labels: string[]; }[];