import { G as GraphNode, a as GraphEdge, A as Area, R as ResolverKind, V as VgGraph, F as FileParse, E as EdgeKind, b as Fact, c as GroundingKind, d as GroundingEdge, S as SCHEMA_VERSION } from './types-DHqH0XIE.js'; export { e as AnalysisTier, C as Centrality, D as DerivedBy, f as EpistemicTier, g as FactConfidence, h as FactKind, i as GraphMeta, j as GraphSummaries, H as HubBlastSummary, N as NodeKind, P as Provenance, k as SUPPORTED_SCHEMA_VERSIONS, l as Span, m as SupportedSchemaVersion, T as Toolchain, U as Unknown } from './types-DHqH0XIE.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; declare const VERSION = "2026.819.3"; /** * Analysis stage: importance, centrality, hubs, communities, and surprise. * * - **Centrality** blends PageRank + betweenness + eigenvector + degree over the * dependency graph (call/import/extends/implements/references), catching * high-fan-in critical nodes that degree-only ranking misses. * - **Communities** via Louvain (`graphology-communities-louvain`, MIT), seeded * and single-pass for determinism. Leiden (via a permissive WASM impl) is a * later enhancement; the cluster mode is reported honestly, never silent. * - **Hubs** are centrality outliers (importance ≥ mean + 2σ). * - **Surprise** flags improbable cross-area edges (architectural smells), * surfaced by `vg oddities`. * * Pure and deterministic: identical (nodes, edges) → identical result. */ type ClusterMode = 'leiden' | 'louvain' | 'none'; type AnalysisTier = 'full' | 'large' | 'xl'; interface AnalyzeOptions { cluster?: ClusterMode; /** Skip betweenness above this node count (O(V·E) — too slow for huge graphs). */ betweennessLimit?: number; /** * Analysis cost tier. When omitted, auto-selected from node count: * full ≤5k nodes — PR + betweenness + eigenvector + Louvain * large ≤50k — PR + eigenvector + Louvain (no betweenness) * xl >50k — PR + degree; Louvain only on file-level contraction */ tier?: AnalysisTier; } interface AnalyzeResult { nodes: GraphNode[]; edges: GraphEdge[]; areas: Area[]; cluster: ClusterMode; tier: AnalysisTier; } declare function analyze(nodes: GraphNode[], edges: GraphEdge[], options?: AnalyzeOptions): AnalyzeResult; /** * Resource safeguards for the graph build. * * Building the map holds every parse table, node, and edge in memory at once, * so a pathological corpus (a vendored 200 MB bundle, a million-file tree, a * giant TS program) can OOM-kill the process — an uncatchable crash that takes * the caller (e.g. a `scan --push`) down with it. These limits convert that * crash into either a deterministic skip (per-file size cap, tsc rung cap) or * a catchable, actionable error (corpus cap, heap budget). * * Every limit is overridable via environment variable, and `0` always means * "disabled". Skips are pure functions of the input (file size, file count) — * never of observed memory — so identical input still yields identical output. */ interface ResourceLimits { /** Per-file source cap in bytes. Larger files stay stat-tracked for * freshness but are not parsed into the graph. 0 disables. */ maxFileBytes: number; /** Ceiling on discovered corpus files; exceeding aborts with guidance * instead of grinding toward an OOM. 0 disables. */ maxFiles: number; /** Ceiling on TS/JS files handed to the in-process TypeScript Compiler API * rung (a ts.Program over the whole corpus is the largest single memory * consumer). Above it the rung is skipped; the heuristic floor remains. * 0 disables. */ tscMaxFiles: number; /** Heap budget in MiB checked at phase boundaries; exceeding aborts with a * clear error before V8 hard-crashes. 0 disables. */ memoryBudgetMb: number; } /** A build stopped by a resource safeguard — catchable, unlike an OOM. The * message is user-facing and must carry its own remedy. */ declare class ResourceLimitError extends Error { readonly isResourceLimitError = true; constructor(message: string); } /** * Resolve effective limits: explicit overrides (tests, programmatic callers) * win over environment variables, which win over defaults. */ declare function resolveLimits(overrides?: Partial): ResourceLimits; /** * Stage wall-clock timers for build diagnostics. * Pure measurement — never enters the serialized graph artifact. */ type StageName = 'discover' | 'hash' | 'parse' | 'resolve' | 'tsc' | 'scip' | 'tests' /** Structural extraction over the infrastructure/CI corpus (engine/toolchain/). */ | 'toolchain' | 'analyze' | 'facts' | 'ground' | 'index' | 'total'; type StageTimings = Partial>; /** * Module resolution for import edges. Resolves an import specifier (relative, * `tsconfig` path alias, or workspace-package name) to a repo-relative file — * the fix for alias-heavy TS monorepos (Nx/Turborepo) where every cross-package * import looked "external" and tanked call resolution. * * Deterministic: it reads the repo's tsconfig(s) and workspace manifests once at * construction, then `resolve()` is pure over those in-memory maps. Still the * heuristic rung — precise SCIP/stack-graphs slot above it later — but now * scope-aware of the project's own module map. */ interface ModuleResolver { /** Resolve `source` imported from `fromRel`; returns a repo-rel path or null (external). */ resolve(fromRel: string, source: string): string | null; } declare function buildModuleResolver(root: string, relSet: Set): ModuleResolver; /** A resolver that only handles relative imports (no fs) — for tests/embedding. */ declare function relativeResolver(relSet: Set): ModuleResolver; declare function parseJsonc(text: string): T; /** * Resolution: turn per-file symbol/edge tables into a connected graph of nodes * and typed, id'd edges. * * The Phase-0 resolver is the deterministic **heuristic** rung of the ladder * (VG-ENGINE-TEARDOWN §3.2). It is already well beyond a * single-candidate label match: it is scope-aware (same-file first), import-aware * (callees reachable through imported files next), and arity/visibility-honest * (records its confidence and resolution rung per edge rather than silently * dropping ambiguity). SCIP/stack-graphs rungs slot in above it later, recorded * via `edge.resolution`. */ /** * A reference the heuristic resolver could not connect to a definition — the raw * material for `vg unknowns`. `from` is the node id of the enclosing definition * (or file) where the reference occurs; `name` is the callee/type it names. We * keep the source-relative path so a later precise rung that covers this file can * suppress the unknown (the compiler is authoritative there). */ interface UnresolvedRef { from: string; name: string; kind: 'call' | 'extends' | 'implements'; count: number; fromRel: string; } interface ResolveResult { nodes: GraphNode[]; edges: GraphEdge[]; /** References the heuristic rung could not resolve (deduped, sorted). */ unresolved: UnresolvedRef[]; /** File-level resolved import targets (rel → rels), for downstream * change-scoping (the tsc cache's dependency-closure keys). Not serialized. */ importsByFile: Map>; /** Diagnostic counts, surfaced by `vg status`. */ stats: { callsResolved: number; callsUnresolved: number; importsResolvedToFile: number; importsExternal: number; resolvers: ResolverKind[]; }; } /** Architectural layer classification */ type ArchitectureLayer = 'routing' | 'middleware' | 'services' | 'domain' | 'data-access' | 'infrastructure' | 'presentation' | 'config' | 'testing' | 'shared'; /** * Finer than {@link ArchitectureLayer}. Omit when unknown (absent ≠ empty). * Syntax confirmation of a path/folder prior — not a second layer taxonomy. */ type ArchitectureFileRole = 'controller' | 'service' | 'repository' | 'entity' | 'handler' | 'router'; interface AstRoleHit { filePath: string; role: ArchitectureFileRole; layer: ArchitectureLayer; confidence: number; signal: string; packId: string; } interface BuildOptions { /** Directory to build (default cwd). */ root: string; /** Restrict to language ids. */ only?: string[]; /** Extra ignore globs (gitignore syntax). */ exclude?: string[]; /** Sub-paths to scope to. */ paths?: string[]; /** Worker count; 1 forces inline. */ jobs?: number; /** Force single-threaded parsing. */ inline?: boolean; /** Disable the incremental cache (full rebuild). */ noCache?: boolean; /** Heavier open passes (recorded in provenance; Phase 1+ wires the analyses). */ deep?: boolean; /** Community detection mode (default 'louvain'). */ cluster?: ClusterMode; /** Coverage report paths (default: auto-detect lcov/istanbul). */ coverage?: string[]; /** Skip coverage ingestion. */ noCoverage?: boolean; /** Skip grounding (free knowledge pack). Default: grounding on. */ noGround?: boolean; /** Path to a SCIP index to ingest (default: auto-detect index.scip). */ scip?: string; /** Skip SCIP ingestion even if an index is present. */ noScip?: boolean; /** Skip the in-process TypeScript Compiler API resolver (heuristic floor only). */ noTsc?: boolean; /** * Fast mode: skip tsc precise resolve (heuristic only). Useful for XL cold * builds when precision can wait for a focused rebuild. */ fast?: boolean; /** Force analysis tier (default: auto by node count). */ analysisTier?: AnalysisTier; /** Skip writing the SQLite serve index. */ noIndex?: boolean; /** Pin the artifact timestamp for byte-deterministic output. */ generatedAt?: string; /** Live progress during the parse phase (files done of total). */ onParseProgress?: (done: number, total: number) => void; /** Override directory for grammar .wasm files (offline / air-gapped). */ grammarsDir?: string; /** Resource-safeguard overrides (else VG_MAX_FILE_BYTES / VG_MAX_FILES / * VG_TSC_MAX_FILES / VG_MEMORY_BUDGET_MB env vars, else defaults). */ limits?: Partial; } /** Stat + content hash of one corpus file at build time. */ interface FileStat { rel: string; size: number; mtimeMs: number; hash: string; } interface BuildResult { graph: VgGraph; timing: { totalMs: number; stages: StageTimings; }; reparsed: number; reused: number; /** Files skipped via mtime+size fingerprint (subset of reused). */ statHits: number; totalFiles: number; /** Stat+hash of every file in the corpus — input for the freshness snapshot. */ fileStats: FileStat[]; resolveStats: ResolveResult['stats']; /** Present when the TypeScript Compiler API resolver ran (TS/JS files). */ tsc?: { files: number; calls: number; jsx: number; heritage: number; resolved: number; shards?: number; /** Files whose checker output was reused from the tsc cache (change-scoped * downstream, gap-closure 2c). Subset of `files`. */ reusedFiles?: number; }; /** Present when a SCIP index was ingested. */ scip?: { documents: number; references: number; resolved: number; tool?: string; }; /** SQLite index write result. */ index?: { ok: boolean; path?: string; reason?: string; }; warnings: string[]; /** Architecture role hits extracted during the parse already paid for. */ fileRoles: AstRoleHit[]; } declare function buildGraph(options: BuildOptions): Promise; /** * Load the code map for a repository. * * When `graphPath` is omitted, prefers an existing global-store snapshot, then * the legacy `.vibgrate/graph.json`, matching {@link resolveGraphPath}. * Prefers the SQLite index when its corpusHash matches the committed map * (faster cold serve on large repos). Returns null if none exists. */ declare function loadGraph(root: string, graphPath?: string): VgGraph | null; /** * Deterministic serialization of `graph.json`. * * Object keys are sorted recursively and arrays are emitted in the (already * stable) order the engine produced them, so two runs over identical content * yield byte-identical output — the determinism contract (VG-CLI-SPEC §1.3). * Pretty-printed (2-space) and newline-terminated so the committed artifact is * human-diffable and plays well with the union merge driver. */ declare function serializeGraph(graph: VgGraph, opts?: { compact?: boolean; }): string; declare function stableStringify(value: unknown, indent?: number): string; declare function parseGraph(json: string): VgGraph; /** * Graph artifact layout (Fusion Runtime Phase 1). * * By default the code map lives in the **global** application store * (XDG / Application Support), keyed by repository id — not inside the repo. * That keeps `vg` / `vg serve` / auto-refresh from dirtying the working tree. * * Legacy in-repo path `.vibgrate/graph.json` is still **read** when present * (migration), and is still the write target when: * - `VIBGRATE_GRAPH_IN_REPO=1` (or true/yes), or * - callers pass an explicit `graphPath` / `--graph`. * * `vg share` continues to opt into a committed in-repo map by writing under * `.vibgrate/` (and rewriting its gitignore). */ interface WriteOptions { root: string; html?: boolean; report?: boolean; graphPath?: string; } interface WrittenArtifacts { graphPath: string; reportPath?: string; htmlPath?: string; factsPath?: string; } declare function vibgrateDir(root: string): string; /** Historical in-repo map path (still read as a fallback). */ declare function legacyGraphPath(root: string): string; /** * When true, default writes go to `.vibgrate/graph.json` (pre–Phase-1 layout). * Useful for CI that asserts in-repo artifacts, and for `vg share` workflows. */ declare function preferInRepoGraph(env?: NodeJS.ProcessEnv): boolean; /** * Default **write** path for the map. Global store unless the operator opts * into the legacy in-repo layout. When git is available, prefer a * branch-keyed snapshot (Fusion §4.1.1) so switching branches does not * overwrite another ref's on-disk map. */ declare function defaultGraphPath(root: string, env?: NodeJS.ProcessEnv): string; /** * Resolve where to **read** the map from. * Order: explicit override → legacy in-repo when `VIBGRATE_GRAPH_IN_REPO` is set * → branch-keyed global (if on a git ref) → `current` global snapshot → * legacy in-repo → default write path. * * The in-repo env short-circuit matters for CI and the release benchmark: they * force portable `.vibgrate/graph.json` artifacts and must not pay a synchronous * `git rev-parse` (or prefer a leftover global snapshot) on every resolve. */ declare function resolveGraphPath(root: string, override?: string, env?: NodeJS.ProcessEnv): string; declare function writeArtifacts(graph: VgGraph, options: WriteOptions): WrittenArtifacts; interface VerifyResult { ok: boolean; checks: { name: string; ok: boolean; detail?: string; }[]; digest: string; } declare function verifyDeterminism(opts: { root: string; only?: string[]; exclude?: string[]; jobs?: number; }): Promise; /** * Deterministic Markdown summary (`GRAPH_REPORT.md`). Given a pinned * `generatedAt`, the report is byte-stable. Numbers are derived purely from the * graph, so the report can be regenerated from a committed `graph.json`. */ declare function renderReport(graph: VgGraph): string; declare function renderHtml(graph: VgGraph): string; /** * SCIP ingestion — the precise resolution rung (VG-ENGINE-TEARDOWN §3.2). * * Reads a real SCIP index (`index.scip`) produced by a language indexer * (scip-typescript, scip-python, scip-java, rust-analyzer→SCIP, …) and turns its * precise occurrences into call/reference edges at `resolution: "scip"`, * confidence 1.0 — a real SCIP indexer win. vg does * NOT bundle indexers; it consumes an index the user/CI generates (deterministic, * offline). The heuristic resolver remains the floor for files SCIP didn't cover. * * A small, dependency-free protobuf reader decodes only the fields we need, so * the core stays lean. */ interface ScipOccurrence { /** [startLine, startChar, endLine, endChar] or [startLine, startChar, endChar], 0-based. */ range: number[]; symbol: string; roles: number; } interface ScipDocument { relativePath: string; occurrences: ScipOccurrence[]; } interface ScipIndex { documents: ScipDocument[]; toolName?: string; toolVersion?: string; } declare function decodeScipIndex(buf: Uint8Array): ScipIndex; interface ScipResult { edges: GraphEdge[]; /** Repo-relative files SCIP covered (it is authoritative for these). */ coveredFiles: Set; /** counts for status/provenance. */ stats: { documents: number; references: number; resolved: number; }; } /** * Build precise edges from a SCIP index, mapped onto our nodes by (file, line). * A reference occurrence's enclosing node → the node where its symbol is defined. */ declare function scipEdges(index: ScipIndex, nodes: GraphNode[], relForScip: (p: string) => string): ScipResult; /** * Language registry: 20 grammar-backed languages (first wave + the Phase-3 * expansion) plus the embedded-script container formats (Vue/Svelte/Astro * single-file components, parsed via sfc.ts with the JS/TS grammars). * * Each language maps to a tree-sitter grammar shipped (pre-compiled to .wasm) by * `tree-sitter-wasms`. `grammarFile` is the base name under that package's `out/` * directory (and our bundled `grammars/` copy). The grammar *version* is recorded * in provenance so the determinism contract is explicit about its inputs. */ interface LanguageDef { /** Canonical short id used in the schema (`lang` field) and `--only`. */ id: string; /** Human label. */ label: string; /** File extensions (lowercase, with leading dot). */ extensions: string[]; /** tree-sitter-wasms grammar base name, e.g. `tree-sitter-typescript`. */ grammarFile: string; } declare const LANGUAGES: LanguageDef[]; declare function langForExtension(ext: string): LanguageDef | undefined; declare function langById(id: string): LanguageDef | undefined; declare function allLanguageIds(): string[]; /** * Deterministic file discovery. * * Respects `.gitignore` (including nested ones), explicit config excludes, and a * built-in SKIP_DIRS set consistent with the scanner. Results are returned * sorted by relative POSIX path so the build is order-independent of the * filesystem. */ declare const SKIP_DIRS: Set; declare const SKIP_FILES: Set; interface DiscoverOptions { /** Absolute root directory. */ root: string; /** Restrict to these language ids (e.g. ['ts','py']). Empty = all. */ only?: string[]; /** Additional ignore globs (gitignore syntax), e.g. from config `exclude`. */ exclude?: string[]; /** Explicit sub-paths to scope to (relative or absolute). */ paths?: string[]; } interface DiscoveredFile { /** Relative POSIX path from root. */ rel: string; /** Absolute path. */ abs: string; lang: LanguageDef; } declare function discover(options: DiscoverOptions): DiscoveredFile[]; /** A usage error that maps to exit code 5 at the CLI boundary. */ declare class UsageError extends Error { readonly isUsageError = true; constructor(message: string); } declare function parseSource(rel: string, langId: string, source: string): Promise; /** * Local-embedding semantic search for `vg ask --semantic`/`--deep` (no API key). * * The embedding backend is OPTIONAL and lazily loaded (the vendored * `src/vendor/fastembed` dense backend over `onnxruntime-node`, local ONNX) * so the core install stays lean and `ask` never breaks: * if the backend or model isn't available it degrades to (prefix-fuzzy) lexical * search with a clear note. Per-repo vectors live in a binary `*.embeddings` * file **alongside the code map** (global store under Application Support / * XDG data, or `.vibgrate/embeddings` in-repo) — never model-named, never under * `.vibgrate/cache/`, and NEVER inside the committed `graph.json`, so the map * stays byte-deterministic. The model id is recorded inside the file header so * a model change still invalidates the cache without putting the name in the * path. * * `--local` disables the model download (semantic is skipped unless already * cached via an injected backend), keeping the air-gapped guarantee. */ interface Embedder { /** Stable model id (recorded with cached vectors so a model change invalidates them). */ id: string; /** Embed documents → unit-or-raw vectors (cosine handles normalization). */ embed(texts: string[]): Promise; /** Embed a single query string. */ embedQuery(text: string): Promise; } /** Why semantic fell back to lexical — for calm, specific messaging. */ type EmbedUnavailable = 'not-installed' | 'no-permission' | 'download-failed' | 'init-failed'; interface LoadEmbedderOptions { local?: boolean; model?: string; noDownload?: boolean; showDownloadProgress?: boolean; /** * Called (not thrown) when the backend can't load. `detail` carries the * underlying loader error when there is one, so a host can log something * actionable instead of a bare category. */ onUnavailable?: (reason: EmbedUnavailable, detail?: string) => void; } /** * Whether this repo already has a cached vector set for `modelId` — i.e. semantic * search has run here before, so the next run is fast (no first-use download/embed). * Lets `ask` show the one-time setup note only when it's actually warranted. * Checks the binary sidecar next to the map (and the legacy JSON path once). */ declare function embeddingsCached(root: string, modelId: string): boolean; /** * Path of the binary embeddings file for this repo's current map — always named * `embeddings` (or `.embeddings` next to a branch-keyed graph), never * after the model. Exported for `vg embed --where` and tests. */ declare function embeddingsPath(root: string): string; /** Sidecar path next to a map file: `…/branch-main.graph.json` → `…/branch-main.embeddings`. */ declare function embeddingsPathFor(graphPath: string): string; /** * Try to load the optional local embedding backend. Returns null (→ caller falls * back to lexical) when running `--local`, when the dependency isn't installed, * or when the model can't initialize. */ declare function loadEmbedder(options?: LoadEmbedderOptions): Promise; /** * The text we embed for a node. Alongside identity + signature we add the * strongest available signal — the node's **doc-comment / docstring** summary — * plus lightweight context already on the graph (file-path words, area label), so * a tersely-named symbol (`Table`, `NotificationJob`) a concept query can reach. * * `document` nodes (markdown, manifests, Docker, CI, OpenAPI, …) put the * scrubbed body in `doc` — that body is the primary embed signal so `vg ask` * can answer project-context questions, not only code symbols. */ declare function nodeEmbedText(node: GraphNode, areaLabel?: string): string; declare function cosine(a: number[], b: number[]): number; /** Reports embedding progress: how many of `total` nodes are done so far. */ type EmbedProgress = (done: number, total: number) => void; /** * Node embeddings for the searchable (non-file/external) nodes, cache-backed: * only nodes whose embed-text changed are re-embedded. The first run embeds in * chunks, **persists the cache incrementally** (so an interrupted/timed-out run * resumes instead of wasting the work), and reports progress via `onProgress`. */ declare function getNodeEmbeddings(graph: VgGraph, embedder: Embedder, root: string, onProgress?: EmbedProgress): Promise>; interface RelevanceExpansion { /** Single lowercase word, ready for identifier-part matching. */ term: string; /** The question token/phrase (or topic id) that produced it. */ from: string; /** 0..1 relative confidence; scales the expansion's scoring contribution. */ weight: number; } interface RelevanceTopic { id: string; /** 0..1, normalized within one analysis. */ score: number; } /** One level of a matched taxonomy path, root-first. */ interface RelevanceTaxonomyLevel { id: string; path: string; /** 0..1 — never lower than the levels beneath it. */ score: number; /** This level's own vocabulary. */ terms: string[]; } /** A hierarchical match: the most specific node plus its ancestor chain. */ interface RelevanceTaxonomyMatch { /** "infrastructure/networking/dns/cname" */ path: string; levels: RelevanceTaxonomyLevel[]; score: number; /** Absolute evidence behind the match, not relative to other matches. */ evidence: number; /** What matched — "~" prefixes a fuzzy repair. */ via: string[]; /** The matched node's OWN vocabulary, for explaining the domain. */ terms: string[]; /** Filenames and extensions this node's work lives in, nearest first. */ files: string[]; /** Standards governing this node, current revision first. */ standards: RelevanceStandard[]; } /** A product the ask names, including through a misspelling. */ interface RelevanceVendorMatch { name: string; from: string; node?: string; topic: string; score: number; /** Filenames and extensions this vendor's configuration lives in. */ files: string[]; } /** A standard governing the matched area, at the revision the pack tracks. */ interface RelevanceStandard { name: string; publisher: string; node: string; /** "standard" or "regulation". */ kind: string; /** Lowercase category slugs from the website's own vocabulary. */ categories: string[]; } /** A misspelling the provider resolved. */ interface RelevanceCorrection { from: string; to: string; distance: number; } interface RelevanceAnalysis { version: string; topics: RelevanceTopic[]; expansions: RelevanceExpansion[]; /** Hierarchical matches, most specific first. Absent from older providers. */ taxonomy?: RelevanceTaxonomyMatch[]; vendors?: RelevanceVendorMatch[]; corrections?: RelevanceCorrection[]; /** Every file hint the analysis implies, most specific first. */ files?: string[]; /** Standards governing what the ask is about, most specific node first. */ standards?: RelevanceStandard[]; /** Deduped lowercase category slugs across those standards. */ categories?: string[]; } /** * Deterministic retrieval for `vg ask` (VG-CLI-SPEC §3.2). * * Builds a structured, fact-annotated, budget-bounded context block for a * question — designed to drop straight into an assistant's context. The default * is deterministic lexical+structural retrieval: identifier/term matching with * morphological prefix-fuzzing, term-role weighting (process verbs like "add" * only corroborate, never seed — see engine/concepts.ts), static concept * expansion ("payments" → stripe/billing/…, "direct debit" → sepa/bacs/mandate), * and a multi-term coverage bonus, ranked with importance as a mild tiebreaker. * `--semantic`/`--deep` adds * a hybrid local-embedding pass (`queryGraphSemantic`) that surfaces conceptually * related code even when no word is shared — still no API key. */ interface QueryOptions { budget?: number; limit?: number; /** Optional pre-computed relevance analysis (engine/relevance-provider.ts). * Injected by async callers so this module stays pure and sync; when * absent, ranking is exactly the built-in lexicon path. */ relevance?: RelevanceAnalysis | null; /** Optional per-node topic tags (engine/relevance-enrich.ts). Combined with * `relevance.topics` into a bounded affinity bonus: a node structurally * tagged with a topic the question is about ranks above an equal textual * match outside it. Never seeds alone — it multiplies existing evidence. */ topicTags?: Map | null; /** Previous conversational ask (multi-turn `vg code`). Its content terms — * never its process verbs — join ranking at a damped weight (CARRY_WEIGHT) * so a follow-up like "can we use direct debits?" keeps the prior turn's * topic ("where is stripe used?") instead of ranking the repo on the * follow-up's words alone. Absent → behaviour is unchanged. */ priorQuestion?: string | null; } interface QueryMatch { node: GraphNode; score: number; why: string; } interface QueryResult { question: string; matches: QueryMatch[]; context: string; tokensEstimate: number; } declare function queryGraph(graph: VgGraph, question: string, options?: QueryOptions): QueryResult; interface SemanticQueryOptions extends QueryOptions { /** Local embedder. Optional only when {@link semanticRanked} supplies the pass. */ embedder?: Embedder; /** Precomputed node vectors (from getNodeEmbeddings); falls back to lexical for nodes without one. */ nodeVectors?: Map; /** * A semantic pass someone else already ran — vgd ranking the question * against its resident slot index. Supplied instead of `embedder` + * `nodeVectors` so the caller pays neither a model load nor a vector scan, * and no vectors cross the socket. * * Only the ORDER of this list is consumed (RRF fuses rankings, not scores), * so a truncated top-K is not an approximation: a candidate ranked past a * few hundred cannot reach a 12-row answer. */ semanticRanked?: Array<{ id: string; score: number; }>; } /** * Hybrid lexical + local-embedding retrieval, combined with Reciprocal Rank * Fusion (Phase 3.2 — replaces the former 50/50 score blend), so a question * like "where do we handle auth failures?" can surface `verify_token` even * with no shared identifier. Topic affinity and importance stay multiplicative * tiebreakers on the fused score. Deterministic given the same model + cached * vectors; embeddings live in a binary sidecar next to the map, never in `graph.json`. */ declare function queryGraphSemantic(graph: VgGraph, question: string, options: SemanticQueryOptions): Promise; /** * camelCase / snake_case / kebab split of an identifier → lowercased parts. * The separator alternative is Unicode-letter-aware (`\p{L}\p{N}`, not * ASCII-only `a-zA-Z0-9`) so non-Latin identifiers split on punctuation * without losing every character to it; the camelCase boundary lookaround * stays ASCII-only since upper/lowercase casing is itself an ASCII-script * concept — scripts without case simply never trigger it and fall through to * the separator split. */ declare function identifierParts(name: string): Set; /** * Lenient node resolution (VG-CLI-SPEC §3.3): resolve `` by content-hash * id, qualified name, `file:line`, short name, or glob. Returns candidates * ranked by importance so the best match is first; ambiguity is surfaced (the * caller offers `--pick`). Deterministic ordering throughout. */ declare function findNodes(graph: VgGraph, query: string): GraphNode[]; /** Resolve to a single node, honoring a 1-based `--pick`. */ declare function resolveOne(graph: VgGraph, query: string, pick?: number): { node?: GraphNode; candidates: GraphNode[]; }; declare function nodeById(graph: VgGraph, id: string): GraphNode | undefined; interface ImpactItem { id: string; name: string; kind: string; file: string; line: number; depth: number; confidence: number; } interface ImpactResult { root: { id: string; name: string; }; depth: number; affected: ImpactItem[]; direct: number; transitive: number; /** Lowest edge confidence encountered (e.g. a dynamic-dispatch edge). */ minEdgeConfidence: number; } declare function impactOf(graph: VgGraph, rootId: string, opts?: { depth?: number; }): ImpactResult; /** * Shortest connection between two nodes (`vg path`). Uses graphology's * bidirectional BFS over the directed graph; falls back to the reverse direction * so "how does A connect to B" still answers when the dependency arrow runs B→A. */ interface PathResult { ids: string[]; direction: 'forward' | 'reverse'; } declare function shortestPath(graph: VgGraph, srcId: string, dstId: string): PathResult | null; /** Indexes over a graph for O(1) neighbor lookups (built once per command). */ declare class GraphIndex { readonly graph: VgGraph; readonly nodeById: Map; private outById; private inById; constructor(graph: VgGraph); out(id: string, kind?: EdgeKind): GraphEdge[]; in(id: string, kind?: EdgeKind): GraphEdge[]; node(id: string): GraphNode | undefined; /** * Resolved nodes called by `id` — invocations (`call`) plus structural * dependency references (`references`, e.g. a constructor-injected field's * type), since both represent real usage a caller/impact question cares * about. `references` is otherwise only emitted by the precise SCIP/tsc * rungs and (for Java DI wiring) the heuristic rung — never a guess. */ callees(id: string): { edge: GraphEdge; node: GraphNode; }[]; /** Resolved nodes that call or structurally reference `id`. */ callers(id: string): { edge: GraphEdge; node: GraphNode; }[]; private resolveTargets; } /** * Wire protocol for the lightweight Vibgrate daemon (`vgd`) — Fusion Runtime Phase 2 prototype. * * Line-delimited JSON over a local Unix domain socket (named pipe on Windows). * No credentials leave this process; the socket is local-only. */ declare const VGD_PROTOCOL_VERSION: "vgd/0"; interface WorkspaceRecord { /** Stable repository id (same as global store key). */ id: string; /** Absolute repository root. */ root: string; /** Absolute path to the current graph snapshot (may not exist yet). */ graphPath: string; /** ISO timestamp when this workspace was last registered / refreshed. */ registeredAt: string; /** Optional federation label. */ label?: string; /** Optional federation role. */ role?: 'primary' | 'member'; /** Git branch or detached SHA at registration (multi-branch ActiveGraph). */ gitRef?: string; } /** Lightweight ActiveGraph slot summary (no full graph payload). */ interface GraphSlotSummary { repositoryId: string; gitRef: string; loadedAt: number; lastAccessAt: number; nodeCount: number; current: boolean; idleMs: number; evictable: boolean; } type VgdRequest = { op: 'ping'; } | { op: 'status'; } /** Ask the daemon to exit cleanly (honoured only by a standalone `vg daemon start`). */ | { op: 'shutdown'; } | { op: 'list'; } | { op: 'register'; root: string; label?: string; role?: 'primary' | 'member'; } | { op: 'unregister'; root: string; } | { op: 'register-federation'; primaryRoot: string; members: Array<{ root: string; label?: string; role?: 'primary' | 'member'; }>; } | { op: 'list-graph-slots'; repositoryId?: string; } | { op: 'select-git-ref'; repositoryId: string; gitRef: string; } | { op: 'put-graph'; repositoryId: string; gitRef: string; graph: unknown; } /** * Load the workspace's on-disk code map into ActiveGraph inside the daemon * (binary snapshot first, JSON fallback). The fast-path alternative to * shipping the whole graph over the socket with put-graph — use it whenever * the map already exists on disk. */ | { op: 'load-graph'; root: string; gitRef?: string; graphPath?: string; } /** Lexical/structural query against the ActiveGraph for a repository. */ | { op: 'query-graph'; repositoryId: string; query: string; limit?: number; gitRef?: string; semantic?: boolean; } /** Blast-radius impact for a symbol id or qualified name. */ | { op: 'impact-of'; repositoryId: string; symbol: string; depth?: number; gitRef?: string; } /** Compact graph meta for the current (or named) slot — not the full graph. */ | { op: 'graph-summary'; repositoryId: string; gitRef?: string; } /** Semantic warm (docs/VGD-SEMANTIC-WARM-SPEC.md): worker + per-slot index state. */ | { op: 'embed-status'; repositoryId?: string; } /** Embed one string in the daemon's warm worker; the caller ranks locally. */ | { op: 'embed-query'; text: string; } /** Ensure the slot's vector index exists, building it if needed. */ | { op: 'embed-index'; repositoryId: string; gitRef?: string; wait?: boolean; } | { op: 'embed-rank'; repositoryId: string; gitRef?: string; text: string; limit?: number; } | { op: 'dep-context'; repositoryId: string; } /** * Hold this connection open and stream slot changes on it. The only op that * does not answer once and stop — everything else is request/response. */ | { op: 'watch-slots'; repositoryId?: string; } /** Approach B host broker: warm model status inside vgd. */ | { op: 'host-status'; } | { op: 'host-load'; modelPath: string; } | { op: 'host-unload'; modelPath?: string; } | { op: 'host-generate'; modelPath: string; messages: Array<{ role: string; content: string; }>; grammar?: string; requireGrammar?: boolean; maxTokens?: number; temperature?: number; }; /** Semantic warm state: the worker, and one row per resident slot index. */ interface EmbedStatusPayload { worker: 'stopped' | 'starting' | 'ready' | 'unavailable'; pid?: number; model?: string; crashes: number; reason?: string; slots: Array<{ repositoryId: string; gitRef: string; state: string; vectors: number; nodeCount: number; /** Targets still missing a vector on a slot that is not ready. */ pending?: number; builtAt?: number; buildMs?: number; error?: string; }>; } /** Compact match row for query-graph (no full node payloads). */ interface VgdQueryMatch { id: string; qualifiedName: string; kind: string; file: string; line: number; score: number; why: string; } interface VgdImpactItem { id: string; name: string; kind: string; file: string; line: number; depth: number; confidence: number; } type VgdResponse = { ok: true; pong: true; version: typeof VGD_PROTOCOL_VERSION; } | { ok: true; pid: number; uptimeMs: number; workspaces: number; /** Resident multi-branch ActiveGraph slots (Fusion §4.1.1). */ graphSlots?: number; version: typeof VGD_PROTOCOL_VERSION; socketPath: string; /** Calendar version of the CLI process serving this socket. */ cliVersion?: string; /** What the daemon is watching, and what it is mid-rebuild on. */ freshness?: Array<{ repositoryId: string; root: string; gitRef: string; watching: boolean; pending: number; building: boolean; lastRebuildAt?: number; }>; } | { ok: true; workspaces: WorkspaceRecord[]; } | { ok: true; workspace: WorkspaceRecord; } | { ok: true; workspaces: WorkspaceRecord[]; federation: true; } | { ok: true; removed: boolean; } | { ok: true; stopping: true; } | { ok: true; slots: GraphSlotSummary[]; } | { ok: true; watching: true; repositoryId?: string; } /** * An unsolicited frame on a `watch-slots` connection: this repo's map * changed and the subscriber should reload it. Carries the corpus hash so a * subscriber that already has that map can ignore the event. */ | { ok: true; event: 'slot-changed'; repositoryId: string; gitRef: string; nodeCount: number; corpusHash: string | null; } | { ok: true; semantic: EmbedStatusPayload; } | { ok: true; repositoryId: string; /** Digest of every manifest and lockfile in the tree. */ manifestHash: string; /** Declared/installed dependency records, as `engine/drift.ts` builds them. */ dependencies: Array<{ name: string; ecosystem: string; declared: string; installed?: string; }>; builtAt: number; } | { ok: true; vector: number[]; model?: string; } | { ok: true; ranked: Array<{ id: string; score: number; }>; repositoryId: string; gitRef: string; state: string; vectors: number; model?: string; rankMs: number; } | { ok: true; indexed: true; repositoryId: string; gitRef: string; state: string; vectors: number; buildMs?: number; } | { ok: true; selected: true; repositoryId: string; gitRef: string; } | { ok: true; stored: true; repositoryId: string; gitRef: string; nodeCount: number; } | { ok: true; query: string; repositoryId: string; gitRef: string; /** How the ranking was produced — `lexical` when semantic was not available. */ mode?: string; matches: VgdQueryMatch[]; tokensEstimate: number; } | { ok: true; repositoryId: string; gitRef: string; symbol: string; root: { id: string; name: string; }; depth: number; affected: VgdImpactItem[]; direct: number; transitive: number; } | { ok: true; repositoryId: string; gitRef: string; summary: { nodeCount: number; edgeCount: number; languages: string[]; corpusHash: string | null; root: string | null; }; } | { ok: true; host: { poolSize: number; loadedModels: string[]; bindingReady: boolean; }; } | { ok: true; hostLoaded: true; modelPath: string; } | { ok: true; hostUnloaded: true; cleared: number; } | { ok: true; hostGenerated: true; text: string; model: string; constrained: boolean; grammarApplied?: boolean; draftAcceptedChars?: number; latencyMs?: number; unknownIdentifiers?: string[]; } | { ok: false; error: string; code?: string; /** Present when `code` is `semantic_warming` so the client can show progress. */ state?: string; vectors?: number; pending?: number; nodeCount?: number; }; interface VgdClientOptions { socketPath?: string; /** End-to-end response timeout in ms (default: per-op, see {@link defaultVgdTimeoutMs}). */ timeoutMs?: number; } /** * Send one request to a running vgd and return the parsed response. * Rejects if the daemon is not reachable. */ declare function vgdRequest(request: VgdRequest, options?: VgdClientOptions): Promise; /** True when a local vgd answers ping. */ declare function vgdIsRunning(options?: VgdClientOptions): Promise; type VgdPublishOutcome = { status: 'not-running'; } | { status: 'failed'; error: string; } /** The daemon already holds this exact map — nothing was sent. */ | { status: 'current'; repositoryId: string; gitRef: string; } | { status: 'published'; repositoryId: string; gitRef: string; nodeCount: number; semantic?: string; }; /** * The one way an ordinary command joins the local runtime. * * Until now vgd only ever existed because `vg code` or the VS Code extension * started it, so a developer running `vg`, `vg build` and `vg ask` all day * never had a runtime at all — every invocation re-read the map and re-loaded * the embedder from cold. Auto-start makes the daemon the normal case, and * publishing on attach makes it hold the thing it is supposed to accelerate. * * Three rules keep this safe to put in front of every command: * * - **Never blocking.** `ensureVgdSoft` spawns and waits ~1.5s, not the 30s * `vg daemon ensure` waits. A daemon that is still coming up simply is not * used this time; the command runs its in-process path and finds a warm * runtime on the next call. * - **Never fatal.** Every outcome is a value, never a throw. "No daemon" is a * supported configuration, and it must stay one — the fallbacks are the * code paths that exist today. * - **Always escapable.** `--no-daemon`, `VG_NO_DAEMON=1`, and `CI` all turn * auto-start off. A one-shot CI job should not leave a background process * behind, and a user who does not want one must be able to say so once. */ type AttachStatus = 'attached' | 'disabled' | 'unavailable'; interface AttachResult { status: AttachStatus; /** Why, when not attached — safe to show verbatim. */ reason?: string; /** True when this call is what started the daemon. */ started?: boolean; socketPath?: string; repositoryId?: string; gitRef?: string; /** Outcome of the map publish, when one was requested. */ published?: VgdPublishOutcome; } interface AttachOptions { socketPath?: string; /** Spawn a daemon when none is listening (default true). */ autoStart?: boolean; /** How long to wait for a just-spawned daemon before giving up (default 1500ms). */ readyBudgetMs?: number; /** Publish this repo's map after attaching (default true). */ publish?: boolean; /** * The corpus hash of the map the caller already holds. When the daemon's * slot reports the same hash the publish is skipped entirely. * * This matters more than it looks: `load-graph` runs the registry's slot * funnel, and `onGraphPut` deliberately drops the slot's vectors — so * re-publishing an unchanged map on every `vg ask` invalidated and re-seeded * the semantic index once per command, for nothing. */ corpusHash?: string; /** Also wait for the slot's semantic index (default false — warming is background work). */ warmSemantic?: boolean; gitRef?: string; /** Custom `--graph` artifact path. */ graphPath?: string; /** Explicit opt-out from the caller's parsed flags (`--no-daemon`). */ disabled?: boolean; /** Injected (tests). */ isRunning?: typeof vgdIsRunning; request?: typeof vgdRequest; ensure?: (socketPath: string, readyBudgetMs: number) => Promise<{ running: boolean; started: boolean; }>; env?: NodeJS.ProcessEnv; } /** * Ensure a local vgd is running and knows about this repository's map. * Resolves to a description of what happened; never throws. */ declare function attachVgd(root: string, options?: AttachOptions): Promise; /** * One way to ask the daemon for a semantic ranking, shared by every surface. * * `vg ask`, `vg serve` and `vg lsp` each grew their own copy of "load the * embedder, embed the corpus, rank" — and `engine/refresh-scheduler.ts` records * what that costs: the per-server copies diverged, and one of them blocked an * editor Ask for ninety seconds. This is the single implementation of the * daemon path so that cannot happen again. * * Long-lived callers (the MCP server, the language server) hold one of these * for the process: it attaches once, remembers the slot, and per request sends * only the question. Short-lived callers get the same behaviour with the attach * folded into the first call. * * Every failure resolves to `null`, never a throw. `null` means "rank it * yourself" — the in-process path each caller already has, which is also what * happens when no daemon is running at all. */ interface DaemonRanking { ranked: Array<{ id: string; score: number; }>; /** How many vectors the slot holds — for logging, not correctness. */ vectors: number; model?: string; } interface SemanticProgress { state: string; vectors: number; pending?: number; nodeCount?: number; model?: string; /** One line for a live status spinner. */ detail: string; } interface RankWaitOptions { /** Called whenever the slot's status is sampled during a wait. */ onProgress?: (status: SemanticProgress) => void; /** How often to poll `embed-status` while the index is warming (default 300ms). */ pollMs?: number; /** Give up waiting and return null (default 10 minutes). */ timeoutMs?: number; now?: () => number; sleep?: (ms: number) => Promise; } interface SemanticSessionOptions extends Omit { /** How long to wait before retrying after a failed attach (default 30s). */ retryAfterMs?: number; now?: () => number; /** Injected (tests). */ attach?: typeof attachVgd; request?: typeof vgdRequest; } declare class DaemonSemanticSession { private repositoryId; private gitRef; private socketPath; /** The map the daemon was last told about, so an unchanged one is not resent. */ private publishedHash; /** Do not re-attach before this time — a down daemon must not cost every request. */ private retryAfter; private attaching; private readonly root; private readonly options; private readonly retryAfterMs; private readonly now; private readonly attachImpl; private readonly requestImpl; constructor(root: string, options?: SemanticSessionOptions); /** True once a slot is known — useful for a one-line status log. */ get attached(): boolean; /** * Rank `question` against the daemon's index for this repo. * `corpusHash` is the caller's current map: when it differs from what the * daemon holds, the map is republished before ranking, so a locally * refreshed map never ranks against yesterday's vectors. */ rank(question: string, corpusHash?: string, wait?: RankWaitOptions): Promise; private readProgress; private requestRank; /** * The daemon's shared dependency context for this repo — the manifest digest * and the dependency records — computed once per daemon instead of once per * process. Null whenever the daemon cannot answer, so the caller falls back * to computing it locally. */ depContext(): Promise<{ manifestHash: string; dependencies: Array<{ name: string; ecosystem: string; declared: string; installed?: string; }>; } | null>; private reset; /** Attach once; concurrent callers share the one attempt. */ private ensureAttached; } /** * The read-only tool set for the LOCAL `vg serve` MCP. Every tool is * side-effect-free and `readOnlyHint: true` (auto-approvable), and independent of * Vibgrate's hosted cloud MCP. The server is local-first; network access is * limited to the embedder's one-time model fetch, `upgrade_impact`'s `changelog` * option, and `library_docs`' hosted-catalog fall-through on a thin/missing local * doc — all disabled under `--local` (the hard airgap). * * Phase 2/3 add `tests_for`, `get_facts`, `guide_node`, `check_drift`, * `list_models`, `resolve_library`, `library_docs`. */ interface ToolContext { /** Project root (for filesystem-backed tools: drift, models). */ root: string; /** `--local`: keep the server air-gapped — no model download, lexical only. */ local?: boolean; /** `--dedup`: collapse a node's heavy relation lists on repeat reads this session. */ dedup?: boolean; /** Per-session set of node ids already returned in full (drives `--dedup`). */ seen?: Set; /** Path the served graph was loaded from (locates its tags sidecar). */ graphPath?: string; /** * The local runtime's semantic index, when one is reachable. Held for the * life of the server: attaching once and sending only the question per call * is what removes this process's own model load and vector scan. */ semanticSession?: DaemonSemanticSession; } interface VgTool { name: string; description: string; inputSchema: Record; handler: (graph: VgGraph, args: Record, ctx: ToolContext) => unknown | Promise; } declare const TOOLS: VgTool[]; /** * Server-side listing surface (the complement of the client-side deferral * below, for hosts that cannot defer): `--surface hot` / `VG_MCP_SURFACE=hot` * lists only the hot core, and `--tools a,b` / `VG_MCP_TOOLS=a,b` lists an * explicit subset. LISTING ONLY — every tool in `TOOLS` stays callable * whatever is listed (dispatch always resolves against the full array), so * behaviour, ranking, and responses are byte-identical across surfaces; the * only thing that changes is which schemas the host bills per step. Unknown * names are dropped; an empty resolved set falls back to the full surface * (fail-open — a typo must never produce a toolless server). */ interface ToolSurface { /** 'hot' lists only HOT_TOOLS; 'full' (default) lists everything. */ surface?: 'hot' | 'full'; /** Explicit tool names to list (wins over `surface`). */ tools?: string[]; } /** The graph-affecting discovery/build scope, replayed verbatim on refresh. */ interface BuildScope { only?: string[]; exclude?: string[]; paths?: string[]; deep?: boolean; noGround?: boolean; scip?: string; noScip?: boolean; noTsc?: boolean; cluster?: string; grammarsDir?: string; } interface SnapshotFile { version: string; /** corpusHash of the build this snapshot belongs to. */ corpusHash: string; scope: BuildScope; files: Record; } interface Drift { /** Files whose *content* changed (stat moved AND hash differs). */ changed: string[]; /** Files present now but absent from the snapshot. */ added: string[]; /** Snapshot files no longer present. */ removed: string[]; } interface ProbeResult { drift: Drift; /** The recorded build scope — what a refresh must replay. */ scope: BuildScope; /** corpusHash the current map was built from. */ corpusHash: string; } /** Persist the snapshot after a successful build. Best-effort (cache-only). */ declare function writeSnapshot(root: string, corpusHash: string, fileStats: FileStat[], scope?: BuildScope): void; declare function loadSnapshot(root: string): SnapshotFile | null; declare function hasDrift(drift: Drift): boolean; /** Total drifted files — the number shown to humans. */ declare function driftCount(drift: Drift): number; /** * Compare the working tree to the snapshot. Returns null when no snapshot * exists (nothing was ever built on this machine — auto-refresh stays off * rather than guessing the build scope). Stat-only except for files whose * stat moved; touch-only moves are absorbed back into the snapshot. */ declare function probeFreshness(root: string): ProbeResult | null; /** * Auto-refresh: bring the code map back in sync with the working tree when the * freshness probe says it drifted. The rebuild is the ordinary incremental * `buildGraph` (warm parse cache → only changed files re-parse), replaying the * scope recorded at the last explicit build, guarded by a cross-process lock * so a serving MCP process and a foreground command never write at once. * * Two properties keep this safe to run implicitly: * - **No git churn**: if the rebuilt corpusHash equals the snapshot's (e.g. a * drift that reverted itself), `graph.json` is left untouched — the artifact * stays byte-identical. * - **No surprise artifacts**: `GRAPH_REPORT.md`/`graph.html` are rewritten * only if they already exist; a refresh never adds files a user's explicit * build chose not to produce. */ interface RefreshOptions { /** Force single-threaded parsing (tests / constrained hosts). */ inline?: boolean; /** Worker count for the parse pool. */ jobs?: number; /** * The map path already resolved by the caller (e.g. `vg serve`'s startup * resolution). Passed straight through to `writeArtifacts` so a refresh * never re-resolves it: `defaultGraphPath` shells out to `git rev-parse` * (Fusion §4.1.1 branch keying), and re-running that on every drift-driven * refresh put a synchronous git spawn back on the hot tool-call path this * function exists to keep off of. Omit only when no caller-known path * exists (falls back to a fresh `defaultGraphPath` resolution). */ graphPath?: string; } type RefreshOutcome = /** Map already matches the working tree. */ { status: 'fresh'; } /** No freshness snapshot — no build ever ran here, so scope is unknown. */ | { status: 'no-snapshot'; } /** Another vg process is rebuilding right now; its write will land shortly. */ | { status: 'locked'; } /** Rebuilt. `wrote` is false when the corpus turned out unchanged. */ | { status: 'refreshed'; drift: Drift; ms: number; reparsed: number; totalFiles: number; wrote: boolean; } | { status: 'error'; message: string; }; /** * Probe, and rebuild incrementally if the tree drifted from the map. * Silent (no output) — callers own the messaging for their surface. */ declare function refreshIfStale(root: string, opts?: RefreshOptions): Promise; /** * How a navigation call reached the map: * - `mcp` — a tool call over the local `vg serve` MCP server; * - `cli` — a `vg ` invocation that identified itself with `--client`. * Both are recorded into one ledger under a shared tool vocabulary (CLI * subcommands are normalised to their MCP tool names via CLI_TOOL_ALIASES), so * `(tool, source)` is the command-vs-MCP split and the token math stays unified. * Absent on ledger lines written before sources existed → read as `mcp` (the * only path that recorded then). */ type Source = 'mcp' | 'cli'; /** * Outcome of a recorded navigation call: * - `complete` — returned results, with nothing capped or paginated; * - `partial` — returned results, but more were available/truncated; * - `miss` — returned no result (no match, not-found, not-connected). */ type Outcome = 'complete' | 'partial' | 'miss'; interface SavingEntry { ts: number; tool: string; outcome?: Outcome; vgTokens: number; baselineTokens: number; source?: Source; client?: string; provider?: string; model?: string; ms?: number; } /** Whether a savings ledger exists for this repo (i.e. `vg serve --savings` has recorded). */ declare function savingsRecorded(root: string): boolean; declare function recordSaving(root: string, entry: Omit, now: number): void; interface SavingsReport { enabled: boolean; days: number; queries: number; vgTokens: number; baselineTokens: number; ratio: number; estCostVg: number; estCostBaseline: number; saved: number; rateLabel: string; } declare function readSavings(root: string, days: number, now: number, ratePerM?: number): SavingsReport; /** * Live, in-memory session stats for `vg serve` — the "is it earning its keep?" * display. While the MCP server runs, every tool call is aggregated per tool * and per client (which AI is calling, how many calls, how long they take, and * the context tokens served vs the grep/read baseline they replaced), and a * status block on stderr keeps the operator posted. CLI navigation calls made * while serving (`vg impact … --client=` etc.) are folded in from the local * ledger by ./ledger-tail.ts, so agents that shell out to `vg` instead of * calling MCP tools still show up here. * * Privacy: everything here lives and dies with the serve session — nothing is * persisted or uploaded, so the display is always on (GUARDRAILS §3.4 applies * to the opt-in ledger/upload, which remain separate and off by default). * Counts only — never code, paths beyond what the operator already sees, or * question text. Sibling serve processes in the same repo (an assistant's own * spawned stdio server) surface their counts to a TTY display through the * ephemeral live-stats bus (./live-stats.ts) — same counts-only data, swept * on exit. * * Output discipline: stderr only. Under stdio transport, stdout IS the MCP * protocol stream and carries nothing else. */ interface CallSample { tool: string; /** Coarse, sanitized client label ('claude', 'cursor', … or 'unknown'). */ client: string; outcome: Outcome; /** * How the call arrived: an MCP tool call into this serve process, or a * `vg --client=` CLI invocation folded in from the local ledger * (see ./ledger-tail.ts). Absent reads as 'mcp'. */ source?: 'mcp' | 'cli'; /** Wall time of the call, ms. Absent = not measured (CLI ledger lines carry none) — never 0. */ ms?: number; /** Context tokens vg actually returned (savings tools only; else 0). */ vgTokens: number; /** Grep/read baseline estimate those tokens replaced (savings tools only; else 0). */ baselineTokens: number; } interface RollupRow { key: string; calls: number; complete: number; partial: number; miss: number; /** Calls that carried a measured wall time — the avg-ms denominator. */ timed: number; totalMs: number; vgTokens: number; baselineTokens: number; } interface SessionSnapshot { startedAt: number; /** Bumped on every recorded call — cheap dirty check for renderers. */ revision: number; /** Epoch ms of the most recent call, or null when none yet (never 0). */ lastCallAt: number | null; totals: RollupRow; /** Sorted by calls desc, then key — deterministic display order. */ clients: RollupRow[]; tools: RollupRow[]; /** The mcp-vs-cli split ('mcp' / 'cli' rows), same ordering. */ sources: RollupRow[]; } /** Aggregates tool calls for the lifetime of one serve process. */ declare class SessionStats { readonly startedAt: number; private revision; private lastCallAt; private readonly totals; private readonly byClient; private readonly byTool; private readonly bySource; constructor(now?: number); record(sample: CallSample, now?: number): void; snapshot(): SessionSnapshot; private rowFor; } type RefreshImpl = typeof refreshIfStale; interface GraphSourceTuning { probeIntervalMs?: number; refreshBudgetMs?: number; /** * Workspace root for freshness probes. Prefer passing this explicitly — * deriving it from `graphPath` via `dirname` twice only works for the legacy * `root/.vibgrate/graph.json` layout, not the global branch-keyed store. */ root?: string; /** Tests only: inject a slow/fake refresh to assert the micro-budget. */ refreshImpl?: RefreshImpl; } interface ServeOptions { /** Record local, counts-only usage savings (opt-in). */ savings?: boolean; /** * Periodically upload the counts-only ledger to Vibgrate (opt-in; off by * default). Implies recording. The upload itself is driven by the serve * command (see commands/serve.ts + engine/stats-share.ts); here it just also * turns recording on so there's something to send. */ shareStats?: boolean; /** Air-gapped mode (no model downloads). */ local?: boolean; /** Collapse repeat heavy relation lists within a session (opt-in). */ dedup?: boolean; /** Auto-refresh the map when the working tree drifts (default true). */ refresh?: boolean; /** `--no-daemon`: never auto-start or use the local runtime. */ daemon?: boolean; /** * Event-driven refresh: recursive fs.watch on the workspace so a save * rebuilds in ~400 ms instead of waiting out the freshness poll (default * true when refresh is on; `--no-watch` opts out). Where recursive watch is * unavailable the poll silently remains the only mechanism. */ watch?: boolean; /** * Workspace root (project directory). When set, freshness probes and tools * use this instead of inferring root from the graph path. */ root?: string; /** * In-memory session stats behind the live `vg serve` status display. Always * safe to pass: nothing recorded here is persisted or uploaded — it dies with * the process (the opt-in ledger above is a separate concern). */ stats?: SessionStats; /** * Listing surface (`--surface hot` / `--tools a,b`). Filters ONLY what * `tools/list` advertises; every tool stays callable so behaviour is * byte-identical across surfaces. See `listedToolNames` in ./tools.ts. */ toolSurface?: ToolSurface; } declare class GraphSource { readonly graphPath: string; private readonly refresh; /** Timing / root overrides (production passes `root`; tests may pass more). */ private readonly tuning; private cachedMtimeMs; /** * True while the daemon owns freshness for this workspace. Set only after * the daemon confirms a subscription, cleared the instant it drops — so the * failure mode is "this process watches again", never "nobody watches". */ private daemonOwnsFreshness; private cached; /** Project root used for freshness probes and rebuilds. */ readonly root: string; /** Debounce, single-flight, self-tuning and budget cap — shared with `vg lsp`. */ private readonly refresher; /** * Files seen changing since the last COMMITTED refresh (watcher events, * filename → last-seen ms). Entries are cleared only after a refresh that * started at-or-after their last event completes with 'fresh'/'refreshed' — * a change landing mid-refresh stays pending, so the staleness signal can * be a false positive but never a false negative. */ private readonly pendingChanges; private watcher; private watchTimer; constructor(graphPath: string, refresh?: boolean, /** Timing / root overrides (production passes `root`; tests may pass more). */ tuning?: GraphSourceTuning); /** * Hand freshness to the daemon: stop probing on every call and stop watching. * The daemon pushes `slot-changed` and we reload from disk then. */ deferFreshnessToDaemon(): void; /** The daemon went away — resume owning freshness locally. */ resumeLocalFreshness(): void; /** The daemon says the map moved; drop the cache so the next get() re-reads. */ reloadFromDisk(): void; /** Current graph: auto-refreshed if the tree drifted, reloaded if the file changed. */ get(): Promise; /** * Debounced, single-flight refresh — the shared scheduler does the work (see * engine/refresh-scheduler.ts). Never throws: a refresh problem must degrade * to "answer from the current map", not break the tool call. */ private maybeRefresh; /** * A COMMITTED outcome (map verified fresh, or rebuilt) clears the pending * set — but only entries whose last event predates the refresh start. * 'locked'/'error'/'no-snapshot' clear nothing: the map may still be behind * those changes. */ private onRefreshSettled; /** * Record a source change (watcher event, or a test). Arms the next probe to * run immediately (bypassing the self-tuned interval — a real event is not a * poll) and schedules a debounced background refresh so the rebuild happens * BETWEEN tool calls instead of on the next call's 100 ms budget. */ notePendingChange(filename: string): void; /** * Event-driven freshness (the serve-loop watcher): a recursive `fs.watch` * on the workspace feeds `notePendingChange`, so a save triggers a rebuild * in ~WATCH_DEBOUNCE_MS instead of waiting out the 2–30 s poll. The poll * stays armed as the fallback — on filesystems where recursive watch fails * (some containers/NFS) this returns false and behaviour is unchanged. */ startWatching(): boolean; stopWatching(): void; /** * In-band staleness signal for tool responses: what has changed since the * last committed refresh. Null when the map is current (the common case — * responses carry zero overhead then). */ stalenessNote(): string | null; } declare function createServer(source: GraphSource, opts?: ServeOptions): Server; declare function serveStdio(graphPath: string, opts?: ServeOptions): Promise; /** * Test-awareness (VG-ENGINE-TEARDOWN §3.6). * * Deterministic, two signals: * 1. **Static linkage** — calls from a test file into product code become `test` * edges (test file → covered node), so we can answer "which tests exercise * this" from structure alone, no runner needed. * 2. **Coverage** (coverage.ts) — runtime-grounded line coverage applied as * `coverage` on nodes (stronger than static linkage when present). * * A node's `tested` flag is true when it has any incoming test/coverage signal; * false for analyzable code with none; null for non-analyzable kinds. */ declare function isTestFile(rel: string): boolean; interface TestAwarenessResult { nodes: GraphNode[]; edges: GraphEdge[]; testFiles: string[]; testEdgeCount: number; } /** * Apply static test linkage. Adds `test` edges from each test file node to the * product-code nodes its functions call, and sets `tested` on analyzable nodes. */ declare function applyStaticTestLinkage(nodes: GraphNode[], edges: GraphEdge[]): TestAwarenessResult; /** * Answering the wedge questions: "which tests cover X" (`vg tests`) and "which * tests must I run if I change X" (`vg impact --tests`). Deterministic, from the * `test` edges + coverage produced at build time. */ interface CoveringTest { file: string; basis: 'call' | 'coverage'; confidence: number; } declare function coveringTests(graph: VgGraph, node: GraphNode, index?: GraphIndex): CoveringTest[]; interface TestImpact { affectedTestFiles: string[]; untestedAffected: { id: string; name: string; file: string; }[]; } /** The test files that exercise any node in the impact set of `rootId`. */ declare function testsToRun(graph: VgGraph, rootId: string, depth?: number): TestImpact; interface Runner { name: string; command: (testFiles: string[]) => string; } declare function detectRunner(root: string, lang?: string): Runner; /** * Coverage ingestion (VG-ENGINE-TEARDOWN §3.6) — runtime-grounded test linkage. * Parses LCOV (`coverage/lcov.info`) and Istanbul (`coverage-final.json`) into a * per-file line→hits map, then sets each node's `coverage` (fraction of its span * that ran) and `tested` flag. Stronger than static linkage where present. */ type LineHits = Map; type CoverageMap = Map; /** Find and parse coverage reports under root. Returns null if none found. */ declare function loadCoverage(root: string, explicit?: string[]): CoverageMap | null; /** Apply coverage to nodes: set `coverage` fraction over the node's span + `tested`. */ declare function applyCoverage(nodes: GraphNode[], coverage: CoverageMap): GraphNode[]; /** * The open facts subset (VG-PACKAGE-AND-SCHEMA §5) — reimplemented fresh in the * open engine: deterministic, no runtime, no corpus, no LLM, no hidden pipeline. * Three commodity fact kinds, each epistemic-typed so it never claims more than * the open layer can prove: * * - **contract** — from a public signature/type (declared → Observed) * - **invariant** — from a static assert/guard (static → Derived) * - **characterization** — from existing test linkage (static → Observed) * * Emitted on every build (cheap, deterministic). `--deep` is reserved for * heavier semantic layers, not for these open facts. */ declare function buildFacts(parses: FileParse[], nodes: GraphNode[], edges: GraphEdge[]): Fact[]; /** * The free knowledge pack shipped in the open CLI (VG-PACKAGE-AND-SCHEMA §6): * our own paraphrased guidance + openly-licensed standards (OWASP Top 10 2021, * CWE). Never verbatim proprietary text; every entry cites a public source. * Matching is deterministic (imports / called APIs / identifier keywords). */ interface MatchRule { imports?: string[]; calls?: string[]; keywords?: string[]; } interface PackEntry { id: string; topic: string; summary: string; citation: { title: string; url: string; }; kind: GroundingKind; rationale: 'recommended' | 'conjectured'; match: MatchRule; } interface KnowledgePack { id: string; version: string; license: string; entries: PackEntry[]; } declare const FREE_PACK: KnowledgePack; /** * Grounding (VG-PACKAGE-AND-SCHEMA §6) — match nodes to knowledge-pack entries by * deterministic signals (file imports, called APIs, identifier keywords) and * attach cited framing edges. Closed-world tools can't follow without building a * corpus. Deterministic-first; the free pack ships in the open CLI. */ declare function groundGraph(nodes: GraphNode[], edges: GraphEdge[], parses: FileParse[], packs?: KnowledgePack[]): GroundingEdge[]; /** * Dependency currency (VG-LOCAL-MODELS §9 / VG-DEVELOPMENT-PLAN Phase 2.4). * * Default path is **offline and deterministic**: inventory dependencies from * manifests and resolve installed versions from node_modules. Currency against * "latest/EOL/CVE" needs data, so it is strictly **opt-in** (`--online`, which * queries the public npm registry) — the offline core never touches the network. * Full DriftScore/CVE/EOL governance is the Vibgrate platform (the funnel). */ /** Supported dependency ecosystems (manifest + lockfile). Extend this list to widen coverage. */ declare const ECOSYSTEMS: readonly ["npm", "pypi", "go", "rust", "ruby", "php", "dotnet", "swift", "dart", "java"]; type Ecosystem = (typeof ECOSYSTEMS)[number]; interface DepRecord { name: string; ecosystem: Ecosystem; declared: string; installed?: string; latest?: string; drift?: 'major' | 'minor' | 'patch' | 'current' | 'unknown'; } interface DriftInventory { records: DepRecord[]; counts: { total: number; } & Record; } declare function inventory(root: string): DriftInventory; /** Opt-in online enrichment: query npm for `latest` and classify drift. */ declare function enrichOnline(records: DepRecord[], fetchImpl?: typeof fetch): Promise; /** * Local-model discovery (VG-LOCAL-MODELS §9.2) — be a no-key *consumer* of the * developer's local model fleet. Fully offline and deterministic: inspect the * on-disk layouts of Ollama / LM Studio / llama.cpp / the Vibgrate weight store, * never the network. No runtime is built or launched. */ interface LocalModel { runtime: 'ollama' | 'lm-studio' | 'gguf'; name: string; path: string; } declare function discoverModels(home?: string): LocalModel[]; /** * `vg lib` — a deterministic, on-disk library-currency catalog: version-correct * usage docs for the **exact version in your lockfile**, drift-annotated, from * on-disk sources (no key). * * The catalog (`vibgrate.lib.json`) is small and committable; doc bodies live in * `.vibgrate/lib/.md` so the team shares them on pull. Ingestion is * deterministic from local sources; URL/llms.txt ingestion is opt-in network. */ declare const LIB_SCHEMA: "vg-lib/1.0"; interface LibSource { type: 'local' | 'llms.txt' | 'website' | 'openapi' | 'git'; location: string; } interface LibEntry { id: string; name: string; version: string; source: LibSource; docFile: string; docHash: string; bytes: number; } interface LibCatalog { schemaVersion: typeof LIB_SCHEMA; libraries: Record; } declare function libId(name: string): string; declare function loadCatalog(root: string): LibCatalog; declare function saveCatalog(root: string, catalog: LibCatalog): void; /** Resolve a fuzzy name to a catalog entry (exact id, name, or substring). */ declare function resolveLib(catalog: LibCatalog, name: string): LibEntry | undefined; interface DriftNote { cataloged: string; installed?: string; drift: 'current' | 'behind' | 'ahead' | 'unknown'; } declare function driftFor(root: string, entry: LibEntry, inv?: DriftInventory): DriftNote; interface AddOptions { root: string; name?: string; version?: string; /** Allow network for URL/llms.txt sources. */ allowNetwork?: boolean; fetchImpl?: typeof globalThis.fetch; } /** Ingest docs for a library from a local path, a git repo, or (opt-in) a URL. */ declare function addLibrary(source: string, opts: AddOptions): Promise; declare function readDoc(root: string, entry: LibEntry): string; interface ServeLaunch { command: string; args: string[]; /** Human-readable explanation when the launch is not the plain `vg serve`. */ note?: string; } /** * Per-assistant install registry (a focused subset of VG-ASSISTANT-INSTALL §2; * the remaining 20+ assistants are added in Phase 3). All paths are repo-local * (the team-shareable, safe default); writes are idempotent. */ interface McpTarget { file: string; key: 'mcpServers' | 'servers' | 'mcp_servers'; /** Config syntax. Defaults to JSON; Grok's project config is TOML. */ format?: 'json' | 'toml'; vscode?: boolean; } interface NudgeTarget { file: string; kind: 'block' | 'file'; } interface Assistant { id: string; label: string; skill?: string; mcp?: McpTarget; nudge?: NudgeTarget; /** * Signs this assistant is in use, checked by `detectAssistants`: * `markers` are project-relative paths, `homeMarkers` are relative to the * user's home folder, `bin` are executables looked up on PATH. Detection is * best-effort presence-checking only — nothing is read or executed. */ markers?: string[]; homeMarkers?: string[]; bin?: string[]; } declare const ASSISTANTS: Assistant[]; declare function assistantById(id: string): Assistant | undefined; interface InstallOptions { root: string; hook?: boolean; smallRepo: boolean; /** Resolved MCP launch command; defaults to detectServeLaunch(). */ launch?: ServeLaunch; } interface InstallAction { wrote: string[]; skipped: string[]; /** Explanation when the MCP entry is not the plain `vg serve` (e.g. PATH fallback). */ note?: string; } declare function installAssistant(a: Assistant, opts: InstallOptions): InstallAction; declare function uninstallAssistant(a: Assistant, root: string, purge: boolean): string[]; /** * Deterministic exporters (VG-CLI-SPEC §4.2). One `vg export ` verb, format * inferred from the extension. Live graph-DB push is deliberately out (a file * import covers the same need offline). CycloneDX/SPDX power the SBOM/AI-BOM seam * (VG-LOCAL-MODELS §9.4). */ type ExportFormat = 'json' | 'ndjson' | 'graphml' | 'dot' | 'cypher' | 'sql' | 'md' | 'html' | 'cyclonedx' | 'spdx'; declare function formatForExt(ext: string): ExportFormat | null; interface ExportContext { graph: VgGraph; deps?: DepRecord[]; models?: LocalModel[]; generatedAt: string; /** Force compact JSON (no indent). Default: auto when nodes > COMPACT_JSON_NODES. */ compact?: boolean; /** Drop area members + grounding for smaller artifacts. */ slim?: boolean; } declare function exportGraph(format: ExportFormat, ctx: ExportContext): string; /** * The deferred, decoupled push envelope (VG-PACKAGE-AND-SCHEMA §7). In the open * CLI `vg push` is **specified but not built**: it assembles and redacts the * envelope and prints a notice, but performs **no network upload**. Nothing in * the free path depends on it. Drift-over-time/governance is the separate * commercial product. */ interface GraphUploadEnvelope { /** Mirrors the graph's own schema version — never pinned to a literal here. */ schemaVersion: typeof SCHEMA_VERSION; artifactType: 'graph'; scanIngestId?: string; vcs: { sha: string; shortSha: string; branch: string; }; repository?: { name?: string; remoteUrl?: string; }; generatedAt: string; graph: VgGraph; } declare function buildEnvelope(root: string, graph: VgGraph, scanIngestId?: string): GraphUploadEnvelope; /** * Redaction pass (GUARDRAILS §1: redact before storage). The graph is structure, * not content, but we defensively scrub any signature/name that matches a * credential-shaped pattern, and strip credentials from the remote URL. */ declare function redactGraph(graph: VgGraph): VgGraph; /** * The first existing directory that holds a grammar .wasm set. Skips the * vendor overlays — they hold single replacement files, never a full set. * For a complete per-language resolution use resolvedGrammarFiles(). */ declare function grammarsSourceDir(): string | null; /** The graph-grounded context handed to the model, plus what fed it. */ interface CodeContext { instruction: string; /** Symbols the retrieval surfaced as most relevant, with their relations. */ seeds: { node: GraphNode; why: string; }[]; /** Files the edit is expected to touch, in stable order. */ targetFiles: string[]; /** Blast radius: symbols that call/depend on the seeds (impact-aware review). */ impacted: { node: GraphNode; via: string; }[]; /** Hard constraints (declared facts) pinned so compaction can't drop them. */ pinnedFacts: string[]; /** Plain-language concept-map lines: how the ask's words were interpreted * (concept expansions, relevance topics, carried prior-turn terms). Empty * when nothing fired. Rendered so small models can follow the inference. */ conceptMap: string[]; /** The rendered, budget-bounded prompt block. */ rendered: string; tokensEstimate: number; } /** * Graph-grounded context assembly for `vg code` (VG-CLI-CODE §3). * * A generic coding agent starts blind and reconstructs structure by grepping and * reading whole files — which is exactly what blows the context window and * degrades the model. This module instead uses the deterministic code graph to * hand the planner a *small, high-signal, budget-bounded* context: the symbols * most relevant to the instruction, their immediate relations, the blast radius * of changing them, and any declared facts (hard constraints) that must not be * dropped by later compaction. Deterministic given a graph + instruction, so it * is fully offline-testable and benchmarkable. */ interface BuildContextOptions { /** Approx token budget for the rendered block (default 3000). */ budget?: number; /** How many retrieval seeds to expand (default 8). */ seeds?: number; /** Impact BFS depth for the blast radius (default 2). */ impactDepth?: number; /** Restrict the edit surface to these files (from `--file`), if given. */ files?: string[]; /** Optional pre-computed relevance analysis (engine/relevance-provider.ts), * loaded by the async caller; widens seed vocabulary deterministically. */ relevance?: RelevanceAnalysis | null; /** Optional per-node topic tags (engine/relevance-enrich.ts). */ topicTags?: Map | null; /** The previous conversational ask (multi-turn `vg code`). Its content * terms join seed ranking at a damped weight so follow-ups ("do we * support direct debits?" after "where is stripe used?") stay anchored to * the conversation's topic. Absent → single-turn behaviour, unchanged. */ priorInstruction?: string | null; } /** * Build the context block for a coding instruction. The ordering is * cache-stable by design (see router.ts): the invariant, repo-derived material * (facts, symbols, relations) comes first and the volatile instruction is * echoed last, so a provider's prompt cache can reuse the stable prefix across * turns. */ declare function buildCodeContext(graph: VgGraph, instruction: string, options?: BuildContextOptions): CodeContext; /** * Source-bearing Task Capsule compiler (Fusion Runtime Phase 0). * * Today's {@link buildCodeContext} pays for graph metadata, then the model still * calls `read_file` — double payment. This module compiles a Task Capsule that * includes exact source ranges (from Tree-sitter spans already on graph nodes) * so the first inference can solve without navigation tool calls (ZNS@1 path). * * Deterministic given (graph, instruction, file contents, options). Injectable * `readFile` keeps unit tests offline and pure. * * Schema: docs/fusion/task-capsule-v0.schema.json */ declare const TASK_CAPSULE_SCHEMA_VERSION: "task-capsule/0"; /** Frozen ranking policy id — bump when the heuristic changes (benchmark gate). */ /** Bumped 2026.07.1: strip URL/quoted needles from seed ranking (no path-token false positives). */ /** Bumped 2026.08.1: term roles (weak process verbs never seed alone), concept/bigram * expansion (payments→stripe, "direct debit"→sepa/bacs/mandate), multi-term coverage * bonus, directory-segment evidence — gated by the ask-quality corpus (bench/ask-corpus.mjs). */ /** Bumped 2026.08.2: optional relevance-provider seam — sanitized provider expansions * join term preparation (own 0..1 weight capped at EXPANSION_WEIGHT; weak-provenance * dropped); provider version recorded as `relevanceVersion` provenance. Absent * provider = 2026.08.1 behaviour exactly — gated by the same corpus, dual-mode. */ /** Bumped 2026.08.3: multi-turn field report ("do we support direct debits?" after a * stripe ask seeded direct* distractors). (a) Tokens consumed by a fired bigram * concept are demoted to the weak role — "direct" corroborates, never seeds. * (b) Conversation carry-over: the previous ask's content terms join ranking at * CARRY_WEIGHT when the caller passes `priorInstruction`. (c) A plain-language * "how the ask was interpreted" concept map is rendered for small local models. * No prior instruction + no bigram ask = 2026.08.2 behaviour exactly. */ declare const CAPSULE_RANKING_VERSION: "capsule-rank@2026.08.3"; declare const CAPSULE_COMPILER_ID: "vg-task-capsule/0"; interface BuildCapsuleOptions extends BuildContextOptions { /** * Read file contents relative to the repository root. Required for source * slices; when omitted, the capsule still builds metadata + empty slices * (useful for schema/shape tests). */ readFile?: (relativePath: string) => string | null; /** Extra lines of context around each symbol span (default 1). */ padding?: number; /** Max source slices after merge (default 12). */ maxSlices?: number; repositoryId?: string | null; /** Optional provenance from the Model Execution Profile / security ladder. */ provenance?: CapsuleProvenanceExtras; /** * Extra pinned facts (e.g. high-confidence federation bridge edges) appended * after graph-derived facts. Secret-free, short strings only. */ extraPinnedFacts?: string[]; } interface CapsuleSymbolRef { id: string; qualifiedName: string; kind: string; file: string; span: { start: number; end: number; }; signature?: string | null; why: string; importance: number; } interface SourceSlice { file: string; start: number; end: number; content: string; contentHash: string; symbolIds: string[]; } interface CapsuleRelationship { kind: 'calls' | 'called-by' | 'impacts' | 'contains' | 'other'; from: string; to: string; } interface VerificationPlan { syntaxFiles: string[]; suggestedTests: string[]; notes: string[]; } interface TaskCapsule { schemaVersion: typeof TASK_CAPSULE_SCHEMA_VERSION; instruction: string; primary: CapsuleSymbolRef[]; supporting: CapsuleSymbolRef[]; sourceSlices: SourceSlice[]; relationships: CapsuleRelationship[]; pinnedFacts: string[]; /** Plain-language interpretation of the ask (concept expansions, relevance * topics, carried prior-turn terms) — see engine/query.ts conceptMapLines. */ conceptMap: string[]; targetFiles: string[]; verificationPlan: VerificationPlan; rendered: string; tokensEstimate: number; provenance: { compiler: string; rankingVersion: string; graphCorpusHash: string | null; repositoryId: string | null; /** Model Execution Profile id when resolved (Fusion Phase 4/7). */ modelProfileId?: string | null; /** Security tier for shell during this task. */ securityTier?: string | null; /** Frozen policy / ranking patch id if any. */ policyVersion?: string | null; /** Version of the optional relevance provider that widened seed * vocabulary for this capsule, or null when none was active. */ relevanceVersion?: string | null; }; } interface CapsuleProvenanceExtras { modelProfileId?: string | null; securityTier?: string | null; policyVersion?: string | null; } /** Host-safe capsule summary for VS Code / stream-json capsule transparency. */ interface CapsuleSummary { schemaVersion: string; instruction: string; primary: Array<{ qualifiedName: string; file: string; kind: string; }>; supporting: Array<{ qualifiedName: string; file: string; kind: string; }>; sourceSliceCount: number; sourceFiles: string[]; tokensEstimate: number; rankingVersion: string; /** Truncated rendered capsule for display (not the full prompt dump). */ preview: string; } /** Host-safe capsule summary (capsule transparency UI / stream-json). */ declare function summarizeCapsule(capsule: TaskCapsule): CapsuleSummary; /** * Compile a source-bearing Task Capsule. Reuses the same seed / impact / * fact-pinning path as {@link buildCodeContext}, then attaches exact source * slices and a verification sketch. */ declare function buildTaskCapsule(graph: VgGraph, instruction: string, options?: BuildCapsuleOptions): TaskCapsule; /** * Project a capsule into the legacy {@link CodeContext} shape so the existing * agent prompt path can consume it without a full rewrite (A/B flag). */ declare function capsuleToCodeContext(capsule: TaskCapsule): CodeContext; /** * `search_symbols` — the hybrid flashlight next to the map * (docs/graph/VG-GRAPH-OPTIMIZATION-PLAN.md P1). * * Two passes, both bounded and deterministic: * 1. symbol pass — the graph's own name index via findNodes (exact id / * qualified name / short name / case-insensitive / substring), ranked; * 2. literal pass — a repo-root-jailed substring scan over source files for * strings the graph does not model (config keys, log messages, comments), * only run when the symbol pass has spare result budget. * * Rows are tiny by contract ({kind, name, file, line, score|preview}) — this * tool exists to make "I know the name" discovery one cheap call, so the model * never flails through graph queries for a plain string lookup. */ interface SymbolHit { kind: string; name: string; file: string; line: number; score: number; } interface TextHit { kind: 'text'; file: string; line: number; preview: string; } interface SearchResult { matches: (SymbolHit | TextHit)[]; moreAvailable: boolean; /** * Total literal (text) matches across the scanned tree, reported when a * literal sweep ran (a whitespace/phrase query). Lets a caller doing a "find * every occurrence" sweep know whether the shown text rows are the complete * set (`totalTextMatches` === shown text rows) or a page of a larger set * (`totalTextMatches` > shown) — so it never mistakes a truncated list for a * complete one, and never has to fall back to grep to be sure. A trailing `+` * intent is signalled via `moreAvailable`; absent for single-name lookups. */ totalTextMatches?: number; /** * Present when nothing matched (the pivot to take) or when a literal sweep was * truncated (how to get the rest). */ hint?: string; } declare function searchSymbols(graph: VgGraph, root: string, query: string, limit: number): Promise; export { ASSISTANTS, type AnalyzeOptions, type AnalyzeResult, Area, type Assistant, type BuildCapsuleOptions, type BuildContextOptions, type BuildOptions, type BuildResult, type BuildScope, CAPSULE_COMPILER_ID, CAPSULE_RANKING_VERSION, type CapsuleSummary, type CapsuleSymbolRef, type ClusterMode, type DepRecord, type DiscoverOptions, type DiscoveredFile, type Drift, type DriftInventory, type DriftNote, EdgeKind, type Embedder, type ExportContext, type ExportFormat, FREE_PACK, Fact, FileParse, GraphEdge, GraphIndex, GraphNode, GraphSource, type GraphUploadEnvelope, GroundingEdge, GroundingKind, type ImpactItem, type ImpactResult, type KnowledgePack, LANGUAGES, type LanguageDef, type LibCatalog, type LibEntry, type LibSource, type LoadEmbedderOptions, type LocalModel, type ModuleResolver, type PackEntry, type PathResult, type ProbeResult, type QueryMatch, type QueryOptions, type QueryResult, type RefreshOptions, type RefreshOutcome, ResolverKind, ResourceLimitError, type ResourceLimits, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, type SavingsReport, type ScipDocument, type ScipIndex, type ScipOccurrence, type SearchResult, type SemanticQueryOptions, type ServeOptions, type SourceSlice, type SymbolHit, TASK_CAPSULE_SCHEMA_VERSION, TOOLS, type TaskCapsule, type TextHit, UsageError, VERSION, type VerifyResult, VgGraph, type VgTool, type WriteOptions, type WrittenArtifacts, addLibrary, allLanguageIds, analyze, applyCoverage, applyStaticTestLinkage, assistantById, buildCodeContext, buildEnvelope, buildFacts, buildGraph, buildModuleResolver, buildTaskCapsule, capsuleToCodeContext, cosine, coveringTests, createServer, decodeScipIndex, defaultGraphPath, inventory as dependencyInventory, detectRunner, discover, discoverModels, driftCount, driftFor, embeddingsCached, embeddingsPath, embeddingsPathFor, enrichOnline, exportGraph, findNodes, formatForExt, getNodeEmbeddings, grammarsSourceDir, groundGraph, hasDrift, identifierParts, impactOf, installAssistant, isTestFile, langById, langForExtension, legacyGraphPath, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, nodeById, nodeEmbedText, parseGraph, parseJsonc, parseSource, preferInRepoGraph, probeFreshness, queryGraph, queryGraphSemantic, readDoc, readSavings, recordSaving, redactGraph, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveGraphPath, resolveLib, resolveLimits, resolveOne, saveCatalog, savingsRecorded, scipEdges, searchSymbols, serializeGraph, serveStdio, shortestPath, stableStringify, summarizeCapsule, testsToRun, uninstallAssistant, verifyDeterminism, vibgrateDir, writeArtifacts, writeSnapshot };