@renseiai/agentfactory-code-intelligence 0.8.8 → 0.8.10

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.
Files changed (66) hide show
  1. package/dist/src/embedding/__tests__/embedding.test.d.ts +2 -0
  2. package/dist/src/embedding/__tests__/embedding.test.d.ts.map +1 -0
  3. package/dist/src/embedding/__tests__/embedding.test.js +339 -0
  4. package/dist/src/embedding/chunker.d.ts +40 -0
  5. package/dist/src/embedding/chunker.d.ts.map +1 -0
  6. package/dist/src/embedding/chunker.js +135 -0
  7. package/dist/src/embedding/embedding-provider.d.ts +15 -0
  8. package/dist/src/embedding/embedding-provider.d.ts.map +1 -0
  9. package/dist/src/embedding/embedding-provider.js +1 -0
  10. package/dist/src/embedding/voyage-provider.d.ts +39 -0
  11. package/dist/src/embedding/voyage-provider.d.ts.map +1 -0
  12. package/dist/src/embedding/voyage-provider.js +146 -0
  13. package/dist/src/index.d.ts +14 -2
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +10 -1
  16. package/dist/src/indexing/__tests__/vector-indexing.test.d.ts +2 -0
  17. package/dist/src/indexing/__tests__/vector-indexing.test.d.ts.map +1 -0
  18. package/dist/src/indexing/__tests__/vector-indexing.test.js +291 -0
  19. package/dist/src/indexing/incremental-indexer.d.ts +4 -0
  20. package/dist/src/indexing/incremental-indexer.d.ts.map +1 -1
  21. package/dist/src/indexing/incremental-indexer.js +45 -0
  22. package/dist/src/indexing/vector-indexer.d.ts +63 -0
  23. package/dist/src/indexing/vector-indexer.d.ts.map +1 -0
  24. package/dist/src/indexing/vector-indexer.js +197 -0
  25. package/dist/src/plugin/code-intelligence-plugin.d.ts.map +1 -1
  26. package/dist/src/plugin/code-intelligence-plugin.js +4 -2
  27. package/dist/src/reranking/__tests__/reranker.test.d.ts +2 -0
  28. package/dist/src/reranking/__tests__/reranker.test.d.ts.map +1 -0
  29. package/dist/src/reranking/__tests__/reranker.test.js +503 -0
  30. package/dist/src/reranking/cohere-reranker.d.ts +26 -0
  31. package/dist/src/reranking/cohere-reranker.d.ts.map +1 -0
  32. package/dist/src/reranking/cohere-reranker.js +110 -0
  33. package/dist/src/reranking/reranker-provider.d.ts +40 -0
  34. package/dist/src/reranking/reranker-provider.d.ts.map +1 -0
  35. package/dist/src/reranking/reranker-provider.js +6 -0
  36. package/dist/src/reranking/voyage-reranker.d.ts +27 -0
  37. package/dist/src/reranking/voyage-reranker.d.ts.map +1 -0
  38. package/dist/src/reranking/voyage-reranker.js +111 -0
  39. package/dist/src/search/__tests__/hybrid-search.test.d.ts +2 -0
  40. package/dist/src/search/__tests__/hybrid-search.test.d.ts.map +1 -0
  41. package/dist/src/search/__tests__/hybrid-search.test.js +437 -0
  42. package/dist/src/search/__tests__/query-classifier.test.d.ts +2 -0
  43. package/dist/src/search/__tests__/query-classifier.test.d.ts.map +1 -0
  44. package/dist/src/search/__tests__/query-classifier.test.js +136 -0
  45. package/dist/src/search/hybrid-search.d.ts +56 -0
  46. package/dist/src/search/hybrid-search.d.ts.map +1 -0
  47. package/dist/src/search/hybrid-search.js +299 -0
  48. package/dist/src/search/query-classifier.d.ts +20 -0
  49. package/dist/src/search/query-classifier.d.ts.map +1 -0
  50. package/dist/src/search/query-classifier.js +58 -0
  51. package/dist/src/search/score-normalizer.d.ts +16 -0
  52. package/dist/src/search/score-normalizer.d.ts.map +1 -0
  53. package/dist/src/search/score-normalizer.js +26 -0
  54. package/dist/src/types.d.ts +83 -0
  55. package/dist/src/types.d.ts.map +1 -1
  56. package/dist/src/types.js +36 -2
  57. package/dist/src/vector/__tests__/vector-store.test.d.ts +2 -0
  58. package/dist/src/vector/__tests__/vector-store.test.d.ts.map +1 -0
  59. package/dist/src/vector/__tests__/vector-store.test.js +278 -0
  60. package/dist/src/vector/hnsw-store.d.ts +48 -0
  61. package/dist/src/vector/hnsw-store.d.ts.map +1 -0
  62. package/dist/src/vector/hnsw-store.js +437 -0
  63. package/dist/src/vector/vector-store.d.ts +15 -0
  64. package/dist/src/vector/vector-store.d.ts.map +1 -0
  65. package/dist/src/vector/vector-store.js +1 -0
  66. package/package.json +1 -1
