/** * Disk-backed adapter for the code-anchored memory engine. * (change: add-code-anchored-memory-staleness) * * Bridges the pure {@link ./anchor.ts} engine to the running project: it reads * the call graph from the edge store and function/file source from disk to supply * {@link AnchorNode}s (for resolution) and a {@link GraphFreshnessView} (for * verdicts). All operations are deterministic static analysis — no LLM. * * File buffers are read once and cached per operation; an anchor set touches only * a handful of files, so this stays cheap. The freshness-view builder is exported * standalone ({@link makeFreshnessView}) so callers that already hold an open * edge store (e.g. orient) can reuse it instead of opening a second handle. */ import { EdgeStore } from '../services/edge-store.js'; import type { FunctionNode } from '../analyzer/call-graph.js'; import type { StructuralAnchor, GroundingCertificate } from '../../types/index.js'; import { type AnchorNode, type GraphFreshnessView } from './anchor.js'; /** * Read a node's current source span text (code-unit offsets) from disk, confined * to the project root, or `undefined` when the file is unreadable. Exported so the * continuity carry-forward computes span hashes the SAME way the freshness engine * does (one slicing source — avoids a latent divergence). */ export declare function nodeSpanText(rootPath: string, node: FunctionNode, cache: Map): string | undefined; /** Per-view memos keyed by file path; a view's file cache holds one content per path. */ interface ViewMemo { offsetsCurrent: Map; newlines: Map; /** Per node id: its span hash, and its span text with CRLF read as LF. */ nodeHashes: Map; nodeSources: Map; } /** * Whether the stored offsets of `node` still describe the file on disk * (add-memory-anchor-verdicts). Offsets come from the last analysis; after an edit * they point at other text, and a span search there could find the span in a * neighboring symbol. The watcher records the hash of each file it re-indexed; a * full analyze records the content key of each file it extracted. `current` when * one matches the content on disk; `changed` when a recorded key does not; `unknown` * when the index has no key for the file (an imported bundle carries none). Only * `current` offsets are trusted for a span search. Memoized per file. */ type OffsetsState = 'current' | 'changed' | 'unknown'; /** * Why a requested span was not recorded on an anchor (add-memory-anchor-verdicts): * the hint named no symbol that resolved, the text does not occur exactly once in * the symbol's body, the source is unreadable, or the file changed since it was * analyzed (its stored offsets no longer locate the symbol). The anchor is then recorded without the span, and * `remember` says so. */ export type SpanNotRecordedReason = 'no-symbol' | 'not-found' | 'ambiguous' | 'unreadable' | 'index-stale' | 'index-unverified'; /** * Build a {@link GraphFreshnessView} over an already-open edge store + disk. The * view carries its own short-lived file cache. No rename map by default — a * missing symbol is `orphaned` unless the caller supplies `renameOf`. */ export declare function makeFreshnessView(store: EdgeStore, rootPath: string, renameOf?: (nodeId: string) => string | undefined, shared?: { cache: Map; memo: ViewMemo; }): GraphFreshnessView; export declare class AnchorContext { private readonly store; private readonly rootPath; private fileCache; private viewMemo; private constructor(); /** Open the adapter, or return null when no analysis (edge store) exists yet. */ static open(rootPath: string): AnchorContext | null; close(): void; /** * Test-only: how many distinct files this adapter has read from disk. * * The observable that pins anchor resolution to the symbol's own file rather than * the whole repository. (change: optimize-serving-hot-path-caches) */ _filesReadForTesting(): number; private spanHash; /** Build resolvable {@link AnchorNode}s for every internal node in the given files. */ anchorNodesForFiles(files: readonly string[]): AnchorNode[]; /** * Whether the analysis indexed this file at its current content * (add-memory-anchor-verdicts): see {@link offsetsState}; `undefined` when the file * is unreadable (a deleted file has no offsets left to be wrong about). */ fileIndexedAsOnDisk(filePath: string): OffsetsState | undefined; /** * The file a symbol anchor's freshness is actually checked in: its node's file, or * the file its stable id relocated it to (add-memory-anchor-verdicts). */ resolvedFileOf(anchor: StructuralAnchor): string; /** Current whole-file content hash, or undefined when the file is gone. */ fileContentHash(filePath: string): string | undefined; /** A {@link GraphFreshnessView} backed by this adapter's edge store + disk. */ freshnessView(): GraphFreshnessView; /** * Build a grounding certificate for an anchor — the evidence behind a `fresh` * verdict (add-trust-calibrated-context-economy). For a symbol anchor it is the * node's symbol/file/line-span and the *current* span hash (the same hash the * freshness check compared); for a relocated symbol it follows the stable id; * for a file anchor it is the file path and whole-file hash. Returns undefined * when the anchored ground can no longer be located (caller only builds these * for `fresh` facts, where it resolves). No new extraction beyond the span hash * the freshness check already computes. */ certificateForAnchor(anchor: StructuralAnchor): GroundingCertificate | undefined; /** Evidence for a span anchor: the lines its unique occurrence covers and the span's hash. */ private spanCertificate; /** * The current span hash AND 1-based inclusive line range of a node, computed * from its code-unit offsets against the live file (the edge store keeps * offsets, not lines). Reuses the same span — and the same {@link sliceNodeSpan} * — that the freshness hash covers, so `contentHash` here equals the hash the * freshness check compared, and the cited line range matches the hashed span. */ private nodeSpanInfo; /** * Resolve anchors for a decision: symbol-level anchors for any function in the * affected files whose name is mentioned verbatim in the decision text, plus a * file-level anchor (with a captured baseline hash) for each affected file. */ resolveDecisionAnchors(affectedFiles: readonly string[], text: string): StructuralAnchor[]; /** * Resolve caller-supplied anchor hints (for `remember`). Each hint may name a * symbol and/or a file. A symbol that resolves to exactly one node becomes a * symbol anchor; otherwise the file (if given) becomes a file anchor. */ resolveInputAnchors(hints: ReadonlyArray<{ symbol?: string; file?: string; span?: string; }>, spanNotRecorded?: (hintIndex: number, reason: SpanNotRecordedReason) => void): StructuralAnchor[]; /** Whether `span` occurs exactly once in the node's current body. */ private locateHintSpan; } /** Whole-word, case-sensitive mention test for a symbol name in free text. */ export declare function isNamedIn(text: string, name: string): boolean; export {}; //# sourceMappingURL=anchor-adapter.d.ts.map