@vibgrate/cli 2026.803.3 → 2026.803.4
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/agent-WYBWUBCC.js +26 -0
- package/dist/{agent-JTA4YWMQ.js.map → agent-WYBWUBCC.js.map} +1 -1
- package/dist/baseline-3HECX4Q6.js +9 -0
- package/dist/{baseline-2V7PKA7Q.js.map → baseline-3HECX4Q6.js.map} +1 -1
- package/dist/{chunk-TNADGCOX.js → chunk-23UPIFRU.js} +8 -8
- package/dist/{chunk-TNADGCOX.js.map → chunk-23UPIFRU.js.map} +1 -1
- package/dist/chunk-2A7XCANX.js +375 -0
- package/dist/chunk-2A7XCANX.js.map +1 -0
- package/dist/{chunk-QMME6TB4.js → chunk-4LGDQBYK.js} +3 -102
- package/dist/chunk-4LGDQBYK.js.map +1 -0
- package/dist/{chunk-L5PUNO3T.js → chunk-4ZMJKR74.js} +451 -22
- package/dist/chunk-4ZMJKR74.js.map +1 -0
- package/dist/{chunk-CPLG4GFB.js → chunk-537JBQVB.js} +4 -4
- package/dist/{chunk-CPLG4GFB.js.map → chunk-537JBQVB.js.map} +1 -1
- package/dist/{chunk-NUZM7UOR.js → chunk-5TS7N7J2.js} +59 -9
- package/dist/chunk-5TS7N7J2.js.map +1 -0
- package/dist/{chunk-XK5LKOBF.js → chunk-5WMFX7TY.js} +4 -4
- package/dist/{chunk-XK5LKOBF.js.map → chunk-5WMFX7TY.js.map} +1 -1
- package/dist/{chunk-M452MRWI.js → chunk-66B4ZQA3.js} +9 -8
- package/dist/chunk-66B4ZQA3.js.map +1 -0
- package/dist/{chunk-WGPS3NZQ.js → chunk-7BU7CWXC.js} +33 -11
- package/dist/chunk-7BU7CWXC.js.map +1 -0
- package/dist/{chunk-3Q3ZXJZP.js → chunk-7TOIESUT.js} +5 -4
- package/dist/chunk-7TOIESUT.js.map +1 -0
- package/dist/{chunk-4ACXDVON.js → chunk-BRUMBQV7.js} +4 -4
- package/dist/{chunk-4ACXDVON.js.map → chunk-BRUMBQV7.js.map} +1 -1
- package/dist/{chunk-M7773OUN.js → chunk-BUCNKYTW.js} +5 -5
- package/dist/{chunk-M7773OUN.js.map → chunk-BUCNKYTW.js.map} +1 -1
- package/dist/{chunk-NSWEJPLV.js → chunk-FAA7RBIG.js} +9 -9
- package/dist/{chunk-NSWEJPLV.js.map → chunk-FAA7RBIG.js.map} +1 -1
- package/dist/chunk-FZ5Y4KEI.js +6 -0
- package/dist/{chunk-7KNFG44U.js.map → chunk-FZ5Y4KEI.js.map} +1 -1
- package/dist/{chunk-UXR7FBE3.js → chunk-HBQC4XQJ.js} +6 -6
- package/dist/{chunk-UXR7FBE3.js.map → chunk-HBQC4XQJ.js.map} +1 -1
- package/dist/{chunk-KYX2YCXT.js → chunk-HVHCXM2E.js} +9 -9
- package/dist/{chunk-KYX2YCXT.js.map → chunk-HVHCXM2E.js.map} +1 -1
- package/dist/{chunk-NJFQCDGA.js → chunk-INIULQEP.js} +3 -3
- package/dist/{chunk-NJFQCDGA.js.map → chunk-INIULQEP.js.map} +1 -1
- package/dist/{chunk-K6Q6HTFD.js → chunk-JZGVUH57.js} +4 -4
- package/dist/{chunk-K6Q6HTFD.js.map → chunk-JZGVUH57.js.map} +1 -1
- package/dist/{chunk-TM5XQY3L.js → chunk-KFK7KCAH.js} +3 -3
- package/dist/{chunk-TM5XQY3L.js.map → chunk-KFK7KCAH.js.map} +1 -1
- package/dist/{chunk-6DFV4WCQ.js → chunk-OVDL76LB.js} +3 -3
- package/dist/{chunk-6DFV4WCQ.js.map → chunk-OVDL76LB.js.map} +1 -1
- package/dist/{chunk-SJFBXCII.js → chunk-QUI3E3TQ.js} +54 -9
- package/dist/chunk-QUI3E3TQ.js.map +1 -0
- package/dist/chunk-RBMKBYGV.js +102 -0
- package/dist/chunk-RBMKBYGV.js.map +1 -0
- package/dist/{chunk-HC44772H.js → chunk-TVRM57CE.js} +5 -373
- package/dist/chunk-TVRM57CE.js.map +1 -0
- package/dist/{chunk-PXM3VZYR.js → chunk-UWVFLVZX.js} +4 -4
- package/dist/{chunk-PXM3VZYR.js.map → chunk-UWVFLVZX.js.map} +1 -1
- package/dist/{chunk-6JP7HGIF.js → chunk-Y4JO6LYP.js} +3 -3
- package/dist/{chunk-6JP7HGIF.js.map → chunk-Y4JO6LYP.js.map} +1 -1
- package/dist/cli.js +489 -337
- package/dist/cli.js.map +1 -1
- package/dist/embed-worker-main.d.ts +2 -0
- package/dist/embed-worker-main.js +64 -0
- package/dist/embed-worker-main.js.map +1 -0
- package/dist/ensure-map-DZ53QXRT.js +31 -0
- package/dist/{ensure-map-TAVYMPEG.js.map → ensure-map-DZ53QXRT.js.map} +1 -1
- package/dist/graph-backend-3A2M22QN.js +13 -0
- package/dist/{graph-backend-WF4LGBGD.js.map → graph-backend-3A2M22QN.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/{interactive-6PN237LX.js → interactive-DB7IC3BL.js} +31 -29
- package/dist/interactive-DB7IC3BL.js.map +1 -0
- package/dist/load-6KVGUQMU.js +11 -0
- package/dist/{load-T2FOJUPI.js.map → load-6KVGUQMU.js.map} +1 -1
- package/dist/{run-outcome-AMIKAASL.js → run-outcome-YOYRC32U.js} +7 -6
- package/dist/run-outcome-YOYRC32U.js.map +1 -0
- package/dist/runtime-session-XEP7IPGP.js +22 -0
- package/dist/{runtime-session-QAVOPSSH.js.map → runtime-session-XEP7IPGP.js.map} +1 -1
- package/dist/session-5WKEACIF.js +17 -0
- package/dist/{session-SCHQQTHN.js.map → session-5WKEACIF.js.map} +1 -1
- package/dist/{stream-json-A6K2MONY.js → stream-json-3OJCPBWY.js} +13 -11
- package/dist/stream-json-3OJCPBWY.js.map +1 -0
- package/dist/{vg-mcp-bridge-3TSFOL4V.js → vg-mcp-bridge-PM3C4FQL.js} +12 -10
- package/dist/vg-mcp-bridge-PM3C4FQL.js.map +1 -0
- package/dist/{vgd-B67JCNJP.js → vgd-UTLMMPIZ.js} +12 -10
- package/dist/vgd-UTLMMPIZ.js.map +1 -0
- package/package.json +1 -1
- package/scripts/postinstall.mjs +22 -1
- package/dist/agent-JTA4YWMQ.js +0 -24
- package/dist/baseline-2V7PKA7Q.js +0 -9
- package/dist/chunk-3Q3ZXJZP.js.map +0 -1
- package/dist/chunk-7KNFG44U.js +0 -6
- package/dist/chunk-HC44772H.js.map +0 -1
- package/dist/chunk-L5PUNO3T.js.map +0 -1
- package/dist/chunk-M452MRWI.js.map +0 -1
- package/dist/chunk-NUZM7UOR.js.map +0 -1
- package/dist/chunk-QMME6TB4.js.map +0 -1
- package/dist/chunk-SJFBXCII.js.map +0 -1
- package/dist/chunk-WGPS3NZQ.js.map +0 -1
- package/dist/ensure-map-TAVYMPEG.js +0 -29
- package/dist/graph-backend-WF4LGBGD.js +0 -11
- package/dist/interactive-6PN237LX.js.map +0 -1
- package/dist/load-T2FOJUPI.js +0 -10
- package/dist/run-outcome-AMIKAASL.js.map +0 -1
- package/dist/runtime-session-QAVOPSSH.js +0 -20
- package/dist/session-SCHQQTHN.js +0 -15
- package/dist/stream-json-A6K2MONY.js.map +0 -1
- package/dist/vg-mcp-bridge-3TSFOL4V.js.map +0 -1
- package/dist/vgd-B67JCNJP.js.map +0 -1
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import * as fs2 from 'fs';
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
|
-
import * as os from 'os';
|
|
6
|
-
import * as path2 from 'path';
|
|
7
|
-
import { pathToFileURL } from 'url';
|
|
1
|
+
import { cosine } from './chunk-2A7XCANX.js';
|
|
2
|
+
import { bigramConsumedTokens, WEAK_TERMS, expandConcepts } from './chunk-4LGDQBYK.js';
|
|
8
3
|
|
|
9
4
|
// src/engine/relations.ts
|
|
10
5
|
var GraphIndex = class {
|
|
@@ -72,369 +67,6 @@ function indexFor(graph) {
|
|
|
72
67
|
}
|
|
73
68
|
return idx;
|
|
74
69
|
}
|
|
75
|
-
var DEFAULT_LOCK_STALE_MS = 15 * 60 * 1e3;
|
|
76
|
-
function isProcessAlive(pid) {
|
|
77
|
-
try {
|
|
78
|
-
process.kill(pid, 0);
|
|
79
|
-
return true;
|
|
80
|
-
} catch (e) {
|
|
81
|
-
return e.code === "EPERM";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function lockIsStale(file, staleMs) {
|
|
85
|
-
try {
|
|
86
|
-
const { pid, at } = JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
87
|
-
if (typeof at === "number" && Date.now() - at > staleMs) return true;
|
|
88
|
-
if (typeof pid === "number" && !isProcessAlive(pid)) return true;
|
|
89
|
-
return false;
|
|
90
|
-
} catch {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
95
|
-
const write = () => {
|
|
96
|
-
try {
|
|
97
|
-
fs2.mkdirSync(path2.dirname(file), { recursive: true });
|
|
98
|
-
const fd = fs2.openSync(file, "wx");
|
|
99
|
-
fs2.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
100
|
-
fs2.closeSync(fd);
|
|
101
|
-
return true;
|
|
102
|
-
} catch {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
if (write()) return true;
|
|
107
|
-
if (lockIsStale(file, staleMs)) {
|
|
108
|
-
try {
|
|
109
|
-
fs2.rmSync(file, { force: true });
|
|
110
|
-
} catch {
|
|
111
|
-
}
|
|
112
|
-
return write();
|
|
113
|
-
}
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
function releaseLock(file) {
|
|
117
|
-
try {
|
|
118
|
-
fs2.rmSync(file, { force: true });
|
|
119
|
-
} catch {
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// src/engine/embeddings.ts
|
|
124
|
-
function resolveEmbedModel(model) {
|
|
125
|
-
return model ?? "bge-small-en-v1.5";
|
|
126
|
-
}
|
|
127
|
-
function modelCacheDir() {
|
|
128
|
-
const base = process.env.XDG_CACHE_HOME || path2.join(os.homedir(), ".cache");
|
|
129
|
-
return path2.join(base, "vibgrate", "models");
|
|
130
|
-
}
|
|
131
|
-
function modelReadyMarker(modelId) {
|
|
132
|
-
return path2.join(modelCacheDir(), `.ready-${safe(modelId)}`);
|
|
133
|
-
}
|
|
134
|
-
function isModelReady(modelId = resolveEmbedModel()) {
|
|
135
|
-
return fs2.existsSync(modelReadyMarker(modelId));
|
|
136
|
-
}
|
|
137
|
-
function unavailableMessage(reason) {
|
|
138
|
-
switch (reason) {
|
|
139
|
-
case "no-permission":
|
|
140
|
-
return `semantic search couldn't write its model cache at ${modelCacheDir()} \u2014 using fast lexical search instead. Relocate it with the standard XDG_CACHE_HOME, or run with --local to stay lexical quietly. (No admin/sudo is ever needed \u2014 the cache lives in your home folder.)`;
|
|
141
|
-
case "download-failed":
|
|
142
|
-
return `the semantic model isn't downloaded yet and couldn't be fetched (offline?) \u2014 using fast lexical search. It'll try again next time; run with --local to keep lexical without retrying.`;
|
|
143
|
-
case "not-installed":
|
|
144
|
-
return `the optional semantic backend isn't available here \u2014 using fast lexical search.`;
|
|
145
|
-
case "init-failed":
|
|
146
|
-
default:
|
|
147
|
-
return `the semantic model couldn't load \u2014 using fast lexical search.`;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
function modelCacheInfo(modelId = resolveEmbedModel()) {
|
|
151
|
-
const dir = modelCacheDir();
|
|
152
|
-
return { dir, present: isModelReady(modelId) || fs2.existsSync(dir), bytes: dirSize(dir) };
|
|
153
|
-
}
|
|
154
|
-
function clearModelCache() {
|
|
155
|
-
const dir = modelCacheDir();
|
|
156
|
-
const bytes = dirSize(dir);
|
|
157
|
-
try {
|
|
158
|
-
fs2.rmSync(dir, { recursive: true, force: true });
|
|
159
|
-
} catch {
|
|
160
|
-
}
|
|
161
|
-
return bytes;
|
|
162
|
-
}
|
|
163
|
-
function dirSize(dir) {
|
|
164
|
-
let total = 0;
|
|
165
|
-
let entries;
|
|
166
|
-
try {
|
|
167
|
-
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
168
|
-
} catch {
|
|
169
|
-
return 0;
|
|
170
|
-
}
|
|
171
|
-
for (const e of entries) {
|
|
172
|
-
const p = path2.join(dir, e.name);
|
|
173
|
-
if (e.isDirectory()) total += dirSize(p);
|
|
174
|
-
else {
|
|
175
|
-
try {
|
|
176
|
-
total += fs2.statSync(p).size;
|
|
177
|
-
} catch {
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return total;
|
|
182
|
-
}
|
|
183
|
-
function isPermissionError(e) {
|
|
184
|
-
const code = e?.code;
|
|
185
|
-
return code === "EACCES" || code === "EPERM" || code === "EROFS";
|
|
186
|
-
}
|
|
187
|
-
function embeddingsCached(root, modelId) {
|
|
188
|
-
return fs2.existsSync(path2.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));
|
|
189
|
-
}
|
|
190
|
-
async function loadEmbedder(options = {}) {
|
|
191
|
-
if (options.local) return null;
|
|
192
|
-
const modelId = resolveEmbedModel(options.model);
|
|
193
|
-
if (options.noDownload && !isModelReady(modelId)) return null;
|
|
194
|
-
const fail = (reason) => {
|
|
195
|
-
options.onUnavailable?.(reason);
|
|
196
|
-
return null;
|
|
197
|
-
};
|
|
198
|
-
const mod = await importEmbedBackend();
|
|
199
|
-
if (!mod?.FlagEmbedding) return fail("not-installed");
|
|
200
|
-
const cache = modelCacheDir();
|
|
201
|
-
try {
|
|
202
|
-
fs2.mkdirSync(cache, { recursive: true });
|
|
203
|
-
} catch (e) {
|
|
204
|
-
return fail(isPermissionError(e) ? "no-permission" : "init-failed");
|
|
205
|
-
}
|
|
206
|
-
try {
|
|
207
|
-
const model = await withTimeout(
|
|
208
|
-
mod.FlagEmbedding.init({
|
|
209
|
-
model: mapModel(mod, modelId),
|
|
210
|
-
cacheDir: cache,
|
|
211
|
-
showDownloadProgress: options.showDownloadProgress ?? false
|
|
212
|
-
}),
|
|
213
|
-
embedInitTimeoutMs(),
|
|
214
|
-
"embedding model init timed out"
|
|
215
|
-
);
|
|
216
|
-
try {
|
|
217
|
-
fs2.writeFileSync(modelReadyMarker(modelId), (/* @__PURE__ */ new Date(0)).toISOString());
|
|
218
|
-
} catch {
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
id: modelId,
|
|
222
|
-
async embed(texts) {
|
|
223
|
-
const out = [];
|
|
224
|
-
for await (const batch of model.embed(texts, 64)) {
|
|
225
|
-
for (const v of batch) out.push(Array.from(v));
|
|
226
|
-
}
|
|
227
|
-
return out;
|
|
228
|
-
},
|
|
229
|
-
async embedQuery(text) {
|
|
230
|
-
const v = await model.queryEmbed(text);
|
|
231
|
-
return Array.from(v);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
} catch (e) {
|
|
235
|
-
return fail(isPermissionError(e) ? "no-permission" : "download-failed");
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async function importEmbedBackend() {
|
|
239
|
-
const hostDir = process.env.VIBGRATE_EMBEDDER_PATH;
|
|
240
|
-
if (hostDir) {
|
|
241
|
-
try {
|
|
242
|
-
const req = createRequire(path2.join(hostDir, "package.json"));
|
|
243
|
-
const mod = await import(pathToFileURL(req.resolve("fastembed")).href);
|
|
244
|
-
const resolved = mod?.FlagEmbedding ? mod : mod?.default;
|
|
245
|
-
if (resolved?.FlagEmbedding) return resolved;
|
|
246
|
-
} catch {
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return await import('fastembed').catch(() => null);
|
|
250
|
-
}
|
|
251
|
-
function embedInitTimeoutMs() {
|
|
252
|
-
const n = Number(process.env.VG_EMBED_TIMEOUT_MS);
|
|
253
|
-
return Number.isFinite(n) && n > 0 ? n : 6e4;
|
|
254
|
-
}
|
|
255
|
-
async function withTimeout(promise, ms, message) {
|
|
256
|
-
let timer;
|
|
257
|
-
try {
|
|
258
|
-
return await Promise.race([
|
|
259
|
-
promise,
|
|
260
|
-
new Promise((_, reject) => {
|
|
261
|
-
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
262
|
-
timer.unref?.();
|
|
263
|
-
})
|
|
264
|
-
]);
|
|
265
|
-
} finally {
|
|
266
|
-
clearTimeout(timer);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
function mapModel(mod, id) {
|
|
270
|
-
const m = mod.EmbeddingModel ?? {};
|
|
271
|
-
if (/bge-small/i.test(id)) return m.BGESmallENV15 ?? m.BGESmallEN ?? id;
|
|
272
|
-
if (/bge-base/i.test(id)) return m.BGEBaseENV15 ?? id;
|
|
273
|
-
if (/all-minilm/i.test(id)) return m.AllMiniLML6V2 ?? id;
|
|
274
|
-
return id;
|
|
275
|
-
}
|
|
276
|
-
var PATH_NOISE = /* @__PURE__ */ new Set([
|
|
277
|
-
"src",
|
|
278
|
-
"app",
|
|
279
|
-
"apps",
|
|
280
|
-
"lib",
|
|
281
|
-
"libs",
|
|
282
|
-
"dist",
|
|
283
|
-
"build",
|
|
284
|
-
"out",
|
|
285
|
-
"index",
|
|
286
|
-
"main",
|
|
287
|
-
"packages",
|
|
288
|
-
"pkg",
|
|
289
|
-
"test",
|
|
290
|
-
"tests",
|
|
291
|
-
"__tests__",
|
|
292
|
-
"spec",
|
|
293
|
-
"node_modules",
|
|
294
|
-
"internal",
|
|
295
|
-
"com",
|
|
296
|
-
"example",
|
|
297
|
-
"demo",
|
|
298
|
-
"java",
|
|
299
|
-
"net",
|
|
300
|
-
"org",
|
|
301
|
-
"www"
|
|
302
|
-
]);
|
|
303
|
-
function pathContext(file) {
|
|
304
|
-
const parts = file.split("/");
|
|
305
|
-
const stem = (parts.pop() ?? "").replace(/\.[^.]+$/, "");
|
|
306
|
-
const dirs = parts.filter((p) => p && !PATH_NOISE.has(p.toLowerCase()));
|
|
307
|
-
return [...dirs.slice(-2), stem].filter(Boolean).join(" ");
|
|
308
|
-
}
|
|
309
|
-
function nodeEmbedText(node, areaLabel) {
|
|
310
|
-
if (node.kind === "document") {
|
|
311
|
-
const category = (node.signature ?? "document").replace(/^document:/, "");
|
|
312
|
-
const categoryHints = {
|
|
313
|
-
manifest: "package dependencies scripts install dependencies versions",
|
|
314
|
-
docker: "dockerfile container image build runtime",
|
|
315
|
-
compose: "docker-compose services containers networking volumes",
|
|
316
|
-
ci: "continuous integration github actions workflow pipeline test build deploy",
|
|
317
|
-
"build-config": "typescript tsconfig bundler vite webpack eslint prettier test",
|
|
318
|
-
"api-contract": "openapi swagger graphql protobuf api schema endpoints",
|
|
319
|
-
"task-runner": "makefile justfile tasks build test run commands",
|
|
320
|
-
workspace: "monorepo workspace packages pnpm turbo nx",
|
|
321
|
-
infra: "deploy infrastructure kubernetes kubernetes terraform helm kubernetes",
|
|
322
|
-
"env-example": "environment variables configuration secrets template",
|
|
323
|
-
markdown: "documentation readme guide"
|
|
324
|
-
};
|
|
325
|
-
const hint = categoryHints[category] ?? category;
|
|
326
|
-
return [
|
|
327
|
-
node.qualifiedName,
|
|
328
|
-
"document",
|
|
329
|
-
category,
|
|
330
|
-
hint,
|
|
331
|
-
pathContext(node.file),
|
|
332
|
-
(node.doc ?? "").slice(0, 5e3)
|
|
333
|
-
].filter(Boolean).join("\n");
|
|
334
|
-
}
|
|
335
|
-
return [node.qualifiedName, node.kind, node.signature ?? "", node.doc ?? "", pathContext(node.file), areaLabel ?? ""].filter(Boolean).join(" ");
|
|
336
|
-
}
|
|
337
|
-
function cosine(a, b) {
|
|
338
|
-
let dot = 0;
|
|
339
|
-
let na = 0;
|
|
340
|
-
let nb = 0;
|
|
341
|
-
const n = Math.min(a.length, b.length);
|
|
342
|
-
for (let i = 0; i < n; i++) {
|
|
343
|
-
dot += a[i] * b[i];
|
|
344
|
-
na += a[i] * a[i];
|
|
345
|
-
nb += b[i] * b[i];
|
|
346
|
-
}
|
|
347
|
-
if (na === 0 || nb === 0) return 0;
|
|
348
|
-
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
349
|
-
}
|
|
350
|
-
var EMBED_CHUNK = 256;
|
|
351
|
-
var CACHE_WRITE_INTERVAL_MS = 1500;
|
|
352
|
-
function vectorCachePath(root, modelId) {
|
|
353
|
-
return path2.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);
|
|
354
|
-
}
|
|
355
|
-
function countPending(graph, root, modelId) {
|
|
356
|
-
const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);
|
|
357
|
-
const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
|
|
358
|
-
let pending = 0;
|
|
359
|
-
for (const n of graph.nodes) {
|
|
360
|
-
if (n.kind === "file" || n.kind === "external") continue;
|
|
361
|
-
const h = hashString(nodeEmbedText(n, areaLabel.get(n.area)));
|
|
362
|
-
const cached = entries[n.id];
|
|
363
|
-
if (!cached || cached.hash !== h) pending++;
|
|
364
|
-
}
|
|
365
|
-
return pending;
|
|
366
|
-
}
|
|
367
|
-
function readCacheEntries(file, modelId) {
|
|
368
|
-
if (!fs2.existsSync(file)) return {};
|
|
369
|
-
try {
|
|
370
|
-
const loaded = JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
371
|
-
if (loaded.model === modelId && loaded.entries) return loaded.entries;
|
|
372
|
-
} catch {
|
|
373
|
-
}
|
|
374
|
-
return {};
|
|
375
|
-
}
|
|
376
|
-
async function getNodeEmbeddings(graph, embedder, root, onProgress) {
|
|
377
|
-
const file = vectorCachePath(root, embedder.id);
|
|
378
|
-
let cache = { model: embedder.id, entries: {} };
|
|
379
|
-
if (fs2.existsSync(file)) {
|
|
380
|
-
try {
|
|
381
|
-
const loaded = JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
382
|
-
if (loaded.model === embedder.id && loaded.entries) cache = loaded;
|
|
383
|
-
} catch {
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
|
|
387
|
-
const targets = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external");
|
|
388
|
-
const toEmbed = [];
|
|
389
|
-
const vectors = /* @__PURE__ */ new Map();
|
|
390
|
-
for (const n of targets) {
|
|
391
|
-
const text = nodeEmbedText(n, areaLabel.get(n.area));
|
|
392
|
-
const h = hashString(text);
|
|
393
|
-
const cached = cache.entries[n.id];
|
|
394
|
-
if (cached && cached.hash === h) vectors.set(n.id, cached.vec);
|
|
395
|
-
else toEmbed.push({ id: n.id, text, hash: h });
|
|
396
|
-
}
|
|
397
|
-
const persist = () => {
|
|
398
|
-
try {
|
|
399
|
-
fs2.mkdirSync(cacheDir(root), { recursive: true });
|
|
400
|
-
const tmp = `${file}.tmp.${process.pid}`;
|
|
401
|
-
fs2.writeFileSync(tmp, JSON.stringify(cache));
|
|
402
|
-
fs2.renameSync(tmp, file);
|
|
403
|
-
} catch {
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
if (toEmbed.length) {
|
|
407
|
-
const lock = `${file}.lock`;
|
|
408
|
-
if (!acquireLock(lock)) return vectors;
|
|
409
|
-
try {
|
|
410
|
-
onProgress?.(0, toEmbed.length);
|
|
411
|
-
let lastWrite = Date.now();
|
|
412
|
-
for (let i = 0; i < toEmbed.length; i += EMBED_CHUNK) {
|
|
413
|
-
const slice = toEmbed.slice(i, i + EMBED_CHUNK);
|
|
414
|
-
const vecs = await embedder.embed(slice.map((t) => t.text));
|
|
415
|
-
slice.forEach((t, j) => {
|
|
416
|
-
const vec = vecs[j] ?? [];
|
|
417
|
-
vectors.set(t.id, vec);
|
|
418
|
-
cache.entries[t.id] = { hash: t.hash, vec };
|
|
419
|
-
});
|
|
420
|
-
onProgress?.(Math.min(i + EMBED_CHUNK, toEmbed.length), toEmbed.length);
|
|
421
|
-
if (Date.now() - lastWrite >= CACHE_WRITE_INTERVAL_MS) {
|
|
422
|
-
persist();
|
|
423
|
-
lastWrite = Date.now();
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
const live = new Set(targets.map((n) => n.id));
|
|
427
|
-
for (const id of Object.keys(cache.entries)) if (!live.has(id)) delete cache.entries[id];
|
|
428
|
-
persist();
|
|
429
|
-
} finally {
|
|
430
|
-
releaseLock(lock);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return vectors;
|
|
434
|
-
}
|
|
435
|
-
function safe(id) {
|
|
436
|
-
return id.replace(/[^a-z0-9.-]+/gi, "_");
|
|
437
|
-
}
|
|
438
70
|
|
|
439
71
|
// src/engine/query.ts
|
|
440
72
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
@@ -960,6 +592,6 @@ function round2(x) {
|
|
|
960
592
|
return Math.round(x * 1e6) / 1e6;
|
|
961
593
|
}
|
|
962
594
|
|
|
963
|
-
export { GraphIndex,
|
|
964
|
-
//# sourceMappingURL=chunk-
|
|
965
|
-
//# sourceMappingURL=chunk-
|
|
595
|
+
export { GraphIndex, conceptMapLines, extractLiteralNeedles, identifierParts, impactOf, indexFor, isLocateOnlyInstruction, queryGraph, queryGraphSemantic, residualForSymbolSearch };
|
|
596
|
+
//# sourceMappingURL=chunk-TVRM57CE.js.map
|
|
597
|
+
//# sourceMappingURL=chunk-TVRM57CE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engine/relations.ts","../src/engine/query.ts","../src/engine/impact.ts"],"names":["push","text","round"],"mappings":"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","file":"chunk-TVRM57CE.js","sourcesContent":["import type { EdgeKind, GraphEdge, GraphNode, VgGraph } from '../schema.js';\n\n/** Indexes over a graph for O(1) neighbor lookups (built once per command). */\nexport class GraphIndex {\n readonly nodeById = new Map<string, GraphNode>();\n private outById = new Map<string, GraphEdge[]>();\n private inById = new Map<string, GraphEdge[]>();\n\n constructor(readonly graph: VgGraph) {\n for (const n of graph.nodes) this.nodeById.set(n.id, n);\n for (const e of graph.edges) {\n push(this.outById, e.src, e);\n push(this.inById, e.dst, e);\n }\n }\n\n out(id: string, kind?: EdgeKind): GraphEdge[] {\n const all = this.outById.get(id) ?? [];\n return kind ? all.filter((e) => e.kind === kind) : all;\n }\n\n in(id: string, kind?: EdgeKind): GraphEdge[] {\n const all = this.inById.get(id) ?? [];\n return kind ? all.filter((e) => e.kind === kind) : all;\n }\n\n node(id: string): GraphNode | undefined {\n return this.nodeById.get(id);\n }\n\n /**\n * Resolved nodes called by `id` — invocations (`call`) plus structural\n * dependency references (`references`, e.g. a constructor-injected field's\n * type), since both represent real usage a caller/impact question cares\n * about. `references` is otherwise only emitted by the precise SCIP/tsc\n * rungs and (for Java DI wiring) the heuristic rung — never a guess.\n */\n callees(id: string): { edge: GraphEdge; node: GraphNode }[] {\n return this.resolveTargets(this.out(id).filter(isUsageEdge), 'dst');\n }\n\n /** Resolved nodes that call or structurally reference `id`. */\n callers(id: string): { edge: GraphEdge; node: GraphNode }[] {\n return this.resolveTargets(this.in(id).filter(isUsageEdge), 'src');\n }\n\n private resolveTargets(edges: GraphEdge[], end: 'src' | 'dst') {\n const out: { edge: GraphEdge; node: GraphNode }[] = [];\n for (const e of edges) {\n const node = this.nodeById.get(e[end]);\n if (node) out.push({ edge: e, node });\n }\n return out;\n }\n}\n\nconst USAGE_KINDS: ReadonlySet<EdgeKind> = new Set(['call', 'references']);\nfunction isUsageEdge(e: GraphEdge): boolean {\n return USAGE_KINDS.has(e.kind);\n}\n\nfunction push(map: Map<string, GraphEdge[]>, key: string, value: GraphEdge): void {\n const list = map.get(key);\n if (list) list.push(value);\n else map.set(key, [value]);\n}\n\n/**\n * Memoized `GraphIndex`, keyed on the graph object's identity.\n *\n * The index (node-by-id + in/out adjacency) is a pure function of the graph's\n * content, and every read path rebuilds it from scratch — an O(nodes + edges)\n * pass that, on a large map, costs hundreds of milliseconds *per query*. Under\n * `vg serve` the same parsed graph object is reused across many tool calls\n * (`GraphSource` caches it by file mtime and only replaces the reference when\n * `graph.json` actually changes), so rebuilding the index on every call is pure\n * waste. Keying a `WeakMap` on that object identity builds the index once and\n * reuses it until the file is rebuilt — at which point `GraphSource` hands back a\n * *new* object, this map misses, and the stale index is collected with the old\n * graph. Correct and stale-safe by construction: a different graph is a\n * different object, so there is no cross-graph reuse and nothing to invalidate.\n *\n * `GraphIndex` is read-only (no method mutates its maps), so sharing one\n * instance across callers is safe.\n */\nconst INDEX_CACHE = new WeakMap<VgGraph, GraphIndex>();\n\nexport function indexFor(graph: VgGraph): GraphIndex {\n let idx = INDEX_CACHE.get(graph);\n if (!idx) {\n idx = new GraphIndex(graph);\n INDEX_CACHE.set(graph, idx);\n }\n return idx;\n}\n","import { indexFor, type GraphIndex } from './relations.js';\nimport { cosine, type Embedder } from './embeddings.js';\nimport { WEAK_TERMS, expandConcepts, bigramConsumedTokens } from './concepts.js';\nimport type { RelevanceAnalysis } from './relevance-provider.js';\nimport type { GraphNode, VgGraph } from '../schema.js';\n\n/**\n * Deterministic retrieval for `vg ask` (VG-CLI-SPEC §3.2).\n *\n * Builds a structured, fact-annotated, budget-bounded context block for a\n * question — designed to drop straight into an assistant's context. The default\n * is deterministic lexical+structural retrieval: identifier/term matching with\n * morphological prefix-fuzzing, term-role weighting (process verbs like \"add\"\n * only corroborate, never seed — see engine/concepts.ts), static concept\n * expansion (\"payments\" → stripe/billing/…, \"direct debit\" → sepa/bacs/mandate),\n * and a multi-term coverage bonus, ranked with importance as a mild tiebreaker.\n * `--semantic`/`--deep` adds\n * a hybrid local-embedding pass (`queryGraphSemantic`) that surfaces conceptually\n * related code even when no word is shared — still no API key.\n */\n\nexport interface QueryOptions {\n budget?: number; // approx token budget for the context block (default 2000)\n limit?: number; // max seed matches to expand (default 12)\n /** Optional pre-computed relevance analysis (engine/relevance-provider.ts).\n * Injected by async callers so this module stays pure and sync; when\n * absent, ranking is exactly the built-in lexicon path. */\n relevance?: RelevanceAnalysis | null;\n /** Optional per-node topic tags (engine/relevance-enrich.ts). Combined with\n * `relevance.topics` into a bounded affinity bonus: a node structurally\n * tagged with a topic the question is about ranks above an equal textual\n * match outside it. Never seeds alone — it multiplies existing evidence. */\n topicTags?: Map<string, readonly string[]> | null;\n /** Previous conversational ask (multi-turn `vg code`). Its content terms —\n * never its process verbs — join ranking at a damped weight (CARRY_WEIGHT)\n * so a follow-up like \"can we use direct debits?\" keeps the prior turn's\n * topic (\"where is stripe used?\") instead of ranking the repo on the\n * follow-up's words alone. Absent → behaviour is unchanged. */\n priorQuestion?: string | null;\n}\n\nexport interface QueryMatch {\n node: GraphNode;\n score: number;\n why: string;\n}\n\nexport interface QueryResult {\n question: string;\n matches: QueryMatch[];\n context: string;\n tokensEstimate: number;\n}\n\nconst STOPWORDS = new Set([\n 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'to', 'of', 'in', 'on', 'for', 'and', 'or',\n 'where', 'what', 'which', 'how', 'do', 'does', 'did', 'i', 'we', 'it', 'this', 'that', 'with',\n 'when', 'who', 'why', 'can', 'should', 'my', 'our', 'you', 'your', 'from', 'by', 'at', 'as',\n // Discovery-question scaffolding: words a caller uses to FRAME the ask\n // (\"find the code responsible for X\", \"I need to modify X, where do I\n // start?\", \"explain how X works in this codebase\") rather than to name the\n // target. Left in, these compete on equal footing with the real identifier\n // terms and can outrank it outright — e.g. \"find the code responsible for\n // deleteAsync\" let \"find\" alone drag in every FindByIdAsync/FindAll method\n // in the repo, none of them the target (VG-LOCATE-FAILURE-ANALYSIS.md).\n 'find', 'code', 'responsible', 'need', 'modify', 'me',\n 'implementation', 'explain', 'works', 'codebase', 'contains', 'file',\n // String/URL occurrence scaffolding: \"does not exist\", \"find occurrences of …\"\n // must not light up DoesNot*/NonExisting*/commandExists via weak substrings\n // (field report: https://…/signup does not exist find occurrences).\n 'not', 'no', 'exist', 'exists', 'existing', 'occurrence', 'occurrences',\n 'occurence', 'occurences', 'every', 'place', 'places', 'string', 'literal',\n 'text', 'search', 'locate', 'look', 'looking', 'show', 'list', 'all',\n]);\n\n/**\n * Strip sentence punctuation from a URL match without breaking real query\n * strings (`?key=value`). A lone trailing `?` / `!` is English punctuation\n * (\"where is https://…/signup?\") — leaving it in the needle makes the literal\n * sweep miss the bare path in source.\n */\nfunction cleanUrlNeedle(raw: string): string {\n let s = raw.trim();\n // Trailing ) ] } > , ; : ! . from markdown links / sentence ends.\n s = s.replace(/[.,;:!)\\]}>]+$/g, '');\n // Lone trailing `?` → drop. Keep `?key=…` (content after `?`).\n if (s.endsWith('?')) {\n const qi = s.lastIndexOf('?');\n if (qi === s.length - 1) s = s.slice(0, -1);\n }\n return s;\n}\n\n/**\n * Extract string/URL needles a caller is asking to *locate as text*, not as\n * bag-of-words symbol terms. URLs and quoted phrases are exact-match material\n * for the literal sweep (`search_symbols` / `search_code`); feeding their path\n * segments into lexical symbol ranking produces confidently wrong seeds\n * (dash → dashboard package, exist → NonExisting, …).\n */\nexport function extractLiteralNeedles(question: string): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const push = (raw: string, kind: 'url' | 'quote') => {\n let s = raw.trim();\n s = kind === 'url' ? cleanUrlNeedle(s) : s.replace(/[.,;:!)\\]}>?]+$/g, '');\n if (s.length < 2 || seen.has(s)) return;\n seen.add(s);\n out.push(s);\n };\n for (const m of question.matchAll(/https?:\\/\\/[^\\s\"'`<>]+/gi)) push(m[0]!, 'url');\n for (const m of question.matchAll(/([\"'`])((?:(?!\\1)[^\\\\]|\\\\.){2,})\\1/g)) {\n push(m[2]!.replace(/\\\\(.)/g, '$1'), 'quote');\n }\n return out;\n}\n\n/** Question text with URL/quoted needles removed so residual tokens can name symbols. */\nexport function stripLiteralNeedles(question: string): string {\n let s = question;\n // Strip raw URL spans first (may still carry a trailing `?` from the ask).\n for (const m of question.matchAll(/https?:\\/\\/[^\\s\"'`<>]+/gi)) {\n s = s.split(m[0]!).join(' ');\n }\n for (const lit of extractLiteralNeedles(question)) {\n s = s.split(lit).join(' ');\n }\n // Also strip bare quote pairs left behind.\n s = s.replace(/([\"'`])\\s*\\1/g, ' ');\n return s.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Residual ask used for graph symbol ranking when the question embeds a\n * URL/quote. Empty when only framing words remain (\"where is …?\") so we do\n * not light up unrelated symbols like `applyPlan.where`.\n */\nexport function residualForSymbolSearch(question: string): string {\n const literals = extractLiteralNeedles(question);\n if (literals.length === 0) return question.trim();\n const stripped = stripLiteralNeedles(question);\n return tokenize(stripped).length === 0 ? '' : stripped;\n}\n\n/**\n * True when the instruction is a string/URL occurrence locate (not an edit).\n * Used to skip PatchIR grammar and answer from the literal sweep without\n * dumping constrained-decoding JSON into the VG Code panel.\n */\nexport function isLocateOnlyInstruction(instruction: string): boolean {\n const q = instruction.trim();\n if (!q) return false;\n const needles = extractLiteralNeedles(q);\n const hasLocateFrame =\n /\\b(where\\s+is|where\\s+are|find\\s+(all\\s+|every\\s+)?occurrence|find\\s+occurrences|does\\s+not\\s+exist|locate|search\\s+for)\\b/i.test(\n q,\n ) ||\n (needles.length > 0 && /^https?:\\/\\//i.test(q));\n // Strong edit verbs cancel locate-only (ignore \"does not exist\" as non-edit).\n const withoutExist = q.replace(/\\bdoes\\s+not\\s+exist\\b/gi, ' ');\n if (\n /\\b(fix|edit|change|replace|implement|refactor|rename|patch|create\\s+file|delete\\s+file|add\\s+a\\b|remove\\s+the\\b)\\b/i.test(\n withoutExist,\n )\n ) {\n return false;\n }\n if (needles.some((n) => /^https?:\\/\\//i.test(n))) return true;\n return hasLocateFrame && needles.length > 0;\n}\n\nexport function queryGraph(graph: VgGraph, question: string, options: QueryOptions = {}): QueryResult {\n const budget = options.budget ?? 2000;\n const limit = options.limit ?? 12;\n // When the ask embeds a URL or quoted string, score symbols only on the\n // residual framing text. Empty residual → empty matches (honest miss) rather\n // than path-token false positives that poison Task Capsules.\n const literals = extractLiteralNeedles(question);\n const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;\n const prep = prepareTerms(forTokens, options.relevance, priorResidual(options.priorQuestion));\n const weightOf = termWeights(graph, prep.terms.map((t) => t.term));\n const index = indexFor(graph);\n\n const scored: QueryMatch[] = [];\n // Two honest-miss paths: literal-only locate (no identifier terms after\n // stripping needles) and weak-only asks (\"add a new feature\") whose every\n // term is a process verb — seeding those from `add*`/`create*` surface\n // matches is exactly the capsule-poisoning failure (field report 2026-07).\n if (prep.hasContent) {\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n const { score, why } = scoreNode(node, prep.terms, weightOf);\n if (score > 0) {\n const affinity = topicAffinity(node.id, options.relevance, options.topicTags);\n scored.push({\n node,\n score: round(score * affinity.boost * (1 + IMPORTANCE_WEIGHT * node.importance)),\n why: why + affinity.label,\n });\n }\n }\n scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));\n }\n\n const seeds = scored.slice(0, limit);\n const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);\n\n return { question, matches: seeds, context, tokensEstimate };\n}\n\nexport interface SemanticQueryOptions extends QueryOptions {\n embedder: Embedder;\n /** Precomputed node vectors (from getNodeEmbeddings); falls back to lexical for nodes without one. */\n nodeVectors: Map<string, number[]>;\n}\n\n/**\n * Reciprocal Rank Fusion constant (the standard k=60). Fusing RANKINGS\n * instead of blending raw scores removes the need to calibrate two\n * incomparable scales (tiered lexical evidence vs cosine similarity): a node's\n * fused score is Σ 1/(k + rankᵢ) over the lists it appears in, so agreement\n * between signals dominates and a single signal's outlier magnitude cannot.\n */\nconst RRF_K = 60;\n\n/** 1-based ranks for a scored id list (desc score, qualifiedName tiebreak). */\nfunction ranksOf(scores: Map<string, number>, nameOf: (id: string) => string): Map<string, number> {\n const ids = [...scores.entries()]\n .filter(([, s]) => s > 0)\n .sort((a, b) => b[1] - a[1] || nameOf(a[0]).localeCompare(nameOf(b[0])))\n .map(([id]) => id);\n return new Map(ids.map((id, i) => [id, i + 1]));\n}\n\n/**\n * Hybrid lexical + local-embedding retrieval, combined with Reciprocal Rank\n * Fusion (Phase 3.2 — replaces the former 50/50 score blend), so a question\n * like \"where do we handle auth failures?\" can surface `verify_token` even\n * with no shared identifier. Topic affinity and importance stay multiplicative\n * tiebreakers on the fused score. Deterministic given the same model + cached\n * vectors; embeddings live only in the cache, never in `graph.json`.\n */\nexport async function queryGraphSemantic(\n graph: VgGraph,\n question: string,\n options: SemanticQueryOptions,\n): Promise<QueryResult> {\n const budget = options.budget ?? 2000;\n const limit = options.limit ?? 12;\n const literals = extractLiteralNeedles(question);\n const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;\n const prep = prepareTerms(forTokens, options.relevance, priorResidual(options.priorQuestion));\n const terms = prep.terms.map((t) => t.term);\n const weightOf = termWeights(graph, terms);\n const index = indexFor(graph);\n // Embed the residual ask (without URL/quote noise) so semantic neighbours\n // aren't dragged toward package names that share host path segments.\n const queryVec = await options.embedder.embedQuery(forTokens || question);\n\n // Raw lexical scores (pre-importance) — RRF consumes only their ORDER.\n // Weak-only asks get no lexical evidence (same suppression as queryGraph);\n // the semantic cosine below may still surface conceptual neighbours for them.\n const lexRaw = new Map<string, number>();\n const whyById = new Map<string, string>();\n if (prep.hasContent) {\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n const { score, why } = scoreNode(node, prep.terms, weightOf);\n lexRaw.set(node.id, score);\n whyById.set(node.id, why);\n }\n }\n\n const scored: QueryMatch[] = [];\n // Literal-only asks: skip the semantic loop entirely — embeddings over\n // \"does not exist find occurrences\" still surface unrelated hubs.\n if (terms.length > 0 || literals.length === 0) {\n // Independent per-signal rankings, then RRF.\n const semRaw = new Map<string, number>();\n const nodeById = new Map<string, GraphNode>();\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n nodeById.set(node.id, node);\n const vec = options.nodeVectors.get(node.id);\n const sem = vec && terms.length > 0 ? Math.max(0, cosine(queryVec, vec)) : 0;\n if (sem > 0) semRaw.set(node.id, sem);\n }\n const nameOf = (id: string) => nodeById.get(id)?.qualifiedName ?? id;\n const lexRanks = ranksOf(lexRaw, nameOf);\n const semRanks = ranksOf(semRaw, nameOf);\n for (const [id, node] of nodeById) {\n const lr = lexRanks.get(id);\n const sr = semRanks.get(id);\n if (lr === undefined && sr === undefined) continue;\n const fused = (lr !== undefined ? 1 / (RRF_K + lr) : 0) + (sr !== undefined ? 1 / (RRF_K + sr) : 0);\n const sem = semRaw.get(id) ?? 0;\n const lexWhy = whyById.get(id);\n const affinity = topicAffinity(id, options.relevance, options.topicTags);\n const why = (lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : 'weak match')) + affinity.label;\n scored.push({ node, score: round(fused * affinity.boost * (1 + IMPORTANCE_WEIGHT * node.importance)), why });\n }\n scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));\n }\n\n const seeds = scored.slice(0, limit);\n const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);\n return { question, matches: seeds, context, tokensEstimate };\n}\n\n/**\n * Split on anything that isn't a Unicode letter/number (`\\p{L}\\p{N}`, not the\n * ASCII-only `[a-z0-9]`) so identifiers in non-Latin scripts (Japanese,\n * Cyrillic, ...) survive tokenization instead of vanishing entirely — the\n * ASCII-only split treated every character of e.g. `名前` as a separator,\n * producing zero terms and a guaranteed empty result for any question about\n * it (VG-LOCATE-FAILURE-ANALYSIS.md). Length floor is 1, not 2: a single\n * non-stopword letter/digit is still the whole identifier when that's genuinely\n * the symbol's name (adversarial fixtures deliberately use bare `f`/`x`/`h`\n * names to stress this) — STOPWORDS already screens out short function words.\n */\nfunction tokenize(q: string): string[] {\n return [\n ...new Set(\n q\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((t) => t.length >= 1 && !STOPWORDS.has(t)),\n ),\n ];\n}\n\n/** Ordered, lowercased token stream WITH stopwords — bigram concepts (\"sign in\",\n * \"direct debit\") need adjacency over the raw word order. */\nfunction tokenizeOrdered(q: string): string[] {\n return q\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((t) => t.length >= 1);\n}\n\n/**\n * A query term with its role resolved. Roles decide how much evidence a match\n * carries (see scoreNode):\n * - content — names the topic; full weight, counts toward coverage\n * - weak — process verb (\"add\", \"create\", \"via\"); down-weighted, can only\n * corroborate a content match, never carry a seed alone\n * - expansion — concept vocabulary from the lexicon (\"payments\"→\"stripe\");\n * reduced weight, counts as content evidence, provenance in `why`\n */\ninterface QueryTerm {\n term: string;\n weak: boolean;\n /** Source token/bigram when this term came from concept expansion. */\n from?: string;\n /** Provider-supplied confidence (0..1) when this term came from the optional\n * relevance provider; replaces the flat EXPANSION_WEIGHT for that term. */\n providerWeight?: number;\n /** True when this term was carried over from the previous conversational\n * turn (QueryOptions.priorQuestion); scores at CARRY_WEIGHT. */\n carried?: boolean;\n}\n\ninterface PreparedTerms {\n terms: QueryTerm[];\n /** True when at least one content or expansion term exists — the bar a\n * question must clear before ANY node may seed. */\n hasContent: boolean;\n}\n\n/** Weak process verbs corroborate at a fraction of a content term's weight. */\nconst WEAK_TERM_WEIGHT = 0.25;\n/** Concept expansions score below a literal question term of the same rarity. */\nconst EXPANSION_WEIGHT = 0.7;\n/** Per extra distinct content term matched, the multiplicative coverage bonus.\n * Multi-topic evidence (\"direct\" + \"debit\" + \"payments\") must beat any\n * single-term surface hit (\"add\") by construction, not by luck. */\nconst COVERAGE_BONUS = 0.35;\n/** Terms carried from the previous conversational turn score at half weight:\n * enough to keep a follow-up anchored to the conversation's topic (and to\n * rescue weak-only follow-ups like \"can we use it?\"), never enough for stale\n * context to outrank a clearly-named new topic in the current ask. */\nconst CARRY_WEIGHT = 0.5;\n\n/** Resolve term roles + concept expansions for the residual ask text. */\nfunction prepareTerms(forTokens: string, relevance?: RelevanceAnalysis | null, prior?: string | null): PreparedTerms {\n const base = tokenize(forTokens);\n const ordered = tokenizeOrdered(forTokens);\n const baseSet = new Set(base);\n // A token consumed by a fired bigram concept (\"direct\" in \"direct debits\")\n // is demoted to the weak role: the bigram's expansions carry the topic, and\n // the constituent alone is a distractor magnet (directGet, redirectUrl, …).\n const consumed = bigramConsumedTokens(ordered);\n const terms: QueryTerm[] = base.map((t) => ({ term: t, weak: WEAK_TERMS.has(t) || consumed.has(t) }));\n const have = new Set(base);\n for (const e of expandConcepts(ordered)) {\n if (baseSet.has(e.term)) continue;\n terms.push({ term: e.term, weak: false, from: e.from });\n have.add(e.term);\n }\n // Provider expansions (already sanitized by engine/relevance-provider.ts)\n // join as expansion-role terms with their own confidence weight. The\n // built-in lexicon wins ties: a term it already produced is not re-added,\n // so provider presence never changes the score of a lexicon-covered term.\n for (const e of relevance?.expansions ?? []) {\n if (have.has(e.term)) continue;\n terms.push({ term: e.term, weak: false, from: e.from, providerWeight: e.weight });\n have.add(e.term);\n }\n // Conversation carry-over: the previous turn's CONTENT terms (plus their\n // lexicon expansions) join at CARRY_WEIGHT. Weak process verbs from the\n // prior turn are dropped outright — carrying \"add\"/\"use\" forward would\n // re-open the grab-bag failure this module exists to prevent. A term the\n // current ask already produced is never re-added, so the current turn\n // always wins ties and single-turn behaviour is byte-identical.\n if (prior?.trim()) {\n const priorOrdered = tokenizeOrdered(prior);\n const priorConsumed = bigramConsumedTokens(priorOrdered);\n for (const t of tokenize(prior)) {\n if (have.has(t) || WEAK_TERMS.has(t) || priorConsumed.has(t)) continue;\n terms.push({ term: t, weak: false, carried: true });\n have.add(t);\n }\n for (const e of expandConcepts(priorOrdered)) {\n if (have.has(e.term)) continue;\n terms.push({ term: e.term, weak: false, from: e.from, carried: true });\n have.add(e.term);\n }\n }\n const hasContent = terms.some((t) => !t.weak);\n return { terms, hasContent };\n}\n\n/** Residual text of the previous ask, with URL/quoted needles stripped so a\n * pasted link in turn N-1 cannot poison turn N's carried vocabulary. */\nfunction priorResidual(prior: string | null | undefined): string | null {\n const raw = prior?.trim();\n if (!raw) return null;\n return extractLiteralNeedles(raw).length > 0 ? stripLiteralNeedles(raw) : raw;\n}\n\n/**\n * Plain-language concept map for one ask: how its words were interpreted —\n * fired concept/bigram expansions, relevance-kernel topics and vocabulary,\n * and terms carried from the previous conversational turn. Rendered into the\n * Task Capsule so a model (especially a small local one) can read the\n * inference chain (\"direct debits\" → debit/mandate/…; topic payments →\n * stripe/billing/…) as sentences instead of reverse-engineering the `a→b`\n * slugs in per-seed match provenance. Deterministic and bounded; empty when\n * nothing fired (honest-empty asks stay empty).\n */\nexport function conceptMapLines(\n question: string,\n relevance?: RelevanceAnalysis | null,\n priorQuestion?: string | null,\n): string[] {\n const cap = (xs: string[]) => [...new Set(xs)].slice(0, 8).join(', ');\n const literals = extractLiteralNeedles(question);\n const residual = literals.length > 0 ? stripLiteralNeedles(question) : question;\n const lines: string[] = [];\n\n // Lexicon / bigram expansions, grouped by the ask term that fired them.\n const byFrom = new Map<string, string[]>();\n for (const e of expandConcepts(tokenizeOrdered(residual))) {\n const list = byFrom.get(e.from) ?? [];\n list.push(e.term);\n byFrom.set(e.from, list);\n }\n for (const [from, terms] of byFrom) {\n lines.push(`- \"${from}\" in the ask implies these codebase identifiers: ${cap(terms)}.`);\n }\n\n // Relevance-kernel topics + their vocabulary (provider seam, when active).\n if (relevance?.topics?.length) {\n const topicVocab = new Map<string, string[]>();\n for (const e of relevance.expansions ?? []) {\n const list = topicVocab.get(e.from) ?? [];\n list.push(e.term);\n topicVocab.set(e.from, list);\n }\n for (const t of relevance.topics.slice(0, 3)) {\n const vocab = topicVocab.get(t.id);\n lines.push(\n vocab?.length\n ? `- The ask is about the \"${t.id}\" domain; code for it typically uses: ${cap(vocab)}.`\n : `- The ask is about the \"${t.id}\" domain.`,\n );\n }\n }\n\n // Conversation carry-over provenance.\n const priorText = priorResidual(priorQuestion);\n if (priorText) {\n const current = new Set(tokenize(residual));\n const consumed = bigramConsumedTokens(tokenizeOrdered(priorText));\n const carried = tokenize(priorText).filter((t) => !current.has(t) && !WEAK_TERMS.has(t) && !consumed.has(t));\n if (carried.length) {\n lines.push(`- This is a follow-up; topic words carried from the previous ask: ${cap(carried)}.`);\n }\n }\n\n if (lines.length) {\n lines.push(\n '- Seed notation below: `a→b` = ask term \"a\" implied identifier \"b\"; `~t` = word-form match; `↩t` = carried from the previous ask.',\n );\n }\n return lines;\n}\n\nfunction scoreNode(node: GraphNode, terms: QueryTerm[], weightOf: (t: string) => number = () => 1): { score: number; why: string } {\n let score = 0;\n let contentHits = 0;\n const hits: string[] = [];\n const name = node.name.toLowerCase();\n const qn = node.qualifiedName.toLowerCase();\n const file = node.file.toLowerCase();\n // Directory segments of the file path: a term naming a whole directory\n // (\"payments\" → src/payments/…) is domain evidence, stronger than an\n // incidental substring somewhere in the path.\n const dirSegments = new Set(file.split('/').slice(0, -1));\n // Split the ORIGINAL name on camelCase / snake_case, then lowercase the parts\n // (splitting must see the capitals, so it happens before lowercasing).\n const nameParts = identifierParts(node.name);\n for (const { term: t, weak, from, providerWeight, carried } of terms) {\n // Each term's contribution is weighted by its specificity (IDF over symbol\n // names): a match on a distinctive term (\"toComparable\", \"layoutFor\") counts\n // for more than a match on a term shared by hundreds of symbols (\"code\",\n // \"get\", \"run\"). Without this, an incidental exact-name hit on a common word\n // in a natural-language question outranked the conceptually-correct symbol,\n // and importance weighting amplified the wrong hit (VG-NAVIGATION trace).\n // Role multipliers stack on top: process verbs corroborate at a fraction,\n // concept expansions score below a literal term of the same rarity.\n // Expansion role multiplier: provider expansions carry their own 0..1\n // confidence (capped at the lexicon's EXPANSION_WEIGHT so an installed\n // provider can widen vocabulary but never outrank the reviewed lexicon's\n // evidence class); lexicon expansions keep the flat EXPANSION_WEIGHT.\n const expansionW = providerWeight !== undefined ? Math.min(providerWeight, EXPANSION_WEIGHT) : from ? EXPANSION_WEIGHT : 1;\n const w = weightOf(t) * (weak ? WEAK_TERM_WEIGHT : 1) * expansionW * (carried ? CARRY_WEIGHT : 1);\n const label = (carried ? '↩' : '') + (from ? `${from}→${t}` : t);\n let contribution = 0;\n if (name === t) {\n contribution = 10 * w;\n hits.push(label);\n } else if (nameParts.has(t)) {\n contribution = 6 * w;\n hits.push(label);\n } else if (name.includes(t)) {\n contribution = 4 * w;\n hits.push(label);\n } else if (qn.includes(t)) {\n contribution = 3 * w;\n hits.push(label);\n } else if (fuzzyPartMatch(t, nameParts)) {\n // Morphological / subword match: \"authentication\" ↔ \"authenticate\"\n // (shared prefix), so lexical ask survives word-form differences without\n // a model. The semantic path handles non-shared-root synonyms.\n contribution = 2 * w;\n hits.push((carried ? '↩' : '') + (from ? `~${from}→${t}` : `~${t}`));\n } else if (dirSegments.has(t)) {\n contribution = 2 * w;\n hits.push(label);\n } else if (file.includes(t)) {\n contribution = 1 * w;\n hits.push(label);\n }\n if (contribution > 0) {\n score += contribution;\n if (!weak) contentHits += 1;\n }\n }\n // A node whose only evidence is weak process verbs is noise by definition —\n // the `add`-grab-bag capsule failure. Suppress it outright: weak terms\n // corroborate content matches, they never carry a seed.\n if (contentHits === 0) return { score: 0, why: '' };\n // Reward multi-term topic coverage multiplicatively so converging evidence\n // (\"direct\" + \"debit\" + payments-expansion) dominates single-term hits.\n score *= 1 + COVERAGE_BONUS * Math.min(contentHits - 1, 4);\n return { score, why: hits.length ? `matched: ${hits.join(', ')}` : '' };\n}\n\n/**\n * Importance is a mild tiebreaker, not a doubling. The old `1 + importance`\n * let a hub (importance→1) double its score and outrank a stronger textual\n * match on the actual target; at 0.4 a top hub adds at most 40%, enough to\n * break genuine ties without overriding term evidence.\n */\nconst IMPORTANCE_WEIGHT = 0.4;\n\n/**\n * Topic-affinity bonus cap: a node whose enrichment tags intersect the\n * question's inferred topics gains at most this fraction on top of its\n * textual evidence. Sized between IMPORTANCE_WEIGHT (0.4) and nothing:\n * strong enough to break domain ties (\"direct debits\" → the payments file\n * over an equal-scoring name hit elsewhere), never strong enough to outrank\n * a clearly better textual match — and it multiplies an existing non-zero\n * score, so it can never conjure a seed by itself.\n */\nconst TOPIC_AFFINITY_WEIGHT = 0.3;\n\n/** Affinity multiplier + provenance for one node under the current analysis. */\nfunction topicAffinity(\n nodeId: string,\n relevance: RelevanceAnalysis | null | undefined,\n topicTags: Map<string, readonly string[]> | null | undefined,\n): { boost: number; label: string } {\n if (!relevance?.topics?.length || !topicTags) return { boost: 1, label: '' };\n const tags = topicTags.get(nodeId);\n if (!tags?.length) return { boost: 1, label: '' };\n let affinity = 0;\n const matched: string[] = [];\n for (const t of relevance.topics) {\n if (tags.includes(t.id)) {\n affinity += t.score;\n matched.push(t.id);\n }\n }\n if (affinity <= 0) return { boost: 1, label: '' };\n return { boost: 1 + TOPIC_AFFINITY_WEIGHT * Math.min(affinity, 1), label: `, topic:${matched.sort().join('+')}` };\n}\n\n/**\n * Per-term specificity weights (IDF) for one question, computed over the graph's\n * symbol-name vocabulary: `ln((N+1)/(df+1)) + 1`, clamped to a sane band. A term\n * that appears in one symbol name is highly discriminating; one that appears in\n * hundreds (\"get\", \"code\", \"run\", \"handler\") is near-noise. Clamped so a term\n * matching nothing (huge idf, but it scores 0 anyway) or everything can't\n * distort the scale. Cost is one O(nodes) pass, dwarfed by the scoring loop.\n */\nfunction termWeights(graph: VgGraph, terms: string[]): (t: string) => number {\n if (terms.length === 0) return () => 1;\n const df = new Map<string, number>();\n let n = 0;\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n n++;\n const name = node.name.toLowerCase();\n const parts = identifierParts(node.name);\n for (const t of terms) {\n if (parts.has(t) || name.includes(t)) df.set(t, (df.get(t) ?? 0) + 1);\n }\n }\n const w = new Map<string, number>();\n for (const t of terms) {\n const idf = Math.log((n + 1) / ((df.get(t) ?? 0) + 1)) + 1;\n // Ceiling of 8 (vs a natural df≈40 idf ≈ 5.4) keeps genuinely rare terms\n // dominant while a term matching nothing/only a file path can't distort.\n w.set(t, Math.max(0.5, Math.min(8, idf)));\n }\n return (t: string) => w.get(t) ?? 1;\n}\n\n/**\n * camelCase / snake_case / kebab split of an identifier → lowercased parts.\n * The separator alternative is Unicode-letter-aware (`\\p{L}\\p{N}`, not\n * ASCII-only `a-zA-Z0-9`) so non-Latin identifiers split on punctuation\n * without losing every character to it; the camelCase boundary lookaround\n * stays ASCII-only since upper/lowercase casing is itself an ASCII-script\n * concept — scripts without case simply never trigger it and fall through to\n * the separator split.\n */\nexport function identifierParts(name: string): Set<string> {\n return new Set(\n name\n .split(/[^\\p{L}\\p{N}]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/u)\n .filter(Boolean)\n .map((s) => s.toLowerCase()),\n );\n}\n\n/** A term fuzzily matches a part if they share a long-enough prefix (same root). */\nfunction fuzzyPartMatch(term: string, parts: Set<string>): boolean {\n for (const part of parts) {\n const shared = sharedPrefixLen(term, part);\n if (shared >= 5 && shared >= 0.6 * Math.min(term.length, part.length)) return true;\n }\n return false;\n}\n\nfunction sharedPrefixLen(a: string, b: string): number {\n const n = Math.min(a.length, b.length);\n let i = 0;\n while (i < n && a[i] === b[i]) i++;\n return i;\n}\n\nfunction buildContext(\n graph: VgGraph,\n index: GraphIndex,\n question: string,\n seeds: QueryMatch[],\n budget: number,\n literals: string[] = [],\n): { context: string; tokensEstimate: number } {\n const lines: string[] = [];\n lines.push(`# Context for: ${question}`);\n lines.push('');\n if (literals.length) {\n lines.push('## Literal string(s) to locate (exact text, not symbol names)');\n for (const lit of literals) lines.push(`- \\`${lit}\\``);\n lines.push(\n '_Use a quoted phrase / URL search (`search_symbols` or `search_code` with the exact needle) for occurrences. Graph symbols below (if any) are residual identifier hints only._',\n );\n lines.push('');\n }\n if (seeds.length === 0) {\n lines.push(\n literals.length\n ? '_No graph symbols matched residual terms — this is expected for a pure string/URL occurrence search. Run a literal sweep on the needle(s) above._'\n : '_No matching symbols found in the map. Try different terms, or `vg hubs` for the most important code._',\n );\n const text = lines.join('\\n');\n return { context: text, tokensEstimate: estimateTokens(text) };\n }\n\n for (const { node, why } of seeds) {\n const block: string[] = [];\n block.push(`## ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start})`);\n if (node.signature) block.push('`' + node.signature + '`');\n const callees = index.callees(node.id).map((x) => x.node.qualifiedName);\n const callers = index.callers(node.id).map((x) => x.node.qualifiedName);\n if (callees.length) block.push(`calls: ${unique(callees).slice(0, 8).join(', ')}`);\n if (callers.length) block.push(`called by: ${unique(callers).slice(0, 8).join(', ')}`);\n block.push(`importance ${node.importance.toFixed(3)} · area #${node.area}${node.isHub ? ' · hub' : ''} · ${why}`);\n block.push('');\n\n const candidate = lines.concat(block).join('\\n');\n if (estimateTokens(candidate) > budget && lines.length > 2) break;\n lines.push(...block);\n }\n\n const text = lines.join('\\n');\n return { context: text, tokensEstimate: estimateTokens(text) };\n}\n\nfunction unique(xs: string[]): string[] {\n return [...new Set(xs)];\n}\n\nfunction estimateTokens(text: string): number {\n // ~4 chars per token, the standard rough estimate.\n return Math.ceil(text.length / 4);\n}\n\nfunction round(x: number): number {\n return Math.round(x * 1e6) / 1e6;\n}\n","import { indexFor } from './relations.js';\nimport type { EdgeKind, VgGraph } from '../schema.js';\n\n/**\n * Deterministic reverse-reachability impact analysis (VG-CLI-SPEC §3.5).\n *\n * \"What breaks if `<node>` changes\" = everything that (transitively) depends on\n * it: its callers, importers, and subtypes. We BFS *backwards* along dependency\n * edges, bounded by depth, with a confidence that decays with distance (direct =\n * high; transitive lower; ambiguous/dynamic edges already carry lower edge\n * confidence which we fold in). Test-impact selection (`--tests`) lands in\n * Phase 2; this is the structural blast radius.\n */\n\nconst DEPEND_EDGES = new Set<EdgeKind>(['call', 'import', 'extends', 'implements', 'references']);\n\nexport interface ImpactItem {\n id: string;\n name: string;\n kind: string;\n file: string;\n line: number;\n depth: number;\n confidence: number;\n}\n\nexport interface ImpactResult {\n root: { id: string; name: string };\n depth: number;\n affected: ImpactItem[];\n direct: number;\n transitive: number;\n /** Lowest edge confidence encountered (e.g. a dynamic-dispatch edge). */\n minEdgeConfidence: number;\n}\n\nexport function impactOf(graph: VgGraph, rootId: string, opts: { depth?: number } = {}): ImpactResult {\n const maxDepth = Math.max(1, opts.depth ?? 4);\n const index = indexFor(graph);\n const root = index.node(rootId);\n\n const affected = new Map<string, ImpactItem>();\n let minEdgeConfidence = 1;\n\n // BFS over incoming dependency edges (dependents of the frontier).\n let frontier: { id: string; conf: number }[] = [{ id: rootId, conf: 1 }];\n for (let depth = 1; depth <= maxDepth; depth++) {\n const nextFrontier: { id: string; conf: number }[] = [];\n for (const { id, conf } of frontier) {\n for (const e of index.in(id)) {\n if (!DEPEND_EDGES.has(e.kind)) continue;\n minEdgeConfidence = Math.min(minEdgeConfidence, e.confidence);\n const dependentId = e.src;\n if (dependentId === rootId || affected.has(dependentId)) continue;\n const node = index.node(dependentId);\n if (!node) continue;\n const conf2 = round(conf * e.confidence * 0.9); // decay with distance\n affected.set(dependentId, {\n id: node.id,\n name: node.qualifiedName,\n kind: node.kind,\n file: node.file,\n line: node.span.start,\n depth,\n confidence: conf2,\n });\n nextFrontier.push({ id: dependentId, conf: conf2 });\n }\n }\n frontier = nextFrontier;\n if (frontier.length === 0) break;\n }\n\n const items = [...affected.values()].sort(\n (a, b) => a.depth - b.depth || b.confidence - a.confidence || a.name.localeCompare(b.name),\n );\n\n return {\n root: { id: rootId, name: root?.qualifiedName ?? rootId },\n depth: maxDepth,\n affected: items,\n direct: items.filter((i) => i.depth === 1).length,\n transitive: items.filter((i) => i.depth > 1).length,\n minEdgeConfidence: round(minEdgeConfidence),\n };\n}\n\nfunction round(x: number): number {\n return Math.round(x * 1e6) / 1e6;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveIngestHost } from './chunk-HTLDXEHW.js';
|
|
2
|
-
import { VERSION } from './chunk-
|
|
3
|
-
import { cacheDir } from './chunk-
|
|
2
|
+
import { VERSION } from './chunk-FZ5Y4KEI.js';
|
|
3
|
+
import { cacheDir } from './chunk-RBMKBYGV.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import * as path from 'path';
|
|
@@ -212,5 +212,5 @@ var StatsSharer = class {
|
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
export { StatsSharer, installId, isCI, statsEndpoint, telemetryOptOut };
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-UWVFLVZX.js.map
|
|
216
|
+
//# sourceMappingURL=chunk-UWVFLVZX.js.map
|