@@ -0,0 +1,146 @@
1
+ const VOYAGE_API_URL = 'https://api.voyageai.com/v1/embeddings';
2
+ const DEFAULT_MODEL = 'voyage-code-3';
3
+ const DEFAULT_DIMENSIONS = 256;
4
+ const DEFAULT_BATCH_SIZE = 128;
5
+ const DEFAULT_MAX_RETRIES = 3;
6
+ const BASE_RETRY_DELAY_MS = 1000;
7
+ /**
8
+ * Embedding provider for Voyage AI's voyage-code-3 model.
9
+ *
10
+ * Features:
11
+ * - 32K token context window
12
+ * - 2048 native dimensions with Matryoshka support (256–2048)
13
+ * - Batched requests (max 128 texts per API call)
14
+ * - Exponential backoff retry on 429 / 5xx errors
15
+ *
16
+ * Requires the VOYAGE_API_KEY environment variable.
17
+ */
18
+ export class VoyageCodeProvider {
19
+ model;
20
+ dimensions;
21
+ batchSize;
22
+ maxRetries;
23
+ apiKey;
24
+ constructor(config = {}) {
25
+ const apiKey = process.env.VOYAGE_API_KEY;
26
+ if (!apiKey) {
27
+ throw new Error('VOYAGE_API_KEY environment variable is required. '
28
+ + 'Get your API key at https://dash.voyageai.com/');
29
+ }
30
+ this.apiKey = apiKey;
31
+ this.model = config.model ?? DEFAULT_MODEL;
32
+ this.dimensions = config.dimensions ?? DEFAULT_DIMENSIONS;
33
+ this.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;
34
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
35
+ }
36
+ /**
37
+ * Embed multiple texts in batches.
38
+ * Returns one vector per input text, in the same order.
39
+ */
40
+ async embed(texts) {
41
+ if (texts.length === 0)
42
+ return [];
43
+ const results = new Array(texts.length);
44
+ const batches = this.splitIntoBatches(texts);
45
+ let offset = 0;
46
+ for (const batch of batches) {
47
+ const embeddings = await this.callAPI(batch, 'document');
48
+ for (let i = 0; i < embeddings.length; i++) {
49
+ results[offset + i] = embeddings[i];
50
+ }
51
+ offset += batch.length;
52
+ }
53
+ return results;
54
+ }
55
+ /**
56
+ * Embed a single query text.
57
+ * Uses input_type "query" for asymmetric retrieval.
58
+ */
59
+ async embedQuery(text) {
60
+ const [result] = await this.callAPI([text], 'query');
61
+ return result;
62
+ }
63
+ /** Split texts into batches of at most batchSize. */
64
+ splitIntoBatches(texts) {
65
+ const batches = [];
66
+ for (let i = 0; i < texts.length; i += this.batchSize) {
67
+ batches.push(texts.slice(i, i + this.batchSize));
68
+ }
69
+ return batches;
70
+ }
71
+ /** Call the Voyage embeddings API with retry logic. */
72
+ async callAPI(texts, inputType) {
73
+ const body = JSON.stringify({
74
+ model: this.model,
75
+ input: texts,
76
+ input_type: inputType,
77
+ output_dimension: this.dimensions,
78
+ });
79
+ let lastError;
80
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
81
+ try {
82
+ const response = await fetch(VOYAGE_API_URL, {
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ 'Authorization': `Bearer ${this.apiKey}`,
87
+ },
88
+ body,
89
+ });
90
+ if (response.ok) {
91
+ const json = (await response.json());
92
+ // Sort by index to preserve input order
93
+ const sorted = json.data.sort((a, b) => a.index - b.index);
94
+ return sorted.map((d) => d.embedding);
95
+ }
96
+ // Retry on rate limit or server errors
97
+ if (response.status === 429 || response.status >= 500) {
98
+ const errorBody = await response.text();
99
+ lastError = new Error(`Voyage API returned ${response.status}: ${errorBody}`);
100
+ if (attempt < this.maxRetries) {
101
+ await this.sleep(this.getRetryDelay(attempt, response));
102
+ continue;
103
+ }
104
+ }
105
+ else {
106
+ // Non-retryable error
107
+ const errorBody = await response.text();
108
+ let message;
109
+ try {
110
+ const parsed = JSON.parse(errorBody);
111
+ message = parsed.detail ?? parsed.message ?? errorBody;
112
+ }
113
+ catch {
114
+ message = errorBody;
115
+ }
116
+ throw new Error(`Voyage API error (${response.status}): ${message}`);
117
+ }
118
+ }
119
+ catch (error) {
120
+ if (error instanceof Error && error.message.startsWith('Voyage API error')) {
121
+ throw error;
122
+ }
123
+ lastError = error instanceof Error ? error : new Error(String(error));
124
+ if (attempt < this.maxRetries) {
125
+ await this.sleep(BASE_RETRY_DELAY_MS * Math.pow(2, attempt));
126
+ continue;
127
+ }
128
+ }
129
+ }
130
+ throw lastError ?? new Error('Voyage API request failed after retries');
131
+ }
132
+ /** Calculate retry delay with exponential backoff, respecting Retry-After header. */
133
+ getRetryDelay(attempt, response) {
134
+ const retryAfter = response.headers.get('retry-after');
135
+ if (retryAfter) {
136
+ const seconds = Number(retryAfter);
137
+ if (!Number.isNaN(seconds)) {
138
+ return seconds * 1000;
139
+ }
140
+ }
141
+ return BASE_RETRY_DELAY_MS * Math.pow(2, attempt);
142
+ }
143
+ sleep(ms) {
144
+ return new Promise((resolve) => setTimeout(resolve, ms));
145
+ }
146
+ }
@@ -1,5 +1,5 @@
1
- export { SymbolKindSchema, CodeSymbolSchema, FileASTSchema, FileIndexSchema, SearchQuerySchema, SearchResultSchema, MemoryEntrySchema, DedupResultSchema, IndexMetadataSchema, RepoMapEntrySchema, } from './types.js';
2
- export type { SymbolKind, CodeSymbol, FileAST, FileIndex, SearchQuery, SearchResult, MemoryEntry, DedupResult, IndexMetadata, RepoMapEntry, } from './types.js';
1
+ export { SymbolKindSchema, CodeSymbolSchema, FileASTSchema, FileIndexSchema, SearchQuerySchema, SearchResultSchema, MemoryEntrySchema, DedupResultSchema, IndexMetadataSchema, RepoMapEntrySchema, EmbeddingChunkSchema, EmbeddingProviderConfigSchema, VectorSearchResultSchema, VectorIndexConfigSchema, } from './types.js';
2
+ export type { SymbolKind, CodeSymbol, FileAST, FileIndex, SearchQuery, SearchResult, MemoryEntry, DedupResult, IndexMetadata, RepoMapEntry, EmbeddingChunk, EmbeddingProviderConfig, VectorSearchResult, VectorIndexConfig, } from './types.js';
3
3
  export { SymbolExtractor } from './parser/symbol-extractor.js';
