import type { FunctionDefinition, GraphNodeDefinition } from "../types.js"; import type { ImportedFunctionSignature } from "../compilationUnit.js"; import type { BuiltinSignature } from "./types.js"; /** * Names of Agency's *built-in* functions — language primitives with no * `def` source. Users cannot redefine these via `def` or `node`. Single * source of truth — imported by typeChecker/index.ts. * * Stdlib functions (print, read, fetch, etc.) are NOT here. They are * regular Agency code in stdlib/index.agency and users may shadow them. * * Built-ins fall into two parse-time categories: * * 1. Parsed as plain `functionCall`. Their typed signatures live in * BUILTIN_FUNCTION_TYPES, so they resolve as `kind: "builtin"` before * reaching the reserved-name fallback below. Listed here too as * defense-in-depth (if a BUILTIN_FUNCTION_TYPES entry is ever removed, * the reservation still prevents user redefinition). * * 2. Parsed as their own AST node — never reach functionCall handling: * * schema(Type) → SchemaExpression — a language primitive that * bridges *type space* and *value space*: the argument is a * VariableType (not a value expression), and at runtime it * constructs a zod schema from that type. Reserved here only so * that `def schema()` can't create parse ambiguity. The typechecker * currently synthesizes its result type as "any" — populating it * with a structured `Schema` type is future work. * * interrupt ... → InterruptStatement * debugger → DebuggerStatement */ export declare const RESERVED_FUNCTION_NAMES: Set; /** * Registry of JavaScript / Node.js globals that compiled Agency output * is allowed to call. Each entry is either: * - kind: "callable" — a function (sig is optional; populating it * later enables type-checking) * - kind: "namespace" — an object with named members, each of which * is itself a JsRegistryEntry * * Phase 1 uses only the structure (existence checks). Phase 2 will * populate `sig` for entries we want type-checked; the typechecker * starts enforcing arity/types when `sig` is present. * * Names already supported natively by Agency (null, undefined) or rare * enough to defer (NaN, Infinity) are intentionally absent. */ export type JsRegistryEntry = { kind: "callable"; sig?: BuiltinSignature; } | { kind: "namespace"; members: Record; /** When set, this namespace is ALSO directly callable (e.g. * `String(x)` coerces to string, `Number(x)` to number). */ callableSig?: BuiltinSignature; }; export declare const JS_GLOBALS: Record; /** * Walk a namespace path through `JS_GLOBALS`. Returns the leaf entry if * the full chain resolves, otherwise null. * * Examples: * lookupJsMember(["JSON", "parse"]) → { kind: "callable", sig: undefined } * lookupJsMember(["JSON", "banana"]) → null * lookupJsMember(["NotAGlobal", "x"]) → null */ export declare function lookupJsMember(path: string[]): JsRegistryEntry | null; type ResolveCallInput = { functionDefs: Record; nodeDefs: Record; importedFunctions: Record; /** Names imported via `import node { ... } from "..."`. */ importedNodeNames: readonly string[]; /** Names imported via `import { foo } from "./helpers.js"` (non-Agency). */ jsImportedNames?: object; scopeHas: (name: string) => boolean; }; /** * Tagged union describing where a call name resolved to. The diagnostic * only cares whether `kind === "unresolved"`; the other kinds are * informational so future analyses can distinguish "user code" from * "language built-in" from "JS interop" without re-running the lookup. * * def — locally-defined `def` or `node` in this file. * imported — function or node imported from another `.agency` file * (`import { foo } from "./other.agency"` or * `import node { foo } from "./other.agency"`). * builtin — has a typed signature in `BUILTIN_FUNCTION_TYPES`. * True language primitives only: `success`, `failure`, * `llm`, `approve`, `reject`, `propagate`, `checkpoint`, * `getCheckpoint`, `restore`, `isSuccess`, `isFailure`. * Stdlib functions (`print`, `fetch`, `read`, etc.) * resolve as `imported` instead, via the auto-injected * `import { ... } from "std::index"` statement. * scopeBinding — bound in the local scope (lambda, `partial`, * `for` variable, etc.). * jsGlobal — flat callable JS global (`parseInt`, `setTimeout`). * Namespace member calls like `JSON.parse(...)` are * handled separately via `lookupJsMember`. * unresolved — none of the above. The diagnostic emits. * * The other RESERVED_FUNCTION_NAMES entries (`schema` → SchemaExpression, * `interrupt` → InterruptStatement, `debugger` → DebuggerStatement) are * parsed into their own AST node type and never reach `resolveCall` as a * `functionCall`, so there's no `kind: "reserved"` here. */ export type CallResolution = { kind: "def"; } | { kind: "imported"; } | { kind: "jsImported"; } | { kind: "builtin"; } | { kind: "scopeBinding"; } | { kind: "jsGlobal"; } | { kind: "unresolved"; }; /** * Resolution order matters: * * 1. Local `def`/`node` — user code wins. * 2. Imported from another file — cross-file `def`/`node`. Stdlib * functions (print, fetch, read, * …) resolve here via the * auto-injected std::index import. * 3. JS-imported name — `import { foo } from "./helpers.js"`. * 4. `BUILTIN_FUNCTION_TYPES` — Agency language primitives. * 5. Local scope binding — lambdas, `partial`, `for` vars. * 6. Flat JS global callable — parseInt, setTimeout, etc. * 7. Otherwise: unresolved. */ /** * Inputs for {@link isJsGlobalBase}. The shape mirrors {@link ResolveCallInput} * but adds import-node bookkeeping so user definitions of `JSON` / `Math` / * etc. (via `node`, `import node`, or a `let`) cleanly opt out of * JS_GLOBALS validation. */ export type ShadowingInput = { scope: { has(name: string): boolean; }; functionDefs: object; nodeDefs: object; importedFunctions: object; importedNodeNames: readonly string[]; jsImportedNames?: object; }; /** * `true` only when `name` resolves to a JS global *and* nothing user-defined * shadows it. Use this to gate JS-namespace member validation (e.g. * `JSON.parse(...)`) and avoid checking against `JS_GLOBALS` when the user * has their own `node JSON()` / `let JSON = …`. */ export declare function isJsGlobalBase(name: string, input: ShadowingInput): boolean; export declare function resolveCall(name: string, input: ResolveCallInput): CallResolution; export {};