@pellux/goodvibes-sdk 0.37.2 → 0.38.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/contracts/artifacts/operator-contract.json +1 -1
- package/dist/events/communication.d.ts +21 -1
- package/dist/events/communication.d.ts.map +1 -1
- package/dist/platform/agents/message-bus-core.d.ts +7 -0
- package/dist/platform/agents/message-bus-core.d.ts.map +1 -1
- package/dist/platform/agents/message-bus-core.js +2 -1
- package/dist/platform/agents/orchestrator-runner.d.ts +37 -1
- package/dist/platform/agents/orchestrator-runner.d.ts.map +1 -1
- package/dist/platform/agents/orchestrator-runner.js +158 -7
- package/dist/platform/agents/orchestrator.d.ts +40 -0
- package/dist/platform/agents/orchestrator.d.ts.map +1 -1
- package/dist/platform/agents/orchestrator.js +29 -0
- package/dist/platform/agents/turn-knowledge-injection.d.ts +107 -0
- package/dist/platform/agents/turn-knowledge-injection.d.ts.map +1 -0
- package/dist/platform/agents/turn-knowledge-injection.js +152 -0
- package/dist/platform/agents/wrfc-controller.d.ts +23 -0
- package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
- package/dist/platform/agents/wrfc-controller.js +55 -0
- package/dist/platform/config/manager.d.ts +9 -0
- package/dist/platform/config/manager.d.ts.map +1 -1
- package/dist/platform/config/manager.js +15 -0
- package/dist/platform/core/adaptive-planner.d.ts +50 -0
- package/dist/platform/core/adaptive-planner.d.ts.map +1 -1
- package/dist/platform/core/adaptive-planner.js +49 -0
- package/dist/platform/core/index.d.ts +1 -0
- package/dist/platform/core/index.d.ts.map +1 -1
- package/dist/platform/core/index.js +1 -0
- package/dist/platform/core/orchestrator-runtime.d.ts +11 -0
- package/dist/platform/core/orchestrator-runtime.d.ts.map +1 -1
- package/dist/platform/core/orchestrator-turn-loop.d.ts +28 -1
- package/dist/platform/core/orchestrator-turn-loop.d.ts.map +1 -1
- package/dist/platform/core/orchestrator-turn-loop.js +115 -3
- package/dist/platform/core/orchestrator.d.ts +61 -0
- package/dist/platform/core/orchestrator.d.ts.map +1 -1
- package/dist/platform/core/orchestrator.js +74 -1
- package/dist/platform/core/plan-proposal.d.ts +165 -0
- package/dist/platform/core/plan-proposal.d.ts.map +1 -0
- package/dist/platform/core/plan-proposal.js +296 -0
- package/dist/platform/knowledge/knowledge-api.d.ts +26 -0
- package/dist/platform/knowledge/knowledge-api.d.ts.map +1 -1
- package/dist/platform/knowledge/knowledge-api.js +11 -0
- package/dist/platform/orchestration/budget.d.ts +22 -0
- package/dist/platform/orchestration/budget.d.ts.map +1 -0
- package/dist/platform/orchestration/budget.js +35 -0
- package/dist/platform/orchestration/cancellation.d.ts +31 -0
- package/dist/platform/orchestration/cancellation.d.ts.map +1 -0
- package/dist/platform/orchestration/cancellation.js +25 -0
- package/dist/platform/orchestration/controller-compat.d.ts +32 -0
- package/dist/platform/orchestration/controller-compat.d.ts.map +1 -0
- package/dist/platform/orchestration/controller-compat.js +13 -0
- package/dist/platform/orchestration/dirty-guard.d.ts +32 -0
- package/dist/platform/orchestration/dirty-guard.d.ts.map +1 -0
- package/dist/platform/orchestration/dirty-guard.js +147 -0
- package/dist/platform/orchestration/engine.d.ts +57 -0
- package/dist/platform/orchestration/engine.d.ts.map +1 -0
- package/dist/platform/orchestration/engine.js +418 -0
- package/dist/platform/orchestration/index.d.ts +18 -0
- package/dist/platform/orchestration/index.d.ts.map +1 -0
- package/dist/platform/orchestration/index.js +10 -0
- package/dist/platform/orchestration/persistence.d.ts +24 -0
- package/dist/platform/orchestration/persistence.d.ts.map +1 -0
- package/dist/platform/orchestration/persistence.js +176 -0
- package/dist/platform/orchestration/phase-runner.d.ts +87 -0
- package/dist/platform/orchestration/phase-runner.d.ts.map +1 -0
- package/dist/platform/orchestration/phase-runner.js +274 -0
- package/dist/platform/orchestration/scheduler.d.ts +45 -0
- package/dist/platform/orchestration/scheduler.d.ts.map +1 -0
- package/dist/platform/orchestration/scheduler.js +57 -0
- package/dist/platform/orchestration/types.d.ts +250 -0
- package/dist/platform/orchestration/types.d.ts.map +1 -0
- package/dist/platform/orchestration/types.js +15 -0
- package/dist/platform/runtime/emitters/communication.d.ts +11 -0
- package/dist/platform/runtime/emitters/communication.d.ts.map +1 -1
- package/dist/platform/runtime/emitters/communication.js +9 -0
- package/dist/platform/runtime/feature-flags/flags.d.ts.map +1 -1
- package/dist/platform/runtime/feature-flags/flags.js +17 -0
- package/dist/platform/runtime/fleet/adapters/agent.d.ts +56 -0
- package/dist/platform/runtime/fleet/adapters/agent.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/agent.js +143 -0
- package/dist/platform/runtime/fleet/adapters/automation.d.ts +30 -0
- package/dist/platform/runtime/fleet/adapters/automation.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/automation.js +41 -0
- package/dist/platform/runtime/fleet/adapters/background-process.d.ts +12 -0
- package/dist/platform/runtime/fleet/adapters/background-process.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/background-process.js +46 -0
- package/dist/platform/runtime/fleet/adapters/code-index.d.ts +21 -0
- package/dist/platform/runtime/fleet/adapters/code-index.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/code-index.js +44 -0
- package/dist/platform/runtime/fleet/adapters/orchestration.d.ts +47 -0
- package/dist/platform/runtime/fleet/adapters/orchestration.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/orchestration.js +225 -0
- package/dist/platform/runtime/fleet/adapters/schedule.d.ts +14 -0
- package/dist/platform/runtime/fleet/adapters/schedule.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/schedule.js +28 -0
- package/dist/platform/runtime/fleet/adapters/trigger.d.ts +12 -0
- package/dist/platform/runtime/fleet/adapters/trigger.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/trigger.js +22 -0
- package/dist/platform/runtime/fleet/adapters/watcher.d.ts +13 -0
- package/dist/platform/runtime/fleet/adapters/watcher.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/watcher.js +47 -0
- package/dist/platform/runtime/fleet/adapters/workflow.d.ts +11 -0
- package/dist/platform/runtime/fleet/adapters/workflow.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/workflow.js +34 -0
- package/dist/platform/runtime/fleet/adapters/wrfc.d.ts +22 -0
- package/dist/platform/runtime/fleet/adapters/wrfc.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/adapters/wrfc.js +228 -0
- package/dist/platform/runtime/fleet/index.d.ts +10 -0
- package/dist/platform/runtime/fleet/index.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/index.js +6 -0
- package/dist/platform/runtime/fleet/registry.d.ts +113 -0
- package/dist/platform/runtime/fleet/registry.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/registry.js +630 -0
- package/dist/platform/runtime/fleet/types.d.ts +199 -0
- package/dist/platform/runtime/fleet/types.d.ts.map +1 -0
- package/dist/platform/runtime/fleet/types.js +2 -0
- package/dist/platform/runtime/runtime-knowledge-api.d.ts +1 -1
- package/dist/platform/runtime/runtime-knowledge-api.d.ts.map +1 -1
- package/dist/platform/runtime/runtime-knowledge-api.js +1 -0
- package/dist/platform/runtime/services.d.ts +46 -0
- package/dist/platform/runtime/services.d.ts.map +1 -1
- package/dist/platform/runtime/services.js +78 -0
- package/dist/platform/state/code-index-chunking.d.ts +49 -0
- package/dist/platform/state/code-index-chunking.d.ts.map +1 -0
- package/dist/platform/state/code-index-chunking.js +111 -0
- package/dist/platform/state/code-index-db.d.ts +53 -0
- package/dist/platform/state/code-index-db.d.ts.map +1 -0
- package/dist/platform/state/code-index-db.js +101 -0
- package/dist/platform/state/code-index-store.d.ts +175 -0
- package/dist/platform/state/code-index-store.d.ts.map +1 -0
- package/dist/platform/state/code-index-store.js +612 -0
- package/dist/platform/state/index.d.ts +4 -2
- package/dist/platform/state/index.d.ts.map +1 -1
- package/dist/platform/state/index.js +2 -1
- package/dist/platform/state/knowledge-injection.d.ts +23 -0
- package/dist/platform/state/knowledge-injection.d.ts.map +1 -1
- package/dist/platform/state/knowledge-injection.js +34 -19
- package/dist/platform/state/memory-vector-store.d.ts +1 -13
- package/dist/platform/state/memory-vector-store.d.ts.map +1 -1
- package/dist/platform/state/memory-vector-store.js +6 -38
- package/dist/platform/state/sqlite-vec-loader.d.ts +25 -0
- package/dist/platform/state/sqlite-vec-loader.d.ts.map +1 -0
- package/dist/platform/state/sqlite-vec-loader.js +43 -0
- package/dist/platform/tools/agent/manager.d.ts +128 -1
- package/dist/platform/tools/agent/manager.d.ts.map +1 -1
- package/dist/platform/tools/agent/manager.js +137 -1
- package/dist/platform/tools/exec/runtime.d.ts.map +1 -1
- package/dist/platform/tools/exec/runtime.js +139 -44
- package/dist/platform/tools/exec/schema.d.ts +2 -0
- package/dist/platform/tools/exec/schema.d.ts.map +1 -1
- package/dist/platform/tools/fetch/runtime.d.ts +7 -0
- package/dist/platform/tools/fetch/runtime.d.ts.map +1 -1
- package/dist/platform/tools/fetch/runtime.js +8 -6
- package/dist/platform/tools/registry.d.ts +9 -3
- package/dist/platform/tools/registry.d.ts.map +1 -1
- package/dist/platform/tools/registry.js +9 -3
- package/dist/platform/types/tools.d.ts +14 -1
- package/dist/platform/types/tools.d.ts.map +1 -1
- package/dist/platform/version.js +1 -1
- package/package.json +17 -9
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
/** SDK-owned platform module. This implementation is maintained in goodvibes-sdk. */
|
|
2
|
+
/**
|
|
3
|
+
* CodeIndexStore — an incremental, tree-sitter-chunked, embedding-backed index
|
|
4
|
+
* of a repo's source tree (Wave-5, W5.3, Stage A).
|
|
5
|
+
*
|
|
6
|
+
* Deliberately MIRRORS MemoryStore + SqliteVecMemoryIndex (state/memory-store.ts,
|
|
7
|
+
* state/memory-vector-store.ts) rather than reusing them: those classes are
|
|
8
|
+
* hard-keyed to MemoryRecord (schema columns, memoryVectorSourceHash,
|
|
9
|
+
* buildMemoryEmbeddingText, record-shaped search filters). Code chunks are a
|
|
10
|
+
* different record shape entirely, so this module is a parallel, independent
|
|
11
|
+
* implementation that reuses only:
|
|
12
|
+
* - the sqlite-vec native extension loader (sqlite-vec-loader.ts)
|
|
13
|
+
* - MEMORY_VECTOR_DIMS / the shared MemoryEmbeddingProviderRegistry instance,
|
|
14
|
+
* so code and memory embeddings share one provider and one dimensionality.
|
|
15
|
+
* - CodeIntelligence.getSymbols (tree-sitter-backed chunking; the chunking
|
|
16
|
+
* itself lives in code-index-chunking.ts)
|
|
17
|
+
* - the file-walk helpers already shipped for the `find` tool
|
|
18
|
+
* (tools/find/shared.ts: buildGitignoreMatcher, findNestedGitignoreFiles,
|
|
19
|
+
* collectGlobFiles, isBinary, readTextFile) — no new ignore-file parsing
|
|
20
|
+
* or directory-walk logic. Ignore rules: the ROOT .gitignore plus nested
|
|
21
|
+
* .gitignore files (each applied relative to its own directory). Nested
|
|
22
|
+
* coverage is bounded by findNestedGitignoreFiles' own cap (currently the
|
|
23
|
+
* first 5 nested files found, in walk order) — a repo with more nested
|
|
24
|
+
* .gitignore files than that has the excess UNAPPLIED, same as the find
|
|
25
|
+
* tool itself.
|
|
26
|
+
*
|
|
27
|
+
* Storage: a single bun:sqlite database (schema + all table ops live in
|
|
28
|
+
* code-index-db.ts) — `code_chunks` (chunk metadata), `code_vectors` (vec0
|
|
29
|
+
* virtual table), and `code_index_meta` (build provenance: the embedding
|
|
30
|
+
* provider id the index was last fully built with). When the current default
|
|
31
|
+
* embedding provider differs from the stored one, search() refuses to run the
|
|
32
|
+
* vector path — a query embedded in provider-Y space compared against
|
|
33
|
+
* provider-X vectors is meaningless — and degrades to an honestly-labeled
|
|
34
|
+
* lexical match over chunk symbol/path metadata until a rebuild re-embeds
|
|
35
|
+
* (buildFull() detects the mismatch and forces a full re-embed).
|
|
36
|
+
*
|
|
37
|
+
* Incremental reindex is LAZY, never a watcher: a recursive fs.watch over a
|
|
38
|
+
* repo tree is a per-file-descriptor liability and races the agent's own
|
|
39
|
+
* writes. `reindexFile()` re-chunks and re-embeds a single path on demand.
|
|
40
|
+
* `buildFull()` walks the whole tree once; concurrent calls coalesce through a
|
|
41
|
+
* single-flight promise guard, mirroring MemoryStore.rebuildVectorIndexAsync.
|
|
42
|
+
*
|
|
43
|
+
* Lifecycle honesty: `reroot()`/`close()` bump an epoch counter; an in-flight
|
|
44
|
+
* build re-checks the epoch after every await and aborts cleanly when stale
|
|
45
|
+
* (per-file writes are atomic — all of a file's chunks are embedded before any
|
|
46
|
+
* row is written — so an aborted build never writes wrong-rooted chunks into
|
|
47
|
+
* the new database). Aborted builds return stats carrying
|
|
48
|
+
* `abortReason: 'build aborted by reroot'` and never become `lastBuild`.
|
|
49
|
+
*
|
|
50
|
+
* The index never blocks a turn: buildFull() awaits `embedAsync` on a bounded
|
|
51
|
+
* per-file queue with periodic event-loop yields, and every read (`search`,
|
|
52
|
+
* `stats`) returns whatever is currently indexed — it never waits on an
|
|
53
|
+
* in-flight build.
|
|
54
|
+
*/
|
|
55
|
+
import { dirname, join, relative, sep } from 'node:path';
|
|
56
|
+
import { mkdirSync, statSync } from 'node:fs';
|
|
57
|
+
import { Database } from 'bun:sqlite';
|
|
58
|
+
import { loadSqliteVecExtension } from './sqlite-vec-loader.js';
|
|
59
|
+
import { HASHED_MEMORY_EMBEDDING_PROVIDER, MemoryEmbeddingProviderRegistry, normalizeMemoryEmbeddingVector, } from './memory-embeddings.js';
|
|
60
|
+
import { MEMORY_VECTOR_DIMS } from './memory-vector-store.js';
|
|
61
|
+
import { CodeIntelligence } from '../intelligence/facade.js';
|
|
62
|
+
import { chunkFileContent, sha256, } from './code-index-chunking.js';
|
|
63
|
+
import { CHUNK_ROW_COLUMNS, EMBEDDING_PROVIDER_META_KEY, createCodeIndexSchema, countChunksForPath, countIndexedChunks, countIndexedFiles, deleteChunksForPath, getChunkById, getCodeIndexMeta, getFileHash, listIndexedPaths, rowToChunk, setCodeIndexMeta, writeChunk, } from './code-index-db.js';
|
|
64
|
+
import { buildGitignoreMatcher, collectGlobFiles, createFindDiagnostics, findNestedGitignoreFiles, isBinary, readTextFile, } from '../tools/find/shared.js';
|
|
65
|
+
import { logger } from '../utils/logger.js';
|
|
66
|
+
import { summarizeError } from '../utils/error-display.js';
|
|
67
|
+
const DEFAULT_MAX_FILES = 5000;
|
|
68
|
+
const DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
69
|
+
const DEFAULT_MAX_TOTAL_BYTES = 256 * 1024 * 1024;
|
|
70
|
+
const BUILD_ABORTED_BY_REROOT = 'build aborted by reroot';
|
|
71
|
+
function distanceToSimilarity(distance) {
|
|
72
|
+
if (!Number.isFinite(distance))
|
|
73
|
+
return 0;
|
|
74
|
+
return Math.max(0, Math.min(1, 1 - distance / 2));
|
|
75
|
+
}
|
|
76
|
+
function yieldToEventLoop() {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
const timer = setTimeout(resolve, 0);
|
|
79
|
+
timer.unref?.();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function emptySkipReport() {
|
|
83
|
+
return { tooLarge: 0, overFileCap: 0, overTotalBytes: 0, binary: 0, ignoredByGitignore: 0, readErrors: 0, chunkedByWindow: 0 };
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// CodeIndexStore
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
export class CodeIndexStore {
|
|
89
|
+
embeddingRegistry;
|
|
90
|
+
options;
|
|
91
|
+
db = null;
|
|
92
|
+
available = false;
|
|
93
|
+
error;
|
|
94
|
+
building = false;
|
|
95
|
+
buildStartedAtMs = null;
|
|
96
|
+
buildPromise = null;
|
|
97
|
+
lastBuild = null;
|
|
98
|
+
progress = null;
|
|
99
|
+
intelligence;
|
|
100
|
+
intelligenceReadyPromise = null;
|
|
101
|
+
/**
|
|
102
|
+
* Lifecycle generation counter. Bumped by reroot() and close(); an in-flight
|
|
103
|
+
* runBuild() captures the value at start and re-checks it after EVERY await,
|
|
104
|
+
* aborting cleanly when stale so a build started against one root can never
|
|
105
|
+
* write wrong-rooted chunks into a database opened for a different root.
|
|
106
|
+
*/
|
|
107
|
+
epoch = 0;
|
|
108
|
+
rootDir;
|
|
109
|
+
dbPath;
|
|
110
|
+
constructor(rootDir, dbPath, embeddingRegistry, options = {}) {
|
|
111
|
+
this.embeddingRegistry = embeddingRegistry;
|
|
112
|
+
this.options = options;
|
|
113
|
+
this.rootDir = rootDir;
|
|
114
|
+
this.dbPath = dbPath;
|
|
115
|
+
this.intelligence = new CodeIntelligence();
|
|
116
|
+
}
|
|
117
|
+
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
118
|
+
init() {
|
|
119
|
+
if (this.db)
|
|
120
|
+
return;
|
|
121
|
+
try {
|
|
122
|
+
if (this.dbPath !== ':memory:') {
|
|
123
|
+
mkdirSync(dirname(this.dbPath), { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
this.db = new Database(this.dbPath);
|
|
126
|
+
loadSqliteVecExtension(this.db);
|
|
127
|
+
this.available = true;
|
|
128
|
+
createCodeIndexSchema(this.db);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
this.close();
|
|
132
|
+
this.available = false;
|
|
133
|
+
this.error = summarizeError(err);
|
|
134
|
+
logger.warn('Code index unavailable', { backend: 'sqlite-vec', error: this.error });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
close() {
|
|
138
|
+
if (!this.db)
|
|
139
|
+
return;
|
|
140
|
+
this.epoch++;
|
|
141
|
+
this.db.close();
|
|
142
|
+
this.db = null;
|
|
143
|
+
this.available = false;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Re-root the store to a new working directory + db path (workspace swap).
|
|
147
|
+
* Bumps the build epoch (aborting any in-flight build at its next await),
|
|
148
|
+
* closes existing handles, reopens at the new path, and clears in-memory
|
|
149
|
+
* build state. Does NOT re-trigger a build — the caller decides when to
|
|
150
|
+
* call scheduleBuild()/buildFull() again, exactly as it decided to open the
|
|
151
|
+
* store originally.
|
|
152
|
+
*/
|
|
153
|
+
async reroot(newRootDir, newDbPath) {
|
|
154
|
+
this.epoch++;
|
|
155
|
+
this.close();
|
|
156
|
+
this.buildPromise = null;
|
|
157
|
+
this.building = false;
|
|
158
|
+
this.buildStartedAtMs = null;
|
|
159
|
+
this.lastBuild = null;
|
|
160
|
+
this.progress = null;
|
|
161
|
+
this.rootDir = newRootDir;
|
|
162
|
+
this.dbPath = newDbPath;
|
|
163
|
+
this.init();
|
|
164
|
+
}
|
|
165
|
+
// ── Status ───────────────────────────────────────────────────────────────
|
|
166
|
+
isBuilding() {
|
|
167
|
+
return this.building;
|
|
168
|
+
}
|
|
169
|
+
buildProgress() {
|
|
170
|
+
return this.building ? this.progress : null;
|
|
171
|
+
}
|
|
172
|
+
/** Wall-clock start time of the in-flight build, or null when idle. */
|
|
173
|
+
buildStartedAt() {
|
|
174
|
+
return this.building ? this.buildStartedAtMs : null;
|
|
175
|
+
}
|
|
176
|
+
/** True unless the only active embedding provider is the deterministic hashed fallback. */
|
|
177
|
+
hasSemanticProvider() {
|
|
178
|
+
return this.embeddingRegistry.getDefaultProviderId() !== HASHED_MEMORY_EMBEDDING_PROVIDER.id;
|
|
179
|
+
}
|
|
180
|
+
/** Stated once, honest: why Stage-B auto-retrieval (a later wave) would be absent right now. Null when a real provider is active. */
|
|
181
|
+
describeDegradation() {
|
|
182
|
+
return this.hasSemanticProvider()
|
|
183
|
+
? null
|
|
184
|
+
: 'code auto-retrieval disabled: no semantic embedding provider configured';
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Human-readable provider-space mismatch, or null when the stored vectors
|
|
188
|
+
* were built with the current default provider (or provenance is unknown —
|
|
189
|
+
* an index that predates provenance tracking keeps its legacy behavior
|
|
190
|
+
* until the next build stamps it).
|
|
191
|
+
*/
|
|
192
|
+
getProviderMismatch() {
|
|
193
|
+
if (!this.db || !this.available)
|
|
194
|
+
return null;
|
|
195
|
+
const stored = getCodeIndexMeta(this.db, EMBEDDING_PROVIDER_META_KEY);
|
|
196
|
+
const current = this.embeddingRegistry.getDefaultProviderId();
|
|
197
|
+
if (stored === null || stored === current)
|
|
198
|
+
return null;
|
|
199
|
+
return `embeddings built with ${stored}, current provider ${current} — rebuild to re-embed`;
|
|
200
|
+
}
|
|
201
|
+
stats() {
|
|
202
|
+
const provider = this.embeddingRegistry.getDefaultProviderOrNull();
|
|
203
|
+
const mismatch = this.getProviderMismatch();
|
|
204
|
+
return {
|
|
205
|
+
backend: 'sqlite-vec',
|
|
206
|
+
enabled: this.available,
|
|
207
|
+
available: this.available,
|
|
208
|
+
path: this.dbPath,
|
|
209
|
+
dimensions: MEMORY_VECTOR_DIMS,
|
|
210
|
+
indexedFiles: this.db && this.available ? countIndexedFiles(this.db) : 0,
|
|
211
|
+
indexedChunks: this.db && this.available ? countIndexedChunks(this.db) : 0,
|
|
212
|
+
embeddingProviderId: provider?.id ?? this.embeddingRegistry.getDefaultProviderId(),
|
|
213
|
+
embeddingProviderLabel: provider?.label ?? `Unregistered (${this.embeddingRegistry.getDefaultProviderId()})`,
|
|
214
|
+
semanticRetrievalAvailable: this.hasSemanticProvider(),
|
|
215
|
+
...(mismatch ? { embeddingProviderMismatch: mismatch } : {}),
|
|
216
|
+
building: this.building,
|
|
217
|
+
lastBuild: this.lastBuild,
|
|
218
|
+
...(this.error ? { error: this.error } : {}),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// ── Search (Stage A explicit query) ─────────────────────────────────────
|
|
222
|
+
search(query, opts = {}) {
|
|
223
|
+
if (!this.db || !this.available)
|
|
224
|
+
return [];
|
|
225
|
+
const trimmed = query.trim();
|
|
226
|
+
if (!trimmed)
|
|
227
|
+
return [];
|
|
228
|
+
const limit = Math.max(1, Math.min(50, opts.limit ?? 10));
|
|
229
|
+
// Provider-space honesty: a query embedded under the CURRENT provider is
|
|
230
|
+
// meaningless against vectors stored under a DIFFERENT one — skip the
|
|
231
|
+
// vector path entirely and degrade to lexical metadata matching.
|
|
232
|
+
if (this.getProviderMismatch() !== null) {
|
|
233
|
+
return this.searchLexical(trimmed, limit);
|
|
234
|
+
}
|
|
235
|
+
let embedding;
|
|
236
|
+
try {
|
|
237
|
+
const result = this.embeddingRegistry.embedSync({
|
|
238
|
+
text: trimmed,
|
|
239
|
+
dimensions: MEMORY_VECTOR_DIMS,
|
|
240
|
+
usage: 'query',
|
|
241
|
+
});
|
|
242
|
+
embedding = normalizeMemoryEmbeddingVector(result.vector, MEMORY_VECTOR_DIMS);
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
logger.warn('Code index query embedding failed', { error: summarizeError(err) });
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
const rows = this.db.query('SELECT rowid, chunk_id, distance FROM code_vectors WHERE embedding MATCH ? AND k = ? ORDER BY distance').all(embedding, limit);
|
|
249
|
+
const label = this.hasSemanticProvider() ? 'semantic' : 'lexical';
|
|
250
|
+
const results = [];
|
|
251
|
+
for (const row of rows) {
|
|
252
|
+
const chunk = getChunkById(this.db, row.chunk_id);
|
|
253
|
+
if (!chunk)
|
|
254
|
+
continue;
|
|
255
|
+
results.push({
|
|
256
|
+
chunk,
|
|
257
|
+
distance: Number(row.distance),
|
|
258
|
+
similarity: distanceToSimilarity(Number(row.distance)),
|
|
259
|
+
label,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return results;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Lexical fallback used when the vector path is disabled by a provider
|
|
266
|
+
* mismatch: token match over chunk symbol/path metadata (chunk text is not
|
|
267
|
+
* stored, so this is name/path matching only — labeled 'lexical', never
|
|
268
|
+
* 'semantic'). Similarity is the matched-token fraction.
|
|
269
|
+
*/
|
|
270
|
+
searchLexical(query, limit) {
|
|
271
|
+
if (!this.db)
|
|
272
|
+
return [];
|
|
273
|
+
const tokens = Array.from(new Set(query.toLowerCase().split(/[^a-z0-9_$]+/).filter((token) => token.length >= 2)));
|
|
274
|
+
if (tokens.length === 0)
|
|
275
|
+
return [];
|
|
276
|
+
const where = tokens.map(() => '(lower(symbol) LIKE ? OR lower(path) LIKE ?)').join(' OR ');
|
|
277
|
+
const bindings = tokens.flatMap((token) => [`%${token}%`, `%${token}%`]);
|
|
278
|
+
const rows = this.db.query(`SELECT ${CHUNK_ROW_COLUMNS} FROM code_chunks WHERE ${where} LIMIT ?`).all(...bindings, limit * 5);
|
|
279
|
+
const scored = rows.map((row) => {
|
|
280
|
+
const haystack = `${row.symbol} ${row.path}`.toLowerCase();
|
|
281
|
+
const matched = tokens.filter((token) => haystack.includes(token)).length;
|
|
282
|
+
const similarity = matched / tokens.length;
|
|
283
|
+
return {
|
|
284
|
+
chunk: rowToChunk(row),
|
|
285
|
+
distance: 2 * (1 - similarity),
|
|
286
|
+
similarity,
|
|
287
|
+
label: 'lexical',
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
scored.sort((a, b) => b.similarity - a.similarity);
|
|
291
|
+
return scored.slice(0, limit);
|
|
292
|
+
}
|
|
293
|
+
// ── Build / reindex ──────────────────────────────────────────────────────
|
|
294
|
+
/** Fire-and-forget kickoff; concurrent/repeated calls while a build is running are no-ops. Never awaited by the caller — never blocks a turn. */
|
|
295
|
+
scheduleBuild() {
|
|
296
|
+
if (this.building)
|
|
297
|
+
return;
|
|
298
|
+
void this.buildFull().catch((err) => {
|
|
299
|
+
logger.warn('Code index build failed', { error: summarizeError(err) });
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/** Full source-tree walk. Concurrent calls coalesce onto the same in-flight promise (mirrors MemoryStore.rebuildVectorIndexAsync). */
|
|
303
|
+
async buildFull() {
|
|
304
|
+
if (!this.db || !this.available) {
|
|
305
|
+
return this.lastBuild ?? emptyBuildStats();
|
|
306
|
+
}
|
|
307
|
+
if (!this.buildPromise) {
|
|
308
|
+
const buildEpoch = this.epoch;
|
|
309
|
+
this.building = true;
|
|
310
|
+
this.buildStartedAtMs = Date.now();
|
|
311
|
+
this.progress = { scanned: 0, total: 0 };
|
|
312
|
+
this.buildPromise = this.runBuild(buildEpoch).finally(() => {
|
|
313
|
+
// A reroot() already reset these for the NEW lifecycle; a stale
|
|
314
|
+
// build's cleanup must not clobber state belonging to a newer epoch.
|
|
315
|
+
if (this.epoch === buildEpoch) {
|
|
316
|
+
this.building = false;
|
|
317
|
+
this.buildStartedAtMs = null;
|
|
318
|
+
this.buildPromise = null;
|
|
319
|
+
this.progress = null;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return this.buildPromise;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Reindex a single file on demand. Intended to be called from the file
|
|
327
|
+
* read/write tool paths and from an explicit reindex command — wiring
|
|
328
|
+
* those call sites is out of scope for this module (see module doc).
|
|
329
|
+
* A no-op (chunks removed) if the path is gitignored or no longer exists.
|
|
330
|
+
*/
|
|
331
|
+
async reindexFile(absPath) {
|
|
332
|
+
if (!this.db || !this.available)
|
|
333
|
+
return { indexed: false, mode: 'empty' };
|
|
334
|
+
const callEpoch = this.epoch;
|
|
335
|
+
const rel = relative(this.rootDir, absPath);
|
|
336
|
+
if (rel.startsWith('..'))
|
|
337
|
+
return { indexed: false, mode: 'empty' };
|
|
338
|
+
const diagnostics = createFindDiagnostics();
|
|
339
|
+
const isIgnored = this.buildIgnoreMatcher(diagnostics);
|
|
340
|
+
if (isIgnored(absPath, rel)) {
|
|
341
|
+
deleteChunksForPath(this.db, rel);
|
|
342
|
+
return { indexed: false, mode: 'empty' };
|
|
343
|
+
}
|
|
344
|
+
let stat;
|
|
345
|
+
try {
|
|
346
|
+
stat = statSync(absPath);
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
deleteChunksForPath(this.db, rel);
|
|
350
|
+
return { indexed: false, mode: 'empty' };
|
|
351
|
+
}
|
|
352
|
+
const maxFileBytes = this.options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
353
|
+
if (stat.size > maxFileBytes)
|
|
354
|
+
return { indexed: false, mode: 'empty' };
|
|
355
|
+
if (await isBinary(absPath, diagnostics))
|
|
356
|
+
return { indexed: false, mode: 'empty' };
|
|
357
|
+
const text = await readTextFile(absPath, diagnostics);
|
|
358
|
+
if (text === null)
|
|
359
|
+
return { indexed: false, mode: 'empty' };
|
|
360
|
+
const outcome = await this.indexFileContent(rel, absPath, text, stat.mtimeMs, callEpoch, false);
|
|
361
|
+
if (outcome === null)
|
|
362
|
+
return { indexed: false, mode: 'empty' };
|
|
363
|
+
return { indexed: outcome.indexed, mode: outcome.mode };
|
|
364
|
+
}
|
|
365
|
+
// ── Build internals ──────────────────────────────────────────────────────
|
|
366
|
+
async runBuild(buildEpoch) {
|
|
367
|
+
const startedAt = Date.now();
|
|
368
|
+
const diagnostics = createFindDiagnostics();
|
|
369
|
+
const maxFiles = this.options.maxFiles ?? DEFAULT_MAX_FILES;
|
|
370
|
+
const maxFileBytes = this.options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
371
|
+
const maxTotalBytes = this.options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
|
|
372
|
+
const skip = emptySkipReport();
|
|
373
|
+
// Provider-space honesty: if the stored vectors were embedded under a
|
|
374
|
+
// different provider than the current default, the unchanged-file
|
|
375
|
+
// shortcut would silently keep stale-space vectors — force a full
|
|
376
|
+
// re-embed so a completed build always leaves a single-provider index.
|
|
377
|
+
const currentProviderId = this.embeddingRegistry.getDefaultProviderId();
|
|
378
|
+
const storedProviderId = this.db ? getCodeIndexMeta(this.db, EMBEDDING_PROVIDER_META_KEY) : null;
|
|
379
|
+
const forceReembed = storedProviderId !== null && storedProviderId !== currentProviderId;
|
|
380
|
+
const isIgnored = this.buildIgnoreMatcher(diagnostics);
|
|
381
|
+
const scanned = await collectGlobFiles(this.rootDir, ['**/*'], false, false, diagnostics);
|
|
382
|
+
if (this.epoch !== buildEpoch)
|
|
383
|
+
return abortedBuildStats(startedAt, scanned.size, skip);
|
|
384
|
+
// Deterministic order so identical trees always chunk identically (chunking determinism test).
|
|
385
|
+
const candidateFiles = Array.from(scanned).sort();
|
|
386
|
+
const keepPaths = new Set();
|
|
387
|
+
let filesIndexed = 0;
|
|
388
|
+
let filesUnchanged = 0;
|
|
389
|
+
let chunksIndexed = 0;
|
|
390
|
+
let chunksUnchanged = 0;
|
|
391
|
+
let totalBytes = 0;
|
|
392
|
+
let acceptedCount = 0;
|
|
393
|
+
for (const absPath of candidateFiles) {
|
|
394
|
+
const rel = relative(this.rootDir, absPath);
|
|
395
|
+
if (isIgnored(absPath, rel)) {
|
|
396
|
+
skip.ignoredByGitignore++;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (acceptedCount >= maxFiles) {
|
|
400
|
+
skip.overFileCap++;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
let stat;
|
|
404
|
+
try {
|
|
405
|
+
stat = statSync(absPath);
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
skip.readErrors++;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (stat.size > maxFileBytes) {
|
|
412
|
+
skip.tooLarge++;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (totalBytes + stat.size > maxTotalBytes) {
|
|
416
|
+
skip.overTotalBytes++;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const binary = await isBinary(absPath, diagnostics);
|
|
420
|
+
if (this.epoch !== buildEpoch)
|
|
421
|
+
return abortedBuildStats(startedAt, candidateFiles.length, skip);
|
|
422
|
+
if (binary) {
|
|
423
|
+
skip.binary++;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const text = await readTextFile(absPath, diagnostics);
|
|
427
|
+
if (this.epoch !== buildEpoch)
|
|
428
|
+
return abortedBuildStats(startedAt, candidateFiles.length, skip);
|
|
429
|
+
if (text === null) {
|
|
430
|
+
skip.readErrors++;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
acceptedCount++;
|
|
434
|
+
totalBytes += stat.size;
|
|
435
|
+
keepPaths.add(rel);
|
|
436
|
+
const outcome = await this.indexFileContent(rel, absPath, text, stat.mtimeMs, buildEpoch, forceReembed);
|
|
437
|
+
if (outcome === null)
|
|
438
|
+
return abortedBuildStats(startedAt, candidateFiles.length, skip);
|
|
439
|
+
if (outcome.mode === 'window')
|
|
440
|
+
skip.chunkedByWindow++;
|
|
441
|
+
if (outcome.mode === 'unchanged') {
|
|
442
|
+
filesUnchanged++;
|
|
443
|
+
chunksUnchanged += outcome.chunkCount;
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
if (outcome.indexed)
|
|
447
|
+
filesIndexed++;
|
|
448
|
+
chunksIndexed += outcome.chunkCount;
|
|
449
|
+
}
|
|
450
|
+
this.progress = { scanned: acceptedCount, total: candidateFiles.length };
|
|
451
|
+
if (acceptedCount % 20 === 0) {
|
|
452
|
+
await yieldToEventLoop();
|
|
453
|
+
if (this.epoch !== buildEpoch)
|
|
454
|
+
return abortedBuildStats(startedAt, candidateFiles.length, skip);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// Final staleness check before the destructive sweep: a stale build's
|
|
458
|
+
// keepPaths describes the OLD tree and must never delete the new one's rows.
|
|
459
|
+
if (this.epoch !== buildEpoch || !this.db) {
|
|
460
|
+
return abortedBuildStats(startedAt, candidateFiles.length, skip);
|
|
461
|
+
}
|
|
462
|
+
// Sweep: a file that no longer exists (deleted, renamed, or newly gitignored) loses its chunks.
|
|
463
|
+
let filesRemoved = 0;
|
|
464
|
+
for (const path of listIndexedPaths(this.db)) {
|
|
465
|
+
if (!keepPaths.has(path)) {
|
|
466
|
+
deleteChunksForPath(this.db, path);
|
|
467
|
+
filesRemoved++;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
// Stamp provenance only on a COMPLETED build — every chunk that survives
|
|
471
|
+
// to this point is either freshly embedded under currentProviderId or was
|
|
472
|
+
// verified same-provider by the forceReembed logic above.
|
|
473
|
+
setCodeIndexMeta(this.db, EMBEDDING_PROVIDER_META_KEY, currentProviderId);
|
|
474
|
+
const completedAt = Date.now();
|
|
475
|
+
const stats = {
|
|
476
|
+
filesScanned: candidateFiles.length,
|
|
477
|
+
filesIndexed,
|
|
478
|
+
filesUnchanged,
|
|
479
|
+
chunksIndexed,
|
|
480
|
+
chunksUnchanged,
|
|
481
|
+
filesRemoved,
|
|
482
|
+
skip: Object.freeze({ ...skip }),
|
|
483
|
+
startedAt,
|
|
484
|
+
completedAt,
|
|
485
|
+
durationMs: completedAt - startedAt,
|
|
486
|
+
};
|
|
487
|
+
this.lastBuild = stats;
|
|
488
|
+
return stats;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Compose the root .gitignore with any nested .gitignore files (each nested
|
|
492
|
+
* file's patterns apply relative to its own directory, per git semantics).
|
|
493
|
+
* Nested coverage is bounded by findNestedGitignoreFiles' cap (see module doc).
|
|
494
|
+
*/
|
|
495
|
+
buildIgnoreMatcher(diagnostics) {
|
|
496
|
+
const rootGitignorePath = join(this.rootDir, '.gitignore');
|
|
497
|
+
const rootMatcher = buildGitignoreMatcher(rootGitignorePath, diagnostics);
|
|
498
|
+
const nested = findNestedGitignoreFiles(this.rootDir, rootGitignorePath)
|
|
499
|
+
.map((path) => ({ dir: dirname(path), matcher: buildGitignoreMatcher(path, diagnostics) }))
|
|
500
|
+
.filter((entry) => entry.matcher !== null);
|
|
501
|
+
return (absPath, rel) => {
|
|
502
|
+
if (rootMatcher !== null && rootMatcher(rel))
|
|
503
|
+
return true;
|
|
504
|
+
for (const entry of nested) {
|
|
505
|
+
if (absPath.startsWith(entry.dir + sep) && entry.matcher(relative(entry.dir, absPath))) {
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return false;
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Index one file's content. Short-circuits on an unchanged file_hash (no
|
|
514
|
+
* re-chunk, no re-embed) — the "content_hash stable across re-index of
|
|
515
|
+
* unchanged file" contract — unless forceReembed is set (provider mismatch).
|
|
516
|
+
*
|
|
517
|
+
* Epoch honesty: all of a file's chunks are embedded (every await) BEFORE
|
|
518
|
+
* any row is deleted or written, and the epoch is re-checked after each
|
|
519
|
+
* await — so a reroot() mid-file aborts with the database untouched for
|
|
520
|
+
* that file (returns null; the caller abandons the build).
|
|
521
|
+
*/
|
|
522
|
+
async indexFileContent(relPath, absPath, content, mtimeMs, callEpoch, forceReembed) {
|
|
523
|
+
if (!this.db)
|
|
524
|
+
return null;
|
|
525
|
+
const fileHash = sha256(content);
|
|
526
|
+
if (!forceReembed) {
|
|
527
|
+
const existingHash = getFileHash(this.db, relPath);
|
|
528
|
+
if (existingHash === fileHash) {
|
|
529
|
+
return { indexed: true, mode: 'unchanged', chunkCount: countChunksForPath(this.db, relPath) };
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
await this.ensureIntelligenceReady();
|
|
533
|
+
if (this.epoch !== callEpoch)
|
|
534
|
+
return null;
|
|
535
|
+
const { drafts, mode } = await chunkFileContent({
|
|
536
|
+
intelligence: this.intelligence,
|
|
537
|
+
absPath,
|
|
538
|
+
relPath,
|
|
539
|
+
content,
|
|
540
|
+
fileHash,
|
|
541
|
+
mtimeMs,
|
|
542
|
+
windowLines: this.options.windowLines,
|
|
543
|
+
windowOverlapLines: this.options.windowOverlapLines,
|
|
544
|
+
});
|
|
545
|
+
if (this.epoch !== callEpoch)
|
|
546
|
+
return null;
|
|
547
|
+
const embeddings = [];
|
|
548
|
+
for (const draft of drafts) {
|
|
549
|
+
const embedding = await this.embedChunkAsync(draft.embedText);
|
|
550
|
+
if (this.epoch !== callEpoch)
|
|
551
|
+
return null;
|
|
552
|
+
embeddings.push(embedding);
|
|
553
|
+
}
|
|
554
|
+
// All awaits are done — the delete+write below is synchronous, so the
|
|
555
|
+
// whole file lands atomically with respect to reroot()/close().
|
|
556
|
+
if (!this.db)
|
|
557
|
+
return null;
|
|
558
|
+
deleteChunksForPath(this.db, relPath);
|
|
559
|
+
for (let i = 0; i < drafts.length; i++) {
|
|
560
|
+
writeChunk(this.db, drafts[i].chunk, embeddings[i]);
|
|
561
|
+
}
|
|
562
|
+
return { indexed: drafts.length > 0 || mode === 'empty', mode, chunkCount: drafts.length };
|
|
563
|
+
}
|
|
564
|
+
async ensureIntelligenceReady() {
|
|
565
|
+
if (!this.intelligenceReadyPromise) {
|
|
566
|
+
this.intelligenceReadyPromise = this.intelligence.initialize().catch((err) => {
|
|
567
|
+
logger.warn('CodeIndexStore: tree-sitter init failed', { error: summarizeError(err) });
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return this.intelligenceReadyPromise;
|
|
571
|
+
}
|
|
572
|
+
async embedChunkAsync(text) {
|
|
573
|
+
const result = await this.embeddingRegistry.embedAsync({
|
|
574
|
+
text,
|
|
575
|
+
dimensions: MEMORY_VECTOR_DIMS,
|
|
576
|
+
usage: 'record',
|
|
577
|
+
});
|
|
578
|
+
return normalizeMemoryEmbeddingVector(result.vector, MEMORY_VECTOR_DIMS);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function abortedBuildStats(startedAt, filesScanned, skip) {
|
|
582
|
+
const completedAt = Date.now();
|
|
583
|
+
logger.warn('Code index build aborted by reroot', { filesScanned });
|
|
584
|
+
return {
|
|
585
|
+
filesScanned,
|
|
586
|
+
filesIndexed: 0,
|
|
587
|
+
filesUnchanged: 0,
|
|
588
|
+
chunksIndexed: 0,
|
|
589
|
+
chunksUnchanged: 0,
|
|
590
|
+
filesRemoved: 0,
|
|
591
|
+
skip: Object.freeze({ ...skip }),
|
|
592
|
+
startedAt,
|
|
593
|
+
completedAt,
|
|
594
|
+
durationMs: completedAt - startedAt,
|
|
595
|
+
abortReason: BUILD_ABORTED_BY_REROOT,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function emptyBuildStats() {
|
|
599
|
+
const now = Date.now();
|
|
600
|
+
return {
|
|
601
|
+
filesScanned: 0,
|
|
602
|
+
filesIndexed: 0,
|
|
603
|
+
filesUnchanged: 0,
|
|
604
|
+
chunksIndexed: 0,
|
|
605
|
+
chunksUnchanged: 0,
|
|
606
|
+
filesRemoved: 0,
|
|
607
|
+
skip: emptySkipReport(),
|
|
608
|
+
startedAt: now,
|
|
609
|
+
completedAt: now,
|
|
610
|
+
durationMs: 0,
|
|
611
|
+
};
|
|
612
|
+
}
|
|
@@ -24,6 +24,8 @@ export type { MemoryVectorCandidate, MemoryVectorStats } from './memory-vector-s
|
|
|
24
24
|
export { DEFAULT_MEMORY_EMBEDDING_DIMS, HASHED_MEMORY_EMBEDDING_PROVIDER, MemoryEmbeddingProviderRegistry, normalizeMemoryEmbeddingVector, } from './memory-embeddings.js';
|
|
25
25
|
export { createBuiltinMemoryEmbeddingProviders } from './memory-embedding-http.js';
|
|
26
26
|
export type { MemoryEmbeddingDoctorReport, MemoryEmbeddingProvider, MemoryEmbeddingProviderState, MemoryEmbeddingProviderStatus, MemoryEmbeddingRequest, MemoryEmbeddingResult, MemoryEmbeddingUsage, } from './memory-embeddings.js';
|
|
27
|
-
export type { KnowledgeInjection } from './knowledge-injection.js';
|
|
28
|
-
export { selectKnowledgeForTask, buildKnowledgeInjectionPrompt } from './knowledge-injection.js';
|
|
27
|
+
export type { KnowledgeInjection, ScoredKnowledgeInjection } from './knowledge-injection.js';
|
|
28
|
+
export { selectKnowledgeForTask, selectKnowledgeForTaskScored, buildKnowledgeInjectionPrompt } from './knowledge-injection.js';
|
|
29
|
+
export { CodeIndexStore } from './code-index-store.js';
|
|
30
|
+
export type { CodeChunk, CodeChunkMode, CodeContextResult, CodeIndexBuildProgress, CodeIndexBuildStats, CodeIndexOptions, CodeIndexSkipReport, CodeIndexStats, } from './code-index-store.js';
|
|
29
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/platform/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,6BAA6B,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,oCAAoC,GACrC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACzF,OAAO,EACL,6BAA6B,EAC7B,gCAAgC,EAChC,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qCAAqC,EAAE,MAAM,4BAA4B,CAAC;AACnF,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/platform/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,6BAA6B,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,oCAAoC,GACrC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACzF,OAAO,EACL,6BAA6B,EAC7B,gCAAgC,EAChC,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qCAAqC,EAAE,MAAM,4BAA4B,CAAC;AACnF,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAC/H,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACf,MAAM,uBAAuB,CAAC"}
|
|
@@ -15,4 +15,5 @@ export { buildIncidentMemoryAddOptions, buildMcpSecurityMemoryAddOptions, buildP
|
|
|
15
15
|
export { MEMORY_VECTOR_DIMS, embedMemoryText, resolveMemoryVectorDbPath, SqliteVecMemoryIndex, } from './memory-vector-store.js';
|
|
16
16
|
export { DEFAULT_MEMORY_EMBEDDING_DIMS, HASHED_MEMORY_EMBEDDING_PROVIDER, MemoryEmbeddingProviderRegistry, normalizeMemoryEmbeddingVector, } from './memory-embeddings.js';
|
|
17
17
|
export { createBuiltinMemoryEmbeddingProviders } from './memory-embedding-http.js';
|
|
18
|
-
export { selectKnowledgeForTask, buildKnowledgeInjectionPrompt } from './knowledge-injection.js';
|
|
18
|
+
export { selectKnowledgeForTask, selectKnowledgeForTaskScored, buildKnowledgeInjectionPrompt } from './knowledge-injection.js';
|
|
19
|
+
export { CodeIndexStore } from './code-index-store.js';
|
|
@@ -18,6 +18,29 @@ type KnowledgeRegistrySource = {
|
|
|
18
18
|
getAll(): readonly MemoryRecord[];
|
|
19
19
|
searchSemantic?(input: Parameters<MemoryRegistry['searchSemantic']>[0]): readonly MemorySemanticSearchResult[];
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* One scored candidate from the ranking pipeline: the fully-built
|
|
23
|
+
* `KnowledgeInjection` plus the numeric score that placed it, before any
|
|
24
|
+
* `limit` slice is applied. Exists so callers other than the spawn-time
|
|
25
|
+
* baseline (e.g. per-turn retrieval in turn-knowledge-injection.ts) can
|
|
26
|
+
* apply their own relevance floor / budget trim over the SAME ranked list
|
|
27
|
+
* without re-implementing scoreKnowledge/determineReason/determineIngestMode.
|
|
28
|
+
*/
|
|
29
|
+
export interface ScoredKnowledgeInjection {
|
|
30
|
+
readonly injection: KnowledgeInjection;
|
|
31
|
+
readonly score: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Full ranking pipeline, unsliced: scores every registry record that clears
|
|
35
|
+
* the confidence>=55 gate against `task`/`writeScope`, folds in semantic
|
|
36
|
+
* similarity when the registry supports it, and returns every candidate with
|
|
37
|
+
* score>0 sorted best-first. `limit` only widens the semantic-search
|
|
38
|
+
* candidate pool (`Math.max(limit*4,12)`, mirroring the historical
|
|
39
|
+
* behavior) — it does NOT slice the returned array. Callers that want the
|
|
40
|
+
* spawn-time top-N behavior should slice the result themselves; see
|
|
41
|
+
* `selectKnowledgeForTask` below.
|
|
42
|
+
*/
|
|
43
|
+
export declare function selectKnowledgeForTaskScored(registry: KnowledgeRegistrySource, task: string, writeScope?: readonly string[], limit?: number): ScoredKnowledgeInjection[];
|
|
21
44
|
export declare function selectKnowledgeForTask(registry: KnowledgeRegistrySource, task: string, writeScope?: readonly string[], limit?: number): KnowledgeInjection[];
|
|
22
45
|
export declare function buildKnowledgeInjectionPrompt(injections: readonly KnowledgeInjectionPromptInput[]): string | null;
|
|
23
46
|
export {};
|