4
4
  export type { LanguageExtractor } from './parser/symbol-extractor.js';
5
5
  export { TypeScriptExtractor } from './parser/typescript-extractor.js';
@@ -10,10 +10,14 @@ export { MerkleTree, type MerkleNode } from './indexing/merkle-tree.js';
10
10
  export { GitHashProvider } from './indexing/git-hash-provider.js';
11
11
  export { ChangeDetector } from './indexing/change-detector.js';
12
12
  export { IncrementalIndexer } from './indexing/incremental-indexer.js';
13
+ export { VectorIndexer } from './indexing/vector-indexer.js';
13
14
  export { CodeTokenizer } from './search/tokenizer.js';
14
15
  export { InvertedIndex } from './search/inverted-index.js';
15
16
  export { BM25 } from './search/bm25.js';
16
17
  export { SearchEngine } from './search/search-engine.js';
18
+ export { HybridSearchEngine } from './search/hybrid-search.js';
19
+ export { classifyQuery } from './search/query-classifier.js';
20
+ export { minMaxNormalize } from './search/score-normalizer.js';
17
21
  export { DependencyGraph } from './repo-map/dependency-graph.js';
18
22
  export { PageRank } from './repo-map/pagerank.js';
19
23
  export { RepoMapGenerator } from './repo-map/repo-map-generator.js';
@@ -22,5 +26,13 @@ export { SimHash } from './memory/simhash.js';
22
26
  export { DedupPipeline } from './memory/dedup-pipeline.js';
23
27
  export type { MemoryStore } from './memory/memory-store.js';
24
28
  export { InMemoryStore } from './memory/memory-store.js';
29
+ export type { EmbeddingProvider } from './embedding/embedding-provider.js';
30
+ export { VoyageCodeProvider } from './embedding/voyage-provider.js';
31
+ export { Chunker } from './embedding/chunker.js';
32
+ export type { VectorStore } from './vector/vector-store.js';
33
+ export { InMemoryVectorStore } from './vector/hnsw-store.js';
34
+ export type { RerankerProvider, RerankDocument, RerankResult, RerankerConfig } from './reranking/reranker-provider.js';
35
+ export { CohereReranker } from './reranking/cohere-reranker.js';
36
+ export { VoyageReranker } from './reranking/voyage-reranker.js';
25
37
  export { codeIntelligencePlugin } from './plugin/code-intelligence-plugin.js';
