/** * Phantom-reference findings (change: add-phantom-reference-findings). * * Agents invent APIs and packages: an import of a module that is not there, a named import the * target does not export, a package no manifest declares. The invented package name is also an * attack surface — register the name an assistant keeps suggesting and it gets installed. Each of * these is decidable locally, against the repository itself, with no network request. * * For every reference a diff ADDS (never the whole repository), this module answers one question * and reports only the misses: * * | finding | the reference | * |---|---| * | `unresolved-internal-import` | a relative import whose target file is not on disk or in the corpus | * | `unresolved-internal-symbol` | a named import the target module does not export | * | `undeclared-dependency` | a bare specifier no manifest in scope declares | * | `unpinned-dependency` | declared in a manifest the diff changed, but absent from the lockfile beside it | * * **Honesty.** Each claim rests on evidence the repository provides, so each gap is disclosed * instead of guessed: * - the target module's exports are parsed from its CURRENT content, never from the index, so an * export the same diff adds resolves; * - a module that re-exports from somewhere this reader cannot follow (`export *` of an external * package) can always be the name's source, so no symbol finding is made for it; * - a path alias (`@/x`, a tsconfig `paths` entry, a package.json `imports` subpath) is disclosed, * never reported, because the resolver does not read alias configuration; * - a language without import support, a file that could not be read, an ecosystem with no * manifest, and a manifest that did not parse are each `not-assessed`; * - a non-literal dynamic import is disclosed, not checked. * * Nothing here reaches the network. A declared package name is not proof that the package is safe — * that is supply-chain tooling's job; this module only reports names nothing in the repository * accounts for. */ import { type ImportInfo } from '../analyzer/import-parser.js'; import type { GovernanceFinding } from './mcp-handlers/enforcement-policy.js'; import type { ChangedFile } from '../../types/index.js'; /** One reference a diff added, as written. */ export interface AddedReference { /** The importing file, relative to the analyzed root. */ file: string; /** The module specifier as written. */ specifier: string; /** Names taken FROM the target (aliases resolved to the source name); empty for default/namespace. */ names: string[]; line: number; isRelative: boolean; isDynamic: boolean; /** True when the import binds nothing by name (default, namespace, star, side-effect). */ wholeModule: boolean; } /** * The references in `head` that `base` did not have, compared by specifier and bound names so that * moving an import does not read as adding one. `base` is null for a file the diff adds. */ export declare function addedReferences(file: string, baseImports: ImportInfo[] | null, headImports: ImportInfo[]): AddedReference[]; /** * Names in `available` worth offering for a missing `name`: those sharing a prefix of at least * {@link MIN_CANDIDATE_PREFIX} characters with it, longest shared prefix first, then alphabetical. * A shared prefix is an exact comparison — nothing here scores similarity or guesses a correction. */ export declare function prefixCandidates(name: string, available: Iterable): string[]; /** A file, or a whole check, that could not be assessed — never reported as resolving. */ export interface NotAssessed { file: string; reason: string; } /** * One unresolved reference as a FACT: the briefing carries these, while the enforcement-classified * findings belong to `verify_change`, which holds the operator's policy. */ export interface PhantomReferenceFact { code: string; /** The file holding the reference. */ path: string; line?: number; /** The specifier, or `specifier#name`, the finding is about. */ subject: string; } export interface PhantomReferenceResult { findings: GovernanceFinding[]; /** Files whose references were not checked, each with the reason. */ notAssessed: NotAssessed[]; /** Disclosures that apply to the whole check (aliases skipped, missing manifests, caps reached). */ boundaries: string[]; /** How much was looked at, so an empty finding list can be read correctly. */ summary: { filesChecked: number; referencesChecked: number; }; } /** The findings as facts, for a briefing that carries no policy. */ export declare function phantomReferenceFacts(result: PhantomReferenceResult): PhantomReferenceFact[]; /** A package.json's own name, and the workspace globs it declares members under. */ export declare function workspaceDeclaration(text: string): { name: string | null; patterns: string[]; }; /** * Check every reference the diff adds. Read-only, offline, and advisory: each miss is a registered * finding, and everything that could not be judged is disclosed rather than counted as resolved. * * `prefetched` is the diff already read by a caller (blast_radius reads it with lockfiles kept), so * the check costs no second git diff. * * `pending` grades an edit before it is written (the before-edit hook): for each root-relative path * it names, `head` is the text the edit would produce and `base` the file's text now (null when the * file does not exist yet), so only the references this edit adds are checked and nothing is * written (change: add-agent-loop-hooks-codex-pi-and-pending-edits). */ export declare function checkPhantomReferences(root: string, baseRef: string, prefetched?: readonly ChangedFile[], corpusFiles?: Iterable, pending?: { head: ReadonlyMap; base: ReadonlyMap; }): Promise; /** * Re-export statements as imports: `export { a, b } from './x'` reads `a` and `b` from `./x`, and * `export * from './x'` reads the module as a whole. Written as one scan so the names and the line * come from the same place the import parser would give them. */ export declare function reExportsAsImports(content: string): ImportInfo[]; /** Whether a file's language has import support in the language-support matrix. */ export declare function supportsImports(filePath: string): boolean; /** * The imports a file declares, as written. Null when this check cannot read the language's imports: * the matrix may claim import support that only the call-graph resolver provides (Java, Kotlin, C#, * PHP), and a file in one of those is disclosed as not assessed rather than counted as clean. */ export declare function extractImports(filePath: string, content: string): ImportInfo[] | null; /** The finding codes this module emits, as registered in the finding-code catalogue. */ export declare const PHANTOM_REFERENCE_CODES: readonly ["unresolved-internal-import", "unresolved-internal-symbol", "undeclared-dependency", "unpinned-dependency"]; //# sourceMappingURL=phantom-references.d.ts.map