@dreamtree-org/graphify 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2,12 +2,18 @@ import Graph from 'graphology';
2
2
  import { z } from 'zod';
3
3
 
4
4
  type Confidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
5
- type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports';
5
+ type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports' | 'follows';
6
6
  interface GraphNode {
7
7
  id: string;
8
8
  label: string;
9
9
  sourceFile: string;
10
10
  sourceLocation: string;
11
+ /**
12
+ * Node kind for document graphs (document|section|chunk|entity — see
13
+ * docs/KB-PROVIDER.md §5). Optional and schema-additive: classic code
14
+ * nodes encode kind inside the label ("class Foo") and omit this.
15
+ */
16
+ kind?: string;
11
17
  }
12
18
  interface GraphEdge {
13
19
  source: string;
@@ -89,28 +95,111 @@ declare function extract(filePath: string, displayPath?: string): Promise<Extrac
89
95
  * same module) are kept as separate edges.
90
96
  */
91
97
  declare function buildGraph(extractions: ExtractionResult[]): Graph;
98
+ /**
99
+ * The merge loop shared by buildGraph() (fresh graph) and
100
+ * updateCorpusGraph() (incremental, src/ingest/corpus.ts). Callers validate
101
+ * first — this assumes well-formed extractions. A node that already exists
102
+ * only as an auto-vivified placeholder (sourceFile '<unknown>') is upgraded
103
+ * in place when an extraction supplies the real node.
104
+ */
105
+ declare function mergeExtractionsInto(graph: Graph, validated: ExtractionResult[]): void;
92
106
 