26
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,UAAU,EACV,UAAU,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAGtE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAGxD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAGnE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAGxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,6BAA6B,EAC7B,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,UAAU,EACV,UAAU,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,EACd,uBAAuB,EACvB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAGnE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAGxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAGhD,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAG5D,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACtH,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAG/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA"}
package/dist/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Core types and schemas
2
- export { SymbolKindSchema, CodeSymbolSchema, FileASTSchema, FileIndexSchema, SearchQuerySchema, SearchResultSchema, MemoryEntrySchema, DedupResultSchema, IndexMetadataSchema, RepoMapEntrySchema, } from './types.js';
2
+ export { SymbolKindSchema, CodeSymbolSchema, FileASTSchema, FileIndexSchema, SearchQuerySchema, SearchResultSchema, MemoryEntrySchema, DedupResultSchema, IndexMetadataSchema, RepoMapEntrySchema, EmbeddingChunkSchema, EmbeddingProviderConfigSchema, VectorSearchResultSchema, VectorIndexConfigSchema, } from './types.js';
3
3
  // Parser
4
4
  export { SymbolExtractor } from './parser/symbol-extractor.js';
5
5
  export { TypeScriptExtractor } from './parser/typescript-extractor.js';
@@ -11,11 +11,15 @@ export { MerkleTree } from './indexing/merkle-tree.js';
11
11
  export { GitHashProvider } from './indexing/git-hash-provider.js';
12
12
  export { ChangeDetector } from './indexing/change-detector.js';
13
13
  export { IncrementalIndexer } from './indexing/incremental-indexer.js';
14
+ export { VectorIndexer } from './indexing/vector-indexer.js';
14
15
  // Search
15
16
  export { CodeTokenizer } from './search/tokenizer.js';
16
17
  export { InvertedIndex } from './search/inverted-index.js';
17
18
  export { BM25 } from './search/bm25.js';
18
19
  export { SearchEngine } from './search/search-engine.js';
20
+ export { HybridSearchEngine } from './search/hybrid-search.js';
21
+ export { classifyQuery } from './search/query-classifier.js';
22
+ export { minMaxNormalize } from './search/score-normalizer.js';
19
23
  // Repo Map
20
24
  export { DependencyGraph } from './repo-map/dependency-graph.js';
21
25
  export { PageRank } from './repo-map/pagerank.js';
@@ -25,5 +29,10 @@ export { xxhash64 } from './memory/xxhash.js';
25
29
  export { SimHash } from './memory/simhash.js';
26
30
  export { DedupPipeline } from './memory/dedup-pipeline.js';
27
31
  export { InMemoryStore } from './memory/memory-store.js';
32
+ export { VoyageCodeProvider } from './embedding/voyage-provider.js';
33
+ export { Chunker } from './embedding/chunker.js';
34
+ export { InMemoryVectorStore } from './vector/hnsw-store.js';
35
+ export { CohereReranker } from './reranking/cohere-reranker.js';
36
+ export { VoyageReranker } from './reranking/voyage-reranker.js';
28
37
  // Plugin
29
38
  export { codeIntelligencePlugin } from './plugin/code-intelligence-plugin.js';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=vector-indexing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-indexing.test.d.ts","sourceRoot":"","sources":["../../../../src/indexing/__tests__/vector-indexing.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,291 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { VectorIndexer } from '../vector-indexer.js';
