@remnic/coding-graph 9.3.759
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/README.md +130 -0
- package/dist/chunk-5I2DBHOQ.js +1042 -0
- package/dist/chunk-5I2DBHOQ.js.map +1 -0
- package/dist/chunk-CPYJACC5.js +1838 -0
- package/dist/chunk-CPYJACC5.js.map +1 -0
- package/dist/chunk-ZVCMIM4T.js +216 -0
- package/dist/chunk-ZVCMIM4T.js.map +1 -0
- package/dist/cypher/query-parser.d.ts +253 -0
- package/dist/cypher/query-parser.js +17 -0
- package/dist/cypher/query-parser.js.map +1 -0
- package/dist/graph-schema.d.ts +84 -0
- package/dist/graph-schema.js +17 -0
- package/dist/graph-schema.js.map +1 -0
- package/dist/graph-store.d.ts +938 -0
- package/dist/graph-store.js +16 -0
- package/dist/graph-store.js.map +1 -0
- package/dist/index.d.ts +1953 -0
- package/dist/index.js +3509 -0
- package/dist/index.js.map +1 -0
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +79 -0
- package/src/co-change.test.ts +175 -0
- package/src/co-change.ts +167 -0
- package/src/cypher/query-parser.test.ts +1107 -0
- package/src/cypher/query-parser.ts +1692 -0
- package/src/detect-changes.test.ts +533 -0
- package/src/detect-changes.ts +367 -0
- package/src/engine/emit.ts +556 -0
- package/src/engine/engine.test.ts +1417 -0
- package/src/engine/engine.ts +182 -0
- package/src/engine/extractors.ts +486 -0
- package/src/engine/fixtures.ts +364 -0
- package/src/engine/language-sniff.ts +56 -0
- package/src/engine/parser-backend.ts +206 -0
- package/src/engine/utf16-offsets.ts +68 -0
- package/src/git-invoker.test.ts +116 -0
- package/src/git-invoker.ts +426 -0
- package/src/graph-schema.test.ts +541 -0
- package/src/graph-schema.ts +383 -0
- package/src/graph-store-pr2.test.ts +1879 -0
- package/src/graph-store.test.ts +1420 -0
- package/src/graph-store.ts +3489 -0
- package/src/index-status.test.ts +303 -0
- package/src/index-status.ts +135 -0
- package/src/index.ts +384 -0
- package/src/lsp/byte-position.ts +173 -0
- package/src/lsp/characterization.test.ts +174 -0
- package/src/lsp/client.test.ts +275 -0
- package/src/lsp/client.ts +484 -0
- package/src/lsp/config.ts +219 -0
- package/src/lsp/degradation.ts +86 -0
- package/src/lsp/fixtures/fake-server.mjs +198 -0
- package/src/lsp/framing.test.ts +180 -0
- package/src/lsp/framing.ts +177 -0
- package/src/lsp/resolution.test.ts +497 -0
- package/src/lsp/resolution.ts +483 -0
- package/src/lsp/status.ts +140 -0
- package/src/lsp/types.ts +167 -0
- package/src/reindex.test.ts +1038 -0
- package/src/reindex.ts +908 -0
- package/src/row-types.ts +45 -0
- package/src/semantic/canonical-text.test.ts +150 -0
- package/src/semantic/canonical-text.ts +219 -0
- package/src/semantic/config.ts +235 -0
- package/src/semantic/index.ts +78 -0
- package/src/semantic/minhash.test.ts +197 -0
- package/src/semantic/minhash.ts +261 -0
- package/src/semantic/semantic-query.ts +173 -0
- package/src/semantic/semantic.test.ts +1315 -0
- package/src/semantic/similarity.ts +268 -0
- package/src/semantic/types.ts +145 -0
- package/src/semantic/vectors.ts +235 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* semantic_query — natural-language retrieval over the symbol graph
|
|
3
|
+
* (issue #1556 PR3 component).
|
|
4
|
+
*
|
|
5
|
+
* Embed the query via the host provider / EmbeddingFallback
|
|
6
|
+
* (`mode: "lookup"`), top-k symbols by cosine, hydrate each hit with
|
|
7
|
+
* graph context (defining file, direct callers/callees).
|
|
8
|
+
*
|
|
9
|
+
* Rule 34 — degradation matrix. Provider missing / timeout / malformed
|
|
10
|
+
* vector (`normalizeHostEmbeddingVector` returns null) → three distinct
|
|
11
|
+
* `{ok:false}` codes, never an empty result masquerading as "no matches".
|
|
12
|
+
* When the provider is available but returns zero hits, `ok:true` with
|
|
13
|
+
* empty hits is the honest answer.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
import type { HostEmbeddingProvider } from "@remnic/core/host-embedding-provider";
|
|
18
|
+
import {
|
|
19
|
+
EmbeddingProviderUnavailableError,
|
|
20
|
+
EmbeddingTimeoutError,
|
|
21
|
+
} from "@remnic/core/embedding-fallback";
|
|
22
|
+
import { normalizeHostEmbeddingVector } from "@remnic/core/host-embedding-provider";
|
|
23
|
+
|
|
24
|
+
import type { GraphStore } from "../graph-store.js";
|
|
25
|
+
import type { SemanticConfig } from "./config.js";
|
|
26
|
+
import { cosineSimilarity } from "./minhash.js";
|
|
27
|
+
import { modelIdFor } from "./vectors.js";
|
|
28
|
+
import type { SemanticQueryHit, SemanticQueryOutcome } from "./types.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Input to {@link semanticQuery}.
|
|
32
|
+
*/
|
|
33
|
+
export interface SemanticQueryInput {
|
|
34
|
+
readonly store: GraphStore;
|
|
35
|
+
readonly provider: HostEmbeddingProvider | undefined;
|
|
36
|
+
readonly repoRoot: string;
|
|
37
|
+
readonly config: SemanticConfig;
|
|
38
|
+
readonly query: string;
|
|
39
|
+
readonly limit?: number;
|
|
40
|
+
readonly signal?: AbortSignal;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Default top-k for semantic_query.
|
|
45
|
+
*/
|
|
46
|
+
export const DEFAULT_SEMANTIC_QUERY_LIMIT = 10;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run a semantic query: embed → top-k → hydrate.
|
|
50
|
+
*
|
|
51
|
+
* Degradation matrix (rule 34):
|
|
52
|
+
* - !config.enabled → { ok:false, code:"semantic_disabled" }
|
|
53
|
+
* - no provider → { ok:false, code:"provider_unavailable" }
|
|
54
|
+
* - provider throws timeout → { ok:false, code:"provider_timeout" }
|
|
55
|
+
* - provider returns null/malformed → { ok:false, code:"malformed_vector" }
|
|
56
|
+
* - no vectors in table → { ok:false, code:"no_vectors" }
|
|
57
|
+
* - ok but zero hits → { ok:true, hits:[] }
|
|
58
|
+
*/
|
|
59
|
+
export async function semanticQuery(input: SemanticQueryInput): Promise<SemanticQueryOutcome> {
|
|
60
|
+
const { store, provider, repoRoot, config, query, signal } = input;
|
|
61
|
+
// Closed store is a distinct degradation (rule 34) — do not treat it as
|
|
62
|
+
// an empty vectors table that returns 'no_vectors' (cursor Bugbot:
|
|
63
|
+
// 'Closed store reports success').
|
|
64
|
+
if (store.isClosed) {
|
|
65
|
+
return { ok: false, code: "store_closed" };
|
|
66
|
+
}
|
|
67
|
+
if (!config.enabled) {
|
|
68
|
+
return { ok: false, code: "semantic_disabled" };
|
|
69
|
+
}
|
|
70
|
+
if (query.length === 0) {
|
|
71
|
+
return { ok: false, code: "invalid_query", message: "query must be non-empty" };
|
|
72
|
+
}
|
|
73
|
+
if (!provider) {
|
|
74
|
+
return { ok: false, code: "provider_unavailable" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Embed the query (lookup mode — short timeout budget).
|
|
78
|
+
let raw: ArrayLike<number> | null;
|
|
79
|
+
try {
|
|
80
|
+
raw = await provider.embed(query, { signal, inputType: "query" });
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error instanceof EmbeddingTimeoutError) {
|
|
83
|
+
return { ok: false, code: "provider_timeout" };
|
|
84
|
+
}
|
|
85
|
+
if (error instanceof EmbeddingProviderUnavailableError) {
|
|
86
|
+
return { ok: false, code: "provider_unavailable" };
|
|
87
|
+
}
|
|
88
|
+
// Unknown provider error → treat as unavailable (the provider is broken).
|
|
89
|
+
// Do NOT include the raw error message (may leak paths/stacks to clients).
|
|
90
|
+
return { ok: false, code: "provider_unavailable" };
|
|
91
|
+
}
|
|
92
|
+
const queryVec = normalizeHostEmbeddingVector(raw);
|
|
93
|
+
if (!queryVec || queryVec.length === 0) {
|
|
94
|
+
return { ok: false, code: "malformed_vector" };
|
|
95
|
+
}
|
|
96
|
+
const queryF32 = new Float32Array(queryVec);
|
|
97
|
+
|
|
98
|
+
// Brute-force cosine over all vectors for this model.
|
|
99
|
+
const modelId = modelIdFor(provider);
|
|
100
|
+
// Wrap the store read so a transient SQLite error (SQLITE_BUSY / CORRUPT)
|
|
101
|
+
// maps to a tagged db_error instead of escaping semanticQuery (#1680).
|
|
102
|
+
let rows: readonly ReturnType<typeof store.readAllSymbolVectors>[number][];
|
|
103
|
+
try {
|
|
104
|
+
rows = store.readAllSymbolVectors(modelId);
|
|
105
|
+
} catch {
|
|
106
|
+
return { ok: false, code: "db_error" };
|
|
107
|
+
}
|
|
108
|
+
if (rows.length === 0) {
|
|
109
|
+
return { ok: false, code: "no_vectors" };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Validate limit: NaN/Infinity/non-numeric must not produce a NaN/Infinity
|
|
113
|
+
// slice (#1680). NaN → slice(0, NaN) returns no hits; Infinity → every row.
|
|
114
|
+
// Coerce to a finite positive integer, falling back to the default.
|
|
115
|
+
const rawLimit = input.limit;
|
|
116
|
+
const limit = (typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0)
|
|
117
|
+
? Math.max(1, Math.floor(rawLimit))
|
|
118
|
+
: DEFAULT_SEMANTIC_QUERY_LIMIT;
|
|
119
|
+
// Only score vectors whose dimensionality matches the query embedding.
|
|
120
|
+
// cosineSimilarity compares over the SHORTER length, so a row from a
|
|
121
|
+
// different model size would otherwise get a misleading partial-overlap
|
|
122
|
+
// score and rank incorrectly instead of being excluded (cursor Bugbot:
|
|
123
|
+
// 'Mismatched embedding lengths scored').
|
|
124
|
+
const queryDims = queryF32.length;
|
|
125
|
+
const scored = rows
|
|
126
|
+
.filter((r) => r.dims === queryDims)
|
|
127
|
+
.map((r) => ({
|
|
128
|
+
nodeId: r.nodeId,
|
|
129
|
+
qualifiedName: r.qualifiedName,
|
|
130
|
+
filePath: r.filePath,
|
|
131
|
+
kind: r.kind,
|
|
132
|
+
dims: r.dims,
|
|
133
|
+
score: cosineSimilarity(queryF32, r.vector),
|
|
134
|
+
}))
|
|
135
|
+
.filter((r) => Number.isFinite(r.score))
|
|
136
|
+
.sort((a, b) => b.score - a.score)
|
|
137
|
+
.slice(0, limit);
|
|
138
|
+
|
|
139
|
+
// Hydrate each hit with graph context (callers/callees + snippet).
|
|
140
|
+
const hits: SemanticQueryHit[] = [];
|
|
141
|
+
for (const h of scored) {
|
|
142
|
+
const neighbors = store.readNeighborsByNodeId(h.nodeId);
|
|
143
|
+
let snippet = "";
|
|
144
|
+
try {
|
|
145
|
+
// Hydrate by the exact node id from the vector row, not the
|
|
146
|
+
// qualified name — a name duplicated across files would otherwise
|
|
147
|
+
// hit 'ambiguous_name' and leave the snippet empty even though the
|
|
148
|
+
// vector row identified the precise node (chatgpt-codex-connector
|
|
149
|
+
// P2: 'Hydrate snippets by node id as well').
|
|
150
|
+
const snippetResult = await store.snippetFor({ nodeId: h.nodeId, repoRoot });
|
|
151
|
+
if (snippetResult.ok) snippet = snippetResult.text;
|
|
152
|
+
} catch {
|
|
153
|
+
// snippetFor returns tagged failures, not throws — this is defensive.
|
|
154
|
+
}
|
|
155
|
+
hits.push({
|
|
156
|
+
qualifiedName: h.qualifiedName,
|
|
157
|
+
filePath: h.filePath,
|
|
158
|
+
kind: h.kind,
|
|
159
|
+
score: h.score,
|
|
160
|
+
snippet,
|
|
161
|
+
callers: neighbors.callers,
|
|
162
|
+
callees: neighbors.callees,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { ok: true, hits };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Read a short snippet (first ~20 lines) from a file for hydration. The
|
|
171
|
+
* full source span is available via store.snippetFor; here we just want
|
|
172
|
+
* enough context for the agent to orient.
|
|
173
|
+
*/
|