93
- interface ResolveOptions {
107
+ /**
108
+ * Provider-agnostic interface for the semantic (LLM-driven) extraction
109
+ * pass. The first-class, tested path is Anthropic (see anthropic.ts) —
110
+ * this interface exists so an OpenAI-compatible endpoint can be swapped in
111
+ * later without redesigning callers. Do not build out a large provider
112
+ * matrix for v1 (see master prompt §7 v2 ideas).
113
+ */
114
+ interface SemanticExtractor {
115
+ extractSemantic(path: string, content: string): Promise<ExtractionResult>;
116
+ }
117
+
118
+ /**
119
+ * Smart document ingestion (docs/KB-PROVIDER.md §5): replaces the flat
120
+ * `text -> string[]` chunker contract with `document -> { chunks + graph
121
+ * fragment }`. One document per call — providers ingest from queue workers
122
+ * a page at a time; updateCorpusGraph() (corpus.ts) folds each fragment
123
+ * into the corpus graph. Structural pass only: sections, chunks,
124
+ * contains/follows edges, and explicit relative markdown links — fully
125
+ * offline and deterministic. Entities in prose need the optional
126
+ * caller-injected SemanticExtractor.
127
+ */
128
+ interface DocumentInput {
94
129
  /**
95
- * The scanned package's own name(s) (from package.json). Tests very
96
- * commonly import the package's PUBLIC entry (`import { persist } from
97
- * 'zustand/middleware'`) rather than a relative path — without this,
98
- * those land on an opaque `module:` node and the test never connects to
99
- * the source it exercises (so `graphify tests` under-selects).
130
+ * Plain text or markdown. Binary parsing (PDF/DOCX/...) is the caller's
131
+ * job this API takes extracted text.
100
132
  */
101
- selfNames?: string[];
133
+ text: string;
134
+ /**
135
+ * Stable caller-side identity — becomes GraphNode.sourceFile and the id
136
+ * prefix for every node from this document. Re-ingesting the same
137
+ * sourceRef through updateCorpusGraph() replaces that document's subgraph.
138
+ */
139
+ sourceRef: string;
140
+ /** Routes the structural pass. Markdown headings are honored unless this says text/plain. */
141
+ mimeType?: string;
142
+ title?: string;
143
+ /** Echoed onto every chunk, never interpreted. */
144
+ metadata?: Record<string, string>;
145
+ }
146
+ interface ChunkOptions {
147
+ /** Soft target per chunk, in estimated tokens (~4 chars each). Default 400. */
148
+ targetTokens?: number;
149
+ /** Hard cap — over-long paragraphs are split to fit. Default 512. */
150
+ maxTokens?: number;
151
+ /**
152
+ * Overlap is OFF by default: `follows` edges + expandRetrieval() replace
153
+ * what sliding-window overlap approximates. Callers migrating from
154
+ * window-chunkers can turn it back on.
155
+ */
156
+ overlapTokens?: number;
102
157
  }
103
- interface ResolveStats {
104
- /** Edge endpoints re-pointed from a guessed id to a real node id. */
105
- resolvedEndpoints: number;
106
- /** Placeholder module nodes dropped because the real file node replaced them. */
107
- droppedPlaceholders: number;
158
+ interface IngestOptions {
159
+ chunking?: ChunkOptions;
160
+ /**
161
+ * Optional semantic pass for entities/relationships in prose. Injected,
162
+ * never built-in — same stance as embeddings (docs/KB-PROVIDER.md §6).
163
+ * Omitted = structural-only, no LLM, no API key.
164
+ */
165
+ semanticExtractor?: SemanticExtractor;
166
+ }
167
+ interface IngestedChunk {
168
+ /**
169
+ * Graph node id of this chunk — store it next to the chunk row: it is
170
+ * what makes expandRetrieval() hits O(1) instead of lexical.
171
+ */
172
+ nodeId: string;
173
+ /** What the caller embeds and stores. */
174
+ content: string;
175
+ tokenEstimate: number;
176
+ /** Reading order within the document. */
177
+ index: number;
178
+ /** Heading trail, e.g. ["Q3 Results", "Revenue"]. Empty for plain text. */
179
+ sectionPath: string[];
180
+ /** sha256 of normalized content — caller-side dedup across sources. */
181
+ contentHash: string;
182
+ metadata: Record<string, string>;
183
+ }
184
+ interface IngestResult {
185
+ chunks: IngestedChunk[];
186
+ /** Graph fragment for this document — feed to updateCorpusGraph() or runPipeline's extraExtractions. */
187
+ extraction: ExtractionResult;
188
+ stats: {
189
+ sections: number;
190
+ entities: number;
191
+ tokenTotal: number;
192
+ };
108
193
  }
109
194
  /**
110
- * Mutates `extractions` in place: re-points guessed edge endpoints at real
111
- * node ids and drops the placeholder module nodes those guesses created.
195
+ * Turn one document into structure-aware chunks plus a typed graph
196
+ * fragment: a `document` node, `section` nodes nested via `contains`,
197
+ * `chunk` nodes under their section (`contains`), chained in reading order
198
+ * (`follows`), with relative markdown links as `references` edges. The
199
+ * chunks are graph nodes — the caller stores each chunk's nodeId, which is
200
+ * what fuses their vector search with expandRetrieval() at query time.
112
201
  */
113
- declare function resolveCrossFileReferences(extractions: ExtractionResult[], options?: ResolveOptions): ResolveStats;
202
+ declare function ingestDocument(input: DocumentInput, options?: IngestOptions): Promise<IngestResult>;
114
203
 
115
204
  type ClusterAlgorithm = 'louvain' | 'leiden';
116
205
  interface ClusterOptions {
@@ -144,6 +233,60 @@ interface ClusterOptions {
144
233
  */
145
234
  declare function cluster(graph: Graph, options?: ClusterOptions): Graph;
146
235
 
236
+ interface UpdateCorpusOptions {
237
+ algorithm?: ClusterAlgorithm;
238
+ /**
239
+ * Clustering strategy per update. `'auto'` (default) keeps bulk loading
240
+ * safe without the caller thinking about it: new nodes adopt the majority
241
+ * community of their neighbors (cheap, local, deterministic), and a full
242
+ * global re-cluster runs only when accumulated churn since the last full
243
+ * pass exceeds `reclusterRatio` of the graph — or on a never-clustered
244
+ * graph. `true` forces a full re-cluster now (e.g. once after a batch);
245
+ * `false` skips community maintenance entirely for this call (churn is
246
+ * still tracked, so a later `'auto'` call sees the backlog).
247
+ */
248
+ recluster?: boolean | 'auto';
249
+ /**
250
+ * Auto mode only: fraction of the graph's nodes that must have churned
251
+ * since the last full clustering before one is triggered. Default 0.1.
252
+ */
253
+ reclusterRatio?: number;
254
+ }
255
+ /**
256
+ * Fold one document's extraction into an existing corpus graph — the
257
+ * streaming-ingestion companion to ingestDocument() (docs/KB-PROVIDER.md
258
+ * §5). Pass null for the store.load() no-graph-yet case. Re-ingesting a
259
+ * document is replace-not-duplicate: every node whose sourceFile matches
260
+ * one of the extraction's sourceFiles is dropped (with its edges) before
261
+ * the fragment is merged. Placeholder nodes that earlier documents' link
262
+ * edges auto-vivified are upgraded in place when this document is the real
263
+ * thing. Community upkeep is incremental by default and self-tunes to bulk
264
+ * loads — see UpdateCorpusOptions.recluster. Mutates and returns the graph.
265
+ */
266
+ declare function updateCorpusGraph(graph: Graph | null, extraction: ExtractionResult, options?: UpdateCorpusOptions): Graph;
267
+
268
+ interface ResolveOptions {
269
+ /**
270
+ * The scanned package's own name(s) (from package.json). Tests very
271
+ * commonly import the package's PUBLIC entry (`import { persist } from
272
+ * 'zustand/middleware'`) rather than a relative path — without this,
273
+ * those land on an opaque `module:` node and the test never connects to
274
+ * the source it exercises (so `graphify tests` under-selects).
275
+ */
276
+ selfNames?: string[];
277
+ }
278
+ interface ResolveStats {
279
+ /** Edge endpoints re-pointed from a guessed id to a real node id. */
280
+ resolvedEndpoints: number;
281
+ /** Placeholder module nodes dropped because the real file node replaced them. */
282
+ droppedPlaceholders: number;
283
+ }
284
+ /**
285
+ * Mutates `extractions` in place: re-points guessed edge endpoints at real
286
+ * node ids and drops the placeholder module nodes those guesses created.
287
+ */
288
+ declare function resolveCrossFileReferences(extractions: ExtractionResult[], options?: ResolveOptions): ResolveStats;
289
+
147
290
  /**
148
291
  * Surface god-nodes (excessive fan-in/out), structural surprises (self
149
292
  * references, AMBIGUOUS edges), and open questions (isolated nodes,
@@ -731,6 +874,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
731
874
  label: z.ZodString;
732
875
  sourceFile: z.ZodString;
733
876
  sourceLocation: z.ZodString;
877
+ kind: z.ZodOptional<z.ZodString>;
734
878
  }, z.core.$strip>>;
735
879
  edges: z.ZodArray<z.ZodObject<{
736
880
  source: z.ZodString;
@@ -747,6 +891,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
747
891
  contains: "contains";
748
892
  method: "method";
749
893
  re_exports: "re_exports";
894
+ follows: "follows";
750
895
  }>;
751
896
  confidence: z.ZodEnum<{
752
897
  EXTRACTED: "EXTRACTED";
@@ -772,4 +917,4 @@ declare class ExtractionValidationError extends Error {
772
917
  */
773
918
  declare function validateExtraction(value: unknown, context?: string): ExtractionResult;
774
919
 
775
- export { type AffectedNode, type Analysis, type ChangedSymbol, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExpandOptions, type ExpandedContext, type ExpandedNode, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RetrievalHit, type RewiredSymbol, type RulesConfig, type SavedResult, type SerializedGraph, type TestSelection, type TraversalEdge, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, expandRetrieval, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, isTestFile, loadGraph, loadResults, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, validateExtraction, validateRules };
920
+ export { type AffectedNode, type Analysis, type ChangedSymbol, type ChunkOptions, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DocumentInput, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExpandOptions, type ExpandedContext, type ExpandedNode, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, type IngestOptions, type IngestResult, type IngestedChunk, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RetrievalHit, type RewiredSymbol, type RulesConfig, type SavedResult, type SemanticExtractor, type SerializedGraph, type TestSelection, type TraversalEdge, type UpdateCorpusOptions, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, expandRetrieval, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, ingestDocument, isTestFile, loadGraph, loadResults, mergeExtractionsInto, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, updateCorpusGraph, validateExtraction, validateRules };
package/dist/index.d.ts CHANGED
@@ -2,12 +2,18 @@ import Graph from 'graphology';
2
2
  import { z } from 'zod';
3
3
 
4
4
  type Confidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
5
- type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports';
5
+ type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports' | 'follows';
6
6
  interface GraphNode {
7
7
  id: string;
8
8
  label: string;
9
9
  sourceFile: string;
10
10
  sourceLocation: string;
11
+ /**
12
+ * Node kind for document graphs (document|section|chunk|entity — see
13
+ * docs/KB-PROVIDER.md §5). Optional and schema-additive: classic code
14
+ * nodes encode kind inside the label ("class Foo") and omit this.
15
+ */
16
+ kind?: string;
11
17
  }
12
18
  interface GraphEdge {
13
19
  source: string;
@@ -89,28 +95,111 @@ declare function extract(filePath: string, displayPath?: string): Promise<Extrac
89
95
  * same module) are kept as separate edges.
90
96
  */
91
97
  declare function buildGraph(extractions: ExtractionResult[]): Graph;
98
+ /**
99
+ * The merge loop shared by buildGraph() (fresh graph) and
100
+ * updateCorpusGraph() (incremental, src/ingest/corpus.ts). Callers validate
101
+ * first — this assumes well-formed extractions. A node that already exists
102
+ * only as an auto-vivified placeholder (sourceFile '<unknown>') is upgraded
103
+ * in place when an extraction supplies the real node.
104
+ */
105
+ declare function mergeExtractionsInto(graph: Graph, validated: ExtractionResult[]): void;
92
106
 
93
- interface ResolveOptions {
107
+ /**
108
+ * Provider-agnostic interface for the semantic (LLM-driven) extraction
109
+ * pass. The first-class, tested path is Anthropic (see anthropic.ts) —
110
+ * this interface exists so an OpenAI-compatible endpoint can be swapped in
111
+ * later without redesigning callers. Do not build out a large provider
112
+ * matrix for v1 (see master prompt §7 v2 ideas).
113
+ */
114
+ interface SemanticExtractor {
115
+ extractSemantic(path: string, content: string): Promise<ExtractionResult>;
116
+ }
117
+
118
+ /**
119
+ * Smart document ingestion (docs/KB-PROVIDER.md §5): replaces the flat
120
+ * `text -> string[]` chunker contract with `document -> { chunks + graph
121
+ * fragment }`. One document per call — providers ingest from queue workers
122
+ * a page at a time; updateCorpusGraph() (corpus.ts) folds each fragment
123
+ * into the corpus graph. Structural pass only: sections, chunks,
124
+ * contains/follows edges, and explicit relative markdown links — fully
125
+ * offline and deterministic. Entities in prose need the optional
126
+ * caller-injected SemanticExtractor.
127
+ */
128
+ interface DocumentInput {
94
129
  /**
95
- * The scanned package's own name(s) (from package.json). Tests very
96
- * commonly import the package's PUBLIC entry (`import { persist } from
97
- * 'zustand/middleware'`) rather than a relative path — without this,
98
- * those land on an opaque `module:` node and the test never connects to
99
- * the source it exercises (so `graphify tests` under-selects).
130
+ * Plain text or markdown. Binary parsing (PDF/DOCX/...) is the caller's
131
+ * job this API takes extracted text.
100
132
  */
101
- selfNames?: string[];
133
+ text: string;
134
+ /**
135
+ * Stable caller-side identity — becomes GraphNode.sourceFile and the id
136
+ * prefix for every node from this document. Re-ingesting the same
137
+ * sourceRef through updateCorpusGraph() replaces that document's subgraph.
138
+ */
139
+ sourceRef: string;
140
+ /** Routes the structural pass. Markdown headings are honored unless this says text/plain. */
141
+ mimeType?: string;
142
+ title?: string;
143
+ /** Echoed onto every chunk, never interpreted. */
144
+ metadata?: Record<string, string>;
145
+ }
146
+ interface ChunkOptions {
147
+ /** Soft target per chunk, in estimated tokens (~4 chars each). Default 400. */
148
+ targetTokens?: number;
149
+ /** Hard cap — over-long paragraphs are split to fit. Default 512. */
150
+ maxTokens?: number;
151
+ /**
152
+ * Overlap is OFF by default: `follows` edges + expandRetrieval() replace
153
+ * what sliding-window overlap approximates. Callers migrating from
154
+ * window-chunkers can turn it back on.
155
+ */
156
+ overlapTokens?: number;
102
157
  }
103
- interface ResolveStats {
104
- /** Edge endpoints re-pointed from a guessed id to a real node id. */
105
- resolvedEndpoints: number;
106
- /** Placeholder module nodes dropped because the real file node replaced them. */
107
- droppedPlaceholders: number;
158
+ interface IngestOptions {
159
+ chunking?: ChunkOptions;
160
+ /**
161
+ * Optional semantic pass for entities/relationships in prose. Injected,
162
+ * never built-in — same stance as embeddings (docs/KB-PROVIDER.md §6).
163
+ * Omitted = structural-only, no LLM, no API key.
164
+ */
165
+ semanticExtractor?: SemanticExtractor;
166
+ }
167
+ interface IngestedChunk {
168
+ /**
169
+ * Graph node id of this chunk — store it next to the chunk row: it is
170
+ * what makes expandRetrieval() hits O(1) instead of lexical.
171
+ */
172
+ nodeId: string;
173
+ /** What the caller embeds and stores. */
174
+ content: string;
175
+ tokenEstimate: number;
176
+ /** Reading order within the document. */
177
+ index: number;
178
+ /** Heading trail, e.g. ["Q3 Results", "Revenue"]. Empty for plain text. */
179
+ sectionPath: string[];
180
+ /** sha256 of normalized content — caller-side dedup across sources. */
181
+ contentHash: string;
182
+ metadata: Record<string, string>;
183
+ }
184
+ interface IngestResult {
185
+ chunks: IngestedChunk[];
186
+ /** Graph fragment for this document — feed to updateCorpusGraph() or runPipeline's extraExtractions. */
187
+ extraction: ExtractionResult;
188
+ stats: {
189
+ sections: number;
190
+ entities: number;
191
+ tokenTotal: number;
192
+ };
108
193
  }
109
194
  /**
110
- * Mutates `extractions` in place: re-points guessed edge endpoints at real
111
- * node ids and drops the placeholder module nodes those guesses created.
195
+ * Turn one document into structure-aware chunks plus a typed graph
196
+ * fragment: a `document` node, `section` nodes nested via `contains`,
197
+ * `chunk` nodes under their section (`contains`), chained in reading order
198
+ * (`follows`), with relative markdown links as `references` edges. The
199
+ * chunks are graph nodes — the caller stores each chunk's nodeId, which is
200
+ * what fuses their vector search with expandRetrieval() at query time.
112
201
  */
113
- declare function resolveCrossFileReferences(extractions: ExtractionResult[], options?: ResolveOptions): ResolveStats;
202
+ declare function ingestDocument(input: DocumentInput, options?: IngestOptions): Promise<IngestResult>;
114
203
 
115
204
  type ClusterAlgorithm = 'louvain' | 'leiden';
116
205
  interface ClusterOptions {
@@ -144,6 +233,60 @@ interface ClusterOptions {
144
233
  */
145
234
  declare function cluster(graph: Graph, options?: ClusterOptions): Graph;
146
235
 
236
+ interface UpdateCorpusOptions {
237
+ algorithm?: ClusterAlgorithm;
238
+ /**
239
+ * Clustering strategy per update. `'auto'` (default) keeps bulk loading
240
+ * safe without the caller thinking about it: new nodes adopt the majority
241
+ * community of their neighbors (cheap, local, deterministic), and a full
242
+ * global re-cluster runs only when accumulated churn since the last full
243
+ * pass exceeds `reclusterRatio` of the graph — or on a never-clustered
244
+ * graph. `true` forces a full re-cluster now (e.g. once after a batch);
245
+ * `false` skips community maintenance entirely for this call (churn is
246
+ * still tracked, so a later `'auto'` call sees the backlog).
247
+ */
248
+ recluster?: boolean | 'auto';
249
+ /**
250
+ * Auto mode only: fraction of the graph's nodes that must have churned
251
+ * since the last full clustering before one is triggered. Default 0.1.
252
+ */
253
+ reclusterRatio?: number;
254
+ }
255
+ /**
256
+ * Fold one document's extraction into an existing corpus graph — the
257
+ * streaming-ingestion companion to ingestDocument() (docs/KB-PROVIDER.md
258
+ * §5). Pass null for the store.load() no-graph-yet case. Re-ingesting a
259
+ * document is replace-not-duplicate: every node whose sourceFile matches
260
+ * one of the extraction's sourceFiles is dropped (with its edges) before
261
+ * the fragment is merged. Placeholder nodes that earlier documents' link
262
+ * edges auto-vivified are upgraded in place when this document is the real
263
+ * thing. Community upkeep is incremental by default and self-tunes to bulk
264
+ * loads — see UpdateCorpusOptions.recluster. Mutates and returns the graph.
265
+ */
266
+ declare function updateCorpusGraph(graph: Graph | null, extraction: ExtractionResult, options?: UpdateCorpusOptions): Graph;
267
+
268
+ interface ResolveOptions {
269
+ /**
270
+ * The scanned package's own name(s) (from package.json). Tests very
271
+ * commonly import the package's PUBLIC entry (`import { persist } from
272
+ * 'zustand/middleware'`) rather than a relative path — without this,
273
+ * those land on an opaque `module:` node and the test never connects to
274
+ * the source it exercises (so `graphify tests` under-selects).
275
+ */
276
+ selfNames?: string[];
277
+ }
278
+ interface ResolveStats {
279
+ /** Edge endpoints re-pointed from a guessed id to a real node id. */
280
+ resolvedEndpoints: number;
281
+ /** Placeholder module nodes dropped because the real file node replaced them. */
282
+ droppedPlaceholders: number;
283
+ }
284
+ /**
285
+ * Mutates `extractions` in place: re-points guessed edge endpoints at real
286
+ * node ids and drops the placeholder module nodes those guesses created.
287
+ */
288
+ declare function resolveCrossFileReferences(extractions: ExtractionResult[], options?: ResolveOptions): ResolveStats;
289
+
147
290
  /**
148
291
  * Surface god-nodes (excessive fan-in/out), structural surprises (self
149
292
  * references, AMBIGUOUS edges), and open questions (isolated nodes,
@@ -731,6 +874,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
731
874
  label: z.ZodString;
732
875
  sourceFile: z.ZodString;
733
876
  sourceLocation: z.ZodString;
877
+ kind: z.ZodOptional<z.ZodString>;
734
878
  }, z.core.$strip>>;
735
879
  edges: z.ZodArray<z.ZodObject<{
736
880
  source: z.ZodString;
@@ -747,6 +891,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
747
891
  contains: "contains";
748
892
  method: "method";
749
893
  re_exports: "re_exports";
894
+ follows: "follows";
750
895
  }>;
751
896
  confidence: z.ZodEnum<{
752
897
  EXTRACTED: "EXTRACTED";
@@ -772,4 +917,4 @@ declare class ExtractionValidationError extends Error {
772
917
  */
773
918
  declare function validateExtraction(value: unknown, context?: string): ExtractionResult;
774
919
 
775
- export { type AffectedNode, type Analysis, type ChangedSymbol, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExpandOptions, type ExpandedContext, type ExpandedNode, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RetrievalHit, type RewiredSymbol, type RulesConfig, type SavedResult, type SerializedGraph, type TestSelection, type TraversalEdge, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, expandRetrieval, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, isTestFile, loadGraph, loadResults, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, validateExtraction, validateRules };
920
+ export { type AffectedNode, type Analysis, type ChangedSymbol, type ChunkOptions, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DocumentInput, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExpandOptions, type ExpandedContext, type ExpandedNode, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, type IngestOptions, type IngestResult, type IngestedChunk, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RetrievalHit, type RewiredSymbol, type RulesConfig, type SavedResult, type SemanticExtractor, type SerializedGraph, type TestSelection, type TraversalEdge, type UpdateCorpusOptions, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, expandRetrieval, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, ingestDocument, isTestFile, loadGraph, loadResults, mergeExtractionsInto, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, updateCorpusGraph, validateExtraction, validateRules };