3
+ import { IncrementalIndexer } from '../incremental-indexer.js';
4
+ import { SymbolExtractor } from '../../parser/symbol-extractor.js';
5
+ import { InMemoryVectorStore } from '../../vector/hnsw-store.js';
6
+ // ── Helpers ──────────────────────────────────────────────────────────
7
+ const mockProvider = {
8
+ model: 'mock-model',
9
+ dimensions: 4,
10
+ async embed(texts) {
11
+ return texts.map(t => [t.length / 100, 0.5, 0.3, 0.1]);
12
+ },
13
+ async embedQuery(text) {
14
+ return [text.length / 100, 0.5, 0.3, 0.1];
15
+ },
16
+ };
17
+ function makeSymbol(name, kind, filePath, extra) {
18
+ return {
19
+ name,
20
+ kind: kind,
21
+ filePath,
22
+ line: 0,
23
+ exported: true,
24
+ ...extra,
25
+ };
26
+ }
27
+ function makeFileAST(filePath, language, symbols) {
28
+ return { filePath, language, symbols, imports: [], exports: [] };
29
+ }
30
+ // ── VectorIndexer ────────────────────────────────────────────────────
31
+ describe('VectorIndexer', () => {
32
+ it('indexes files: chunks, embeds, and inserts into store', async () => {
33
+ const store = new InMemoryVectorStore();
34
+ const indexer = new VectorIndexer({
35
+ enabled: true,
36
+ embeddingProvider: mockProvider,
37
+ vectorStore: store,
38
+ });
39
+ const asts = [
40
+ makeFileAST('src/utils.ts', 'typescript', [
41
+ makeSymbol('add', 'function', 'src/utils.ts', {
42
+ line: 0,
43
+ endLine: 3,
44
+ signature: 'function add(a: number, b: number): number',
45
+ }),
46
+ makeSymbol('subtract', 'function', 'src/utils.ts', {
47
+ line: 5,
48
+ endLine: 8,
49
+ signature: 'function subtract(a: number, b: number): number',
50
+ }),
51
+ ]),
52
+ ];
53
+ const fileContents = new Map([
54
+ ['src/utils.ts', 'function add(a: number, b: number): number {\n return a + b\n}\n\nfunction subtract(a: number, b: number): number {\n return a - b\n}\n'],
55
+ ]);
56
+ const result = await indexer.indexFiles(asts, fileContents);
57
+ expect(result.chunked).toBe(2);
58
+ expect(result.embedded).toBe(2);
59
+ expect(store.size()).toBe(2);
60
+ });
61
+ it('handles disabled config (no-ops)', async () => {
62
+ const store = new InMemoryVectorStore();
63
+ const indexer = new VectorIndexer({
64
+ enabled: false,
65
+ embeddingProvider: mockProvider,
66
+ vectorStore: store,
67
+ });
68
+ const asts = [
69
+ makeFileAST('src/utils.ts', 'typescript', [
70
+ makeSymbol('add', 'function', 'src/utils.ts', { line: 0, endLine: 3 }),
71
+ ]),
72
+ ];
73
+ const fileContents = new Map([['src/utils.ts', 'function add() { return 1 }']]);
74
+ const indexResult = await indexer.indexFiles(asts, fileContents);
75
+ expect(indexResult.chunked).toBe(0);
76
+ expect(indexResult.embedded).toBe(0);
77
+ expect(store.size()).toBe(0);
78
+ // removeFiles is also a no-op
79
+ await indexer.removeFiles(['src/utils.ts']);
80
+ expect(store.size()).toBe(0);
81
+ // updateFiles is also a no-op
82
+ const updateResult = await indexer.updateFiles(asts, fileContents);
83
+ expect(updateResult.added).toBe(0);
84
+ expect(updateResult.removed).toBe(0);
85
+ expect(updateResult.unchanged).toBe(0);
86
+ });
87
+ it('removes files by path', async () => {
88
+ const store = new InMemoryVectorStore();
89
+ const indexer = new VectorIndexer({
90
+ enabled: true,
91
+ embeddingProvider: mockProvider,
92
+ vectorStore: store,
93
+ });
94
+ // Index two files
95
+ const asts = [
96
+ makeFileAST('src/a.ts', 'typescript', [
97
+ makeSymbol('foo', 'function', 'src/a.ts', { line: 0, endLine: 2 }),
98
+ ]),
99
+ makeFileAST('src/b.ts', 'typescript', [
100
+ makeSymbol('bar', 'function', 'src/b.ts', { line: 0, endLine: 2 }),
101
+ ]),
102
+ ];
103
+ const fileContents = new Map([
104
+ ['src/a.ts', 'function foo() {}\n'],
105
+ ['src/b.ts', 'function bar() {}\n'],
106
+ ]);
107
+ await indexer.indexFiles(asts, fileContents);
108
+ expect(store.size()).toBe(2);
109
+ // Remove one file
110
+ await indexer.removeFiles(['src/a.ts']);
111
+ expect(store.size()).toBe(1);
112
+ });
113
+ it('updates modified files (only re-embeds changed chunks)', async () => {
114
+ const store = new InMemoryVectorStore();
115
+ const embedSpy = vi.fn(mockProvider.embed.bind(mockProvider));
116
+ const spyProvider = {
117
+ ...mockProvider,
118
+ embed: embedSpy,
119
+ };
120
+ const indexer = new VectorIndexer({
121
+ enabled: true,
122
+ embeddingProvider: spyProvider,
123
+ vectorStore: store,
124
+ batchSize: 128,
125
+ });
126
+ // Initial index with two symbols
127
+ const initialAsts = [
128
+ makeFileAST('src/math.ts', 'typescript', [
129
+ makeSymbol('add', 'function', 'src/math.ts', {
130
+ line: 0,
131
+ endLine: 2,
132
+ signature: 'function add(a: number, b: number): number',
133
+ }),
134
+ makeSymbol('multiply', 'function', 'src/math.ts', {
135
+ line: 4,
136
+ endLine: 6,
137
+ signature: 'function multiply(a: number, b: number): number',
138
+ }),
139
+ ]),
140
+ ];
141
+ const initialContents = new Map([
142
+ ['src/math.ts', 'function add(a: number, b: number): number {\n return a + b\n}\n\nfunction multiply(a: number, b: number): number {\n return a * b\n}\n'],
143
+ ]);
144
+ await indexer.indexFiles(initialAsts, initialContents);
145
+ expect(store.size()).toBe(2);
146
+ const initialEmbedCalls = embedSpy.mock.calls.length;
147
+ // Update: only change `add`, keep `multiply` the same
148
+ const updatedAsts = [
149
+ makeFileAST('src/math.ts', 'typescript', [
150
+ makeSymbol('add', 'function', 'src/math.ts', {
151
+ line: 0,
152
+ endLine: 3,
153
+ signature: 'function add(a: number, b: number): number',
154
+ }),
155
+ makeSymbol('multiply', 'function', 'src/math.ts', {
156
+ line: 5,
157
+ endLine: 7,
158
+ signature: 'function multiply(a: number, b: number): number',
159
+ }),
160
+ ]),
161
+ ];
162
+ const updatedContents = new Map([
163
+ ['src/math.ts', 'function add(a: number, b: number): number {\n // Enhanced add\n return a + b\n}\n\nfunction multiply(a: number, b: number): number {\n return a * b\n}\n'],
164
+ ]);
165
+ const result = await indexer.updateFiles(updatedAsts, updatedContents);
166
+ // `add` content changed, `multiply` content unchanged
167
+ expect(result.unchanged).toBeGreaterThanOrEqual(0); // multiply may or may not have changed depending on chunk IDs
168
+ expect(result.added).toBeGreaterThanOrEqual(1); // at least `add` was re-embedded
169
+ expect(store.size()).toBe(2);
170
+ });
171
+ it('emits progress events', async () => {
172
+ const store = new InMemoryVectorStore();
173
+ const indexer = new VectorIndexer({
174
+ enabled: true,
175
+ embeddingProvider: mockProvider,
176
+ vectorStore: store,
177
+ });
178
+ const asts = [
179
+ makeFileAST('src/a.ts', 'typescript', [
180
+ makeSymbol('foo', 'function', 'src/a.ts', { line: 0, endLine: 2 }),
181
+ ]),
182
+ ];
183
+ const fileContents = new Map([['src/a.ts', 'function foo() { return 1 }']]);
184
+ const progress = [];
185
+ await indexer.indexFiles(asts, fileContents, (p) => {
186
+ progress.push({ ...p });
187
+ });
188
+ // Should have progress events for chunking, embedding, and indexing phases
189
+ const phases = progress.map(p => p.phase);
190
+ expect(phases).toContain('chunking');
191
+ expect(phases).toContain('embedding');
192
+ expect(phases).toContain('indexing');
193
+ });
194
+ it('handles batch splitting', async () => {
195
+ const store = new InMemoryVectorStore();
196
+ const embedSpy = vi.fn(mockProvider.embed.bind(mockProvider));
197
+ const spyProvider = {
198
+ ...mockProvider,
199
+ embed: embedSpy,
200
+ };
201
+ const indexer = new VectorIndexer({
202
+ enabled: true,
203
+ embeddingProvider: spyProvider,
204
+ vectorStore: store,
205
+ batchSize: 2, // Small batch to test splitting
206
+ maxConcurrentBatches: 1,
207
+ });
208
+ // Create 5 symbols => 5 chunks => should be split into 3 batches (2+2+1)
209
+ const symbols = Array.from({ length: 5 }, (_, i) => makeSymbol(`fn${i}`, 'function', 'src/many.ts', { line: i * 3, endLine: i * 3 + 2 }));
210
+ const asts = [
211
+ makeFileAST('src/many.ts', 'typescript', symbols),
212
+ ];
213
+ const lines = Array.from({ length: 15 }, (_, i) => `line ${i}`);
214
+ const fileContents = new Map([['src/many.ts', lines.join('\n')]]);
215
+ await indexer.indexFiles(asts, fileContents);
216
+ // 5 chunks / batchSize 2 = 3 embed calls
217
+ expect(embedSpy).toHaveBeenCalledTimes(3);
218
+ expect(store.size()).toBe(5);
219
+ });
220
+ });
221
+ // ── IncrementalIndexer + VectorIndexer Integration ───────────────────
222
+ describe('IncrementalIndexer with VectorIndexer', () => {
223
+ it('added files get vector-indexed', async () => {
224
+ const extractor = new SymbolExtractor();
225
+ const store = new InMemoryVectorStore();
226
+ const vectorIndexer = new VectorIndexer({
227
+ enabled: true,
228
+ embeddingProvider: mockProvider,
229
+ vectorStore: store,
230
+ });
231
+ const indexer = new IncrementalIndexer(extractor);
232
+ indexer.setVectorIndexer(vectorIndexer);
233
+ const files = new Map([
234
+ ['src/main.ts', 'export function main() { return 1 }'],
235
+ ['src/utils.ts', 'export function helper() { return 42 }'],
236
+ ]);
237
+ const result = await indexer.index(files);
238
+ expect(result.changes.added).toHaveLength(2);
239
+ // Vector store should have chunks for the added files
240
+ expect(store.size()).toBeGreaterThan(0);
241
+ });
242
+ it('deleted files get removed from vector store', async () => {
243
+ const extractor = new SymbolExtractor();
244
+ const store = new InMemoryVectorStore();
245
+ const vectorIndexer = new VectorIndexer({
246
+ enabled: true,
247
+ embeddingProvider: mockProvider,
248
+ vectorStore: store,
249
+ });
250
+ const indexer = new IncrementalIndexer(extractor);
251
+ indexer.setVectorIndexer(vectorIndexer);
252
+ // First index: two files
253
+ const files1 = new Map([
254
+ ['src/a.ts', 'export function foo() { return 1 }'],
255
+ ['src/b.ts', 'export function bar() { return 2 }'],
256
+ ]);
257
+ await indexer.index(files1);
258
+ const sizeAfterFirstIndex = store.size();
259
+ expect(sizeAfterFirstIndex).toBeGreaterThan(0);
260
+ // Second index: remove src/b.ts
261
+ const files2 = new Map([
262
+ ['src/a.ts', 'export function foo() { return 1 }'],
263
+ ]);
264
+ const result = await indexer.index(files2);
265
+ expect(result.changes.deleted).toEqual(['src/b.ts']);
266
+ // Store should have fewer chunks
267
+ expect(store.size()).toBeLessThan(sizeAfterFirstIndex);
268
+ });
269
+ it('works exactly as before without vector indexer', async () => {
270
+ const extractor = new SymbolExtractor();
271
+ const indexer = new IncrementalIndexer(extractor);
272
+ // No setVectorIndexer call — should work fine
273
+ const files = new Map([
274
+ ['src/main.ts', 'export function main() {}'],
275
+ ['src/utils.ts', 'export const helper = () => 42'],
276
+ ]);
277
+ const result = await indexer.index(files);
278
+ expect(result.changes.added).toHaveLength(2);
279
+ expect(result.changes.modified).toHaveLength(0);
280
+ expect(result.indexed).toHaveLength(2);
281
+ expect(result.metadata.totalFiles).toBe(2);
282
+ // Second pass with modification
283
+ const files2 = new Map([
284
+ ['src/main.ts', 'export function main() {}'],
285
+ ['src/utils.ts', 'export const helper = () => 99'],
286
+ ]);
287
+ const result2 = await indexer.index(files2);
288
+ expect(result2.changes.modified).toEqual(['src/utils.ts']);
289
+ expect(result2.indexed).toHaveLength(1);
290
+ });
291
+ });
@@ -1,6 +1,7 @@
1
1
  import type { FileIndex, IndexMetadata, CodeSymbol } from '../types.js';
