{"version":3,"file":"vectors.d.ts","sourceRoot":"","sources":["../../../src/core/workspace/vectors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,WAAW;IACX,OAAO,CAAC,EAAE;IAAtB,YAAoB,EAAE,EAAE,WAAW,EAAI;IAEvC,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAInB,KAAK,CACJ,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EAAE,GACd,IAAI,CAMN;IAED,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAInE;IAED,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAKvF;IAED,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAIlC;IAED,4DAA4D;IAC5D,MAAM,CACL,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;KAAO,GAC9E,WAAW,EAAE,CA0Bf;CACD;AAeD,wBAAgB,2BAA2B,SAE1C","sourcesContent":["/**\n * Vector storage and deterministic nearest-neighbor retrieval.\n *\n * Default backend is an exact, deterministic cosine-similarity scan over stored\n * vectors. This is ample for workspace-scale bounded candidate sets and is\n * deterministic for tests. A `VectorBackend` interface allows swapping in an ANN\n * implementation without changing retrieval semantics.\n */\n\nimport type { WorkspaceDb } from \"./storage.js\";\n\nexport interface VectorRecord {\n\tchunkId: string;\n\tcontentHash: string;\n\tmodelId: string;\n\tdimensions: number;\n\tvector: Float32Array;\n}\n\nexport interface SemanticHit {\n\tchunkId: string;\n\tscore: number;\n}\n\nexport class VectorIndex {\n\tconstructor(private db: WorkspaceDb) {}\n\n\tprivate serialize(v: number[]): Buffer {\n\t\treturn Buffer.from(new Float32Array(v).buffer);\n\t}\n\n\tprivate deserialize(buf: Buffer): Float32Array {\n\t\treturn new Float32Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));\n\t}\n\n\tstore(\n\t\tgenerationId: string,\n\t\tchunkId: string,\n\t\tcontentHash: string,\n\t\tmodelId: string,\n\t\tdimensions: number,\n\t\tvector: number[],\n\t): void {\n\t\tthis.db.db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT OR REPLACE INTO vectors (generation_id, chunk_id, content_hash, model_id, dimensions, embedding) VALUES (?, ?, ?, ?, ?, ?)\",\n\t\t\t)\n\t\t\t.run(generationId, chunkId, contentHash, modelId, dimensions, this.serialize(vector));\n\t}\n\n\tdelete(generationId: string, chunkId: string, modelId: string): void {\n\t\tthis.db.db\n\t\t\t.prepare(\"DELETE FROM vectors WHERE generation_id = ? AND chunk_id = ? AND model_id = ?\")\n\t\t\t.run(generationId, chunkId, modelId);\n\t}\n\n\tmodelIdentity(generationId: string): { modelId: string; dimensions: number } | undefined {\n\t\tconst row = this.db.db\n\t\t\t.prepare(\"SELECT model_id, dimensions FROM vectors WHERE generation_id = ? LIMIT 1\")\n\t\t\t.get(generationId) as { model_id: string; dimensions: number } | undefined;\n\t\treturn row ? { modelId: row.model_id, dimensions: row.dimensions } : undefined;\n\t}\n\n\tcount(generationId: string): number {\n\t\treturn (\n\t\t\tthis.db.db.prepare(\"SELECT COUNT(*) c FROM vectors WHERE generation_id = ?\").get(generationId) as { c: number }\n\t\t).c;\n\t}\n\n\t/** Exact cosine nearest-neighbor search (deterministic). */\n\tsearch(\n\t\tgenerationId: string,\n\t\tquery: number[],\n\t\topts: { limit?: number; modelId?: string; candidateChunkIds?: Set<string> } = {},\n\t): SemanticHit[] {\n\t\tconst limit = Math.max(1, opts.limit ?? 50);\n\t\tconst q = normalize(query);\n\t\tconst modelFilter = opts.modelId;\n\t\tconst rows = this.db.db\n\t\t\t.prepare(\n\t\t\t\tmodelFilter\n\t\t\t\t\t? \"SELECT chunk_id, content_hash, embedding FROM vectors WHERE generation_id = ? AND model_id = ?\"\n\t\t\t\t\t: \"SELECT chunk_id, content_hash, embedding FROM vectors WHERE generation_id = ?\",\n\t\t\t)\n\t\t\t.all(generationId, ...(modelFilter ? [modelFilter] : [])) as Array<{\n\t\t\tchunk_id: string;\n\t\t\tcontent_hash: Uint8Array;\n\t\t\tembedding: Uint8Array;\n\t\t}>;\n\n\t\tconst scores: SemanticHit[] = [];\n\t\tfor (const r of rows) {\n\t\t\tif (opts.candidateChunkIds && !opts.candidateChunkIds.has(r.chunk_id)) continue;\n\t\t\tconst vec = this.deserialize(Buffer.from(r.embedding));\n\t\t\tif (vec.length !== q.length) continue; // dimension mismatch → skip\n\t\t\tconst sim = cosine(q, vec);\n\t\t\tscores.push({ chunkId: r.chunk_id, score: sim });\n\t\t}\n\t\tscores.sort((a, b) => b.score - a.score || a.chunkId.localeCompare(b.chunkId));\n\t\treturn scores.slice(0, limit);\n\t}\n}\n\nfunction normalize(v: number[]): Float32Array {\n\tconst out = new Float32Array(v);\n\tconst norm = Math.sqrt([...out].reduce((a, b) => a + b * b, 0)) || 1;\n\tfor (let i = 0; i < out.length; i++) out[i] /= norm;\n\treturn out;\n}\n\nfunction cosine(a: Float32Array, b: Float32Array): number {\n\tlet dot = 0;\n\tfor (let i = 0; i < a.length; i++) dot += a[i] * b[i];\n\treturn dot;\n}\n\nexport function serializeVectorIndexBackend() {\n\t// Marker function documenting the pluggable backend seam.\n}\n"]}