import type { InterruptEffect } from "../symbolTable.js"; import type { TypeCheckerContext, ScopeInfo } from "./types.js"; import type { AgencyNode } from "../types.js"; import type { HandleBlock } from "../types/handleBlock.js"; import type { InterruptStatement } from "../types/interruptStatement.js"; export type TaggedHandler = { block: HandleBlock; file: string; }; /** Stable cross-file identity for a function/node, of the form * `${file}:${name}`. Used as the key in `InterruptCallGraph` and on * every call edge so that propagation works correctly when the same * top-level name is defined in two modules or when an import is * aliased locally (`import { foo as bar } …`). */ export type QualifiedKey = string; export declare function qualifyName(file: string, name: string): QualifiedKey; export type CallEdge = { /** Local name used at the call site, e.g. `bar` for * `import { foo as bar } …; bar()`. Preserved for diagnostics and * tests; the propagation graph uses `calleeKey`. */ calleeName: string; /** Resolved cross-file identity of the callee. For locally defined * symbols this is `${currentFile}:${name}`. For imports it's * `${originFile}:${originalName}`. For unresolved names (builtins, * unknown identifiers) it falls back to `${currentFile}:${name}`. */ calleeKey: QualifiedKey; enclosingHandlers: TaggedHandler[]; }; export type CallGraphFunction = { /** Local (unqualified) name of the function/node — what users wrote * in the source. Stable within a single file. The outer map's key is * the qualified `${file}:${name}` form. */ name: string; /** Absolute path to the .agency file this function/node is defined in. */ file: string; callEdges: CallEdge[]; interruptSites: { site: InterruptStatement; /** Same as the function's `file`. Carried explicitly so consumers don't * have to look it up. */ file: string; enclosingHandlers: TaggedHandler[]; }[]; }; /** Cross-file call graph keyed by `QualifiedKey`. Built per-file by * `buildInterruptCallGraph` and merged across files in * `lib/analysis/interrupts.ts` (where the qualified keys are what make * the merge collision-free). */ export type InterruptCallGraph = Record; /** * Declarative pipeline: collect per-scope profiles → propagate transitively → format. */ export declare function analyzeInterruptsFromScopes(scopes: ScopeInfo[], ctx: TypeCheckerContext): Record; /** * Build a per-function call graph that, for each call edge and each * `interruptStatement`, records the list of `handle` blocks in the * enclosing function body that lexically wrap it. Each handler is * tagged with its file so the file survives propagation in the * downstream handler-set analyzer. * * Unlike `analyzeInterruptsFromScopes` this does NOT propagate kinds * transitively — it only records direct, per-function structural facts. * The propagation lives in `lib/analysis/interrupts.ts`. */ export declare function buildInterruptCallGraph(scopes: ScopeInfo[], ctx: TypeCheckerContext): InterruptCallGraph; /** * Emit warnings for function calls that may throw interrupts but aren't * inside a handler. * * Scoped to graph-node bodies only. `def` functions are designed to * propagate interrupts to the nearest enclosing handler in the caller — * that's the whole point of the interrupt model. Warning on `def` * bodies floods every library function that calls `read`/`glob`/etc. * with noise and trains users to ignore the diagnostic, defeating its * purpose at the `node` boundary where the prompt actually surfaces to * a human operator. */ export declare function checkUnhandledInterruptWarnings(scopes: ScopeInfo[], interruptEffectsByFunction: Record, ctx: TypeCheckerContext): void; /** * Every interrupt effect kind an (inline handler / handle) `body` can raise, * transitively — direct raises plus the propagated kinds of everything it calls * (via `collectFromBody` + `interruptEffectsByFunction`). The single source of * the "what can this body raise" computation, shared by handler-offender * detection and handler-param typing (H1). */ export declare function collectRaisableEffects(body: AgencyNode[], info: ScopeInfo, interruptEffectsByFunction: Record, ctx: TypeCheckerContext): string[]; /** * `interrupt` is not allowed inside any callback body. Callbacks fire as * side effects; their body cannot pause execution to ask the user a * question. Move the `interrupt` into the calling node/function instead, * or use a runtime guard if you wanted budget enforcement. * * After the `liftCallbacks` preprocessor runs, every * `callback(...) { ... }` block becomes * `callback("hookName", __cb_scope_N)` — a 2-arg call whose second * argument is a `variableName` referencing a lifted top-level * function. We look that function up in `interruptEffectsByFunction` * (transitively populated) and emit an error if it may interrupt. */ export declare function checkCallbackBodyInterrupts(scopes: ScopeInfo[], interruptEffectsByFunction: Record, ctx: TypeCheckerContext): void;