2
2
  import { type ChangeSet } from './change-detector.js';
3
3
  import { SymbolExtractor } from '../parser/symbol-extractor.js';
4
+ import type { VectorIndexer } from './vector-indexer.js';
4
5
  export interface IndexerOptions {
5
6
  indexDir?: string;
6
7
  filePatterns?: string[];
@@ -16,7 +17,10 @@ export declare class IncrementalIndexer {
16
17
  private indexDir;
17
18
  private previousTree;
18
19
  private fileIndex;
20
+ private vectorIndexer;
19
21
  constructor(extractor: SymbolExtractor, options?: IndexerOptions);
22
+ /** Set an optional VectorIndexer for dense vector indexing. */
23
+ setVectorIndexer(indexer: VectorIndexer): void;
20
24
  /**
21
25
  * Index files, returning only the changed ones.
22
26
  * @param files Map of filePath -> content
@@ -1 +1 @@
1
- {"version":3,"file":"incremental-indexer.d.ts","sourceRoot":"","sources":["../../../src/indexing/incremental-indexer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEvE,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAE/D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,SAAS,CAAoC;gBAEzC,SAAS,EAAE,eAAe,EAAE,OAAO,GAAE,cAAmB;IAKpE;;;OAGG;IACG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAC/C,OAAO,EAAE,SAAS,CAAA;QAClB,OAAO,EAAE,SAAS,EAAE,CAAA;QACpB,QAAQ,EAAE,aAAa,CAAA;KACxB,CAAC;IA+DF,0BAA0B;IACpB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW3C,4BAA4B;IACtB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB9C,+BAA+B;IAC/B,aAAa,IAAI,UAAU,EAAE;IAQ7B,kCAAkC;IAClC,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;CAGvC"}
1
+ {"version":3,"file":"incremental-indexer.d.ts","sourceRoot":"","sources":["../../../src/indexing/incremental-indexer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAW,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,aAAa,CAA2B;gBAEpC,SAAS,EAAE,eAAe,EAAE,OAAO,GAAE,cAAmB;IAKpE,+DAA+D;IAC/D,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAI9C;;;OAGG;IACG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAC/C,OAAO,EAAE,SAAS,CAAA;QAClB,OAAO,EAAE,SAAS,EAAE,CAAA;QACpB,QAAQ,EAAE,aAAa,CAAA;KACxB,CAAC;IA0FF,0BAA0B;IACpB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB3C,4BAA4B;IACtB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA8B9C,+BAA+B;IAC/B,aAAa,IAAI,UAAU,EAAE;IAQ7B,kCAAkC;IAClC,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;CAGvC"}
@@ -14,10 +14,15 @@ export class IncrementalIndexer {
14
14
  indexDir;
15
15
  previousTree;
16
16
  fileIndex = new Map();
17
+ vectorIndexer;
17
18
  constructor(extractor, options = {}) {
18
19
  this.extractor = extractor;
19
20
  this.indexDir = options.indexDir ?? '.agentfactory/code-index';
20
21
  }
22
+ /** Set an optional VectorIndexer for dense vector indexing. */
23
+ setVectorIndexer(indexer) {
24
+ this.vectorIndexer = indexer;
25
+ }
21
26
  /**
22
27
  * Index files, returning only the changed ones.
23
28
  * @param files Map of filePath -> content
@@ -41,6 +46,10 @@ export class IncrementalIndexer {
41
46
  }
42
47
  // Index added and modified files
43
48
  const indexed = [];
49
+ const addedAsts = [];
50
+ const addedContents = new Map();
51
+ const modifiedAsts = [];
52
+ const modifiedContents = new Map();
44
53
  const toIndex = [...changes.added, ...changes.modified];
45
54
  for (const path of toIndex) {
46
55
  const content = files.get(path);
@@ -56,6 +65,27 @@ export class IncrementalIndexer {
56
65
  };
57
66
  this.fileIndex.set(path, fileIdx);
58
67
  indexed.push(fileIdx);
68
+ // Collect ASTs/contents for vector indexing
69
+ if (changes.added.includes(path)) {
70
+ addedAsts.push(ast);
71
+ addedContents.set(path, content);
72
+ }
73
+ else {
74
+ modifiedAsts.push(ast);
75
+ modifiedContents.set(path, content);
76
+ }
77
+ }
78
+ // Vector indexing (optional)
79
+ if (this.vectorIndexer) {
80
+ if (changes.deleted.length > 0) {
81
+ await this.vectorIndexer.removeFiles(changes.deleted);
82
+ }
83
+ if (addedAsts.length > 0) {
84
+ await this.vectorIndexer.indexFiles(addedAsts, addedContents);
85
+ }
86
+ if (modifiedAsts.length > 0) {
87
+ await this.vectorIndexer.updateFiles(modifiedAsts, modifiedContents);
88
+ }
59
89
  }
60
90
  this.previousTree = newTree;
61
91
  // Collect all symbols for metadata
@@ -87,6 +117,11 @@ export class IncrementalIndexer {
87
117
  rootHash: this.previousTree?.getRootHash() ?? '',
88
118
  };
89
119
  await writeFile(join(dir, 'index.json'), JSON.stringify(data, null, 2));
120
+ // Save vector store if available
121
+ if (this.vectorIndexer) {
122
+ const vectorDir = join(dir, 'vectors');
123
+ await this.vectorIndexer.getStore().save(vectorDir);
124
+ }
90
125
  }
91
126
  /** Load index from disk. */
92
127
  async load(basePath) {
@@ -101,6 +136,16 @@ export class IncrementalIndexer {
101
136
  fileHashes.set(path, fi.gitHash);
102
137
  }
103
138
  this.previousTree = MerkleTree.fromHashes(fileHashes);
139
+ // Load vector store if available
140
+ if (this.vectorIndexer) {
141
+ const vectorDir = join(basePath, this.indexDir, 'vectors');
142
+ try {
143
+ await this.vectorIndexer.getStore().load(vectorDir);
144
+ }
145
+ catch {
146
+ // Vector store may not exist yet — that's fine
147
+ }
148
+ }
104
149
  return true;
105
150
  }
106
151
  catch {